""" 认证模块API测试 测试内容: - 用户登录 - Token刷新 - 用户登出 - 修改密码 - 验证码获取 """ import pytest # from fastapi.testclient import TestClient # from app.core.config import settings # class TestAuthLogin: # """测试用户登录""" # # def test_login_success(self, client: TestClient, test_user): # """测试登录成功""" # response = client.post( # "/api/v1/auth/login", # json={ # "username": "testuser", # "password": "Test123", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # assert response.status_code == 200 # data = response.json() # assert data["code"] == 200 # assert "access_token" in data["data"] # assert "refresh_token" in data["data"] # assert data["data"]["token_type"] == "Bearer" # assert "user" in data["data"] # # def test_login_wrong_password(self, client: TestClient): # """测试密码错误""" # response = client.post( # "/api/v1/auth/login", # json={ # "username": "testuser", # "password": "WrongPassword", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # assert response.status_code == 401 # data = response.json() # assert data["code"] == 10001 # 用户名或密码错误 # # def test_login_user_not_found(self, client: TestClient): # """测试用户不存在""" # response = client.post( # "/api/v1/auth/login", # json={ # "username": "nonexistent", # "password": "Test123", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # assert response.status_code == 401 # # def test_login_missing_fields(self, client: TestClient): # """测试缺少必填字段""" # response = client.post( # "/api/v1/auth/login", # json={"username": "testuser"} # ) # assert response.status_code == 422 # Validation error # # @pytest.mark.parametrize("username", [ # "", # 空字符串 # "ab", # 太短 # "a" * 51, # 太长 # ]) # def test_login_invalid_username(self, client: TestClient, username): # """测试无效用户名""" # response = client.post( # "/api/v1/auth/login", # json={ # "username": username, # "password": "Test123", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # assert response.status_code == 422 # # @pytest.mark.parametrize("password", [ # "", # 空字符串 # "short", # 太短 # "nospecial123", # 缺少特殊字符 # "NOlower123!", # 缺少小写字母 # "noupper123!", # 缺少大写字母 # "NoNumber!!", # 缺少数字 # ]) # def test_login_invalid_password(self, client: TestClient, password): # """测试无效密码""" # response = client.post( # "/api/v1/auth/login", # json={ # "username": "testuser", # "password": password, # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # # 某些情况可能是422(验证失败),某些情况可能是401(认证失败) # assert response.status_code in [400, 422, 401] # # def test_login_account_locked(self, client: TestClient, db): # """测试账户被锁定""" # # 创建一个锁定的账户 # # ... 创建锁定用户逻辑 # response = client.post( # "/api/v1/auth/login", # json={ # "username": "lockeduser", # "password": "Test123", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # assert response.status_code == 403 # # def test_login_account_disabled(self, client: TestClient, db): # """测试账户被禁用""" # # ... 创建禁用用户逻辑 # response = client.post( # "/api/v1/auth/login", # json={ # "username": "disableduser", # "password": "Test123", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # assert response.status_code == 403 # class TestTokenRefresh: # """测试Token刷新""" # # def test_refresh_token_success(self, client: TestClient, test_user): # """测试刷新Token成功""" # # 先登录获取refresh_token # login_response = client.post( # "/api/v1/auth/login", # json={ # "username": "testuser", # "password": "Test123", # "captcha": "1234", # "captcha_key": "test-uuid" # } # ) # refresh_token = login_response.json()["data"]["refresh_token"] # # # 刷新Token # response = client.post( # "/api/v1/auth/refresh", # json={"refresh_token": refresh_token} # ) # assert response.status_code == 200 # data = response.json() # assert data["code"] == 200 # assert "access_token" in data["data"] # assert "expires_in" in data["data"] # # def test_refresh_token_invalid(self, client: TestClient): # """测试无效的refresh_token""" # response = client.post( # "/api/v1/auth/refresh", # json={"refresh_token": "invalid_token"} # ) # assert response.status_code == 401 # data = response.json() # assert data["code"] == 10004 # Token无效 # # def test_refresh_token_expired(self, client: TestClient): # """测试过期的refresh_token""" # response = client.post( # "/api/v1/auth/refresh", # json={"refresh_token": "expired_token"} # ) # assert response.status_code == 401 # data = response.json() # assert data["code"] == 10003 # Token过期 # class TestAuthLogout: # """测试用户登出""" # # def test_logout_success(self, client: TestClient, auth_headers): # """测试登出成功""" # response = client.post( # "/api/v1/auth/logout", # headers=auth_headers # ) # assert response.status_code == 200 # data = response.json() # assert data["code"] == 200 # assert data["message"] == "登出成功" # # def test_logout_without_auth(self, client: TestClient): # """测试未认证登出""" # response = client.post("/api/v1/auth/logout") # assert response.status_code == 401 # class TestChangePassword: # """测试修改密码""" # # def test_change_password_success(self, client: TestClient, auth_headers): # """测试修改密码成功""" # response = client.put( # "/api/v1/auth/change-password", # headers=auth_headers, # json={ # "old_password": "Test123", # "new_password": "NewTest456", # "confirm_password": "NewTest456" # } # ) # assert response.status_code == 200 # data = response.json() # assert data["code"] == 200 # assert data["message"] == "密码修改成功" # # def test_change_password_wrong_old_password(self, client: TestClient, auth_headers): # """测试旧密码错误""" # response = client.put( # "/api/v1/auth/change-password", # headers=auth_headers, # json={ # "old_password": "WrongPassword", # "new_password": "NewTest456", # "confirm_password": "NewTest456" # } # ) # assert response.status_code == 400 # # def test_change_password_mismatch(self, client: TestClient, auth_headers): # """测试两次密码不一致""" # response = client.put( # "/api/v1/auth/change-password", # headers=auth_headers, # json={ # "old_password": "Test123", # "new_password": "NewTest456", # "confirm_password": "DifferentPass789" # } # ) # assert response.status_code == 400 # # def test_change_password_weak_password(self, client: TestClient, auth_headers): # """测试弱密码""" # response = client.put( # "/api/v1/auth/change-password", # headers=auth_headers, # json={ # "old_password": "Test123", # "new_password": "weak", # "confirm_password": "weak" # } # ) # assert response.status_code == 400 # # def test_change_password_without_auth(self, client: TestClient): # """测试未认证修改密码""" # response = client.put( # "/api/v1/auth/change-password", # json={ # "old_password": "Test123", # "new_password": "NewTest456", # "confirm_password": "NewTest456" # } # ) # assert response.status_code == 401 # class TestCaptcha: # """测试验证码""" # # def test_get_captcha_success(self, client: TestClient): # """测试获取验证码成功""" # response = client.get("/api/v1/auth/captcha") # assert response.status_code == 200 # data = response.json() # assert data["code"] == 200 # assert "captcha_key" in data["data"] # assert "captcha_image" in data["data"] # assert data["data"]["captcha_image"].startswith("data:image/png;base64,") # # @pytest.mark.parametrize("count", range(5)) # def test_get_captcha_multiple_times(self, client: TestClient, count): # """测试多次获取验证码,每次应该不同""" # response = client.get("/api/v1/auth/captcha") # assert response.status_code == 200 # data = response.json() # assert data["data"]["captcha_key"] is not None # class TestRateLimiting: # """测试请求频率限制""" # # def test_login_rate_limiting(self, client: TestClient): # """测试登录接口频率限制""" # # 登录接口限制10次/分钟 # for i in range(11): # response = client.post( # "/api/v1/auth/login", # json={ # "username": "testuser", # "password": "wrongpass", # "captcha": "1234", # "captcha_key": f"test-{i}" # } # ) # # # 第11次应该被限流 # assert response.status_code == 429 # data = response.json() # assert data["code"] == 429 # assert "retry_after" in data["data"] # 测试SQL注入攻击 # class TestSecurity: # """测试安全性""" # # def test_sql_injection_prevention(self, client: TestClient): # """测试防止SQL注入""" # malicious_inputs = [ # "admin' OR '1'='1", # "admin'--", # "admin'/*", # "' OR 1=1--", # "'; DROP TABLE users--" # ] # # for malicious_input in malicious_inputs: # response = client.post( # "/api/v1/auth/login", # json={ # "username": malicious_input, # "password": "Test123", # "captcha": "1234", # "captcha_key": "test" # } # ) # # 应该返回认证失败,而不是数据库错误 # assert response.status_code in [401, 400, 422]