From e2520f6aa85113820e1e49177897dd3453c5a36a Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Wed, 21 May 2025 17:55:43 +0200 Subject: [PATCH] Test(InventoryPersistence): add persist integration tests --- .../clan-cli/clan_lib/persist/fixtures/1.json | 1 + pkgs/clan-cli/clan_lib/persist/fixtures/1.nix | 29 +++++ .../clan_lib/persist/inventory_store_test.py | 110 ++++++++++++++++++ pkgs/clan-cli/default.nix | 4 + pkgs/clan-cli/flake-module.nix | 2 +- pkgs/clan-cli/shell.nix | 5 + 6 files changed, 150 insertions(+), 1 deletion(-) create mode 100644 pkgs/clan-cli/clan_lib/persist/fixtures/1.json create mode 100644 pkgs/clan-cli/clan_lib/persist/fixtures/1.nix diff --git a/pkgs/clan-cli/clan_lib/persist/fixtures/1.json b/pkgs/clan-cli/clan_lib/persist/fixtures/1.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/pkgs/clan-cli/clan_lib/persist/fixtures/1.json @@ -0,0 +1 @@ +{} diff --git a/pkgs/clan-cli/clan_lib/persist/fixtures/1.nix b/pkgs/clan-cli/clan_lib/persist/fixtures/1.nix new file mode 100644 index 000000000..6918b0e37 --- /dev/null +++ b/pkgs/clan-cli/clan_lib/persist/fixtures/1.nix @@ -0,0 +1,29 @@ +{ clanLib, lib, ... }: +let + eval = lib.evalModules { + modules = [ + { + # Trying to write into the default + options.foo = lib.mkOption { + type = lib.types.str; + default = "bar"; + }; + options.protected = lib.mkOption { + type = lib.types.str; + }; + } + { + # Cannot write into the default set prio + protected = "protected"; + } + # Merge the "inventory.json" + (builtins.fromJSON (builtins.readFile ./1.json)) + ]; + }; +in +{ + clanInternals.inventoryClass.inventory = eval.config; + clanInternals.inventoryClass.introspection = clanLib.introspection.getPrios { + options = eval.options; + }; +} diff --git a/pkgs/clan-cli/clan_lib/persist/inventory_store_test.py b/pkgs/clan-cli/clan_lib/persist/inventory_store_test.py index e69de29bb..bfa6c2f54 100644 --- a/pkgs/clan-cli/clan_lib/persist/inventory_store_test.py +++ b/pkgs/clan-cli/clan_lib/persist/inventory_store_test.py @@ -0,0 +1,110 @@ +# ruff: noqa: SLF001 +import json +import os +import shutil +import subprocess +from pathlib import Path +from tempfile import TemporaryDirectory +from typing import Any + +import pytest + +from clan_lib.errors import ClanError +from clan_lib.persist.inventory_store import InventoryStore + + +class MockFlake: + def __init__(self, default: Path) -> None: + f = default + assert f.exists(), f"File {f} does not exist" + self._file = f + + def select( + self, + selector: str, + nix_options: list[str] | None = None, + ) -> Any: + nixpkgs = os.environ.get("NIXPKGS") + select = os.environ.get("NIX_SELECT") + clan_core_path = os.environ.get("CLAN_CORE_PATH") + + assert nixpkgs, "NIXPKGS environment variable is not set" + assert select, "NIX_SELECT environment variable is not set" + assert clan_core_path, "CLAN_CORE_PATH environment variable is not set" + + output = subprocess.run( + [ + "nix", + "eval", + "--impure", + "--json", + "--expr", + f""" + let + pkgs = import {nixpkgs} {{}}; + inherit (pkgs) lib; + clanLib = import {Path(clan_core_path)}/lib {{ inherit lib; self = null; nixpkgs = {nixpkgs}; }}; + select = (import {select}/select.nix).select; + result = import {self._file} {{ inherit pkgs lib clanLib; }}; + in + select "{selector}" result + """, + ], + capture_output=True, + ) + res_str = output.stdout.decode() + + if output.returncode != 0: + msg = f"Failed to evaluate {selector} in {self._file}: {output.stderr.decode()}" + raise ClanError(msg) + return json.loads(res_str) + + @property + def path(self) -> Path: + return self._file.parent + + +folder_path = Path(__file__).parent.resolve() + + +def test_for_johannes() -> None: + nix_file = folder_path / "fixtures/1.nix" + json_file = folder_path / "fixtures/1.json" + with TemporaryDirectory() as tmp: + shutil.copyfile( + str(nix_file), + str(Path(tmp) / "1.nix"), + ) + shutil.copyfile( + str(json_file), + str(Path(tmp) / "1.json"), + ) + + store = InventoryStore( + flake=MockFlake(Path(tmp) / "1.nix"), + inventory_file_name="1.json", + ) + assert store.read() == {"foo": "bar", "protected": "protected"} + + data = {"foo": "foo"} + store.write(data, "test", commit=False) # type: ignore + # Default method to access the inventory + assert store.read() == {"foo": "foo", "protected": "protected"} + + # Test the data is actually persisted + assert store._get_persisted() == data + + # clan_lib.errors.ClanError: Key 'protected' is not writeable. + invalid_data = {"protected": "foo"} + with pytest.raises(ClanError) as e: + store.write(invalid_data, "test", commit=False) # type: ignore + assert str(e.value) == "Key 'protected' is not writeable." + + # Test the data is not touched + assert store.read() == {"foo": "foo", "protected": "protected"} + assert store._get_persisted() == data + + # Remove the foo key from the persisted data + # Technically data = { } should also work + data = {"protected": "protected"} + store.write(data, "test", commit=False) # type: ignore diff --git a/pkgs/clan-cli/default.nix b/pkgs/clan-cli/default.nix index 18fcca1a0..debddc29e 100644 --- a/pkgs/clan-cli/default.nix +++ b/pkgs/clan-cli/default.nix @@ -274,6 +274,10 @@ pythonRuntime.pkgs.buildPythonApplication { xargs cp --recursive --target "$CLAN_TEST_STORE/nix/store" < "$closureInfo/store-paths" nix-store --load-db --store "$CLAN_TEST_STORE" < "$closureInfo/registration" + # used for tests without flakes + export NIXPKGS=${nixpkgs} + export NIX_SELECT=${nix-select} + # limit build cores to 4 jobs="$((NIX_BUILD_CORES>4 ? 4 : NIX_BUILD_CORES))" diff --git a/pkgs/clan-cli/flake-module.nix b/pkgs/clan-cli/flake-module.nix index 7ff498cc2..95a42ab0f 100644 --- a/pkgs/clan-cli/flake-module.nix +++ b/pkgs/clan-cli/flake-module.nix @@ -35,7 +35,7 @@ in { devShells.clan-cli = pkgs.callPackage ./shell.nix { - inherit self'; + inherit self' self; inherit (self'.packages) clan-cli; }; packages = { diff --git a/pkgs/clan-cli/shell.nix b/pkgs/clan-cli/shell.nix index f5a76fdcd..7057c4423 100644 --- a/pkgs/clan-cli/shell.nix +++ b/pkgs/clan-cli/shell.nix @@ -4,6 +4,7 @@ clan-cli, mkShell, ruff, + self, self', }: @@ -35,6 +36,10 @@ mkShell { export CLAN_CORE_PATH="$GIT_ROOT" + # used for tests without flakes + export NIXPKGS=${self.inputs.nixpkgs.outPath} + export NIX_SELECT=${self.inputs.nix-select.outPath} + # Add current package to PYTHONPATH export PYTHONPATH="$PKG_ROOT''${PYTHONPATH:+:$PYTHONPATH:}"