"""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, **kwargs: 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"