"""Dual sync: offline catch-up — reorder рейсов и группы выгрузки.""" from __future__ import annotations import unittest import uuid from sqlalchemy import func, 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_OFFLINE_CATCHUP_GROUPS = "DUAL_OFFLINE_CATCHUP_GROUPS" class SyncDualOfflineCatchupTests(unittest.TestCase): def setUp(self) -> None: self.harness = SyncDualInstanceHarness() self.harness.start() def tearDown(self) -> None: self.harness.stop() def test_offline_terminal_catches_reorder_and_new_group(self) -> None: with self.harness.server_ctx(): recipe_id, ing_ids, grp_ids = create_recipe_with_children(with_groups=True) recipe = db.session.get(Recipe, recipe_id) for ing_id in ing_ids: enqueue_sync_queue_task("ingredient", ing_id, "create", priority=2) for gid in grp_ids: enqueue_sync_queue_task("unloading_group", gid, "create", priority=2) enqueue_sync_queue_task("recipe", recipe_id, "create", priority=2) db.session.commit() self.harness.sync_terminal("a") with self.harness.server_ctx(): recipe = db.session.get(Recipe, recipe_id) assert recipe is not None ings = list( db.session.execute( select(Ingredient).where( Ingredient.recipe_id == recipe_id, Ingredient.is_deleted.is_(False), ).order_by(Ingredient.order.asc()) ).scalars().all() ) groups = list( db.session.execute( select(UnloadingGroup).where( UnloadingGroup.recipe_id == recipe_id, UnloadingGroup.is_deleted.is_(False), ).order_by(UnloadingGroup.order.asc()) ).scalars().all() ) swapped = [ ingredient_payload_from_row(ings[1], order=1), ingredient_payload_from_row(ings[0], order=2), ] new_grp = { "id": str(uuid.uuid4()), "name": "Новая", "distribution_type": "percent", "value": 20.0, "weight": 0.0, "order": len(groups) + 1, } update_recipe_from_payload( recipe_id, recipe_update_payload( recipe, ingredients=swapped, groups=[group_payload_from_row(g) for g in groups] + [new_grp], ), ) db.session.commit() self.harness.drain_sync() for term in ("a", "b"): with self.harness.terminal_ctx(term): ing_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() self.assertEqual( [str(rid) for rid, _ in ing_rows], [ing_ids[1], ing_ids[0]], msg=f"reorder terminal {term}", ) grp_count = db.session.scalar( select(func.count()) .select_from(UnloadingGroup) .where( UnloadingGroup.recipe_id == recipe_id, UnloadingGroup.is_deleted.is_(False), ) ) self.assertEqual(int(grp_count or 0), 3, msg=f"groups terminal {term}") if __name__ == "__main__": unittest.main()