test(cma): clear 10 pre-existing baseline failures (cma-baseline-tests-cleanup-20260915)

No production code changed. Root causes were all test-side staleness:

- test_cash TestVoucherImport: fixture filename contained 'test' -> rejected by the intentional import guard (d71c7fe); renamed, and missing-columns case now asserts the column check is what returns 400

- test_budget_tech_improve TestP1ValueCollect: seeded voucher_details source rows; collector deliberately skips empty aggregates since d71c7fe (no more implicit 0), and preview call now passes an explicit period

- test_roadmap_r2r5 / test_budget / test_ai_suggestions: seeds hardcoded 2026-08 (authoring month) while detectors/rule engine use the current month -> replaced with current-period seeds

pytest tests/ -q -rf: 1094 passed, 1 xfailed, 0 failed (baseline was 10 failed, 1084 passed, 1 xfailed)
This commit is contained in:
Hermes CI Fix
2026-09-15 18:36:46 +08:00
parent 9d9a79545a
commit fdf805ab8f
5 changed files with 65 additions and 15 deletions
+6 -3
View File
@@ -231,9 +231,12 @@ class TestRuleSuggestions:
create_test_user(db)
token = get_token_for_user(client)
kpi = create_test_kpi(db, target_value=100.0)
db.add(KPIValue(kpi_id=kpi.id, period="2026-08", actual_value=200.0))
db.add(BudgetPlan(entity_id=1, kpi_id=kpi.id, period="2026-08", budget_value=100.0,
budget_year=2026, budget_month=8, status="active"))
# 规则引擎按「当月」取预算(ai_analysis.py: _generate_rule_suggestions 的 period)
# 原用例写死 2026-08(编写时的当月),跨月后与实现错配 → 恒 0 条(2026-09 跨月实证)
period = datetime.now().strftime("%Y-%m")
db.add(KPIValue(kpi_id=kpi.id, period=period, actual_value=200.0))
db.add(BudgetPlan(entity_id=1, kpi_id=kpi.id, period=period, budget_value=100.0,
budget_year=int(period[:4]), budget_month=int(period[5:7]), status="active"))
db.commit()
resp = client.get("/api/cma/ai/dashboard-analysis", headers=auth_header(token))
+11 -3
View File
@@ -3,6 +3,7 @@ import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
from tests.conftest import create_test_user, get_token_for_user, auth_header, create_test_kpi
from datetime import datetime
from app.models.budget_plan import BudgetPlan
from app.models import KPIValue, SystemConfig, CashPlan
@@ -341,14 +342,21 @@ class TestBudgetRollForward:
data = resp.json()
assert "滚动预算已延展" in data["message"]
assert len(data["rolled_kpis"]) == 1
# 新增月份 = 当前月(08) + 12 = 明年08
assert data["rolled_kpis"][0]["added_period"].startswith("2027-")
# 新增月份 = 当前月 + rolling_months(12)(实现口径见 budget.py:budget_roll_forward)
# 用例原写死 2026-08+12=2027-08,跨月后与实现错配(2026-09 跨月实证)
now = datetime.now()
exp_year, exp_month = now.year, now.month + 12
while exp_month > 12:
exp_month -= 12
exp_year += 1
expected_period = f"{exp_year}-{exp_month:02d}"
assert data["rolled_kpis"][0]["added_period"] == expected_period
# 最早月(2026-01)被删除
from app.models import BudgetPlan as BP
periods = [p.period for p in db.query(BP).filter(BP.kpi_id == kpi.id).all()]
assert "2026-01" not in periods
assert "2027-08" in periods
assert expected_period in periods
class TestBudgetComparison:
+25 -1
View File
@@ -11,17 +11,36 @@
import pytest
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
import datetime
from tests.conftest import create_test_user, get_token_for_user, auth_header, create_test_kpi
from app.models import (
BudgetPlan, KPIValue, BudgetDeviationAlert, KPIAlert,
KPIValueSource, KPIValueCollectLog,
KPIValueSource, KPIValueCollectLog, VoucherDetail,
BudgetZeroBasedItem, BudgetDerivationRule,
CashPlanClassifyRule, CashPlanUnclassified, CashPlan,
ScenarioSuggestion, KPIDefinition,
)
def _seed_vouchers(db, period="2026-06", entity_id=1, credit=1500.0):
"""给自动归集造源数据(voucher_details 贷方发生额)。
采集器口径(d71c7fe「自动归集空汇总不写0」):源表无记录 → 返回 None 并记 skipped,
不落 0 值 KPIValue。故本类用例必须先造源数据,否则 collected 恒为 0。
"""
y, m = int(period[:4]), int(period[5:7])
db.add(VoucherDetail(
entity_id=entity_id, voucher_no="COLLECT-001",
voucher_date=datetime.datetime(y, m, 15),
subject_code="6001", subject_name="主营业务收入",
debit_amount=0.0, credit_amount=credit,
summary="自动归集单测", carry_forward=0, period=period,
))
db.flush()
class TestP1AlertAttribution:
"""P1-③ 告警归因: 告警从'差多少'到'差在哪+怎么办'"""
@@ -114,6 +133,7 @@ class TestP1ValueCollect:
create_test_user(db)
token = get_token_for_user(client)
kpi = create_test_kpi(db, kpi_code="COLLECT_KPI")
_seed_vouchers(db) # 源数据(无源数据采集器按设计跳过)
# 建映射
resp = client.post(f"{self.BASE}/value-sources", headers=auth_header(token), json={
@@ -135,9 +155,11 @@ class TestP1ValueCollect:
"aggregate": "sum",
"filter_rule": {"direction": "credit"},
"period_field": "period",
"period": "2026-06", # 显式期间(不传则默认当月,与造数期间错配 → value=None)
})
assert test_resp.status_code == 200
assert test_resp.json()["value"] is not None
assert test_resp.json()["value"] == 1500.0 # 贷方发生额合计(unit_conversion=1)
# 手动触发采集
run_resp = client.post(f"{self.BASE}/value-collect/run", headers=auth_header(token),
@@ -169,6 +191,7 @@ class TestP1ValueCollect:
create_test_user(db)
token = get_token_for_user(client)
kpi = create_test_kpi(db, kpi_code="COLLECT_IDEMP")
_seed_vouchers(db)
client.post(f"{self.BASE}/value-sources", headers=auth_header(token), json={
"kpi_id": kpi.id, "source_table": "voucher_details",
"source_field": "credit_amount", "aggregate": "sum",
@@ -188,6 +211,7 @@ class TestP1ValueCollect:
create_test_user(db)
token = get_token_for_user(client)
kpi = create_test_kpi(db, kpi_code="COLLECT_LOG")
_seed_vouchers(db)
client.post(f"{self.BASE}/value-sources", headers=auth_header(token), json={
"kpi_id": kpi.id, "source_table": "voucher_details",
"source_field": "credit_amount", "aggregate": "sum",
+4 -1
View File
@@ -234,7 +234,9 @@ class TestVoucherImport:
buf.seek(0)
return buf
def _upload(self, client, token, buf, fname="test_vouchers.xlsx"):
def _upload(self, client, token, buf, fname="vouchers_import.xlsx"):
"""注意: 文件名不得含 test/示例/sample/demo 等测试标识——导入端点有生产防线
(d71c7fe「Excel导入示例文件防线」),含标识直接 400,与三条校验规则的用例无关。"""
return client.post(
f"{BASE}/import/vouchers", headers=auth_header(token),
files={"file": (fname, buf,
@@ -315,3 +317,4 @@ class TestVoucherImport:
buf = self._xlsx([["2026-08-01", "记-001", 100, 0]], cols=["日期", "凭证号", "借方金额", "贷方金额"])
resp = self._upload(client, token, buf)
assert resp.status_code == 400
assert "缺少必要列" in resp.json()["detail"] # 断言是列校验拦下,而非文件名防线
+19 -7
View File
@@ -16,6 +16,15 @@ from scripts.opportunity_detector import (
from scripts.closed_loop_check import check_entity, build_report
def _cur_period() -> str:
"""当前月字符串。
detect_budget_headroom 的口径是「当月预算执行率<70%」(实现内取 datetime.now()),
用例里硬编码月份会在跨月后与实现错配、恒判 0 条(2026-09 跨月实证:原写死 2026-08)。
"""
return datetime.now().strftime("%Y-%m")
def _kpi(db, code, target=100.0, **kw):
return create_test_kpi(db, kpi_code=code, target_value=target, **kw)
@@ -68,9 +77,10 @@ class TestOpportunityR2:
def test_budget_headroom(self, db):
"""当月预算执行率<70% → 预算余量机会"""
p = _cur_period()
kpi = _kpi(db, "OPP_04", target=1000.0)
_value(db, kpi.id, "2026-08", 300.0)
_budget(db, kpi.id, "2026-08", 1000.0)
_value(db, kpi.id, p, 300.0)
_budget(db, kpi.id, p, 1000.0)
db.commit()
out = detect_budget_headroom(db, 1)
assert len(out) == 1
@@ -78,18 +88,20 @@ class TestOpportunityR2:
def test_budget_headroom_negative_skip(self, db):
"""实际值为负(现金流异常)不误判为余量"""
p = _cur_period()
kpi = _kpi(db, "OPP_05", target=1000.0)
_value(db, kpi.id, "2026-08", -500.0)
_budget(db, kpi.id, "2026-08", 1000.0)
_value(db, kpi.id, p, -500.0)
_budget(db, kpi.id, p, 1000.0)
db.commit()
assert detect_budget_headroom(db, 1) == []
def test_budget_headroom_dedup(self, db):
"""同KPI同期间多版本预算只取一条"""
p = _cur_period()
kpi = _kpi(db, "OPP_06", target=1000.0)
_value(db, kpi.id, "2026-08", 300.0)
_budget(db, kpi.id, "2026-08", 1000.0)
b2 = _budget(db, kpi.id, "2026-08", 2000.0)
_value(db, kpi.id, p, 300.0)
_budget(db, kpi.id, p, 1000.0)
b2 = _budget(db, kpi.id, p, 2000.0)
b2.version = "v2.0"
db.commit()
assert len(detect_budget_headroom(db, 1)) == 1