Fixed failing tests in machines and history cli, removed test_machines_api.py
Some checks failed
checks / test (pull_request) Failing after 1m14s
checks-impure / test (pull_request) Successful in 1m15s

This commit is contained in:
Luis Hebendanz 2023-12-14 18:25:42 +01:00
parent e139628fbd
commit 1a36ef242f
9 changed files with 45 additions and 295 deletions

View File

@ -106,6 +106,10 @@ class ClanURI:
@classmethod
def from_str(cls, url: str, params: ClanParameters | None = None) -> Self: # noqa
prefix = "clan://"
if url.startswith(prefix):
url = url[len(prefix) :]
if params is None:
return cls(f"clan://{url}")
@ -118,4 +122,4 @@ class ClanURI:
return cls(f"clan://{new_url}")
def __str__(self) -> str:
return f"ClanURI({self._components.geturl()})"
return self.get_full_uri()

View File

@ -5,6 +5,7 @@ from dataclasses import dataclass
from pathlib import Path
from ..errors import ClanError
from ..machines.list import list_machines
from ..nix import nix_config, nix_eval, nix_metadata
@ -24,6 +25,12 @@ def inspect_flake(flake_url: str | Path, flake_attr: str) -> FlakeConfig:
config = nix_config()
system = config["system"]
machines = list_machines(flake_url)
if flake_attr not in machines:
raise ClanError(
f"Machine {flake_attr} not found in {flake_url}. Available machines: {', '.join(machines)}"
)
cmd = nix_eval(
[
f'{flake_url}#clanInternals.machines."{system}"."{flake_attr}".config.clanCore.clanIcon'

View File

@ -77,12 +77,12 @@ def add_history(uri: ClanURI) -> list[HistoryEntry]:
def add_history_command(args: argparse.Namespace) -> None:
add_history(args.path)
add_history(args.uri)
# takes a (sub)parser and configures it
def register_add_parser(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"uri", type=ClanURI, help="Path to the flake", default=ClanURI(".")
"uri", type=ClanURI.from_str, help="Path to the flake", default="."
)
parser.set_defaults(func=add_history_command)

View File

@ -11,12 +11,12 @@ from ..nix import nix_config, nix_eval
log = logging.getLogger(__name__)
def list_machines(flake_dir: Path) -> list[str]:
def list_machines(flake_url: Path | str) -> list[str]:
config = nix_config()
system = config["system"]
cmd = nix_eval(
[
f"{flake_dir}#clanInternals.machines.{system}",
f"{flake_url}#clanInternals.machines.{system}",
"--apply",
"builtins.attrNames",
"--json",

View File

@ -125,6 +125,11 @@ def test_from_str() -> None:
assert uri.params.flake_attr == "defaultVM"
assert uri.get_internal() == "~/Downloads/democlan"
uri_str = "clan://~/Downloads/democlan"
uri = ClanURI.from_str(url=uri_str)
assert uri.params.flake_attr == "defaultVM"
assert uri.get_internal() == "~/Downloads/democlan"
def test_remote_with_all_params() -> None:
# Create a ClanURI object from a remote URI with parameters

View File

@ -1,10 +1,12 @@
import json
from typing import TYPE_CHECKING
import pytest
from cli import Cli
from fixtures_flakes import FlakeForTest
from pytest import CaptureFixture
from clan_cli.clan_uri import ClanParameters, ClanURI
from clan_cli.dirs import user_history_file
from clan_cli.history.add import HistoryEntry
@ -12,14 +14,17 @@ if TYPE_CHECKING:
pass
@pytest.mark.impure
def test_history_add(
test_flake_with_core: FlakeForTest,
) -> None:
cli = Cli()
params = ClanParameters(flake_attr="vm1")
uri = ClanURI.from_path(test_flake_with_core.path, params=params)
cmd = [
"history",
"add",
str(test_flake_with_core.path),
str(uri),
]
cli.run(cmd)
@ -30,11 +35,14 @@ def test_history_add(
assert history[0].flake.flake_url == str(test_flake_with_core.path)
@pytest.mark.impure
def test_history_list(
capsys: CaptureFixture,
test_flake_with_core: FlakeForTest,
) -> None:
cli = Cli()
params = ClanParameters(flake_attr="vm1")
uri = ClanURI.from_path(test_flake_with_core.path, params=params)
cmd = [
"history",
"list",
@ -43,6 +51,6 @@ def test_history_list(
cli.run(cmd)
assert str(test_flake_with_core.path) not in capsys.readouterr().out
cli.run(["history", "add", str(test_flake_with_core.path)])
cli.run(["history", "add", str(uri)])
cli.run(cmd)
assert str(test_flake_with_core.path) in capsys.readouterr().out

View File

@ -1,279 +0,0 @@
import pytest
from api import TestClient
from fixtures_flakes import FlakeForTest
def test_machines(api: TestClient, test_flake: FlakeForTest) -> None:
response = api.get(f"/api/machines?flake_dir={test_flake.path}")
assert response.status_code == 200
assert response.json() == {"machines": []}
response = api.put(
f"/api/machines/test/config?flake_dir={test_flake.path}", json={}
)
assert response.status_code == 200
response = api.get(f"/api/machines/test?flake_dir={test_flake.path}")
assert response.status_code == 200
assert response.json() == {"machine": {"name": "test", "status": "unknown"}}
response = api.get(f"/api/machines?flake_dir={test_flake.path}")
assert response.status_code == 200
assert response.json() == {"machines": [{"name": "test", "status": "unknown"}]}
@pytest.mark.with_core
def test_schema_errors(api: TestClient, test_flake_with_core: FlakeForTest) -> None:
# make sure that eval errors do not raise an internal server error
response = api.put(
f"/api/schema?flake_dir={test_flake_with_core.path}",
json={"imports": ["some-invalid-import"]},
)
assert response.status_code == 422
assert (
"error: string 'some-invalid-import' doesn't represent an absolute path"
in response.json()["detail"][0]["msg"]
)
@pytest.mark.with_core
def test_schema_invalid_clan_imports(
api: TestClient, test_flake_with_core: FlakeForTest
) -> None:
response = api.put(
f"/api/schema?flake_dir={test_flake_with_core.path}",
json={"clanImports": ["non-existing-clan-module"]},
)
assert response.status_code == 400
assert (
"Some requested clan modules could not be found"
in response.json()["detail"]["msg"]
)
def test_create_machine_invalid_hostname(
api: TestClient, test_flake: FlakeForTest
) -> None:
response = api.put(
f"/api/machines/-invalid-hostname/config?flake_dir={test_flake.path}",
json=dict(),
)
assert response.status_code == 422
assert (
"Machine name must be a valid hostname" in response.json()["detail"][0]["msg"]
)
@pytest.mark.with_core
def test_verify_config_without_machine(
api: TestClient, test_flake_with_core: FlakeForTest
) -> None:
response = api.put(
f"/api/machines/test/verify?flake_dir={test_flake_with_core.path}",
json=dict(),
)
assert response.status_code == 200
assert response.json() == {"success": True, "error": None}
@pytest.mark.with_core
def test_ensure_empty_config_is_valid(
api: TestClient, test_flake_with_core: FlakeForTest
) -> None:
response = api.put(
f"/api/machines/test/config?flake_dir={test_flake_with_core.path}",
json=dict(),
)
assert response.status_code == 200
response = api.get(
f"/api/machines/test/verify?flake_dir={test_flake_with_core.path}"
)
assert response.status_code == 200
assert response.json() == {"success": True, "error": None}
@pytest.mark.with_core
def test_configure_machine(api: TestClient, test_flake_with_core: FlakeForTest) -> None:
# ensure error 404 if machine does not exist when accessing the config
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}"
)
assert response.status_code == 404
# create the machine
response = api.put(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}", json={}
)
assert response.status_code == 200
# ensure an empty config is returned by default for a new machine
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}"
)
assert response.status_code == 200
assert response.json() == {
"clanImports": [],
"clan": {},
}
# get jsonschema for without imports
response = api.put(
f"/api/schema?flake_dir={test_flake_with_core.path}",
json={"clanImports": []},
)
assert response.status_code == 200
json_response = response.json()
assert "schema" in json_response and "properties" in json_response["schema"]
# an invalid config setting some non-existent option
invalid_config = dict(
clan=dict(),
foo=dict(
bar=True,
),
services=dict(
nginx=dict(
enable=True,
),
),
)
# verify an invalid config (foo option does not exist)
response = api.put(
f"/api/machines/machine1/verify?flake_dir={test_flake_with_core.path}",
json=invalid_config,
)
assert response.status_code == 200
assert "error: The option `foo' does not exist" in response.json()["error"]
# set come invalid config (foo option does not exist)
response = api.put(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
json=invalid_config,
)
assert response.status_code == 200
# ensure the config has actually been updated
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}"
)
assert response.status_code == 200
assert response.json() == dict(clanImports=[], **invalid_config)
# set some valid config
config2 = dict(
clan=dict(),
services=dict(
nginx=dict(
enable=True,
),
),
)
response = api.put(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
json=config2,
)
assert response.status_code == 200
# ensure the config has been applied
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
)
assert response.status_code == 200
assert response.json() == dict(clanImports=[], **config2)
# get the config again
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}"
)
assert response.status_code == 200
assert response.json() == {"clanImports": [], **config2}
# ensure PUT on the config is idempotent by passing the config again
# For example, this should not result in the boot.loader.grub.devices being
# set twice (eg. merged)
response = api.put(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
json=config2,
)
assert response.status_code == 200
# ensure the config has been applied
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
)
assert response.status_code == 200
assert response.json() == dict(clanImports=[], **config2)
# verify the machine config evaluates
response = api.get(
f"/api/machines/machine1/verify?flake_dir={test_flake_with_core.path}"
)
assert response.status_code == 200
assert response.json() == {"success": True, "error": None}
# get the schema with an extra module imported
response = api.put(
f"/api/schema?flake_dir={test_flake_with_core.path}",
json={"clanImports": ["diskLayouts"]},
)
# expect the result schema to contain the deltachat option
assert response.status_code == 200
assert (
response.json()["schema"]["properties"]["diskLayouts"]["properties"][
"singleDiskExt4"
]["properties"]["device"]["type"]
== "string"
)
# new config importing an extra clanModule (clanModules.fake-module)
config_with_imports: dict = {
"clanImports": ["fake-module"],
"clan": {
"fake-module": {
"fake-flag": True,
},
},
}
# set the fake-module.fake-flag option to true
response = api.put(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
json=config_with_imports,
)
assert response.status_code == 200
# ensure the config has been applied
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
)
assert response.status_code == 200
assert response.json() == {
"clanImports": ["fake-module"],
"clan": {
"fake-module": {
"fake-flag": True,
},
},
}
# remove the import from the config
response = api.put(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
json=dict(
clanImports=[],
),
)
assert response.status_code == 200
# ensure the config has been applied
response = api.get(
f"/api/machines/machine1/config?flake_dir={test_flake_with_core.path}",
)
assert response.status_code == 200
assert response.json() == {
"clan": {},
"clanImports": [],
}

