import unittest from fastapi import HTTPException from fastapi.testclient import TestClient from app.api.auth import get_auth_service from app.main import app class StubAuthService: def login(self, account, password): return { "access_token": "token-demo", "token_type": "Bearer", "expires_in": 7200, "user": { "id": "user-demo-001", "tenant_id": "tenant-demo-001", "username": "admin@example.com", "display_name": "管理员", "role": "admin", "status": "active", }, } def register(self, username, password, display_name): return { "id": "user-member-001", "tenant_id": "tenant-demo-001", "username": username, "display_name": display_name, "role": "member", "status": "active", } def get_current_user(self, authorization): if authorization != "Bearer token-demo": raise HTTPException(status_code=401, detail="missing bearer token") return { "id": "user-demo-001", "tenant_id": "tenant-demo-001", "username": "admin@example.com", "display_name": "管理员", "role": "admin", "status": "active", } def update_current_user(self, authorization, display_name, password): if authorization != "Bearer token-demo": raise HTTPException(status_code=401, detail="missing bearer token") if not display_name.strip(): raise HTTPException(status_code=400, detail="display name is required") if password is not None and password.strip() and len(password.strip()) < 6: raise HTTPException(status_code=400, detail="password must be at least 6 characters") return { "id": "user-demo-001", "tenant_id": "tenant-demo-001", "username": "admin@example.com", "display_name": display_name.strip(), "role": "admin", "status": "active", } def seed_default_admin(self): return { "username": "admin@example.com", "password": None, "created": False, } class AuthApiTestCase(unittest.TestCase): def setUp(self): app.dependency_overrides[get_auth_service] = lambda: StubAuthService() self.client = TestClient(app) def tearDown(self): app.dependency_overrides.clear() def test_register_returns_created_member(self): response = self.client.post( "/api/v1/auth/register", json={ "username": "member@example.com", "password": "123456", "display_name": "测试成员", }, ) self.assertEqual(response.status_code, 200) payload = response.json()["data"] self.assertEqual(payload["username"], "member@example.com") self.assertEqual(payload["display_name"], "测试成员") self.assertEqual(payload["role"], "member") def test_register_rejects_invalid_email(self): response = self.client.post( "/api/v1/auth/register", json={ "username": "not-an-email", "password": "123456", "display_name": "测试成员", }, ) self.assertEqual(response.status_code, 422) def test_register_rejects_short_password(self): response = self.client.post( "/api/v1/auth/register", json={ "username": "member@example.com", "password": "12345", "display_name": "测试成员", }, ) self.assertEqual(response.status_code, 422) def test_register_then_login_uses_same_credentials(self): register_response = self.client.post( "/api/v1/auth/register", json={ "username": "member@example.com", "password": "123456", "display_name": "测试成员", }, ) self.assertEqual(register_response.status_code, 200) login_response = self.client.post( "/api/v1/auth/login", json={ "account": "member@example.com", "password": "123456", }, ) self.assertEqual(login_response.status_code, 200) self.assertEqual(login_response.json()["data"]["access_token"], "token-demo") def test_get_current_user(self): response = self.client.get("/api/v1/auth/me", headers={"Authorization": "Bearer token-demo"}) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()["data"]["username"], "admin@example.com") def test_update_current_user(self): response = self.client.put( "/api/v1/auth/me", headers={"Authorization": "Bearer token-demo"}, data={"display_name": "法务负责人", "password": "654321"}, ) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()["data"]["display_name"], "法务负责人") def test_reject_blank_display_name(self): response = self.client.put( "/api/v1/auth/me", headers={"Authorization": "Bearer token-demo"}, data={"display_name": " ", "password": ""}, ) self.assertEqual(response.status_code, 400) self.assertEqual(response.json()["message"], "display name is required") def test_reject_short_password(self): response = self.client.put( "/api/v1/auth/me", headers={"Authorization": "Bearer token-demo"}, data={"display_name": "法务负责人", "password": "12345"}, ) self.assertEqual(response.status_code, 400) self.assertEqual(response.json()["message"], "password must be at least 6 characters") def test_seed_admin_returns_nullable_password(self): response = self.client.post("/api/v1/auth/seed-admin") self.assertEqual(response.status_code, 200) payload = response.json()["data"] self.assertEqual(payload["username"], "admin@example.com") self.assertIsNone(payload["password"]) self.assertFalse(payload["created"]) if __name__ == "__main__": unittest.main()