api/set_machine: add unit tests

This commit is contained in:
Johannes Kirschbauer
2025-07-26 23:59:51 +02:00
parent 798c1a9277
commit bf2eb000d5

View File

@@ -1,12 +1,16 @@
from collections.abc import Callable from collections.abc import Callable
from typing import cast from typing import cast
from unittest.mock import ANY, patch
import pytest import pytest
from clan_lib.errors import ClanError
from clan_lib.flake import Flake from clan_lib.flake import Flake
from clan_lib.nix_models.clan import Clan, Unknown from clan_lib.machines import actions as actions_module
from clan_lib.machines.machines import Machine
from clan_lib.nix_models.clan import Clan, InventoryMachine, Unknown
from .actions import list_machines from .actions import get_machine, get_machine_writeability, list_machines, set_machine
@pytest.mark.with_core @pytest.mark.with_core
@@ -49,3 +53,124 @@ def test_list_inventory_machines(clan_flake: Callable[..., Flake]) -> None:
machines = list_machines(flake) machines = list_machines(flake)
assert list(machines.keys()) == ["jon", "sara", "vanessa"] assert list(machines.keys()) == ["jon", "sara", "vanessa"]
@pytest.mark.with_core
def test_set_machine_no_op(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(
# clan.nix, cannot be changed
clan={
"inventory": {
"machines": {
"jon": {},
"sara": {},
},
}
},
)
# No-op roundtrip should not change anything in the inventory
machine_jon = get_machine(flake, "jon")
with patch(f"{actions_module.__name__}.InventoryStore._write") as mock_write:
set_machine(Machine("jon", flake), machine_jon)
# Assert _write was never called
mock_write.assert_not_called()
# Change something to make sure the mock_write is actually called
machine_jon["machineClass"] = "darwin"
set_machine(Machine("jon", flake), machine_jon)
# This is a bit internal - we want to make sure the write is called
# with only the changed value, so we don't persist the whole machine
mock_write.assert_called_once_with(
{"machines": {"jon": {"machineClass": "darwin"}}}, post_write=ANY
)
@pytest.mark.with_core
def test_set_machine_fully_defined_in_nix(clan_flake: Callable[..., Flake]) -> None:
flake = clan_flake(
# clan.nix, cannot be changed
clan={
"inventory": {
"machines": {
"jon": {
"machineClass": "nixos",
"description": "A NixOS machine",
"icon": "nixos",
"deploy": {
"targetHost": "jon.example.com",
"buildHost": "jon.example.com",
},
"tags": ["server", "backup"],
},
},
}
},
)
# No-op roundtrip should not change anything in the inventory
machine_jon = get_machine(flake, "jon")
machine_jon["description"] = "description updated"
with patch(f"{actions_module.__name__}.InventoryStore._write") as mock_write:
with pytest.raises(ClanError) as exc_info:
set_machine(Machine("jon", flake), machine_jon)
assert (
"Key 'machines.jon.description' is not writeable. It seems its value is statically defined in nix."
in str(exc_info.value)
)
# Assert _write should not be called
mock_write.assert_not_called()
@pytest.mark.with_core
def test_set_machine_manage_tags(clan_flake: Callable[..., Flake]) -> None:
"""Test adding/removing tags on a machine with validation of immutable base tags."""
flake = clan_flake(
clan={
"inventory": {
"machines": {
"jon": {"tags": ["nix1", "nix2"]},
},
}
},
)
def get_jon() -> InventoryMachine:
return get_machine(flake, "jon")
def set_jon(tags: list[str]) -> None:
machine = get_jon()
machine["tags"] = tags
set_machine(Machine("jon", flake), machine)
# --- Add UI tags ---
initial_tags = get_jon().get("tags", [])
new_tags = [*initial_tags, "ui1", "ui2"]
set_jon(new_tags)
updated_tags = get_jon().get("tags", [])
expected_tags = ["nix1", "nix2", "ui1", "ui2", "all", "nixos"]
assert updated_tags == expected_tags
# --- Remove UI tags (allowed) ---
allowed_removal_tags = ["nix1", "nix2", "all", "nixos"]
set_jon(allowed_removal_tags)
assert get_jon().get("tags", []) == allowed_removal_tags
# --- Attempt to remove mandatory tags (should raise) ---
invalid_tags = ["all", "nixos"] # Removing 'nix1', 'nix2' is disallowed
with pytest.raises(ClanError) as exc_info:
set_jon(invalid_tags)
assert "Key 'machines.jon.tags' doesn't contain items ['nix1', 'nix2']" in str(
exc_info.value
)