Files
site/WESP_REL/tests/test_sync_recipe_children_dual.py
T
2026-07-17 12:57:18 +03:00

240 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Dual sync: reorder ингредиентов + группы выгрузки (баготест #5#6)."""
from __future__ import annotations
import unittest
import uuid
from sqlalchemy import select
from app import db
from app.models import Ingredient, Recipe, UnloadingGroup
from app.services.recipe_update_service import update_recipe_from_payload
from app.services.sync_manager import enqueue_sync_queue_task
from tests.helpers.mill_recipe_fixtures import (
create_recipe_with_children,
group_payload_from_row,
ingredient_payload_from_row,
recipe_update_payload,
)
from tests.helpers.sync_dual_harness import SyncDualInstanceHarness
SCENARIO_DUAL_REORDER_AND_NEW_GROUP = "DUAL_REORDER_AND_NEW_GROUP"
SCENARIO_DUAL_ADD_GROUP_UPDATES_EXISTING = "DUAL_ADD_GROUP_UPDATES_EXISTING"
class SyncRecipeChildrenDualTests(unittest.TestCase):
def setUp(self) -> None:
self.harness = SyncDualInstanceHarness()
self.harness.start()
def tearDown(self) -> None:
self.harness.stop()
def _seed_recipe_on_terminals(self, recipe_id: str) -> None:
with self.harness.server_ctx():
enqueue_sync_queue_task("recipe", recipe_id, "create", priority=2, target_node_id=None)
for ing in db.session.execute(
select(Ingredient).where(
Ingredient.recipe_id == recipe_id,
Ingredient.is_deleted.is_(False),
)
).scalars().all():
enqueue_sync_queue_task("ingredient", str(ing.id), "create", priority=2)
for grp in db.session.execute(
select(UnloadingGroup).where(
UnloadingGroup.recipe_id == recipe_id,
UnloadingGroup.is_deleted.is_(False),
)
).scalars().all():
enqueue_sync_queue_task("unloading_group", str(grp.id), "create", priority=2)
db.session.commit()
self.harness.drain_sync()
def _ingredient_orders(self, which: str, recipe_id: str) -> list[tuple[str, int]]:
with self.harness.terminal_ctx(which):
rows = db.session.execute(
select(Ingredient.id, Ingredient.order)
.where(
Ingredient.recipe_id == recipe_id,
Ingredient.is_deleted.is_(False),
)
.order_by(Ingredient.order.asc())
).all()
return [(str(rid), int(ord_)) for rid, ord_ in rows]
def _group_snapshot(self, which: str, recipe_id: str) -> list[tuple[str, str, float]]:
with self.harness.terminal_ctx(which):
rows = db.session.execute(
select(
UnloadingGroup.name,
UnloadingGroup.distribution_type,
UnloadingGroup.value,
)
.where(
UnloadingGroup.recipe_id == recipe_id,
UnloadingGroup.is_deleted.is_(False),
)
.order_by(UnloadingGroup.order.asc())
).all()
return [(n, t, float(v)) for n, t, v in rows]
def test_reorder_ingredients_and_add_group_reach_both_terminals(self) -> None:
SCENARIO_DUAL_REORDER_AND_NEW_GROUP # noqa: F841
with self.harness.server_ctx():
recipe_id, ing_ids, _ = create_recipe_with_children(with_groups=False)
grp_id = str(uuid.uuid4())
db.session.add(
UnloadingGroup(
id=grp_id,
name="1 заезд",
distribution_type="percent",
value=75.0,
weight=1000.0,
order=1,
recipe_id=recipe_id,
created_by="system",
updated_by="system",
)
)
db.session.commit()
self._seed_recipe_on_terminals(recipe_id)
with self.harness.server_ctx():
recipe = db.session.get(Recipe, recipe_id)
ing_a = db.session.get(Ingredient, ing_ids[0])
ing_b = db.session.get(Ingredient, ing_ids[1])
grp1 = db.session.get(UnloadingGroup, grp_id)
payload = recipe_update_payload(
recipe,
ingredients=[
ingredient_payload_from_row(ing_b, order=1),
ingredient_payload_from_row(ing_a, order=2),
],
groups=[
group_payload_from_row(grp1, order=1),
{
"name": "2 заезд",
"distribution_type": "percent",
"value": 25.0,
"weight": 400.0,
"order": 2,
},
],
)
update_recipe_from_payload(recipe_id, payload)
db.session.commit()
self.harness.drain_sync()
for term in ("a", "b"):
self.assertEqual(
self._ingredient_orders(term, recipe_id),
[(ing_ids[1], 1), (ing_ids[0], 2)],
msg=f"ingredient order terminal {term}",
)
snap = self._group_snapshot(term, recipe_id)
self.assertEqual(len(snap), 2, msg=f"groups count terminal {term}")
self.assertEqual(snap[0][2], 75.0)
self.assertEqual(snap[1], ("2 заезд", "percent", 25.0))
def test_split_groups_60_40_on_both_terminals_not_140_percent(self) -> None:
SCENARIO_DUAL_ADD_GROUP_UPDATES_EXISTING # noqa: F841
with self.harness.server_ctx():
recipe_id, ing_ids, _ = create_recipe_with_children(with_groups=False)
grp_id = str(uuid.uuid4())
db.session.add(
UnloadingGroup(
id=grp_id,
name="4 гр 1 зам",
distribution_type="percent",
value=100.0,
weight=1590.0,
order=1,
recipe_id=recipe_id,
created_by="system",
updated_by="system",
)
)
db.session.commit()
self._seed_recipe_on_terminals(recipe_id)
with self.harness.server_ctx():
recipe = db.session.get(Recipe, recipe_id)
grp1 = db.session.get(UnloadingGroup, grp_id)
ings = [
ingredient_payload_from_row(db.session.get(Ingredient, iid))
for iid in ing_ids
]
payload = recipe_update_payload(
recipe,
ingredients=ings,
groups=[
{
**group_payload_from_row(grp1, order=1),
"value": 60.0,
},
{
"name": "еще одна",
"distribution_type": "percent",
"value": 40.0,
"weight": 635.0,
"order": 2,
},
],
)
update_recipe_from_payload(recipe_id, payload)
db.session.commit()
self.harness.drain_sync()
for term in ("a", "b"):
snap = self._group_snapshot(term, recipe_id)
self.assertEqual(len(snap), 2, msg=f"terminal {term}")
self.assertEqual(snap[0][2], 60.0, msg=f"grp1 value terminal {term}")
self.assertEqual(snap[1][2], 40.0, msg=f"grp2 value terminal {term}")
total_percent = sum(v for _, t, v in snap if t == "percent")
self.assertAlmostEqual(total_percent, 100.0, places=1)
def test_dry_matter_locked_syncs_to_both_terminals(self) -> None:
with self.harness.server_ctx():
recipe_id, ing_ids, _ = create_recipe_with_children(with_groups=False)
recipe = db.session.get(Recipe, recipe_id)
recipe.dry_matter_locked = True
db.session.commit()
self._seed_recipe_on_terminals(recipe_id)
with self.harness.server_ctx():
recipe = db.session.get(Recipe, recipe_id)
comp_id = db.session.get(Ingredient, ing_ids[0]).component_id
update_recipe_from_payload(
recipe_id,
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=[],
)
| {"dry_matter_locked": True, "heads_count": 10, "trip_percent": 100.0},
)
db.session.commit()
self.harness.drain_sync()
for term in ("a", "b"):
row = self.harness.recipe_on_terminal(term, recipe_id)
self.assertTrue(row.dry_matter_locked, msg=f"terminal {term}")
if __name__ == "__main__":
unittest.main()