test(local-scripts): add tests for local script hooks
continuous-integration/drone/pr Build is passing

This commit is contained in:
2026-08-31 13:02:57 +02:00
parent c8e6346a33
commit 35a1151a7d
2 changed files with 217 additions and 0 deletions
+184
View File
@@ -0,0 +1,184 @@
"""Tests for local script functionality in hooks."""
import logging
import os
import stat
import sys
import click
import pytest
from click.testing import CliRunner
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import alakazam
from alakazam import (create_secrets, deploy_apps, execute_cmds, get_abra_dir,
resolve_path, run_local_script, run_secret_hooks)
class FakeProcess:
def __init__(self, returncode):
self.returncode = returncode
def make_script(tmp_path, name="script.sh", executable=True):
"""Write a script file under tmp_path, executable by default."""
path = tmp_path / name
path.write_text("#!/bin/sh\nexit 0\n")
if executable:
path.chmod(path.stat().st_mode | stat.S_IXUSR)
return path
class TestResolvePath:
def test_an_absolute_path_passes_through_unchanged(self, tmp_path):
absolute = tmp_path / "script.sh"
assert resolve_path(str(absolute)) == absolute
def test_a_tilde_path_expands_against_home(self, monkeypatch, tmp_path):
monkeypatch.setenv("HOME", str(tmp_path))
assert resolve_path("~/script.sh") == tmp_path / "script.sh"
def test_a_relative_path_resolves_against_the_given_base(self, tmp_path):
base = tmp_path / "instance"
assert resolve_path("scripts/script.sh", base) == base / "scripts/script.sh"
def test_a_relative_path_resolves_against_root_path_by_default(self, monkeypatch, tmp_path):
monkeypatch.setattr(alakazam, "ROOT_PATH", tmp_path)
assert resolve_path("scripts/script.sh") == tmp_path / "scripts/script.sh"
class TestRunLocalScript:
def test_the_script_receives_its_env_vars_and_arguments(self, monkeypatch, tmp_path):
script = make_script(tmp_path)
calls = []
monkeypatch.setattr(alakazam.subprocess, "run",
lambda cmd, env: calls.append((cmd, env)) or FakeProcess(0))
run_local_script(["script", str(script), "arg1"], "login.a.org", "a.org", "a.org")
[(cmd, env)] = calls
assert cmd == [str(script), "arg1"]
assert env["ALAKAZAM_APP_DOMAIN"] == "login.a.org"
assert env["ALAKAZAM_APP_SERVER"] == "a.org"
assert env["ALAKAZAM_INSTANCE_DOMAIN"] == "a.org"
def test_a_non_executable_script_is_not_run(self, monkeypatch, tmp_path, caplog):
script = make_script(tmp_path, executable=False)
monkeypatch.setattr(alakazam.subprocess, "run",
lambda *a, **k: pytest.fail("a non-executable script must not run"))
with caplog.at_level(logging.ERROR):
run_local_script(["script", str(script)], "login.a.org", "a.org", "a.org")
assert "not executable" in caplog.text
def test_dry_run_does_not_execute_the_script(self, monkeypatch, tmp_path, capsys):
script = make_script(tmp_path)
monkeypatch.setattr(alakazam.subprocess, "run",
lambda *a, **k: pytest.fail("dry_run must not execute anything"))
run_local_script(["script", str(script)], "login.a.org", "a.org", "a.org", dry_run=True)
assert "Run local script" in capsys.readouterr().out
def test_a_non_zero_exit_only_warns_by_default(self, monkeypatch, tmp_path, caplog):
script = make_script(tmp_path)
monkeypatch.setattr(alakazam.subprocess, "run", lambda *a, **k: FakeProcess(1))
with caplog.at_level(logging.WARNING):
run_local_script(["script", str(script)], "login.a.org", "a.org", "a.org")
assert "exited with code 1" in caplog.text
def test_a_non_zero_exit_aborts_in_strict_mode(self, monkeypatch, tmp_path):
script = make_script(tmp_path)
monkeypatch.setattr(alakazam.subprocess, "run", lambda *a, **k: FakeProcess(1))
with pytest.raises(click.ClickException) as excinfo:
run_local_script(["script", str(script)], "login.a.org", "a.org", "a.org", strict=True)
assert "exited with code 1" in excinfo.value.message
def test_a_non_executable_script_aborts_in_strict_mode(self, monkeypatch, tmp_path):
script = make_script(tmp_path, executable=False)
monkeypatch.setattr(alakazam.subprocess, "run",
lambda *a, **k: pytest.fail("a non-executable script must not run"))
with pytest.raises(click.ClickException) as excinfo:
run_local_script(["script", str(script)], "login.a.org", "a.org", "a.org", strict=True)
assert "not executable" in excinfo.value.message
class TestScriptHookDispatch:
def test_a_script_secret_hook_dispatches_to_run_local_script(self, monkeypatch, tmp_path):
script = make_script(tmp_path)
calls = []
monkeypatch.setattr(alakazam, "run_local_script", lambda *a, **k: calls.append((a, k)))
monkeypatch.setattr(alakazam, "abra", lambda *a, **k: pytest.fail("should not call abra"))
run_secret_hooks("login.a.org", {"secret_hooks": [f"script {script}"], "server": "a.org"}, "a.org")
[(args, kwargs)] = calls
assert args == (["script", str(script)], "login.a.org", "a.org", "a.org")
def test_a_normal_secret_hook_goes_through_abra(self, monkeypatch):
calls = []
monkeypatch.setattr(alakazam, "abra", lambda *a, **k: calls.append((a, k)) or "")
run_secret_hooks("login.a.org", {"secret_hooks": ["insert_cert"], "server": "a.org"})
assert calls, "a non-script hook must still run through abra"
def test_a_script_command_dispatches_to_run_local_script(self, monkeypatch, tmp_path):
script = make_script(tmp_path)
calls = []
monkeypatch.setattr(alakazam, "run_local_script", lambda *a, **k: calls.append((a, k)))
monkeypatch.setattr(alakazam, "abra", lambda *a, **k: pytest.fail("should not call abra"))
execute_cmds({"app_domain": "login.a.org", "server": "a.org",
"initial-hooks": [f"script {script} arg1"]}, initial=True)
[(args, kwargs)] = calls
assert args == (["script", str(script), "arg1"], "login.a.org", "a.org", "", False, False)
def test_a_normal_command_goes_through_abra(self, monkeypatch):
calls = []
monkeypatch.setattr(alakazam, "abra", lambda *a, **k: calls.append((a, k)) or "")
execute_cmds({"app_domain": "login.a.org", "server": "a.org",
"initial-hooks": ["app set_default_quota"]}, initial=True)
assert calls, "a non-script command must still run through abra"
def test_a_script_token_with_a_missing_file_falls_back_to_abra(self, monkeypatch, tmp_path):
"""A 'script' hook whose path does not exist is not a script hook at all, just an abra
command whose container happens to be named 'script'."""
missing = tmp_path / "does-not-exist.sh"
calls = []
monkeypatch.setattr(alakazam, "run_local_script",
lambda *a, **k: pytest.fail("should not run a missing script"))
monkeypatch.setattr(alakazam, "abra", lambda *a, **k: calls.append((a, k)) or "")
execute_cmds({"app_domain": "login.a.org", "server": "a.org",
"initial-hooks": [f"script {missing}"]}, initial=True)
assert calls, "a missing script path must fall back to a normal abra command"
class TestInstanceDomainThreading:
"""create_secrets(), deploy_apps() and the 'cmd' CLI command each thread instance_domain
into run_secret_hooks()/execute_cmds() for script hooks."""
@pytest.fixture
def app_config(self, monkeypatch):
"""A single 'authentik' app on instance 'a.org', shared by tests below."""
config = {"app_domain": "login.a.org", "server": "a.org"}
monkeypatch.setattr(alakazam, "INSTANCE_CONFIGS", {"a.org": {"authentik": config}})
return config
def test_create_secrets_threads_the_instance_domain(self, monkeypatch, app_config):
calls = []
monkeypatch.setattr(alakazam, "run_secret_hooks", lambda *a, **k: calls.append((a, k)))
create_secrets(recipes=(), variants={"secret-hooks"})
[(args, kwargs)] = calls
assert args == ("login.a.org", app_config, "a.org")
def test_deploy_apps_threads_the_instance_domain(self, monkeypatch, app_config):
app_config["version"] = "1.0.0"
monkeypatch.setattr(alakazam, "abra", lambda *a, **k: "")
calls = []
monkeypatch.setattr(alakazam, "execute_cmds", lambda app_config, **k: calls.append(k))
deploy_apps({"a.org": [["authentik", "login.a.org"]]}, execute_hooks=True)
[kwargs] = calls
assert kwargs["deploy"] is True
assert kwargs["instance_domain"] == "a.org"
def test_cmd_threads_the_instance_domain(self, monkeypatch, app_config):
monkeypatch.setattr(alakazam, "get_deployed_apps", lambda apps: {"login.a.org": "1.0.0"})
calls = []
monkeypatch.setattr(alakazam, "execute_cmds", lambda app_config, **k: calls.append(k))
result = CliRunner().invoke(alakazam.cmd, ["-i"], standalone_mode=False)
assert result.exception is None
[kwargs] = calls
assert kwargs["initial"] is True
assert kwargs["instance_domain"] == "a.org"
+33
View File
@@ -1,6 +1,7 @@
"""Tests for the strict hook mode that lets a scratch build fail on a broken hook."""
import os
import stat
import sys
import click
@@ -12,6 +13,14 @@ import alakazam
from alakazam import execute_cmds, run_secret_hooks
def failing_script(tmp_path):
"""A tiny local script that exits non-zero, for testing 'script' hook failure handling."""
script = tmp_path / "fail.sh"
script.write_text("#!/bin/sh\nexit 1\n")
script.chmod(script.stat().st_mode | stat.S_IXUSR)
return script
class TestStrictHooks:
def test_a_secret_hook_failure_is_tolerated_by_default(self, monkeypatch):
"""'secrets' has always continued past a failing hook, that must not change."""
@@ -39,3 +48,27 @@ class TestStrictHooks:
with pytest.raises(click.ClickException) as excinfo:
execute_cmds({"app_domain": "login.a.org", "server": "a.org", "initial-hooks": ["app init"]}, initial=True, strict=True)
assert "command 'app init' failed" in excinfo.value.message
def test_a_script_secret_hook_failure_is_tolerated_by_default(self, tmp_path):
"""A failing local script must be tolerated by default too, same as a failing abra.sh hook."""
script = failing_script(tmp_path)
run_secret_hooks("login.a.org", {"secret_hooks": [f"script {script}"], "server": "a.org"})
def test_a_script_secret_hook_failure_aborts_in_strict_mode(self, tmp_path):
script = failing_script(tmp_path)
with pytest.raises(click.ClickException) as excinfo:
run_secret_hooks("login.a.org", {"secret_hooks": [f"script {script}"], "server": "a.org"}, strict=True)
assert str(script) in excinfo.value.message
assert "failed" in excinfo.value.message
def test_a_script_command_failure_is_tolerated_by_default(self, tmp_path):
script = failing_script(tmp_path)
execute_cmds({"app_domain": "login.a.org", "server": "a.org", "initial-hooks": [f"script {script}"]}, initial=True)
def test_a_script_command_failure_aborts_in_strict_mode(self, tmp_path):
script = failing_script(tmp_path)
with pytest.raises(click.ClickException) as excinfo:
execute_cmds({"app_domain": "login.a.org", "server": "a.org", "initial-hooks": [f"script {script}"]},
initial=True, strict=True)
assert str(script) in excinfo.value.message
assert "failed" in excinfo.value.message