pkgs/cli: Move the test folder inside the python module

Move the `tests` folder to `clan_cli/tests`.

As we now want part of our tests to live next to the functions that are
tested - tests that are not in the `/tests` module also need access to
the configured test fixtures that are exposed by the `pytest_plugins`
declaration.

The following folder structure doesn't support this model:

```
├── clan_cli
│   ├── api
│   │    └── api_init_test.py
├── tests/
│   ├── conftest.py
│   └── ...
```

Here `api_init_test.py` even when importing the test functions will not
have the fixtures configured.

There is a way to configure python to import the fixtures from another
[`project/module`](https://docs.pytest.org/en/stable/how-to/fixtures.html#using-fixtures-from-other-projects), but this seems to *generally* be discouraged.

So moving the `conftest.py` to the toplevel and the `/tests` folder into
the toplevel seems to be a sensible choice choice.
This commit is contained in:
a-kenji
2025-02-28 11:56:38 +07:00
committed by Johannes Kirschbauer
parent a503c92c3e
commit 309c132b63
72 changed files with 117 additions and 100 deletions

View File

@@ -51,10 +51,10 @@
"docs/site/static/asciinema-player/asciinema-player.css" "docs/site/static/asciinema-player/asciinema-player.css"
"docs/site/static/asciinema-player/asciinema-player.min.js" "docs/site/static/asciinema-player/asciinema-player.min.js"
"nixosModules/clanCore/vars/secret/sops/eval-tests/populated/vars/my_machine/my_generator/my_secret" "nixosModules/clanCore/vars/secret/sops/eval-tests/populated/vars/my_machine/my_generator/my_secret"
"pkgs/clan-cli/tests/data/gnupg.conf" "pkgs/clan-cli/clan_cli/tests/data/gnupg.conf"
"pkgs/clan-cli/tests/data/password-store/.gpg-id" "pkgs/clan-cli/clan_cli/tests/data/password-store/.gpg-id"
"pkgs/clan-cli/tests/data/ssh_host_ed25519_key" "pkgs/clan-cli/clan_cli/tests/data/ssh_host_ed25519_key"
"pkgs/clan-cli/tests/data/sshd_config" "pkgs/clan-cli/clan_cli/tests/data/sshd_config"
"pkgs/clan-vm-manager/.vscode/lhebendanz.weaudit" "pkgs/clan-vm-manager/.vscode/lhebendanz.weaudit"
"pkgs/clan-vm-manager/bin/clan-vm-manager" "pkgs/clan-vm-manager/bin/clan-vm-manager"
"pkgs/distro-packages/vagrant_insecure_key" "pkgs/distro-packages/vagrant_insecure_key"

View File

@@ -1,20 +1,23 @@
import pytest import pytest
from clan_cli.custom_logger import setup_logging from clan_cli.custom_logger import setup_logging
# collect_ignore = ["./nixpkgs"]
pytest_plugins = [ pytest_plugins = [
"temporary_dir", "clan_cli.tests.temporary_dir",
"root", "clan_cli.tests.root",
"age_keys", "clan_cli.tests.age_keys",
"gpg_keys", "clan_cli.tests.gpg_keys",
"git_repo", "clan_cli.tests.git_repo",
"sshd", "clan_cli.tests.sshd",
"command", "clan_cli.tests.command",
"ports", "clan_cli.tests.ports",
"hosts", "clan_cli.tests.hosts",
"runtime", "clan_cli.tests.runtime",
"fixtures_flakes", "clan_cli.tests.fixtures_flakes",
"stdout", "clan_cli.tests.stdout",
"nix_config", "clan_cli.tests.nix_config",
] ]

View File

@@ -4,7 +4,7 @@ from pathlib import Path
import pytest import pytest
from clan_cli.secrets.folders import sops_secrets_folder from clan_cli.secrets.folders import sops_secrets_folder
from helpers import cli from clan_cli.tests.helpers import cli
class KeyPair: class KeyPair:

View File

@@ -14,9 +14,10 @@ import pytest
from clan_cli.dirs import TemplateType, clan_templates, nixpkgs_source from clan_cli.dirs import TemplateType, clan_templates, nixpkgs_source
from clan_cli.locked_open import locked_open from clan_cli.locked_open import locked_open
from clan_cli.nix import nix_test_store from clan_cli.nix import nix_test_store
from fixture_error import FixtureError
from root import CLAN_CORE from clan_cli.tests.temporary_dir import TEMPDIR
from temporary_dir import TEMPDIR from clan_cli.tests.fixture_error import FixtureError
from clan_cli.tests.root import CLAN_CORE
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@@ -189,7 +190,8 @@ class ClanFlake:
self.path / "machines" / machine_name / "configuration.nix" self.path / "machines" / machine_name / "configuration.nix"
) )
configuration_nix.parent.mkdir(parents=True, exist_ok=True) configuration_nix.parent.mkdir(parents=True, exist_ok=True)
configuration_nix.write_text(f""" configuration_nix.write_text(
f"""
{{clan-core, ...}}: {{clan-core, ...}}:
{{ {{
imports = [ imports = [
@@ -197,7 +199,8 @@ class ClanFlake:
{imports} {imports}
]; ];
}} }}
""") """
)
set_machine_settings(self.path, machine_name, machine_config) set_machine_settings(self.path, machine_name, machine_config)
sp.run(["git", "add", "."], cwd=self.path, check=True) sp.run(["git", "add", "."], cwd=self.path, check=True)
sp.run( sp.run(

View File

@@ -4,7 +4,7 @@ import pwd
import pytest import pytest
from clan_cli.ssh.host import Host from clan_cli.ssh.host import Host
from clan_cli.ssh.host_key import HostKeyCheck from clan_cli.ssh.host_key import HostKeyCheck
from sshd import Sshd from clan_cli.tests.sshd import Sshd
@pytest.fixture @pytest.fixture

View File

@@ -8,7 +8,7 @@ PROJECT_ROOT = TEST_ROOT.parent
if CLAN_CORE_ := os.environ.get("CLAN_CORE_PATH"): if CLAN_CORE_ := os.environ.get("CLAN_CORE_PATH"):
CLAN_CORE = Path(CLAN_CORE_) CLAN_CORE = Path(CLAN_CORE_)
else: else:
CLAN_CORE = PROJECT_ROOT.parent.parent CLAN_CORE = PROJECT_ROOT.parent.parent.parent
@pytest.fixture(scope="session") @pytest.fixture(scope="session")

View File

@@ -12,8 +12,8 @@ from typing import TYPE_CHECKING
import pytest import pytest
if TYPE_CHECKING: if TYPE_CHECKING:
from command import Command from .command import Command
from ports import PortFunction from .ports import PortFunction
class SshdError(Exception): class SshdError(Exception):

View File

@@ -78,11 +78,13 @@ def load_dataclass_from_file(
try: try:
sys.path.insert(0, root_dir) sys.path.insert(0, root_dir)
spec = importlib.util.spec_from_file_location(module_name, file_path) spec = importlib.util.spec_from_file_location(module_name, file_path)
print(spec)
if not spec: if not spec:
msg = f"Could not load spec from file: {file_path}" msg = f"Could not load spec from file: {file_path}"
raise ClanError(msg) raise ClanError(msg)
module = importlib.util.module_from_spec(spec) module = importlib.util.module_from_spec(spec)
print(module)
if not module: if not module:
msg = f"Could not create module: {file_path}" msg = f"Could not create module: {file_path}"
raise ClanError(msg) raise ClanError(msg)

View File

@@ -1,6 +1,6 @@
import pytest import pytest
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
from helpers import cli from clan_cli.tests.helpers import cli
@pytest.mark.impure @pytest.mark.impure

View File

@@ -19,7 +19,7 @@ from clan_cli.templates import (
get_template, get_template,
list_templates, list_templates,
) )
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
# Function to write clan attributes to a file # Function to write clan attributes to a file

View File

@@ -3,7 +3,7 @@ from pathlib import Path
import pytest import pytest
from clan_cli.clan_uri import ClanURI from clan_cli.clan_uri import ClanURI
from clan_cli.flake import Flake from clan_cli.flake import Flake
from fixtures_flakes import ClanFlake from clan_cli.tests.fixtures_flakes import ClanFlake
def test_get_url() -> None: def test_get_url() -> None:

View File

@@ -1,6 +1,6 @@
import pytest import pytest
from helpers import cli from clan_cli.tests.helpers import cli
from stdout import CaptureOutput from clan_cli.tests.stdout import CaptureOutput
def test_help(capture_output: CaptureOutput) -> None: def test_help(capture_output: CaptureOutput) -> None:

View File

@@ -5,9 +5,10 @@ from pathlib import Path
import pytest import pytest
from clan_cli.cmd import run from clan_cli.cmd import run
from clan_cli.nix import nix_flake_show from clan_cli.nix import nix_flake_show
from fixtures_flakes import FlakeForTest, substitute
from helpers import cli from clan_cli.tests.fixtures_flakes import FlakeForTest, substitute
from stdout import CaptureOutput from clan_cli.tests.helpers import cli
from clan_cli.tests.stdout import CaptureOutput
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@@ -2,7 +2,7 @@ import logging
import pytest import pytest
from clan_cli.flake import Flake, FlakeCache, FlakeCacheEntry from clan_cli.flake import Flake, FlakeCache, FlakeCacheEntry
from fixtures_flakes import ClanFlake from clan_cli.tests.fixtures_flakes import ClanFlake
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@@ -1,9 +1,9 @@
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import pytest import pytest
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
from helpers import cli from clan_cli.tests.helpers import cli
from stdout import CaptureOutput from clan_cli.tests.stdout import CaptureOutput
if TYPE_CHECKING: if TYPE_CHECKING:
pass pass

View File

@@ -4,9 +4,9 @@ from typing import TYPE_CHECKING
import pytest import pytest
from clan_cli.dirs import user_history_file from clan_cli.dirs import user_history_file
from clan_cli.history.add import HistoryEntry from clan_cli.history.add import HistoryEntry
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
from helpers import cli from clan_cli.tests.helpers import cli
from stdout import CaptureOutput from clan_cli.tests.stdout import CaptureOutput
if TYPE_CHECKING: if TYPE_CHECKING:
pass pass

View File

@@ -2,12 +2,12 @@ from pathlib import Path
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import pytest import pytest
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
from helpers import cli from clan_cli.tests.helpers import cli
from stdout import CaptureOutput from clan_cli.tests.stdout import CaptureOutput
if TYPE_CHECKING: if TYPE_CHECKING:
from age_keys import KeyPair from .age_keys import KeyPair
def test_import_sops( def test_import_sops(

View File

@@ -1,10 +1,11 @@
import fixtures_flakes
import pytest import pytest
from age_keys import SopsSetup, assert_secrets_file_recipients from age_keys import SopsSetup, assert_secrets_file_recipients
from clan_cli.inventory import load_inventory_json from clan_cli.inventory import load_inventory_json
from clan_cli.secrets.folders import sops_machines_folder from clan_cli.secrets.folders import sops_machines_folder
from helpers import cli
from stdout import CaptureOutput from clan_cli.tests import fixtures_flakes
from clan_cli.tests.helpers import cli
from clan_cli.tests.stdout import CaptureOutput
@pytest.mark.impure @pytest.mark.impure
@@ -89,9 +90,9 @@ def test_machine_delete(
cli.run(set_shared_secret) cli.run(set_shared_secret)
my_machine_sops_folder = sops_machines_folder(flake.path) / "my-machine" my_machine_sops_folder = sops_machines_folder(flake.path) / "my-machine"
assert my_machine_sops_folder.is_dir(), ( assert (
"A sops folder for `my-machine` should have been created with its public key" my_machine_sops_folder.is_dir()
) ), "A sops folder for `my-machine` should have been created with its public key"
# define some vars generator for `my-machine`: # define some vars generator for `my-machine`:
config = flake.machines["my-machine"] config = flake.machines["my-machine"]
@@ -109,16 +110,16 @@ def test_machine_delete(
cli.run(["vars", "generate", "--flake", str(flake.path), "my-machine"]) cli.run(["vars", "generate", "--flake", str(flake.path), "my-machine"])
my_machine_vars_store = flake.path / "vars/per-machine" / "my-machine" my_machine_vars_store = flake.path / "vars/per-machine" / "my-machine"
assert my_machine_vars_store.is_dir(), ( assert (
"A vars directory should have been created for `my-machine`" my_machine_vars_store.is_dir()
) ), "A vars directory should have been created for `my-machine`"
cli.run(["machines", "delete", "--flake", str(flake.path), "my-machine"]) cli.run(["machines", "delete", "--flake", str(flake.path), "my-machine"])
assert not my_machine_vars_store.exists(), ( assert (
"The vars directory for `my-machine` should have been deleted" not my_machine_vars_store.exists()
) ), "The vars directory for `my-machine` should have been deleted"
assert not my_machine_sops_folder.exists(), ( assert (
"The sops folder holding the public key for `my-machine` should have been deleted" not my_machine_sops_folder.exists()
) ), "The sops folder holding the public key for `my-machine` should have been deleted"
expected_recipients = [admin_key, machine2_key] expected_recipients = [admin_key, machine2_key]
assert_secrets_file_recipients(flake.path, shared_secret_name, expected_recipients) assert_secrets_file_recipients(flake.path, shared_secret_name, expected_recipients)

View File

@@ -13,14 +13,14 @@ from clan_cli.inventory import (
) )
from clan_cli.machines.create import CreateOptions, create_machine from clan_cli.machines.create import CreateOptions, create_machine
from clan_cli.nix import nix_eval, run_no_stdout from clan_cli.nix import nix_eval, run_no_stdout
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
if TYPE_CHECKING: if TYPE_CHECKING:
from age_keys import KeyPair from .age_keys import KeyPair
# from clan_cli.vars.var import machine_get_fact # from clan_cli.vars.var import machine_get_fact
from clan_cli.machines.machines import Machine as MachineMachine from clan_cli.machines.machines import Machine as MachineMachine
from helpers import cli from clan_cli.tests.helpers import cli
@pytest.mark.with_core @pytest.mark.with_core

View File

@@ -9,13 +9,15 @@ from typing import TYPE_CHECKING
import pytest import pytest
from age_keys import assert_secrets_file_recipients from age_keys import assert_secrets_file_recipients
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from fixtures_flakes import FlakeForTest
from gpg_keys import GpgKey from gpg_keys import GpgKey
from helpers import cli from clan_cli.secrets.folders import sops_secrets_folder
from stdout import CaptureOutput from clan_cli.tests.fixtures_flakes import FlakeForTest
from clan_cli.tests.helpers import cli
from clan_cli.tests.stdout import CaptureOutput
if TYPE_CHECKING: if TYPE_CHECKING:
from age_keys import KeyPair from .age_keys import KeyPair
log = logging.getLogger(__name__) log = logging.getLogger(__name__)

View File

@@ -7,12 +7,12 @@ from clan_cli.flake import Flake
from clan_cli.machines.facts import machine_get_fact from clan_cli.machines.facts import machine_get_fact
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.secrets.folders import sops_secrets_folder from clan_cli.secrets.folders import sops_secrets_folder
from fixtures_flakes import FlakeForTest from clan_cli.tests.fixtures_flakes import FlakeForTest
from helpers import cli from clan_cli.tests.helpers import cli
from helpers.validator import is_valid_age_key from clan_cli.tests.helpers.validator import is_valid_age_key
if TYPE_CHECKING: if TYPE_CHECKING:
from age_keys import KeyPair from .age_keys import KeyPair
@pytest.mark.impure @pytest.mark.impure

View File

@@ -8,8 +8,8 @@ from clan_cli.machines.facts import machine_get_fact
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_shell from clan_cli.nix import nix_shell
from clan_cli.ssh.host import Host from clan_cli.ssh.host import Host
from fixtures_flakes import ClanFlake from clan_cli.tests.fixtures_flakes import ClanFlake
from helpers import cli from clan_cli.tests.helpers import cli
@pytest.mark.impure @pytest.mark.impure

View File

@@ -2,11 +2,11 @@ from typing import TYPE_CHECKING
import pytest import pytest
from clan_cli.ssh.host import Host from clan_cli.ssh.host import Host
from fixtures_flakes import ClanFlake from clan_cli.tests.fixtures_flakes import ClanFlake
from helpers import cli from clan_cli.tests.helpers import cli
if TYPE_CHECKING: if TYPE_CHECKING:
from age_keys import KeyPair from .age_keys import KeyPair
@pytest.mark.with_core @pytest.mark.with_core

View File

@@ -4,11 +4,13 @@ import shutil
from pathlib import Path from pathlib import Path
import pytest import pytest
from age_keys import SopsSetup
from clan_cli.errors import ClanError from clan_cli.errors import ClanError
from clan_cli.flake import Flake from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_eval, run from clan_cli.nix import nix_eval, run
from clan_cli.tests.age_keys import SopsSetup
from clan_cli.tests.fixtures_flakes import ClanFlake
from clan_cli.tests.helpers import cli
from clan_cli.vars.check import check_vars from clan_cli.vars.check import check_vars
from clan_cli.vars.generate import Generator, generate_vars_for_machine from clan_cli.vars.generate import Generator, generate_vars_for_machine
from clan_cli.vars.get import get_var from clan_cli.vars.get import get_var
@@ -17,8 +19,9 @@ from clan_cli.vars.list import stringify_all_vars
from clan_cli.vars.public_modules import in_repo from clan_cli.vars.public_modules import in_repo
from clan_cli.vars.secret_modules import password_store, sops from clan_cli.vars.secret_modules import password_store, sops
from clan_cli.vars.set import set_var from clan_cli.vars.set import set_var
from fixtures_flakes import ClanFlake
from helpers import cli if TYPE_CHECKING:
from .age_keys import KeyPair
def test_dependencies_as_files(temp_dir: Path) -> None: def test_dependencies_as_files(temp_dir: Path) -> None:
@@ -326,9 +329,9 @@ def test_generated_shared_secret_sops(
shared_generator["script"] = "echo hello > $out/my_shared_secret" shared_generator["script"] = "echo hello > $out/my_shared_secret"
m2_config = flake.machines["machine2"] m2_config = flake.machines["machine2"]
m2_config["nixpkgs"]["hostPlatform"] = "x86_64-linux" m2_config["nixpkgs"]["hostPlatform"] = "x86_64-linux"
m2_config["clan"]["core"]["vars"]["generators"]["my_shared_generator"] = ( m2_config["clan"]["core"]["vars"]["generators"][
shared_generator.copy() "my_shared_generator"
) ] = shared_generator.copy()
flake.refresh() flake.refresh()
monkeypatch.chdir(flake.path) monkeypatch.chdir(flake.path)
machine1 = Machine(name="machine1", flake=Flake(str(flake.path))) machine1 = Machine(name="machine1", flake=Flake(str(flake.path)))
@@ -769,9 +772,9 @@ def test_migration(
my_service = config["clan"]["core"]["facts"]["services"]["my_service"] my_service = config["clan"]["core"]["facts"]["services"]["my_service"]
my_service["public"]["my_value"] = {} my_service["public"]["my_value"] = {}
my_service["secret"]["my_secret"] = {} my_service["secret"]["my_secret"] = {}
my_service["generator"]["script"] = ( my_service["generator"][
"echo -n hello > $facts/my_value && echo -n hello > $secrets/my_secret" "script"
) ] = "echo -n hello > $facts/my_value && echo -n hello > $secrets/my_secret"
my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"] my_generator = config["clan"]["core"]["vars"]["generators"]["my_generator"]
my_generator["files"]["my_value"]["secret"] = False my_generator["files"]["my_value"]["secret"] = False
my_generator["files"]["my_secret"]["secret"] = True my_generator["files"]["my_secret"]["secret"] = True
@@ -840,9 +843,9 @@ def test_fails_when_files_are_left_from_other_backend(
regenerate=False, regenerate=False,
) )
# Will raise. It was secret before, but now it's not. # Will raise. It was secret before, but now it's not.
my_secret_generator["files"]["my_secret"]["secret"] = ( my_secret_generator["files"]["my_secret"][
False # secret -> public (NOT OK) "secret"
) ] = False # secret -> public (NOT OK)
# WIll not raise. It was not secret before, and it's secret now. # WIll not raise. It was not secret before, and it's secret now.
my_value_generator["files"]["my_value"]["secret"] = True # public -> secret (OK) my_value_generator["files"]["my_value"]["secret"] = True # public -> secret (OK)
flake.refresh() flake.refresh()
@@ -948,13 +951,15 @@ def test_dynamic_invalidation(
# this is an abuse # this is an abuse
custom_nix = flake.path / "machines" / machine.name / "hardware-configuration.nix" custom_nix = flake.path / "machines" / machine.name / "hardware-configuration.nix"
custom_nix.write_text(""" custom_nix.write_text(
"""
{ config, ... }: let { config, ... }: let
p = config.clan.core.vars.generators.my_generator.files.my_value.path; p = config.clan.core.vars.generators.my_generator.files.my_value.path;
in { in {
clan.core.vars.generators.dependent_generator.validation = if builtins.pathExists p then builtins.readFile p else null; clan.core.vars.generators.dependent_generator.validation = if builtins.pathExists p then builtins.readFile p else null;
} }
""") """
)
flake.refresh() flake.refresh()
machine.flush_caches() machine.flush_caches()

View File

@@ -3,15 +3,15 @@ import subprocess
from contextlib import ExitStack from contextlib import ExitStack
import pytest import pytest
from age_keys import SopsSetup
from clan_cli import cmd from clan_cli import cmd
from clan_cli.flake import Flake from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.nix import nix_eval, run from clan_cli.nix import nix_eval, run
from clan_cli.tests.age_keys import SopsSetup
from clan_cli.tests.fixtures_flakes import ClanFlake
from clan_cli.tests.helpers import cli
from clan_cli.tests.nix_config import ConfigItem
from clan_cli.vms.run import inspect_vm, spawn_vm from clan_cli.vms.run import inspect_vm, spawn_vm
from fixtures_flakes import ClanFlake
from helpers import cli
from nix_config import ConfigItem
@pytest.mark.impure @pytest.mark.impure

View File

@@ -4,13 +4,13 @@ from typing import TYPE_CHECKING
import pytest import pytest
from clan_cli.flake import Flake from clan_cli.flake import Flake
from clan_cli.machines.machines import Machine from clan_cli.machines.machines import Machine
from clan_cli.tests.fixtures_flakes import ClanFlake, FlakeForTest
from clan_cli.tests.helpers import cli
from clan_cli.tests.stdout import CaptureOutput
from clan_cli.vms.run import inspect_vm, spawn_vm from clan_cli.vms.run import inspect_vm, spawn_vm
from fixtures_flakes import ClanFlake, FlakeForTest
from helpers import cli
from stdout import CaptureOutput
if TYPE_CHECKING: if TYPE_CHECKING:
from age_keys import KeyPair from .age_keys import KeyPair
no_kvm = not Path("/dev/kvm").exists() no_kvm = not Path("/dev/kvm").exists()