"""Unit-тесты пересчёта весов при замене компонента в плане.""" from __future__ import annotations import unittest from app.models import Component, Ingredient, Recipe from app.services.daily_plan.ingredient_weights import resolve_plan_ingredient_weights class PlanIngredientWeightsTests(unittest.TestCase): def _make(self, *, locked: bool, wph: float, dm_pct: float, repl_dm: float, dm_ph=None): recipe = Recipe(id="r1", name="R", dry_matter_locked=locked, content_hash="") ing = Ingredient( id="i1", name="Ing", weight_per_head=wph, amount=0.0, dry_matter=dm_pct, dry_matter_per_head=dm_ph, order=1, recipe_id="r1", component_id="c1", created_by="system", updated_by="system", ) components = { "c1": Component(id="c1", name="A", type="grain", dry_matter=dm_pct), "c2": Component(id="c2", name="B", type="silage", dry_matter=repl_dm), } return ing, recipe, components def test_weight_mode_keeps_kg_per_head(self) -> None: ing, recipe, components = self._make(locked=False, wph=2.0, dm_pct=55.0, repl_dm=35.0) out = resolve_plan_ingredient_weights( ing, recipe, heads=10, components_by_id=components, replacement_component_id="c2" ) self.assertEqual(out["recalculationMode"], "weight") self.assertEqual(out["weightPerHead"], 2.0) self.assertEqual(out["totalKg"], 20.0) self.assertEqual(out["dryMatterPct"], 35.0) self.assertAlmostEqual(out["dryMatterPerHead"], 0.7, places=4) def test_dry_matter_mode_keeps_dm_per_head(self) -> None: ing, recipe, components = self._make( locked=True, wph=1.0, dm_pct=55.0, repl_dm=35.0, dm_ph=0.55 ) out = resolve_plan_ingredient_weights( ing, recipe, heads=20, components_by_id=components, replacement_component_id="c2" ) self.assertEqual(out["recalculationMode"], "dry_matter") self.assertAlmostEqual(out["weightPerHead"], 1.57, places=2) self.assertAlmostEqual(out["totalKg"], 31.4, places=1) self.assertAlmostEqual(out["dryMatterPerHead"], 0.55, places=4) def test_dry_matter_pct_adjustment_locked(self) -> None: ing, recipe, components = self._make( locked=True, wph=1.0, dm_pct=55.0, repl_dm=35.0, dm_ph=0.55 ) out = resolve_plan_ingredient_weights( ing, recipe, heads=10, components_by_id=components, component_adjustment={ "dry_matter": 40.0, "dry_matter_locked": True, "weight_per_head": None, "dry_matter_per_head": None, }, ) self.assertTrue(out["adjustedToday"]) self.assertEqual(out["dryMatterPct"], 40.0) self.assertAlmostEqual(out["dryMatterPerHead"], 0.55, places=4) self.assertAlmostEqual(out["weightPerHead"], 1.38, places=2) def test_dry_matter_pct_adjustment(self) -> None: ing, recipe, components = self._make(locked=False, wph=2.0, dm_pct=55.0, repl_dm=35.0) out = resolve_plan_ingredient_weights( ing, recipe, heads=10, components_by_id=components, component_adjustment={"dry_matter": 40.0, "weight_per_head": None, "dry_matter_per_head": None}, ) self.assertTrue(out["adjustedToday"]) self.assertEqual(out["weightPerHead"], 2.0) self.assertEqual(out["dryMatterPct"], 40.0) self.assertAlmostEqual(out["dryMatterPerHead"], 0.8, places=4) self.assertEqual(out["originalWeightPerHead"], 2.0) self.assertEqual(out["originalDryMatterPct"], 55.0) def test_weight_mode_adjustment(self) -> None: ing, recipe, components = self._make(locked=False, wph=2.0, dm_pct=55.0, repl_dm=35.0) out = resolve_plan_ingredient_weights( ing, recipe, heads=10, components_by_id=components, component_adjustment={"weight_per_head": 3.0, "dry_matter_per_head": None}, ) self.assertTrue(out["adjustedToday"]) self.assertEqual(out["weightPerHead"], 3.0) self.assertAlmostEqual(out["dryMatterPerHead"], 1.65, places=4) self.assertEqual(out["originalWeightPerHead"], 2.0) def test_dry_matter_mode_adjustment(self) -> None: ing, recipe, components = self._make( locked=True, wph=1.0, dm_pct=55.0, repl_dm=35.0, dm_ph=0.55 ) out = resolve_plan_ingredient_weights( ing, recipe, heads=20, components_by_id=components, component_adjustment={"weight_per_head": None, "dry_matter_per_head": 0.7}, ) self.assertTrue(out["adjustedToday"]) self.assertAlmostEqual(out["dryMatterPerHead"], 0.7, places=4) self.assertAlmostEqual(out["weightPerHead"], 1.27, places=2) if __name__ == "__main__": unittest.main()