Files
fa-agent/tests/test_formula_metric_availability.py
T
2026-09-26 01:12:10 +08:00

127 lines
4.1 KiB
Python

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"]