from __future__ import annotations from datetime import datetime, timezone from vpp_contracts.report_request import ReportRequest from vpp_skills.report import generate_report, resolve, verify_report CLOCK = lambda: datetime(2026, 3, 14, 8, 30, tzinfo=timezone.utc) # noqa: E731 SOLVER_OUTPUT = { "market_date": "2026-03-15", "prices_yuan_per_mwh": {"interval_minutes": 15, "date": "2026-03-15", "values": ["1.0"] * 96}, "daily_energy_mwh": "1200.000", "expected_revenue_yuan": "510600.00", "revenue_distribution_yuan": {"p10": "432000.00", "p50": "510600.00", "p90": "588000.00"}, "solver": {"name": "highs", "status": "OPTIMAL", "objective_value": "487020.00", "wall_time_ms": 12}, "binding_constraints": ["daily_energy_max"], } def _req(kind="DAY_AHEAD_BID_SUMMARY") -> ReportRequest: return ReportRequest.model_validate( { "kind": kind, "market_date": "2026-03-15", "sources": [{"tool_call_id": "tc-001", "tool": "bid-optimization-milp", "version": "1.0.0", "output": SOLVER_OUTPUT}], } ) def test_every_metric_is_a_reference_that_resolves_to_its_value(): req = _req() rep = generate_report(req, CLOCK) names = {m.name for m in rep.sections[0].metrics} assert {"expected_revenue_yuan", "daily_energy_mwh", "revenue_distribution_yuan.p10", "solver.objective_value"} <= names assert "prices_yuan_per_mwh" not in " ".join(names) # curves are not inlined assert verify_report(rep, req) == [] for m in rep.sections[0].metrics: assert m.value == resolve(SOLVER_OUTPUT, m.ref.path) assert m.ref.tool_call_id == "tc-001" def test_units_inferred_from_field_names(): rep = generate_report(_req(), CLOCK) units = {m.name: m.unit for m in rep.sections[0].metrics} assert units["expected_revenue_yuan"] == "yuan" assert units["daily_energy_mwh"] == "mwh" assert units["revenue_distribution_yuan.p90"] == "yuan" def test_notes_carry_solver_status_and_binding_constraints(): rep = generate_report(_req(), CLOCK) notes = " | ".join(rep.sections[0].notes) assert "solver status: OPTIMAL" in notes and "daily_energy_max" in notes def test_deterministic_id_and_verify_catches_tampering(): req = _req() a, b = generate_report(req, CLOCK), generate_report(req, CLOCK) assert a.id == b.id and a.id.startswith("rep-day_ahead_bid_summary-2026-03-15-") tampered = a.model_copy(deep=True) tampered.sections[0].metrics[0].value = "999999.00" assert verify_report(tampered, req)