from __future__ import annotations from knoe.core.cnpg_placement import ( load_cnpg_placement_plan, plan_cnpg_placement, save_cnpg_placement_plan, ) class TestCnpgPlacementPlanner: def test_deterministic_eligible_node_ordering_and_round_robin(self): plan = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=5, candidate_nodes=[" db-b ", "db-a", "db-b", "db-c"], ) assert plan["eligible_nodes"] == ["db-a", "db-b", "db-c"] assert plan["assignments"] == { "0": "db-a", "1": "db-b", "2": "db-c", "3": "db-a", "4": "db-b", } def test_reuses_persisted_plan_when_still_valid(self): prior = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=3, candidate_nodes=["db-a", "db-b"], ) plan = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=3, candidate_nodes=["db-b", "db-a", "db-c"], prior_plan=prior, ) assert plan["metadata"]["reused"] is True assert plan["metadata"]["regenerated"] is False # New eligible node must not reshuffle existing ordinals. assert plan["assignments"] == prior["assignments"] def test_regenerates_when_assigned_node_becomes_ineligible(self): prior = { "cluster_name": "knoe-db", "assignments": {"0": "db-a", "1": "db-b", "2": "db-a"}, } plan = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=3, candidate_nodes=["db-a", "db-c"], prior_plan=prior, ) assert plan["metadata"]["regenerated"] is True assert plan["metadata"]["reason"] == "assigned_node_no_longer_eligible" assert set(plan["assignments"].values()) <= {"db-a", "db-c"} def test_rebalance_regenerates_even_with_valid_prior(self): prior = { "cluster_name": "knoe-db", "assignments": {"0": "db-a", "1": "db-a", "2": "db-a"}, } plan = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=3, candidate_nodes=["db-a", "db-b", "db-c"], prior_plan=prior, rebalance=True, ) assert plan["metadata"]["regenerated"] is True assert plan["metadata"]["reason"] == "rebalance_requested" assert plan["assignments"] == {"0": "db-a", "1": "db-b", "2": "db-c"} def test_graceful_when_fewer_eligible_nodes_than_instances(self): plan = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=4, candidate_nodes=["db-a"], ) assert plan["assignments"] == { "0": "db-a", "1": "db-a", "2": "db-a", "3": "db-a", } class TestCnpgPlacementPersistence: def test_persisted_plan_can_be_reloaded_and_reused(self, tmp_path): plan_path = tmp_path / "cnpg-placement" / "knoe-system-knoe-db.json" initial = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=3, candidate_nodes=["db-a", "db-b"], ) save_cnpg_placement_plan(plan_path, initial) prior = load_cnpg_placement_plan(plan_path) assert prior is not None reused = plan_cnpg_placement( cluster_name="knoe-db", desired_instances=3, candidate_nodes=["db-a", "db-b", "db-c"], prior_plan=prior, ) assert reused["metadata"]["reused"] is True assert reused["assignments"] == initial["assignments"]