"""CUS-001 ~ CUS-010: 客户管理测试。 覆盖功能点: - 新增客户成功与重复校验 - 客户列表筛选 - 客户详情 - 业务员客户范围隔离 """ from __future__ import annotations import pytest @pytest.mark.customer @pytest.mark.p0 class TestCustomerCreation: """CUS-001 ~ CUS-003: 客户创建测试。""" def test_create_customer_success(self, client, admin_headers): """CUS-001: 新增客户成功。""" resp = client.post("/api/customers", headers=admin_headers, json={ "customer_name": "新客户测试", "mobile": "13800001001", "settlement_type": "immediate", }) assert resp.status_code == 200 data = resp.json() assert data["code"] == 0 def test_duplicate_customer_name_mobile(self, client, admin_headers, make_customer): """CUS-002: 姓名+手机号重复应返回 40006。""" make_customer(customer_name="重复客户", mobile="13800009999") resp = client.post("/api/customers", headers=admin_headers, json={ "customer_name": "重复客户", "mobile": "13800009999", }) assert resp.status_code in (400, 409) data = resp.json() assert data["code"] == 40006 def test_customer_mobile_required(self, client, admin_headers): """CUS-003: 手机号为空应返回验证错误。 注意: FastAPI 默认返回 422 Unprocessable Entity (Pydantic 校验), 而非 400 Bad Request。 """ resp = client.post("/api/customers", headers=admin_headers, json={ "customer_name": "无手机号客户", }) # FastAPI Pydantic 校验返回 422,自定义校验返回 400 assert resp.status_code in (400, 422) @pytest.mark.customer @pytest.mark.p0 class TestCustomerList: """CUS-004: 客户列表筛选测试。""" def test_list_customers(self, client, admin_headers, make_customer): """客户列表查询成功。""" make_customer() make_customer() resp = client.get("/api/customers", headers=admin_headers) assert resp.status_code == 200 data = resp.json() assert "list" in data["data"] assert data["data"]["total"] >= 2 def test_filter_customers_by_name(self, client, admin_headers, make_customer): """按姓名筛选客户。""" make_customer(customer_name="特殊客户A") make_customer(customer_name="普通客户B") resp = client.get("/api/customers?keyword=特殊", headers=admin_headers) assert resp.status_code == 200 @pytest.mark.customer @pytest.mark.p0 class TestCustomerIsolation: """CUS-008: 业务员客户范围隔离测试。""" def test_salesman_only_see_own_customers(self, client, salesman_headers, salesman2_headers, make_customer): """业务员只能看到自己的客户。""" make_customer(customer_name="业务员A客户", salesman_id=2) make_customer(customer_name="业务员B客户", salesman_id=3) # 业务员A查看客户列表 resp = client.get("/api/customers", headers=salesman_headers) data = resp.json() # 应该只能看到自己的客户 for c in data["data"]["list"]: assert c["salesman_id"] in (2, None) def test_admin_see_all_customers(self, client, admin_headers, make_customer): """管理员可以看到所有客户。""" make_customer(customer_name="客户1", salesman_id=2) make_customer(customer_name="客户2", salesman_id=3) resp = client.get("/api/customers", headers=admin_headers) data = resp.json() assert data["data"]["total"] >= 2 @pytest.mark.customer @pytest.mark.p1 class TestCustomerDetail: """CUS-005, CUS-009 ~ CUS-010: 客户详情测试。""" def test_customer_detail(self, client, admin_headers, make_customer): """CUS-005: 客户详情返回完整信息。""" customer = make_customer() resp = client.get(f"/api/customers/{customer.id}", headers=admin_headers) assert resp.status_code == 200 data = resp.json()["data"] assert data["customer_name"] is not None def test_monthly_customer_flag(self, client, admin_headers, make_customer): """CUS-009: 月结客户标识。""" customer = make_customer(settlement_type="monthly", settlement_days=30) resp = client.get(f"/api/customers/{customer.id}", headers=admin_headers) assert resp.status_code == 200 data = resp.json()["data"] assert data["settlement_type"] == "monthly"