"""dry_matter_locked: серверный пересчёт весов при сохранении рецепта.""" from __future__ import annotations import os import tempfile import unittest from app import create_app, db from app.models import Ingredient, Recipe from app.services.recipe_calculator import calculate_recipe from app.services.recipe_update_service import RecipeUpdateError, update_recipe_from_payload from config import TestingConfig from tests.helpers.mill_recipe_fixtures import ( create_component, create_recipe_with_children, ingredient_payload_from_row, recipe_update_payload, ) class RecipeDryMatterLockedConfig(TestingConfig): _TMP_DIR = tempfile.mkdtemp(prefix="wesp-dm-lock-") SQLALCHEMY_DATABASE_URI = f"sqlite:///{os.path.join(_TMP_DIR, 'dm_lock_test.db')}" SQLALCHEMY_BINDS = {"reports": f"sqlite:///{os.path.join(_TMP_DIR, 'reports_test.db')}"} AUTH_LOGIN = "dm-lock-admin" AUTH_PASSWORD = "dm-lock-secret" SCENARIO_SAVE_DRY_MATTER_LOCKED = "SAVE_DRY_MATTER_LOCKED" class RecipeDryMatterLockedTests(unittest.TestCase): def setUp(self) -> None: self.app = create_app(RecipeDryMatterLockedConfig) self.client = self.app.test_client() self.ctx = self.app.app_context() self.ctx.push() db.create_all() self.client.post( "/api/auth/login", json={"login": "dm-lock-admin", "password": "dm-lock-secret"}, ) def tearDown(self) -> None: db.session.remove() db.drop_all() self.ctx.pop() def test_update_recipe_from_payload_recalculates_when_locked(self) -> None: comp_id = create_component(dry_matter=50.0) recipe_id, ing_ids, _ = create_recipe_with_children( component_ids=[comp_id], heads=10, with_groups=False ) recipe = db.session.get(Recipe, recipe_id) recipe.dry_matter_locked = True db.session.commit() ing = db.session.get(Ingredient, ing_ids[0]) payload = recipe_update_payload( recipe, ingredients=[ { "id": ing_ids[0], "component_id": comp_id, "dry_matter": 50.0, "dry_matter_per_head": 1.0, "order": 1, } ], groups=[], ) payload["dry_matter_locked"] = True payload["heads_count"] = 10 payload["trip_percent"] = 100.0 update_recipe_from_payload(recipe_id, payload) db.session.commit() db.session.refresh(ing) self.assertEqual(ing.weight_per_head, 2.0) self.assertEqual(ing.amount, 20.0) def test_locked_save_matches_calculate_api_math(self) -> None: comp_id = create_component(dry_matter=60.0) recipe_id, ing_ids, _ = create_recipe_with_children( component_ids=[comp_id], heads=20, with_groups=False ) recipe = db.session.get(Recipe, recipe_id) recipe.dry_matter_locked = True db.session.commit() dm_per_head = 0.6 expected = calculate_recipe( ingredients=[{"dryMatterPerHead": dm_per_head, "dryMatter": 60.0}], heads_count=20, trip_percent=100.0, calculate_from_dry_matter=True, ) resp = self.client.put( f"/api/recipes/{recipe_id}", json={ "name": recipe.name, "heads_count": 20, "trip_percent": 100.0, "mixing_time": recipe.mixing_time, "target_component_id": comp_id, "dry_matter_locked": True, "unloading_link_broken": False, "ingredients": [ { "id": ing_ids[0], "component_id": comp_id, "dry_matter": 60.0, "dry_matter_per_head": dm_per_head, "order": 1, } ], "unloading_groups": [], }, ) self.assertEqual(resp.status_code, 200, resp.get_data(as_text=True)) get_resp = self.client.get(f"/api/recipes/{recipe_id}") self.assertEqual(get_resp.status_code, 200) ing = get_resp.get_json()["ingredients"][0] self.assertEqual( float(ing["weight_per_head"]), expected["ingredients"][0]["weightPerHead"], ) def test_locked_raises_when_dm_percent_missing(self) -> None: comp_id = create_component(dry_matter=0.0) recipe_id, ing_ids, _ = create_recipe_with_children( component_ids=[comp_id], with_groups=False ) recipe = db.session.get(Recipe, recipe_id) recipe.dry_matter_locked = True db.session.commit() payload = recipe_update_payload( recipe, ingredients=[ { "id": ing_ids[0], "component_id": comp_id, "dry_matter": 0, "dry_matter_per_head": 1.0, "order": 1, } ], groups=[], ) payload["dry_matter_locked"] = True with self.assertRaises(RecipeUpdateError) as ctx: update_recipe_from_payload(recipe_id, payload) self.assertEqual(ctx.exception.status_code, 400) if __name__ == "__main__": unittest.main()