235 lines
7.1 KiB
Python
235 lines
7.1 KiB
Python
"""
|
|
项目 API 测试(依据 API 文档 3–7 节)
|
|
TDD:先写失败用例,再实现路由
|
|
"""
|
|
|
|
|
|
def _auth_header(token: str) -> dict:
|
|
return {"Authorization": f"Bearer {token}"}
|
|
|
|
|
|
# ---------- POST /api/projects/list ----------
|
|
|
|
|
|
def test_list_without_token_returns_401(client):
|
|
"""项目列表无 token 时返回 401"""
|
|
r = client.post("/api/projects/list", json={})
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_list_with_token_returns_200_with_list_total_page_page_size(client):
|
|
"""有 token 时返回 200 且包含 list、total、page、pageSize"""
|
|
r = client.post(
|
|
"/api/projects/list",
|
|
headers=_auth_header("valid-token"),
|
|
json={"page": 1, "pageSize": 20},
|
|
)
|
|
assert r.status_code == 200
|
|
body = r.json()
|
|
assert "list" in body
|
|
assert isinstance(body["list"], list)
|
|
assert "total" in body
|
|
assert "page" in body
|
|
assert "pageSize" in body
|
|
|
|
|
|
def test_list_empty_result_has_empty_list_and_total_zero(client):
|
|
"""无数据时 list 为空数组且 total 为 0"""
|
|
r = client.post(
|
|
"/api/projects/list",
|
|
headers=_auth_header("valid-token"),
|
|
json={},
|
|
)
|
|
assert r.status_code == 200
|
|
assert r.json()["list"] == []
|
|
assert r.json()["total"] == 0
|
|
|
|
|
|
# ---------- POST /api/projects/detail ----------
|
|
|
|
|
|
def test_detail_without_token_returns_401(client):
|
|
"""项目详情无 token 时返回 401"""
|
|
r = client.post("/api/projects/detail", json={"id": "any-id"})
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_detail_existing_project_returns_200_with_full_fields(client):
|
|
"""存在项目时返回 200 且包含 id、contract、costControl、receivable、payable、other、createdAt、updatedAt"""
|
|
create_r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("market-token"),
|
|
json={"contract": {"contractCode": "HT001", "projectName": "测试项目"}},
|
|
)
|
|
assert create_r.status_code == 201
|
|
pid = create_r.json()["id"]
|
|
r = client.post(
|
|
"/api/projects/detail",
|
|
headers=_auth_header("valid-token"),
|
|
json={"id": pid},
|
|
)
|
|
assert r.status_code == 200
|
|
body = r.json()
|
|
assert "id" in body
|
|
assert "contract" in body
|
|
assert "costControl" in body
|
|
assert "receivable" in body
|
|
assert "payable" in body
|
|
assert "other" in body
|
|
assert "createdAt" in body
|
|
assert "updatedAt" in body
|
|
|
|
|
|
def test_detail_nonexistent_project_returns_404_with_code_and_message(client):
|
|
"""不存在项目时返回 404 且包含 code 和 message"""
|
|
r = client.post(
|
|
"/api/projects/detail",
|
|
headers=_auth_header("valid-token"),
|
|
json={"id": "non-existent-id"},
|
|
)
|
|
assert r.status_code == 404
|
|
body = r.json()
|
|
assert body.get("code") == 404
|
|
assert "message" in body
|
|
|
|
|
|
# ---------- POST /api/projects/create ----------
|
|
|
|
|
|
def test_create_without_token_returns_401(client):
|
|
"""新建项目无 token 时返回 401"""
|
|
r = client.post(
|
|
"/api/projects/create",
|
|
json={"contract": {"contractCode": "HT001", "projectName": "测试项目"}},
|
|
)
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_create_non_market_role_returns_403(client):
|
|
"""非市场部角色返回 403"""
|
|
r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("engineer-token"),
|
|
json={"contract": {"contractCode": "HT001", "projectName": "测试项目"}},
|
|
)
|
|
assert r.status_code == 403
|
|
assert r.json().get("code") == 403
|
|
|
|
|
|
def test_create_market_with_contract_code_and_name_returns_201_with_id_and_message(client):
|
|
"""市场部且合同编号与项目名称齐全时返回 201 且包含 id 和 message"""
|
|
r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("market-token"),
|
|
json={"contract": {"contractCode": "HT001", "projectName": "测试项目"}},
|
|
)
|
|
assert r.status_code == 201
|
|
body = r.json()
|
|
assert "id" in body
|
|
assert body.get("message") == "创建成功"
|
|
|
|
|
|
def test_create_missing_contract_code_returns_400(client):
|
|
"""缺少合同编号时返回 400"""
|
|
r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("market-token"),
|
|
json={"contract": {"projectName": "测试项目"}},
|
|
)
|
|
assert r.status_code == 400
|
|
assert r.json().get("code") == 400
|
|
|
|
|
|
def test_create_missing_project_name_returns_400(client):
|
|
"""缺少项目名称时返回 400"""
|
|
r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("market-token"),
|
|
json={"contract": {"contractCode": "HT001"}},
|
|
)
|
|
assert r.status_code == 400
|
|
assert r.json().get("code") == 400
|
|
|
|
|
|
# ---------- POST /api/projects/update ----------
|
|
|
|
|
|
def test_update_without_token_returns_401(client):
|
|
"""更新项目无 token 时返回 401"""
|
|
r = client.post("/api/projects/update", json={"id": "any-id"})
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_update_existing_project_returns_200_with_id_and_save_success_message(client):
|
|
"""存在项目时返回 200 且包含 id 和 message「保存成功」"""
|
|
create_r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("market-token"),
|
|
json={"contract": {"contractCode": "HT002", "projectName": "测试项目2"}},
|
|
)
|
|
assert create_r.status_code == 201
|
|
pid = create_r.json()["id"]
|
|
r = client.post(
|
|
"/api/projects/update",
|
|
headers=_auth_header("valid-token"),
|
|
json={"id": pid, "contract": {"projectName": "新名称"}},
|
|
)
|
|
assert r.status_code == 200
|
|
body = r.json()
|
|
assert "id" in body
|
|
assert body.get("message") == "保存成功"
|
|
|
|
|
|
def test_update_nonexistent_project_returns_404(client):
|
|
"""不存在项目时返回 404"""
|
|
r = client.post(
|
|
"/api/projects/update",
|
|
headers=_auth_header("valid-token"),
|
|
json={"id": "non-existent-id"},
|
|
)
|
|
assert r.status_code == 404
|
|
assert r.json().get("code") == 404
|
|
|
|
|
|
# ---------- POST /api/projects/logs ----------
|
|
|
|
|
|
def test_logs_without_token_returns_401(client):
|
|
"""操作日志无 token 时返回 401"""
|
|
r = client.post("/api/projects/logs", json={"id": "any-id"})
|
|
assert r.status_code == 401
|
|
|
|
|
|
def test_logs_with_token_returns_200_with_list_total_page_page_size(client):
|
|
"""有 token 时返回 200 且包含 list、total、page、pageSize"""
|
|
create_r = client.post(
|
|
"/api/projects/create",
|
|
headers=_auth_header("market-token"),
|
|
json={"contract": {"contractCode": "HT003", "projectName": "测试项目3"}},
|
|
)
|
|
assert create_r.status_code == 201
|
|
pid = create_r.json()["id"]
|
|
r = client.post(
|
|
"/api/projects/logs",
|
|
headers=_auth_header("valid-token"),
|
|
json={"id": pid},
|
|
)
|
|
assert r.status_code == 200
|
|
body = r.json()
|
|
assert "list" in body
|
|
assert isinstance(body["list"], list)
|
|
assert "total" in body
|
|
assert "page" in body
|
|
assert "pageSize" in body
|
|
|
|
|
|
def test_logs_nonexistent_project_returns_404(client):
|
|
"""不存在项目时返回 404"""
|
|
r = client.post(
|
|
"/api/projects/logs",
|
|
headers=_auth_header("valid-token"),
|
|
json={"id": "non-existent-id"},
|
|
)
|
|
assert r.status_code == 404
|
|
assert r.json().get("code") == 404
|