mirror of
https://github.com/KohakuBlueleaf/KohakuHub.git
synced 2026-03-11 17:34:08 -05:00
214 lines
8.0 KiB
Python
214 lines
8.0 KiB
Python
"""Authentication API tests.
|
|
|
|
Tests user registration, login, logout, token management, and whoami endpoints.
|
|
Uses actual Pydantic models from the source code.
|
|
"""
|
|
|
|
import uuid
|
|
|
|
import pytest
|
|
|
|
from tests.base import HTTPClient
|
|
from tests.config import config
|
|
|
|
|
|
class TestAuthentication:
|
|
"""Test authentication endpoints."""
|
|
|
|
def test_version_check(self, http_client):
|
|
"""Test API version endpoint."""
|
|
resp = http_client.get("/api/version")
|
|
assert resp.status_code == 200
|
|
data = resp.json()
|
|
assert "api" in data
|
|
assert data["api"] == "kohakuhub"
|
|
assert "version" in data
|
|
|
|
def test_register_login_logout_flow(self, http_client):
|
|
"""Test complete user registration, login, and logout flow."""
|
|
# Import actual Pydantic models
|
|
from kohakuhub.auth.routes import RegisterRequest, LoginRequest
|
|
|
|
# Use unique username for this test
|
|
unique_id = uuid.uuid4().hex[:6]
|
|
test_username = f"user-{unique_id}" # Matches ^[a-z0-9][a-z0-9-]{2,62}$
|
|
test_email = f"test-{unique_id}@example.com"
|
|
test_password = "testpass123"
|
|
|
|
# 1. Register new user using actual model
|
|
payload = RegisterRequest(
|
|
username=test_username, email=test_email, password=test_password
|
|
)
|
|
|
|
resp = http_client.post("/api/auth/register", json=payload.model_dump())
|
|
assert resp.status_code == 200, f"Registration failed: {resp.text}"
|
|
data = resp.json()
|
|
assert data["success"] == True
|
|
|
|
# 2. Login using actual model
|
|
login_payload = LoginRequest(username=test_username, password=test_password)
|
|
|
|
resp = http_client.post("/api/auth/login", json=login_payload.model_dump())
|
|
assert resp.status_code == 200, f"Login failed: {resp.text}"
|
|
data = resp.json()
|
|
assert "username" in data
|
|
assert data["username"] == test_username
|
|
|
|
# Save session
|
|
session_cookies = resp.cookies
|
|
|
|
# 3. Get current user (with session)
|
|
client_with_session = HTTPClient()
|
|
client_with_session.session.cookies.update(session_cookies)
|
|
|
|
resp = client_with_session.get("/api/auth/me")
|
|
assert resp.status_code == 200, f"Get user failed: {resp.text}"
|
|
data = resp.json()
|
|
assert data["username"] == test_username
|
|
|
|
# 4. Logout
|
|
resp = client_with_session.post("/api/auth/logout")
|
|
assert resp.status_code == 200
|
|
|
|
# 5. Verify session is cleared
|
|
resp = client_with_session.get("/api/auth/me")
|
|
assert resp.status_code == 401, "Session should be cleared after logout"
|
|
|
|
def test_token_creation_and_usage(self, authenticated_http_client):
|
|
"""Test API token creation and usage."""
|
|
from kohakuhub.auth.routes import CreateTokenRequest
|
|
|
|
# 1. Create token using actual model
|
|
unique_id = uuid.uuid4().hex[:6]
|
|
token_payload = CreateTokenRequest(name=f"token-{unique_id}")
|
|
|
|
resp = authenticated_http_client.post(
|
|
"/api/auth/tokens/create", json=token_payload.model_dump()
|
|
)
|
|
assert resp.status_code == 200, f"Token creation failed: {resp.text}"
|
|
data = resp.json()
|
|
assert "token" in data
|
|
assert "token_id" in data
|
|
token = data["token"]
|
|
token_id = data["token_id"]
|
|
|
|
# 2. Use token for authentication
|
|
client_with_token = HTTPClient(token=token)
|
|
resp = client_with_token.get("/api/auth/me")
|
|
assert resp.status_code == 200, f"Token auth failed: {resp.text}"
|
|
user_data = resp.json()
|
|
assert user_data["username"] == config.username
|
|
|
|
# 3. List tokens
|
|
resp = authenticated_http_client.get("/api/auth/tokens")
|
|
assert resp.status_code == 200
|
|
tokens_response = resp.json()
|
|
assert "tokens" in tokens_response
|
|
tokens = tokens_response["tokens"]
|
|
assert isinstance(tokens, list)
|
|
# Our token should be in the list
|
|
token_names = [t["name"] for t in tokens]
|
|
assert token_payload.name in token_names
|
|
|
|
# 4. Revoke token
|
|
resp = authenticated_http_client.delete(f"/api/auth/tokens/{token_id}")
|
|
assert resp.status_code == 200
|
|
|
|
# 5. Verify token is revoked
|
|
resp = client_with_token.get("/api/auth/me")
|
|
assert resp.status_code == 401, "Token should be revoked"
|
|
|
|
def test_whoami_endpoint(self, authenticated_http_client):
|
|
"""Test whoami-v2 endpoint for detailed user info."""
|
|
resp = authenticated_http_client.get("/api/whoami-v2")
|
|
assert resp.status_code == 200, f"whoami-v2 failed: {resp.text}"
|
|
data = resp.json()
|
|
|
|
# Validate response structure (based on actual implementation in misc.py)
|
|
assert "type" in data
|
|
assert data["type"] == "user"
|
|
assert "name" in data
|
|
assert data["name"] == config.username
|
|
assert "email" in data
|
|
assert "orgs" in data
|
|
assert isinstance(data["orgs"], list)
|
|
assert "emailVerified" in data
|
|
assert "auth" in data
|
|
|
|
def test_invalid_credentials(self, http_client):
|
|
"""Test login with invalid credentials."""
|
|
from kohakuhub.auth.routes import LoginRequest
|
|
|
|
payload = LoginRequest(username="nonexistent", password="wrongpass")
|
|
|
|
resp = http_client.post("/api/auth/login", json=payload.model_dump())
|
|
assert resp.status_code == 401, "Should reject invalid credentials"
|
|
|
|
def test_unauthenticated_access(self, http_client):
|
|
"""Test accessing protected endpoints without authentication."""
|
|
# Try to access protected endpoint
|
|
resp = http_client.get("/api/auth/me")
|
|
assert resp.status_code == 401, "Should require authentication"
|
|
|
|
# Try to create repo without auth
|
|
from kohakuhub.api.repo.routers.crud import CreateRepoPayload
|
|
|
|
payload = CreateRepoPayload(type="model", name="test-repo")
|
|
|
|
resp = http_client.post("/api/repos/create", json=payload.model_dump())
|
|
assert resp.status_code in [401, 403], "Should require authentication"
|
|
|
|
def test_duplicate_registration(self, http_client):
|
|
"""Test that duplicate usernames are rejected."""
|
|
from kohakuhub.auth.routes import RegisterRequest
|
|
|
|
unique_id = uuid.uuid4().hex[:6]
|
|
test_username = f"dup-{unique_id}"
|
|
test_email = f"dup-{unique_id}@example.com"
|
|
|
|
payload = RegisterRequest(
|
|
username=test_username, email=test_email, password="testpass123"
|
|
)
|
|
|
|
# First registration
|
|
resp = http_client.post("/api/auth/register", json=payload.model_dump())
|
|
assert resp.status_code == 200
|
|
|
|
# Second registration with same username (different email)
|
|
payload2 = RegisterRequest(
|
|
username=test_username,
|
|
email=f"different_{unique_id}@example.com",
|
|
password="testpass123",
|
|
)
|
|
|
|
resp = http_client.post("/api/auth/register", json=payload2.model_dump())
|
|
assert resp.status_code == 400
|
|
assert "username" in resp.text.lower() or "exist" in resp.text.lower()
|
|
|
|
def test_duplicate_email_registration(self, http_client):
|
|
"""Test that duplicate emails are rejected."""
|
|
from kohakuhub.auth.routes import RegisterRequest
|
|
|
|
unique_id = uuid.uuid4().hex[:6]
|
|
test_username = f"email-{unique_id}"
|
|
test_email = f"email-{unique_id}@example.com"
|
|
|
|
payload = RegisterRequest(
|
|
username=test_username, email=test_email, password="testpass123"
|
|
)
|
|
|
|
# First registration
|
|
resp = http_client.post("/api/auth/register", json=payload.model_dump())
|
|
assert resp.status_code == 200
|
|
|
|
# Second registration with same email (different username)
|
|
payload2 = RegisterRequest(
|
|
username=f"different_{unique_id}",
|
|
email=test_email,
|
|
password="testpass123",
|
|
)
|
|
|
|
resp = http_client.post("/api/auth/register", json=payload2.model_dump())
|
|
assert resp.status_code == 400
|
|
assert "email" in resp.text.lower() or "exist" in resp.text.lower()
|