"""用户管理服务 - 基础单元测试模块。 使用 pytest 和 httpx 对 API 接口进行测试。 """ import pytest from fastapi.testclient import TestClient from app.main import app from app.database import db # ────────────────────────────────────────────── # 测试客户端 # ────────────────────────────────────────────── @pytest.fixture(autouse=True) def reset_database(): """每个测试用例执行前重置数据库状态。 保存原始数据并在测试后恢复,避免测试间的数据污染。 """ # 清空数据库并重新初始化种子数据 db._users.clear() db._username_index.clear() db._init_seed_data() yield client = TestClient(app) # ────────────────────────────────────────────── # 健康检查测试 # ────────────────────────────────────────────── class TestHealthCheck: """健康检查接口测试类。""" def test_health_check_returns_ok(self): """测试健康检查接口返回状态 ok。""" response = client.get("/health") assert response.status_code == 200 data = response.json() assert data["status"] == "ok" assert data["service"] == "user-management-service" assert data["total_users"] >= 3 # ────────────────────────────────────────────── # 用户创建测试 # ────────────────────────────────────────────── class TestCreateUser: """用户注册接口测试类。""" def test_create_user_success(self): """测试成功创建用户。""" response = client.post( "/api/users", json={ "username": "newuser", "email": "newuser@example.com", "password": "password123", }, ) assert response.status_code == 201 data = response.json() assert data["username"] == "newuser" assert data["email"] == "newuser@example.com" assert data["is_active"] is True assert "id" in data assert "created_at" in data def test_create_user_duplicate_username(self): """测试创建重复用户名应返回 409。""" response = client.post( "/api/users", json={ "username": "alice", "email": "alice2@example.com", "password": "password123", }, ) assert response.status_code == 409 assert "已被占用" in response.json()["detail"] def test_create_user_duplicate_email(self): """测试创建重复邮箱应返回 409。""" response = client.post( "/api/users", json={ "username": "alice2", "email": "alice@example.com", "password": "password123", }, ) assert response.status_code == 409 assert "已被注册" in response.json()["detail"] def test_create_user_validation_error(self): """测试用户名太短应返回 422。""" response = client.post( "/api/users", json={ "username": "ab", "email": "test@example.com", "password": "password123", }, ) assert response.status_code == 422 # ────────────────────────────────────────────── # 用户查询测试 # ────────────────────────────────────────────── class TestGetUsers: """用户查询接口测试类。""" def test_get_all_users(self): """测试获取所有用户(分页)。""" response = client.get("/api/users?skip=0&limit=10") assert response.status_code == 200 data = response.json() assert len(data) >= 3 # 验证返回的字段不包含密码 for user in data: assert "password" not in user assert "hashed_password" not in user def test_get_user_by_id_success(self): """测试通过 ID 获取用户成功。""" response = client.get("/api/users/user-001") assert response.status_code == 200 data = response.json() assert data["username"] == "alice" assert data["email"] == "alice@example.com" def test_get_user_by_id_not_found(self): """测试获取不存在的用户返回 404。""" response = client.get("/api/users/user-999") assert response.status_code == 404 def test_get_users_pagination(self): """测试分页参数生效。""" response = client.get("/api/users?skip=0&limit=1") assert response.status_code == 200 data = response.json() assert len(data) == 1 # ────────────────────────────────────────────── # 用户更新测试 # ────────────────────────────────────────────── class TestUpdateUser: """用户更新接口测试类。""" def test_update_username_success(self): """测试成功更新用户名。""" response = client.put( "/api/users/user-001", json={"username": "alice_updated"}, ) assert response.status_code == 200 data = response.json() assert data["username"] == "alice_updated" def test_update_user_not_found(self): """测试更新不存在的用户返回 404。""" response = client.put( "/api/users/user-999", json={"username": "new_name"}, ) assert response.status_code == 404 def test_update_username_conflict(self): """测试更新为已存在的用户名返回 409。""" response = client.put( "/api/users/user-001", json={"username": "bob"}, ) assert response.status_code == 409 # ────────────────────────────────────────────── # 用户删除测试 # ────────────────────────────────────────────── class TestDeleteUser: """用户删除接口测试类。""" def test_delete_user_success(self): """测试成功删除用户。""" response = client.delete("/api/users/user-001") assert response.status_code == 200 data = response.json() assert "成功删除" in data["message"] # 验证用户确实被删除 get_response = client.get("/api/users/user-001") assert get_response.status_code == 404 def test_delete_user_not_found(self): """测试删除不存在的用户返回 404。""" response = client.delete("/api/users/user-999") assert response.status_code == 404 # ────────────────────────────────────────────── # 用户登录测试 # ────────────────────────────────────────────── class TestLogin: """用户登录接口测试类。""" def test_login_success(self): """测试使用正确的用户名和密码登录成功。""" response = client.post( "/api/users/login", json={"username": "alice", "password": "abc123"}, ) assert response.status_code == 200 data = response.json() assert data["username"] == "alice" def test_login_wrong_password(self): """测试使用错误密码登录返回 401。""" response = client.post( "/api/users/login", json={"username": "alice", "password": "wrong_password"}, ) assert response.status_code == 401 def test_login_user_not_found(self): """测试使用不存在的用户名登录返回 401。""" response = client.post( "/api/users/login", json={"username": "nonexistent", "password": "password123"}, ) assert response.status_code == 401