v0.2.0: CRM/ERP 系统升级 - 清理 .gitignore 并移除误提交的 venv/env/db 文件
- 更新 .gitignore:全面覆盖环境变量、数据库、日志、缓存、上传文件 - 移除误跟踪的 server/venv/、crm_data.db、.env 文件 - 新增 server/.env.example 模板 - 新增合同管理、利润核算、AI教练等功能模块 - 新增 Playwright e2e 测试套件 - 前后端多项功能升级和 bug 修复
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
"""
|
||||
客户管理模块测试 —— /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"]
|
||||
Reference in New Issue
Block a user