diff --git a/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py b/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py index f32566b74..76cae2d0b 100644 --- a/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py +++ b/pkgs/clan-cli/clan_cli/tests/fixtures_flakes.py @@ -39,6 +39,7 @@ def substitute( file: Path, clan_core_flake: Path | None = None, flake: Path = Path(__file__).parent, + inventory_expr: str = r"{}", ) -> None: sops_key = str(flake.joinpath("sops.key")) buf = "" @@ -54,6 +55,7 @@ def substitute( "https://git.clan.lol/clan/clan-core/archive/main.tar.gz", f"path:{clan_core_flake}", ) + line = line.replace("__INVENTORY_EXPR__", str(inventory_expr)) line = line.replace("__CLAN_SOPS_KEY_PATH__", sops_key) line = line.replace("__CLAN_SOPS_KEY_DIR__", str(flake / "facts")) buf += line @@ -252,6 +254,7 @@ def create_flake( machines: list[str] | None = None, # alternatively specify the machines directly including their config machine_configs: dict[str, dict] | None = None, + inventory_expr: str = r"{}", ) -> Iterator[FlakeForTest]: """ Creates a flake with the given name and machines. @@ -279,7 +282,11 @@ def create_flake( for machine_name in machines: machine_path = Path(__file__).parent / "machines" / machine_name shutil.copytree(machine_path, flake / "machines" / machine_name) - substitute(flake / "machines" / machine_name / "default.nix", flake) + substitute( + flake / "machines" / machine_name / "default.nix", + flake, + inventory_expr=inventory_expr, + ) # generate machines from machineConfigs for machine_name, machine_config in machine_configs.items(): @@ -291,7 +298,7 @@ def create_flake( # provided by get_test_flake_toplevel flake_nix = flake / "flake.nix" # this is where we would install the sops key to, when updating - substitute(flake_nix, clan_core_flake, flake) + substitute(flake_nix, clan_core_flake, flake, inventory_expr=inventory_expr) nix_options = [] if tmp_store := nix_test_store(): nix_options += ["--store", str(tmp_store)] @@ -345,8 +352,12 @@ def test_flake( @pytest.fixture def test_flake_with_core( - monkeypatch: pytest.MonkeyPatch, temporary_home: Path + request: pytest.FixtureRequest, + monkeypatch: pytest.MonkeyPatch, + temporary_home: Path, ) -> Iterator[FlakeForTest]: + test_params = getattr(request, "param", {}) # Default if not parametrized + inventory_expr = test_params.get("inventory_expr", r"{}") # Default empty inventory if not (CLAN_CORE / "flake.nix").exists(): msg = "clan-core flake not found. This test requires the clan-core flake to be present" raise FixtureError(msg) @@ -355,4 +366,5 @@ def test_flake_with_core( flake_template="test_flake_with_core", clan_core_flake=CLAN_CORE, monkeypatch=monkeypatch, + inventory_expr=inventory_expr, ) diff --git a/pkgs/clan-cli/clan_cli/tests/test_flake_with_core/flake.nix b/pkgs/clan-cli/clan_cli/tests/test_flake_with_core/flake.nix index 7998e5818..35a11821a 100644 --- a/pkgs/clan-cli/clan_cli/tests/test_flake_with_core/flake.nix +++ b/pkgs/clan-cli/clan_cli/tests/test_flake_with_core/flake.nix @@ -22,6 +22,9 @@ clan = clan-core.clanLib.buildClan { inherit self; meta.name = "test_flake_with_core"; + # Don't quote this will be replaced by the inventory expression + # Not a string! + inventory = __INVENTORY_EXPR__; machines = { vm1 = { config, ... }: diff --git a/pkgs/clan-cli/clan_cli/tests/test_inventory_serde.py b/pkgs/clan-cli/clan_cli/tests/test_inventory_serde.py new file mode 100644 index 000000000..cec2b6912 --- /dev/null +++ b/pkgs/clan-cli/clan_cli/tests/test_inventory_serde.py @@ -0,0 +1,56 @@ +from typing import Any + +import pytest + +# Functions to test +from clan_cli.inventory import load_inventory_eval +from clan_cli.tests.fixtures_flakes import FlakeForTest + + +@pytest.mark.parametrize( + "test_flake_with_core", + [ + # Emtpy inventory + {"inventory_expr": r"{ }"}, + # Empty machines + { + "inventory_expr": r"""{ + machines.jon = {}; + machines.sara = {}; + }""" + }, + # TODO: Test + # - Function modules + # - Instances with non-deserializable settings ? + # - Tags function modules + # - + # { + # "inventory_expr": r"""{ + # modules.messager = { ... }: { }; + # }""" + # }, + ], + # Important! + # tells pytest to pass these values to the fixture + # So we can write it to the flake fixtures + indirect=True, +) +@pytest.mark.with_core +def test_inventory_deserialize_variants( + test_flake_with_core: FlakeForTest, +) -> None: + """ + Testing different inventory deserializations + Inventory should always be deserializable to a dict + """ + inventory: dict[str, Any] = load_inventory_eval(test_flake_with_core.path) # type: ignore + # Check that the inventory is a dict + assert isinstance(inventory, dict) + + # Check that all keys are present + assert "meta" in inventory + assert "machines" in inventory + assert "services" in inventory + assert "tags" in inventory + assert "modules" in inventory + assert "instances" in inventory