View File

@ -3,20 +3,25 @@ from cli import Cli
from fixtures_flakes import FlakeForTest
@pytest.mark.impure
def test_machine_subcommands(
test_flake: FlakeForTest, capsys: pytest.CaptureFixture
test_flake_with_core: FlakeForTest, capsys: pytest.CaptureFixture
) -> None:
cli = Cli()
cli.run(["--flake", str(test_flake.path), "machines", "create", "machine1"])
cli.run(
["--flake", str(test_flake_with_core.path), "machines", "create", "machine1"]
)
capsys.readouterr()
cli.run(["--flake", str(test_flake.path), "machines", "list"])
cli.run(["--flake", str(test_flake_with_core.path), "machines", "list"])
out = capsys.readouterr()
assert "machine1\n" == out.out
assert "machine1\nvm1\nvm2\n" == out.out
cli.run(["--flake", str(test_flake.path), "machines", "delete", "machine1"])
cli.run(
["--flake", str(test_flake_with_core.path), "machines", "delete", "machine1"]
)
capsys.readouterr()
cli.run(["--flake", str(test_flake.path), "machines", "list"])
cli.run(["--flake", str(test_flake_with_core.path), "machines", "list"])
out = capsys.readouterr()
assert "" == out.out
assert "vm1\nvm2\n" == out.out

View File

@ -4,7 +4,7 @@ from pathlib import Path
from typing import Any
import gi
from clan_cli import flakes, vms
from clan_cli import flakes, history, vms
gi.require_version("GdkPixbuf", "2.0")
from gi.repository import GdkPixbuf
@ -77,7 +77,7 @@ def get_initial_vms(start: int = 0, end: int | None = None) -> list[VM]:
# TODO: list_history() should return a list of dicts, not a list of paths
# Execute `clan flakes add <path>` to democlan for this to work
for entry in flakes.history.list_history():
for entry in history.list.list_history():
flake_config = flakes.inspect.inspect_flake(entry.path, "defaultVM")
vm_config = vms.inspect.inspect_vm(entry.path, "defaultVM")