"""Deterministic seed profile tests.""" import pytest from app.simulation.seed import ( build_profile, clear_simulation_state, large_profile, small_profile, stable_id, ) def test_small_profile_matches_required_logical_shape() -> None: first = small_profile() second = small_profile() assert first == second state = first.logical_state() assert state == second.logical_state() assert state["nodes"] == [{"name": "pve01", "status": "online"}] resources = state["resources"] assert isinstance(resources, list) assert [resource["kind"] for resource in resources].count("qemu") == 2 assert [resource["kind"] for resource in resources].count("lxc") == 1 assert [resource["kind"] for resource in resources].count("storage") == 2 tasks = state["tasks"] assert isinstance(tasks, list) assert len(tasks) == 2 def test_medium_and_fault_profiles_are_deterministic() -> None: medium = build_profile("medium") assert len(medium.nodes) == 3 assert sum(resource.kind == "qemu" for resource in medium.resources) == 50 assert sum(resource.kind == "lxc" for resource in medium.resources) == 20 assert build_profile("ha-demo") == build_profile("ha-demo") broken = build_profile("broken-storage") assert any(resource.state.get("status") == "offline" for resource in broken.resources) def test_large_profile_is_configurable_and_stable() -> None: first = large_profile(node_count=4, resource_count=1_000) second = large_profile(node_count=4, resource_count=1_000) assert first == second assert len(first.nodes) == 4 assert len(first.resources) == 1_000 def test_profile_validation() -> None: with pytest.raises(ValueError, match="unknown seed profile"): build_profile("missing") with pytest.raises(ValueError, match="positive"): large_profile(node_count=0, resource_count=1) def test_demo_cluster_profile_shape() -> None: profile = build_profile("demo-cluster") assert profile.name == "demo-cluster" assert len(profile.nodes) == 20 assert sum(resource.kind == "qemu" for resource in profile.resources) == 850 assert sum(resource.kind == "lxc" for resource in profile.resources) == 150 assert sum(resource.kind == "ceph-osd" for resource in profile.resources) == 300 assert sum(resource.kind == "storage" for resource in profile.resources) >= 62 assert len(profile.tasks) == 250 external_ids = { resource.external_id for resource in profile.resources if resource.kind in {"qemu", "lxc"} } assert len(external_ids) == 1000 def test_demo_cluster_spreads_guests_evenly_across_nodes() -> None: profile = build_profile("demo-cluster") names = {node.id: node.name for node in profile.nodes} def counts(kind: str) -> list[int]: counter: dict[str, int] = {name: 0 for name in names.values()} for resource in profile.resources: if resource.kind == kind: counter[names[resource.node_id]] += 1 return list(counter.values()) for kind, expected_total in (("qemu", 850), ("lxc", 150), ("ceph-osd", 300)): values = counts(kind) assert sum(values) == expected_total assert max(values) - min(values) <= 1 guest_counts = counts("qemu") guest_counts = [a + b for a, b in zip(guest_counts, counts("lxc"), strict=True)] assert max(guest_counts) - min(guest_counts) <= 2 def test_minimal_profile() -> None: profile = build_profile("minimal") assert len(profile.nodes) == 1 assert not any(resource.kind in {"qemu", "lxc"} for resource in profile.resources) def test_stable_ids_are_namespaced_and_repeatable() -> None: assert stable_id("qemu:100") == stable_id("qemu:100") assert stable_id("qemu:100") != stable_id("qemu:101") @pytest.mark.asyncio async def test_clear_simulation_state_wipes_api_created_identity() -> None: executed: list[str] = [] class FakeConnection: async def execute(self, sql: str, *args: object) -> str: del args executed.append(" ".join(sql.split())) return "DELETE 0" await clear_simulation_state(FakeConnection()) joined = "\n".join(executed) for table in ( "resources", "nodes", "principals", "identity_groups", "roles", "storage_contents", "api_tokens", ): assert f"DELETE FROM {table}" in joined # noqa: S608 - asserting SQL text assert "DELETE FROM realms WHERE name NOT IN" in joined assert any(sql.startswith("UPDATE clusters") for sql in executed)