enroll/tests/test_cli_config_and_sops.py
Miguel Jacq 6c3275b44a
All checks were successful
CI / test (push) Successful in 7m4s
Lint / test (push) Successful in 30s
Trivy / test (push) Successful in 22s
Fix tests
2026-01-03 11:46:40 +11:00

189 lines
5.7 KiB
Python

from __future__ import annotations
import argparse
import configparser
import tarfile
from pathlib import Path
def test_discover_config_path_precedence(monkeypatch, tmp_path: Path):
from enroll.cli import _discover_config_path
cfg = tmp_path / "cfg.ini"
cfg.write_text("[enroll]\n", encoding="utf-8")
# --no-config always wins
monkeypatch.setenv("ENROLL_CONFIG", str(cfg))
assert _discover_config_path(["--no-config", "harvest"]) is None
# explicit --config wins
assert _discover_config_path(["--config", str(cfg), "harvest"]) == cfg
# env var used when present
assert _discover_config_path(["harvest"]) == cfg
def test_discover_config_path_finds_local_and_xdg(monkeypatch, tmp_path: Path):
from enroll.cli import _discover_config_path
# local file in cwd
cwd = tmp_path / "cwd"
cwd.mkdir()
local = cwd / "enroll.ini"
local.write_text("[enroll]\n", encoding="utf-8")
monkeypatch.chdir(cwd)
monkeypatch.delenv("ENROLL_CONFIG", raising=False)
monkeypatch.delenv("XDG_CONFIG_HOME", raising=False)
assert _discover_config_path(["harvest"]) == local
# xdg config fallback
monkeypatch.chdir(tmp_path)
xdg = tmp_path / "xdg"
(xdg / "enroll").mkdir(parents=True)
xcfg = xdg / "enroll" / "enroll.ini"
xcfg.write_text("[enroll]\n", encoding="utf-8")
monkeypatch.setenv("XDG_CONFIG_HOME", str(xdg))
assert _discover_config_path(["harvest"]) == xcfg
def test_section_to_argv_supports_bool_append_count_and_unknown(monkeypatch, capsys):
from enroll.cli import _section_to_argv
ap = argparse.ArgumentParser(add_help=False)
ap.add_argument("--flag", action="store_true")
ap.add_argument("--no-flag", action="store_false", dest="flag2")
ap.add_argument("--item", action="append", default=[])
ap.add_argument("-v", action="count", default=0)
cfg = configparser.ConfigParser()
cfg.read_dict(
{
"enroll": {
"flag": "true",
"no_flag": "false",
"item": "a,b",
"v": "2",
"unknown_key": "zzz",
}
}
)
argv = _section_to_argv(ap, cfg, "enroll")
# bools set
assert "--flag" in argv
assert "--no-flag" in argv
# append expanded
assert argv.count("--item") == 2
assert "a" in argv and "b" in argv
# count flag expanded
assert argv.count("-v") == 2
# unknown key prints warning
err = capsys.readouterr().err
assert "unknown option" in err
def test_inject_config_argv_inserts_global_and_command_tokens(tmp_path: Path):
from enroll.cli import _inject_config_argv
root = argparse.ArgumentParser(add_help=False)
root.add_argument("--root-flag", action="store_true")
sub = root.add_subparsers(dest="cmd", required=True)
p_h = sub.add_parser("harvest", add_help=False)
p_h.add_argument("--dangerous", action="store_true")
p_h.add_argument("--include-path", action="append", default=[])
cfg_path = tmp_path / "enroll.ini"
cfg_path.write_text(
"""[enroll]
root-flag = true
[harvest]
dangerous = true
include-path = /etc/one,/etc/two
""",
encoding="utf-8",
)
argv = ["harvest", "--include-path", "/etc/cli"]
injected = _inject_config_argv(
argv,
cfg_path=cfg_path,
root_parser=root,
subparsers={"harvest": p_h},
)
# global inserted before cmd, subcommand tokens right after cmd
assert injected[:2] == ["--root-flag", "harvest"]
# include-path from config inserted before CLI include-path (CLI wins later if duplicates)
joined = " ".join(injected)
assert "--include-path /etc/one" in joined
assert "--include-path /etc/cli" in joined
def test_resolve_sops_out_file_and_encrypt_path(monkeypatch, tmp_path: Path):
from enroll import cli
# directory output should yield harvest.tar.gz.sops inside
out_dir = tmp_path / "o"
out_dir.mkdir()
assert (
cli._resolve_sops_out_file(str(out_dir), hint="h").name == "harvest.tar.gz.sops"
)
# file-like output retained
out_file = tmp_path / "x.sops"
assert cli._resolve_sops_out_file(str(out_file), hint="h") == out_file
# None uses cache dir
class HC:
def __init__(self, d: Path):
self.dir = d
monkeypatch.setattr(
cli, "new_harvest_cache_dir", lambda hint: HC(tmp_path / "cache")
)
p = cli._resolve_sops_out_file(None, hint="h")
assert str(p).endswith("harvest.tar.gz.sops")
# Cover _tar_dir_to quickly (writes a tarball)
bundle = tmp_path / "bundle"
bundle.mkdir()
(bundle / "state.json").write_text("{}", encoding="utf-8")
tar_path = tmp_path / "b.tar.gz"
cli._tar_dir_to(bundle, tar_path)
assert tar_path.exists()
with tarfile.open(tar_path, "r:gz") as tf:
names = tf.getnames()
assert "state.json" in names or "./state.json" in names
def test_encrypt_harvest_dir_to_sops_cleans_up_tmp_tgz(monkeypatch, tmp_path: Path):
from enroll.cli import _encrypt_harvest_dir_to_sops
bundle = tmp_path / "bundle"
bundle.mkdir()
(bundle / "state.json").write_text("{}", encoding="utf-8")
out_file = tmp_path / "out.sops"
seen = {}
def fake_encrypt(src: Path, dst: Path, pgp_fingerprints, mode): # noqa: ARG001
# write something so we can see output created
seen["src"] = src
dst.write_bytes(b"enc")
monkeypatch.setattr("enroll.cli.encrypt_file_binary", fake_encrypt)
# Make os.unlink raise FileNotFoundError to hit the except branch in finally.
monkeypatch.setattr(
"enroll.cli.os.unlink", lambda p: (_ for _ in ()).throw(FileNotFoundError())
)
res = _encrypt_harvest_dir_to_sops(bundle, out_file, fps=["ABC"])
assert res == out_file
assert out_file.read_bytes() == b"enc"