Files
ocean/backend/tests/test_projects_api.py
T
2026-01-31 11:02:59 +08:00

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