Spaces:
Running
Running
| """ | |
| Derived-measure formulas must fail loudly, not generate zeros. | |
| Regression: `_eval_formula` caught every exception and returned 0.0, and | |
| `_eval_node` resolved an unknown identifier to 0.0. So a formula whose column | |
| name did not match — `REVENUE - COGS` when the measures are actually | |
| TOTAL_REVENUE / TOTAL_COGS — evaluated to `0 - 0 = 0` for every single row. | |
| The column loaded 100% zero, the liveboard tile rendered blank, and the run | |
| died much later in the post-load quality gate with no indication of WHICH | |
| formula was wrong. Yodeck failed that gate with 12/12 dead derived columns and | |
| no retry — the run just ended. | |
| Formulas are now validated before a single row is generated, and every problem | |
| is reported at once. | |
| Run with: pytest tests/test_formula_validation.py -v | |
| """ | |
| import pytest | |
| from demoprep_app.dataset.engine import ( | |
| FormulaError, | |
| _eval_formula, | |
| validate_fact_formulas, | |
| ) | |
| from demoprep_app.scenario.blueprint import BlueprintMeasure, FactSpec | |
| def fact(*measures, name="FACT_SALES"): | |
| return FactSpec(name=name, grain="one row per order", | |
| dimension_names=[], measures=list(measures)) | |
| def base(name): | |
| return BlueprintMeasure(name=name, kind="base") | |
| def derived(name, formula): | |
| return BlueprintMeasure(name=name, kind="derived", formula=formula) | |
| class TestTheYodeckCondition: | |
| def test_typod_measure_name_is_reported_not_zeroed(self): | |
| """The exact defect: formula names don't match the measures.""" | |
| spec = fact( | |
| base("TOTAL_REVENUE"), | |
| base("TOTAL_COGS"), | |
| derived("GROSS_MARGIN", "REVENUE - COGS"), | |
| ) | |
| with pytest.raises(FormulaError) as exc: | |
| validate_fact_formulas(spec) | |
| msg = str(exc.value) | |
| assert "GROSS_MARGIN" in msg | |
| assert "'REVENUE'" in msg | |
| assert "unknown" in msg | |
| def test_suggests_the_intended_measure(self): | |
| spec = fact( | |
| base("TOTAL_REVENUE"), | |
| derived("NET", "TOTAL_REVENUEE * 2"), | |
| ) | |
| with pytest.raises(FormulaError) as exc: | |
| validate_fact_formulas(spec) | |
| assert "did you mean 'TOTAL_REVENUE'" in str(exc.value) | |
| def test_reports_every_problem_at_once(self): | |
| """Five typos must not cost five regenerate cycles.""" | |
| spec = fact( | |
| base("A"), | |
| derived("D1", "NOPE1 + A"), | |
| derived("D2", "NOPE2 - A"), | |
| derived("D3", "NOPE3 * A"), | |
| ) | |
| with pytest.raises(FormulaError) as exc: | |
| validate_fact_formulas(spec) | |
| msg = str(exc.value) | |
| assert "3 unusable derived measure formula(s)" in msg | |
| for name in ("NOPE1", "NOPE2", "NOPE3"): | |
| assert name in msg | |
| class TestOtherStructuralFaults: | |
| def test_derived_without_formula(self): | |
| spec = fact(base("A"), derived("D", "")) | |
| with pytest.raises(FormulaError, match="declared derived but has no formula"): | |
| validate_fact_formulas(spec) | |
| def test_unparseable_formula(self): | |
| spec = fact(base("A"), derived("D", "A +")) | |
| with pytest.raises(FormulaError, match="does not parse"): | |
| validate_fact_formulas(spec) | |
| def test_function_call_rejected(self): | |
| spec = fact(base("A"), derived("D", "SUM(A)")) | |
| with pytest.raises(FormulaError, match="calls a function"): | |
| validate_fact_formulas(spec) | |
| def test_unsupported_operator_rejected(self): | |
| spec = fact(base("A"), derived("D", "A % 2")) | |
| with pytest.raises(FormulaError, match="unsupported operator"): | |
| validate_fact_formulas(spec) | |
| def test_comparison_rejected(self): | |
| spec = fact(base("A"), base("B"), derived("D", "A > B")) | |
| with pytest.raises(FormulaError, match="unsupported expression"): | |
| validate_fact_formulas(spec) | |
| class TestValidFormulasPass: | |
| def test_simple_arithmetic(self): | |
| spec = fact( | |
| base("NET_SALES"), | |
| base("COST_AMOUNT"), | |
| derived("MARGIN", "NET_SALES - COST_AMOUNT"), | |
| ) | |
| validate_fact_formulas(spec) # must not raise | |
| def test_derived_referencing_derived(self): | |
| spec = fact( | |
| base("REVENUE"), | |
| base("COGS"), | |
| derived("MARGIN", "REVENUE - COGS"), | |
| derived("MARGIN_RATE", "MARGIN / REVENUE"), | |
| ) | |
| validate_fact_formulas(spec) | |
| def test_constants_and_parens(self): | |
| spec = fact(base("A"), derived("D", "(A * 100) / 2 + 1")) | |
| validate_fact_formulas(spec) | |
| def test_base_measures_are_not_checked(self): | |
| """A base measure with a stray formula string is not a derived defect.""" | |
| m = BlueprintMeasure(name="A", kind="base", formula="WHATEVER") | |
| validate_fact_formulas(fact(m)) | |
| class TestPerRowEvaluation: | |
| def test_division_by_zero_is_still_a_legitimate_zero(self): | |
| """Per-row arithmetic edge cases must stay tolerant.""" | |
| assert _eval_formula("REVENUE / ORDERS", {"REVENUE": 100.0, "ORDERS": 0.0}) == 0.0 | |
| def test_unknown_name_at_eval_time_raises(self): | |
| with pytest.raises(FormulaError, match="not an available measure"): | |
| _eval_formula("REVENUE - MISSING", {"REVENUE": 100.0}) | |
| def test_normal_arithmetic(self): | |
| assert _eval_formula("A - B", {"A": 10.0, "B": 4.0}) == 6.0 | |
| assert _eval_formula("-A", {"A": 3.0}) == -3.0 | |