"""Canonical tests: WESP GfE 2001 ingredient derive (не Excel CSV).""" from __future__ import annotations import json import math import unittest from pathlib import Path from app.lab.calc.gfe_policies import DeriveContext, default_om_digestibility_pct from app.lab.calc.ingredient_derive import derive_ingredient_nutrients _FIXTURES = Path(__file__).resolve().parent / "fixtures" / "lab_derive_canonical.json" def _load_cases() -> list[dict]: with _FIXTURES.open(encoding="utf-8") as f: return json.load(f) class LabIngredientDeriveTests(unittest.TestCase): def _assert_close(self, expected: float, actual: float, label: str) -> None: tol = max(1e-3, abs(expected) * 1e-4) self.assertTrue( math.isclose(expected, actual, abs_tol=tol), f"{label}: expected {expected}, got {actual}", ) def test_derive_canonical_cases(self) -> None: for case in _load_cases(): if "expected" not in case: continue ctx_raw = case.get("context") or {} ctx = DeriveContext( feed_group=ctx_raw.get("feed_group", "unknown"), is_main_feed=bool(ctx_raw.get("is_main_feed")), ) derived = derive_ingredient_nutrients(case["input"], context=ctx) for key, expected in case["expected"].items(): self.assertIn(key, derived, msg=f"{case['id']}: missing {key}") self._assert_close(float(expected), derived[key], f"{case['id']}/{key}") def test_rnb_from_derive(self) -> None: case = next(c for c in _load_cases() if c.get("id") == "rnb_formula") ctx = DeriveContext(feed_group="concentrate") derived = derive_ingredient_nutrients(case["input"], context=ctx) cp = derived["уСП формул"] rnb = (derived["Сыр. Протеин"] - cp) / 6.25 self._assert_close(case["expected_rnb"], rnb, "rnb_formula") self._assert_close(case["expected_rnb"], derived["БРА"], "rnb_header") def test_omd_default_by_feed_class(self) -> None: self.assertEqual(default_om_digestibility_pct(DeriveContext(feed_group="rough")), 65.0) self.assertEqual( default_om_digestibility_pct(DeriveContext(feed_group="succulent")), 72.0, ) self.assertEqual( default_om_digestibility_pct(DeriveContext(feed_group="concentrate")), 91.0, ) self.assertEqual( default_om_digestibility_pct(DeriveContext(is_main_feed=True)), 65.0, ) def test_straw_without_omd_not_91_pct(self) -> None: case = next(c for c in _load_cases() if c.get("id") == "straw_default_omd_65") ctx = DeriveContext(feed_group="rough", is_main_feed=True) derived = derive_ingredient_nutrients(case["input"], context=ctx) organic = case["input"]["СВ"] - case["input"]["Сырая зола"] wrong = organic * 0.91 self.assertLess(derived["Перевар Орг Вещ"], wrong - 100) if __name__ == "__main__": unittest.main()