Default on, because the cache is safe by construction rather than by convention: it engages only where a Cargo.lock exists, and refuses to publish anything the prune assertion cannot vouch for. Two independent off switches -- the flag for one job, `enabled = false` for the host. The store root is resolved after the environment overrides are applied, so CI_ROOT relocates it along with everything else, while an explicit root in the config still wins. Both directions are covered by tests. Also asserts that the Windows branch never receives the argument at all: the cache is Linux-only and the two branches stay separate, per AGENTS.md.
199 lines
6.5 KiB
Python
199 lines
6.5 KiB
Python
"""Tests for ci_orchestrator.config."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from ci_orchestrator.config import load_config
|
|
|
|
|
|
def test_defaults_are_os_aware() -> None:
|
|
cfg = load_config(env={})
|
|
if os.name == "nt":
|
|
assert str(cfg.paths.root) == r"F:\CI"
|
|
else:
|
|
assert str(cfg.paths.root) == "/var/lib/ci"
|
|
|
|
|
|
def test_env_overrides_paths(tmp_path: Path) -> None:
|
|
cfg = load_config(
|
|
env={
|
|
"CI_ROOT": str(tmp_path / "root"),
|
|
"CI_TEMPLATES": str(tmp_path / "tpl"),
|
|
"CI_BUILD_VMS": str(tmp_path / "bvm"),
|
|
"CI_ARTIFACTS": str(tmp_path / "art"),
|
|
"CI_KEYS": str(tmp_path / "keys"),
|
|
}
|
|
)
|
|
assert cfg.paths.root == tmp_path / "root"
|
|
assert cfg.paths.templates == tmp_path / "tpl"
|
|
assert cfg.paths.build_vms == tmp_path / "bvm"
|
|
assert cfg.paths.artifacts == tmp_path / "art"
|
|
assert cfg.paths.keys == tmp_path / "keys"
|
|
|
|
|
|
def test_toml_overrides_defaults(tmp_path: Path) -> None:
|
|
toml = tmp_path / "config.toml"
|
|
toml.write_text(
|
|
'[paths]\n'
|
|
f'root = "{(tmp_path / "altroot").as_posix()}"\n'
|
|
'[backend]\n'
|
|
'type = "workstation"\n'
|
|
'extra = "value"\n',
|
|
encoding="utf-8",
|
|
)
|
|
cfg = load_config(config_path=toml, env={})
|
|
assert cfg.paths.root == tmp_path / "altroot"
|
|
assert cfg.backend.type == "workstation"
|
|
assert cfg.backend.options == {"extra": "value"}
|
|
|
|
|
|
def test_env_wins_over_toml(tmp_path: Path) -> None:
|
|
toml = tmp_path / "config.toml"
|
|
toml.write_text(
|
|
f'[paths]\nroot = "{(tmp_path / "fromtoml").as_posix()}"\n',
|
|
encoding="utf-8",
|
|
)
|
|
cfg = load_config(
|
|
config_path=toml,
|
|
env={"CI_ROOT": str(tmp_path / "fromenv")},
|
|
)
|
|
assert cfg.paths.root == tmp_path / "fromenv"
|
|
|
|
|
|
def test_ci_config_env_loads_toml(tmp_path: Path) -> None:
|
|
toml = tmp_path / "alt.toml"
|
|
toml.write_text(
|
|
f'[paths]\nroot = "{(tmp_path / "rootenv").as_posix()}"\n', encoding="utf-8"
|
|
)
|
|
cfg = load_config(env={"CI_CONFIG": str(toml)})
|
|
assert cfg.paths.root == tmp_path / "rootenv"
|
|
|
|
|
|
def test_default_config_toml_in_root_is_loaded(
|
|
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
|
) -> None:
|
|
"""When no config_path/env is given, ``<root>/config.toml`` is autoloaded."""
|
|
import ci_orchestrator.config as cfg_mod
|
|
from ci_orchestrator.config import Paths
|
|
|
|
fake = Paths(
|
|
root=tmp_path,
|
|
templates=tmp_path / "t",
|
|
build_vms=tmp_path / "b",
|
|
artifacts=tmp_path / "a",
|
|
keys=tmp_path / "k",
|
|
)
|
|
monkeypatch.setattr(cfg_mod, "_platform_defaults", lambda: fake)
|
|
(tmp_path / "config.toml").write_text(
|
|
'[backend]\ntype = "workstation"\noption = "auto"\n', encoding="utf-8"
|
|
)
|
|
cfg = load_config(env={})
|
|
assert cfg.backend.options == {"option": "auto"}
|
|
|
|
|
|
def test_toml_provides_ssh_key_path_and_guest_cred_target(tmp_path: Path) -> None:
|
|
toml = tmp_path / "config.toml"
|
|
key = tmp_path / "id_ed25519"
|
|
key.write_text("k")
|
|
toml.write_text(
|
|
f'ssh_key_path = "{key.as_posix()}"\n'
|
|
'guest_cred_target = "MyTarget"\n',
|
|
encoding="utf-8",
|
|
)
|
|
cfg = load_config(config_path=toml, env={})
|
|
assert cfg.ssh_key_path == key
|
|
assert cfg.guest_cred_target == "MyTarget"
|
|
|
|
|
|
def test_toml_ip_pool_parsed(tmp_path: Path) -> None:
|
|
lease = tmp_path / "ip-pool.json"
|
|
toml = tmp_path / "config.toml"
|
|
toml.write_text(
|
|
"[ip_pool]\n"
|
|
'addresses = ["192.168.79.201", "192.168.79.202"]\n'
|
|
'netmask = "255.255.255.0"\n'
|
|
'gateway = "192.168.79.2"\n'
|
|
f'lease_file = "{lease.as_posix()}"\n',
|
|
encoding="utf-8",
|
|
)
|
|
cfg = load_config(config_path=toml, env={})
|
|
assert cfg.ip_pool is not None
|
|
assert cfg.ip_pool.addresses == ["192.168.79.201", "192.168.79.202"]
|
|
assert cfg.ip_pool.netmask == "255.255.255.0"
|
|
assert cfg.ip_pool.gateway == "192.168.79.2"
|
|
assert cfg.ip_pool.lease_file == lease
|
|
|
|
|
|
def test_toml_ip_pool_default_lease_file(tmp_path: Path) -> None:
|
|
toml = tmp_path / "config.toml"
|
|
toml.write_text(
|
|
"[ip_pool]\n"
|
|
'addresses = ["192.168.79.201"]\n',
|
|
encoding="utf-8",
|
|
)
|
|
cfg = load_config(config_path=toml, env={})
|
|
assert cfg.ip_pool is not None
|
|
assert cfg.ip_pool.lease_file.name == "ip-pool.json"
|
|
|
|
|
|
def test_ip_pool_absent_when_not_configured(tmp_path: Path) -> None:
|
|
toml = tmp_path / "config.toml"
|
|
toml.write_text("[backend]\ntype = \"workstation\"\n", encoding="utf-8")
|
|
cfg = load_config(config_path=toml, env={})
|
|
assert cfg.ip_pool is None
|
|
|
|
|
|
# ── cargo cache section ──────────────────────────────────────────────────────
|
|
|
|
|
|
def _paths_only(tmp_path: Path, extra: str = "") -> Path:
|
|
cfg = tmp_path / "config.toml"
|
|
cfg.write_text(
|
|
'[paths]\nroot = "/var/lib/ci"\ntemplates = "/var/lib/ci/templates"\n'
|
|
'build_vms = "/var/lib/ci/build-vms"\nartifacts = "/var/lib/ci/artifacts"\n'
|
|
'keys = "/var/lib/ci/keys"\n' + extra,
|
|
encoding="utf-8",
|
|
)
|
|
return cfg
|
|
|
|
|
|
def test_cargo_cache_defaults_under_paths_root(tmp_path: Path) -> None:
|
|
cfg = load_config(config_path=_paths_only(tmp_path), env={})
|
|
assert cfg.cargo_cache.enabled is True
|
|
assert cfg.cargo_cache.root == Path("/var/lib/ci/cache/cargo")
|
|
assert cfg.cargo_cache.retention_days == 30
|
|
|
|
|
|
def test_cargo_cache_section_overrides_the_defaults(tmp_path: Path) -> None:
|
|
cfg = load_config(
|
|
config_path=_paths_only(
|
|
tmp_path,
|
|
'\n[cargo_cache]\nenabled = false\nroot = "/mnt/fast/cargo"\n'
|
|
"retention_days = 7\n",
|
|
),
|
|
env={},
|
|
)
|
|
assert cfg.cargo_cache.enabled is False
|
|
assert cfg.cargo_cache.root == Path("/mnt/fast/cargo")
|
|
assert cfg.cargo_cache.retention_days == 7
|
|
|
|
|
|
def test_cargo_cache_root_follows_ci_root_env(tmp_path: Path) -> None:
|
|
"""CI_ROOT relocates every path; the store must move with them."""
|
|
cfg = load_config(
|
|
config_path=_paths_only(tmp_path), env={"CI_ROOT": str(tmp_path / "elsewhere")}
|
|
)
|
|
assert cfg.cargo_cache.root == tmp_path / "elsewhere" / "cache" / "cargo"
|
|
|
|
|
|
def test_explicit_cargo_cache_root_beats_ci_root(tmp_path: Path) -> None:
|
|
cfg = load_config(
|
|
config_path=_paths_only(tmp_path, '\n[cargo_cache]\nroot = "/mnt/fast/cargo"\n'),
|
|
env={"CI_ROOT": str(tmp_path / "elsewhere")},
|
|
)
|
|
assert cfg.cargo_cache.root == Path("/mnt/fast/cargo")
|