Files
AIPortPilot/backend/tests/test_evaluation_api.py

300 lines
10 KiB
Python

"""评价指标体系 API 集成测试。"""
from fastapi.testclient import TestClient
class TestWeightComputeAPI:
"""权重计算 API 测试。"""
def test_compute_weights_success(self, client: TestClient, auth_headers: dict):
"""POST /evaluation/weights/compute 应返回权重计算结果。"""
resp = client.post(
"/api/v1/evaluation/weights/compute",
json={
"fund_type": "early_vc",
"fund_lifecycle": "investment",
"company_stage": "a",
"industry": "ai",
"strategy": "growth",
"investor_type": "investor",
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert "weights" in data
assert "enabled_dimensions" in data
assert "disabled_dimensions" in data
assert "custom_metrics" in data
# 权重总和应接近 100
total = sum(data["weights"].values())
assert abs(total - 100.0) < 1.0
def test_compute_weights_no_auth(self, client: TestClient):
"""未认证应返回 401。"""
resp = client.post(
"/api/v1/evaluation/weights/compute",
json={
"fund_type": "early_vc",
"company_stage": "a",
"industry": "ai",
},
)
assert resp.status_code == 401
def test_compute_weights_hardware_disables_ai(self, client: TestClient, auth_headers: dict):
"""硬科技赛道应禁用 AI 维度。"""
resp = client.post(
"/api/v1/evaluation/weights/compute",
json={
"fund_type": "early_vc",
"fund_lifecycle": "investment",
"company_stage": "a",
"industry": "hardware",
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert "ai_commercial" in data["disabled_dimensions"]
assert "ai_cost" in data["disabled_dimensions"]
class TestTemplateAPI:
"""评价模板 API 测试。"""
def test_list_templates_empty(self, client: TestClient, auth_headers: dict):
"""无模板时应返回空列表。"""
resp = client.get("/api/v1/evaluation/templates", headers=auth_headers)
assert resp.status_code == 200
assert isinstance(resp.json()["data"], list)
def test_list_templates_no_auth(self, client: TestClient):
"""未认证应返回 401。"""
resp = client.get("/api/v1/evaluation/templates")
assert resp.status_code == 401
def test_create_template_auto_weights(self, client: TestClient, auth_headers: dict):
"""创建模板时不传权重应自动计算。"""
resp = client.post(
"/api/v1/evaluation/templates",
json={
"name": "测试模板-早期VC-AI",
"fund_type": "early_vc",
"fund_lifecycle": "investment",
"company_stage": "a",
"industry": "ai",
"strategy": "growth",
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert "id" in data
assert "weights" in data
def test_create_template_with_custom_weights(self, client: TestClient, auth_headers: dict):
"""创建模板时传自定义权重应使用自定义权重。"""
resp = client.post(
"/api/v1/evaluation/templates",
json={
"name": "自定义权重模板",
"fund_type": "early_vc",
"fund_lifecycle": "growth",
"company_stage": "b",
"industry": "saas",
"strategy": "value",
"weights_json": {"financial": 40, "product_tech": 30, "market_compete": 30},
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert data["weights"]["financial"] == 40
def test_get_template_by_id(self, client: TestClient, auth_headers: dict):
"""根据 ID 获取模板详情。"""
# 先创建
create_resp = client.post(
"/api/v1/evaluation/templates",
json={
"name": "查询测试模板",
"fund_type": "pe",
"fund_lifecycle": "growth",
"company_stage": "c",
"industry": "fintech",
},
headers=auth_headers,
)
template_id = create_resp.json()["data"]["id"]
# 再查询
resp = client.get(f"/api/v1/evaluation/templates/{template_id}", headers=auth_headers)
assert resp.status_code == 200
data = resp.json()["data"]
assert data["name"] == "查询测试模板"
assert data["fund_type"] == "pe"
def test_get_template_not_found(self, client: TestClient, auth_headers: dict):
"""查询不存在的模板应返回 404。"""
resp = client.get(
"/api/v1/evaluation/templates/nonexistent-id",
headers=auth_headers,
)
assert resp.status_code == 200
assert resp.json()["code"] == 404
def test_list_templates_with_filter(self, client: TestClient, auth_headers: dict):
"""按基金类型筛选模板。"""
# 创建两个不同类型模板
client.post(
"/api/v1/evaluation/templates",
json={
"name": "筛选-早期VC",
"fund_type": "early_vc",
"company_stage": "a",
"industry": "ai",
},
headers=auth_headers,
)
client.post(
"/api/v1/evaluation/templates",
json={
"name": "筛选-PE",
"fund_type": "pe",
"company_stage": "b",
"industry": "saas",
},
headers=auth_headers,
)
resp = client.get(
"/api/v1/evaluation/templates?fund_type=pe",
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
for tmpl in data:
assert tmpl["fund_type"] == "pe"
class TestScoreCalculateAPI:
"""评分计算 API 测试。"""
def test_calculate_score_without_template(self, client: TestClient, auth_headers: dict, company_id: str):
"""无模板时计算评分应使用默认计算。"""
resp = client.post(
"/api/v1/evaluation/score",
json={
"company_id": company_id,
"structured_data": {
"revenue": {"yoy_change": "30"},
"cash_balance": {"runway_months": 18},
"burn_rate": {"trend": "down"},
},
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert "total_score" in data
assert "dimension_scores" in data
def test_calculate_score_no_auth(self, client: TestClient):
"""未认证应返回 401。"""
resp = client.post(
"/api/v1/evaluation/score",
json={"company_id": "test", "structured_data": {}},
)
assert resp.status_code == 401
def test_calculate_score_with_template(self, client: TestClient, auth_headers: dict, company_id: str):
"""使用模板计算评分。"""
# 先创建模板
tmpl_resp = client.post(
"/api/v1/evaluation/templates",
json={
"name": "评分测试模板",
"fund_type": "early_vc",
"company_stage": "a",
"industry": "ai",
},
headers=auth_headers,
)
template_id = tmpl_resp.json()["data"]["id"]
# 使用模板计算评分
resp = client.post(
"/api/v1/evaluation/score",
json={
"company_id": company_id,
"template_id": template_id,
"structured_data": {
"revenue": {"yoy_change": "25"},
"cash_balance": {"runway_months": 15},
"burn_rate": {"trend": "down"},
"headcount": {"new_hires": 3, "departures": 1},
},
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert "score_id" in data
assert "total_score" in data
assert data["template"] is not None
assert data["template"]["id"] == template_id
class TestScoreHistoryAPI:
"""评分历史 API 测试。"""
def test_list_scores_empty(self, client: TestClient, auth_headers: dict):
"""无评分记录时应返回空列表。"""
resp = client.get("/api/v1/evaluation/scores", headers=auth_headers)
assert resp.status_code == 200
assert isinstance(resp.json()["data"], list)
def test_list_scores_no_auth(self, client: TestClient):
"""未认证应返回 401。"""
resp = client.get("/api/v1/evaluation/scores")
assert resp.status_code == 401
class TestFundAPI:
"""基金管理 API 测试。"""
def test_list_funds_empty(self, client: TestClient, auth_headers: dict):
"""无基金时应返回空列表。"""
resp = client.get("/api/v1/evaluation/funds", headers=auth_headers)
assert resp.status_code == 200
assert isinstance(resp.json()["data"], list)
def test_create_fund(self, client: TestClient, auth_headers: dict):
"""创建基金。"""
resp = client.post(
"/api/v1/evaluation/funds",
json={
"name": "测试基金一期",
"fund_type": "early_vc",
"strategy": "growth",
"established_date": "2023-01-01",
"total_lifespan_months": 84,
"investment_period_months": 48,
"primary_market": "china_mainland",
},
headers=auth_headers,
)
assert resp.status_code == 200
data = resp.json()["data"]
assert "id" in data
assert data["current_lifecycle"] == "investment"
def test_create_fund_no_auth(self, client: TestClient):
"""未认证应返回 401。"""
resp = client.post(
"/api/v1/evaluation/funds",
json={"name": "test", "fund_type": "early_vc"},
)
assert resp.status_code == 401