""" 客户管理模块测试 —— /api/customers 覆盖: CRUD / 搜索 / 归档恢复 / 转移 / 数据权限隔离 """ import uuid import pytest from httpx import AsyncClient from tests.conftest import ( make_auth_headers, ADMIN_USER_ID, SALES_USER_ID, COMPANY_ID, CUSTOMER_ID, ) class TestCreateCustomer: """POST /api/customers""" async def test_create_customer_success(self, client: AsyncClient, admin_headers): """正常创建客户 → 200""" resp = await client.post("/api/customers", headers=admin_headers, json={ "name": "新客户测试公司", "level": "B", "industry": "制造业", "contact": "李经理", "phone": "13900139000", }) assert resp.status_code == 200 data = resp.json()["data"] assert data["name"] == "新客户测试公司" assert data["level"] == "B" async def test_create_customer_minimal(self, client: AsyncClient, admin_headers): """仅必填字段(name) → 200""" resp = await client.post("/api/customers", headers=admin_headers, json={ "name": "最简客户", }) assert resp.status_code == 200 async def test_create_customer_no_auth(self, client: AsyncClient, seed_data): """无认证 → 422""" resp = await client.post("/api/customers", json={"name": "test"}) assert resp.status_code == 422 class TestListCustomers: """GET /api/customers""" async def test_list_customers_success(self, client: AsyncClient, admin_headers): """管理员列表 → 200 + 包含种子客户""" resp = await client.get("/api/customers", headers=admin_headers) assert resp.status_code == 200 data = resp.json()["data"] assert data["total"] >= 1 assert len(data["items"]) >= 1 async def test_list_customers_with_level_filter(self, client: AsyncClient, admin_headers): """等级筛选 level=A → 只返回A级""" resp = await client.get("/api/customers?level=A", headers=admin_headers) assert resp.status_code == 200 for item in resp.json()["data"]["items"]: assert item["level"] == "A" async def test_list_customers_keyword_search(self, client: AsyncClient, admin_headers): """关键词搜索 → 模糊匹配""" resp = await client.get("/api/customers?keyword=中石化", headers=admin_headers) assert resp.status_code == 200 async def test_list_customers_pagination(self, client: AsyncClient, admin_headers): """分页参数 page=1&size=5 → 正常分页""" resp = await client.get("/api/customers?page=1&size=5", headers=admin_headers) assert resp.status_code == 200 data = resp.json()["data"] assert "total" in data assert "items" in data class TestGetCustomer: """GET /api/customers/{id}""" async def test_get_customer_success(self, client: AsyncClient, admin_headers): """获取种子客户 → 200""" resp = await client.get(f"/api/customers/{CUSTOMER_ID}", headers=admin_headers) assert resp.status_code == 200 assert resp.json()["data"]["name"] == "中石化天津分公司" async def test_get_customer_not_found(self, client: AsyncClient, admin_headers): """不存在的 UUID → 404""" fake_id = uuid.uuid4() resp = await client.get(f"/api/customers/{fake_id}", headers=admin_headers) assert resp.status_code == 404 class TestUpdateCustomer: """PUT /api/customers/{id}""" async def test_update_customer_success(self, client: AsyncClient, admin_headers): """更新客户等级 → 200""" resp = await client.put(f"/api/customers/{CUSTOMER_ID}", headers=admin_headers, json={ "level": "B", "industry": "化学工业", }) assert resp.status_code == 200 data = resp.json()["data"] assert data["level"] == "B" class TestDeleteAndRestore: """DELETE + PUT /restore""" async def test_delete_customer(self, client: AsyncClient, admin_headers): """软删除 → 200""" resp = await client.delete(f"/api/customers/{CUSTOMER_ID}", headers=admin_headers) assert resp.status_code == 200 assert "归档" in resp.json()["message"] async def test_restore_customer(self, client: AsyncClient, admin_headers): """恢复归档 → 200""" # 先归档 await client.delete(f"/api/customers/{CUSTOMER_ID}", headers=admin_headers) # 再恢复 resp = await client.put(f"/api/customers/{CUSTOMER_ID}/restore", headers=admin_headers) assert resp.status_code == 200 class TestSearchCustomer: """GET /api/customers/search""" async def test_search_success(self, client: AsyncClient, admin_headers): """搜索 q=中石化 → 返回匹配结果""" resp = await client.get("/api/customers/search?q=中石化", headers=admin_headers) assert resp.status_code == 200 class TestTransferCustomer: """PUT /api/customers/{id}/transfer""" async def test_transfer_success(self, client: AsyncClient, admin_headers): """管理员转移客户 → 200""" resp = await client.put( f"/api/customers/{CUSTOMER_ID}/transfer", headers=admin_headers, json={"new_owner_id": str(ADMIN_USER_ID)} ) assert resp.status_code == 200 assert "转移成功" in resp.json()["message"]