""" 认证集成测试 测试完整的认证流程,包括注册、登录、令牌刷新、登出等。 需要 PostgreSQL 数据库才能运行。在没有数据库的环境中会被跳过。 """ import os import uuid import pytest from fastapi.testclient import TestClient # 检测是否有可用的 PostgreSQL 数据库 _HAS_PG = False try: if os.environ.get("USE_IN_MEMORY_DB", "").lower() != "true": import psycopg conn = psycopg.connect( os.environ.get( "DATABASE_URL", "postgresql+psycopg://postgres:postgres@localhost:5432/xiaoxia_saas", ).replace("postgresql+psycopg://", "postgresql://"), connect_timeout=3, ) conn.close() _HAS_PG = True except Exception: pass needs_pg = pytest.mark.skipif(not _HAS_PG, reason="Requires PostgreSQL database") from apps.api.main import app client = TestClient(app) @needs_pg class TestUserRegistration: """用户注册集成测试""" def test_register_with_valid_data(self): """测试使用有效数据进行注册""" unique = uuid.uuid4().hex[:8] response = client.post( "/api/v1/auth/register", json={ "email": f"newuser-{unique}@example.com", "password": "SecurePass123", "username": f"newuser-{unique}", "display_name": "New User", }, ) assert response.status_code in (200, 201) data = response.json() assert data["username"] == f"newuser-{unique}" assert "user_id" in data assert "message" in data def test_register_with_invalid_email(self): """测试使用无效邮箱进行注册""" response = client.post( "/api/v1/auth/register", json={ "email": "invalid-email", "password": "SecurePass123", "username": "testuser", }, ) assert response.status_code == 422 def test_register_with_weak_password(self): """测试使用弱密码进行注册""" response = client.post( "/api/v1/auth/register", json={ "email": "weak@example.com", "password": "123", "username": "weakuser", }, ) assert response.status_code in [400, 422] def test_register_duplicate_email(self): """测试重复邮箱注册""" unique = uuid.uuid4().hex[:8] email = f"dup-{unique}@example.com" client.post( "/api/v1/auth/register", json={ "email": email, "password": "SecurePass123", "username": f"user1-{unique}", "display_name": "User 1", }, ) response = client.post( "/api/v1/auth/register", json={ "email": email, "password": "SecurePass123", "username": f"user2-{unique}", "display_name": "User 2", }, ) assert response.status_code == 400 body = response.json() message = body.get("detail", "") or body.get("error", {}).get("message", "") assert "邮箱" in message or "already" in message.lower() or "注册" in message @needs_pg class TestUserLogin: """用户登录集成测试""" def setup_method(self): """每个测试前的准备:注册用户""" self.test_email = f"login-{uuid.uuid4().hex[:8]}@example.com" self.test_username = f"loginuser-{uuid.uuid4().hex[:8]}" register_response = client.post( "/api/v1/auth/register", json={ "email": self.test_email, "password": "SecurePass123", "username": self.test_username, "display_name": "Login User", }, ) assert register_response.status_code in (200, 201), f"Register failed: {register_response.json()}" def test_login_with_correct_credentials(self): """测试使用正确凭据登录""" response = client.post( "/api/v1/auth/login", json={ "email": self.test_email, "password": "SecurePass123", }, ) assert response.status_code == 200 data = response.json() assert "access_token" in data assert "refresh_token" in data assert data["token_type"] == "bearer" def test_login_with_wrong_password(self): """测试使用错误密码登录""" response = client.post( "/api/v1/auth/login", json={ "email": self.test_email, "password": "WrongPassword123", }, ) assert response.status_code == 401 def test_login_with_nonexistent_email(self): """测试使用不存在的邮箱登录""" response = client.post( "/api/v1/auth/login", json={ "email": "nonexistent@example.com", "password": "AnyPassword123", }, ) assert response.status_code == 401 def test_login_case_insensitive_email(self): """测试邮箱大小写不敏感登录""" response = client.post( "/api/v1/auth/login", json={ "email": self.test_email.upper(), "password": "SecurePass123", }, ) assert response.status_code == 200 @needs_pg class TestTokenRefresh: """令牌刷新集成测试""" def setup_method(self): """每个测试前的准备:注册并登录获取令牌""" self.test_email = f"refresh-{uuid.uuid4().hex[:8]}@example.com" client.post( "/api/v1/auth/register", json={ "email": self.test_email, "password": "SecurePass123", "username": f"refreshuser-{uuid.uuid4().hex[:8]}", "display_name": "Refresh User", }, ) response = client.post( "/api/v1/auth/login", json={ "email": self.test_email, "password": "SecurePass123", }, ) self.refresh_token = response.json().get("refresh_token") if response.status_code == 200 else None def test_refresh_token_success(self): """测试成功刷新令牌""" if not self.refresh_token: pytest.skip("Refresh token not available") response = client.post( "/api/v1/auth/refresh", json={"refresh_token": self.refresh_token}, ) if response.status_code not in (404, 401): assert response.status_code == 200 data = response.json() assert "access_token" in data @needs_pg class TestCurrentUser: """当前用户信息集成测试""" def setup_method(self): """每个测试前的准备:注册并登录获取令牌""" self.test_email = f"me-{uuid.uuid4().hex[:8]}@example.com" client.post( "/api/v1/auth/register", json={ "email": self.test_email, "password": "SecurePass123", "username": f"meuser-{uuid.uuid4().hex[:8]}", "display_name": "Me User", }, ) response = client.post( "/api/v1/auth/login", json={ "email": self.test_email, "password": "SecurePass123", }, ) if response.status_code != 200: pytest.skip("Login failed during setup") self.token = response.json().get("access_token") self.headers = {"Authorization": f"Bearer {self.token}"} if self.token else {} def test_get_current_user_success(self): """测试获取当前用户信息成功""" if not self.token: pytest.skip("Token not available") response = client.get("/api/v1/auth/me", headers=self.headers) assert response.status_code == 200 data = response.json() assert data["email"] == self.test_email assert "user_id" in data def test_get_current_user_without_token(self): """测试无令牌获取当前用户信息""" response = client.get("/api/v1/auth/me") assert response.status_code in [401, 403] def test_get_current_user_with_invalid_token(self): """测试使用无效令牌获取当前用户信息""" response = client.get( "/api/v1/auth/me", headers={"Authorization": "Bearer invalid-token"}, ) assert response.status_code in [401, 403] @needs_pg class TestPasswordReset: """密码重置集成测试""" def test_request_password_reset_success(self): """测试请求密码重置成功""" test_email = f"reset-{uuid.uuid4().hex[:8]}@example.com" client.post( "/api/v1/auth/register", json={ "email": test_email, "password": "SecurePass123", "username": f"resetuser-{uuid.uuid4().hex[:8]}", "display_name": "Reset User", }, ) response = client.post( "/api/v1/auth/password/forgot", json={"email": test_email}, ) # API returns 200 or 202 on success assert response.status_code in (200, 202) def test_request_password_reset_nonexistent_user(self): """测试请求不存在的用户密码重置""" response = client.post( "/api/v1/auth/password/forgot", json={"email": "nonexistent@example.com"}, ) # API returns 200/202 for non-existent user (security: don't reveal email existence) assert response.status_code in [200, 202, 400] if __name__ == "__main__": pytest.main([__file__, "-v"])