"""Dual sync: dispenser period_recipes — unlink, transfer, reorder, offline.""" from __future__ import annotations import time import unittest from tests.helpers.sync_dual_harness import SyncDualInstanceHarness SCENARIO_DUAL_UNLINK_BOTH_TERMINALS = "DUAL_UNLINK_BOTH_TERMINALS" SCENARIO_DUAL_TRANSFER_BOTH_TERMINALS = "DUAL_TRANSFER_BOTH_TERMINALS" SCENARIO_DUAL_TRANSFER_THEN_REORDER = "DUAL_TRANSFER_THEN_REORDER" SCENARIO_DUAL_OFFLINE_CATCHUP_TRANSFER = "DUAL_OFFLINE_CATCHUP_TRANSFER" SCENARIO_DUAL_LATE_JOIN_AFTER_TRANSFER = "DUAL_LATE_JOIN_AFTER_TRANSFER" class SyncDualRecipesDispenserTests(unittest.TestCase): def setUp(self) -> None: self.harness = SyncDualInstanceHarness() self.harness.start() def tearDown(self) -> None: self.harness.stop() def test_unlink_reaches_both_terminals(self) -> None: SCENARIO_DUAL_UNLINK_BOTH_TERMINALS # noqa: F841 disp_id, pa, _, r1, _ = self.harness.seed_dispenser_two_periods_two_recipes() client = self.harness.server_test_client() resp = client.delete(f"/api/feed_dispensers/{disp_id}/periods/{pa}/recipes/{r1}") self.assertEqual(resp.status_code, 200, resp.get_json()) self.harness.drain_sync() for term in ("a", "b"): self.assertTrue( self.harness.period_recipe_link_deleted(term, pa, r1), msg=f"terminal {term}", ) recipe = self.harness.recipe_on_terminal(term, r1) self.assertIsNotNone(recipe) self.assertFalse(recipe.is_deleted) def test_transfer_reaches_both_terminals(self) -> None: SCENARIO_DUAL_TRANSFER_BOTH_TERMINALS # noqa: F841 disp_id, pa, pb, r1, _ = self.harness.seed_dispenser_two_periods_two_recipes() client = self.harness.server_test_client() resp = client.post( f"/api/feed_dispensers/{disp_id}/periods/{pb}/recipes/{r1}/transfer", json={ "from_dispenser_id": disp_id, "from_period_id": pa, "to_index": 0, }, ) self.assertEqual(resp.status_code, 200, resp.get_json()) self.harness.drain_sync() for term in ("a", "b"): self.assertEqual(self.harness.period_recipe_order(term, pa), ["dual-r2"]) self.assertEqual(self.harness.period_recipe_order(term, pb), ["dual-r1"]) self.assertTrue(self.harness.period_recipe_link_deleted(term, pa, r1)) def test_transfer_then_reorder_on_target_both_terminals(self) -> None: SCENARIO_DUAL_TRANSFER_THEN_REORDER # noqa: F841 disp_id, pa, pb, r1, r2 = self.harness.seed_dispenser_two_periods_two_recipes() client = self.harness.server_test_client() client.post( f"/api/feed_dispensers/{disp_id}/periods/{pb}/recipes/{r1}/transfer", json={ "from_dispenser_id": disp_id, "from_period_id": pa, "to_index": 0, }, ) client.post( f"/api/feed_dispensers/{disp_id}/periods/{pb}/recipes/{r2}/transfer", json={ "from_dispenser_id": disp_id, "from_period_id": pa, "to_index": 1, }, ) self.harness.drain_sync() move = client.put( f"/api/recipes/{r2}/move", json={"period_id": pb, "from_index": 1, "to_index": 0}, ) self.assertEqual(move.status_code, 200, move.get_json()) time.sleep(0.25) # deferred reorder sync enqueue (recipes.py) self.harness.drain_sync() for term in ("a", "b"): self.assertEqual(self.harness.period_recipe_order(term, pb), [r2, r1]) def test_offline_terminal_catches_up_after_transfer(self) -> None: SCENARIO_DUAL_OFFLINE_CATCHUP_TRANSFER # noqa: F841 disp_id, pa, pb, r1, _ = self.harness.seed_dispenser_two_periods_two_recipes() self.harness.sync_terminal("a") client = self.harness.server_test_client() client.post( f"/api/feed_dispensers/{disp_id}/periods/{pb}/recipes/{r1}/transfer", json={ "from_dispenser_id": disp_id, "from_period_id": pa, "to_index": 0, }, ) self.harness.sync_terminal("a") self.harness.drain_sync() self.assertEqual(self.harness.period_recipe_order("a", pb), [r1]) self.assertEqual(self.harness.period_recipe_order("b", pb), [r1]) def test_late_join_after_transfer(self) -> None: SCENARIO_DUAL_LATE_JOIN_AFTER_TRANSFER # noqa: F841 disp_id, pa, pb, r1, _ = self.harness.seed_dispenser_two_periods_two_recipes() client = self.harness.server_test_client() client.post( f"/api/feed_dispensers/{disp_id}/periods/{pb}/recipes/{r1}/transfer", json={ "from_dispenser_id": disp_id, "from_period_id": pa, "to_index": 0, }, ) self.harness.sync_terminal("a") self.harness.sync_terminal("a") self.assertEqual(self.harness.period_recipe_order("b", pb), []) self.harness.sync_terminal("b") self.harness.sync_terminal("b") self.harness.drain_sync() self.assertEqual(self.harness.period_recipe_order("b", pb), [r1]) if __name__ == "__main__": unittest.main()