This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
import json
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from finance.formula_columns import available_metrics, ensure_available_metrics
|
||||
from finance.formula_views import design_column
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def model_response(design):
|
||||
return type(
|
||||
"Response",
|
||||
(),
|
||||
{
|
||||
"raise_for_status": lambda self: None,
|
||||
"json": lambda self: {
|
||||
"status": "completed",
|
||||
"output": [{"content": [{"type": "output_text", "text": json.dumps(design)}]}],
|
||||
},
|
||||
},
|
||||
)()
|
||||
|
||||
|
||||
def test_available_metrics_follow_dataset_mapping(workspace):
|
||||
ds, *_ = workspace
|
||||
assert set(available_metrics(ds)) == {
|
||||
"revenue",
|
||||
"cost",
|
||||
"gross",
|
||||
"roi",
|
||||
"customers",
|
||||
"basket",
|
||||
"margin",
|
||||
"discount_rate",
|
||||
"self_revenue",
|
||||
"upgrade_rate",
|
||||
"list_basket",
|
||||
}
|
||||
|
||||
ds.mapping = {
|
||||
**ds.mapping,
|
||||
"columns": [
|
||||
column
|
||||
for column in ds.mapping["columns"]
|
||||
if column["role"] not in {"cost", "gross", "customer", "list_amount"}
|
||||
],
|
||||
"self_operated_products": [],
|
||||
"margin_rates": {},
|
||||
}
|
||||
assert set(available_metrics(ds)) == {"revenue"}
|
||||
with pytest.raises(ValueError, match="消费人数"):
|
||||
ensure_available_metrics(ds, {"expression": {"metric": "customers"}})
|
||||
|
||||
|
||||
def test_model_cannot_complete_definition_using_unavailable_metrics(workspace, settings):
|
||||
ds, *_ = workspace
|
||||
settings.AI_ENABLED = True
|
||||
settings.OPENAI_API_KEY = "test-key"
|
||||
settings.OPENAI_MODEL = "test-model"
|
||||
definition = {
|
||||
"name": "收入乘消费人数",
|
||||
"meaning": "实际收入乘消费人数",
|
||||
"scope": "按所选分组和期间",
|
||||
"missing": "结果不可用(#N/A);零分母不可用",
|
||||
"expression": {
|
||||
"operator": "multiply",
|
||||
"left": {"metric": "revenue"},
|
||||
"right": {"metric": "customers"},
|
||||
},
|
||||
}
|
||||
ds.mapping = {
|
||||
**ds.mapping,
|
||||
"columns": [column for column in ds.mapping["columns"] if column["role"] != "customer"],
|
||||
}
|
||||
with patch("finance.formula_views.httpx.Client") as client:
|
||||
client.return_value.__enter__.return_value.post.return_value = model_response(
|
||||
{"complete": True, "questions": [], "column": definition}
|
||||
)
|
||||
result = design_column(
|
||||
[{"role": "user", "content": "增加收入乘消费人数列"}],
|
||||
ds,
|
||||
{"current": ["2026-08"], "baseline": ["2025-08"], "filters": {}, "group": "department"},
|
||||
)
|
||||
|
||||
assert result["complete"] is False
|
||||
assert result["column"] is None
|
||||
assert "消费人数" in result["questions"][0]
|
||||
payload = client.return_value.__enter__.return_value.post.call_args.kwargs["json"]
|
||||
system = payload["input"][0]["content"]
|
||||
assert '"customers": "消费人数"' in system.split('"unavailable_metrics":', 1)[1]
|
||||
metric_schema = payload["text"]["format"]["schema"]["properties"]["column"]["anyOf"][1]["properties"][
|
||||
"expression"
|
||||
]["anyOf"][0]["properties"]["metric"]
|
||||
assert "customers" not in metric_schema["enum"]
|
||||
assert "revenue" in metric_schema["enum"]
|
||||
|
||||
|
||||
def test_export_api_rejects_unavailable_metric_even_without_design_dialog(workspace):
|
||||
ds, _, client, *_ = workspace
|
||||
definition = {
|
||||
"name": "人数列",
|
||||
"meaning": "消费人数",
|
||||
"scope": "按分组期间",
|
||||
"missing": "结果不可用(#N/A);零分母不可用",
|
||||
"expression": {"metric": "customers"},
|
||||
}
|
||||
ds.mapping = {
|
||||
**ds.mapping,
|
||||
"columns": [column for column in ds.mapping["columns"] if column["role"] != "customer"],
|
||||
}
|
||||
ds.save(update_fields=["mapping"])
|
||||
response = client.post(
|
||||
f"/api/datasets/{ds.pk}/formula-exports",
|
||||
data={
|
||||
"start": "2026-08",
|
||||
"end": "2026-08",
|
||||
"comparison": "yoy",
|
||||
"group": "department",
|
||||
"custom_columns": [definition],
|
||||
},
|
||||
content_type="application/json",
|
||||
)
|
||||
assert response.status_code == 400
|
||||
assert "消费人数" in response.json()["error"]
|
||||
Reference in New Issue
Block a user