Files
computer-playbook/tests/unit/python/roles/sys-ctl-hlth-csp/files/test_script.py
T
kevinveenbirkenbachandClaude Opus 5 af605b049f refactor(domains): give local testing a .test domain, leave .example to docs
RFC 6761 reserves .test for testing and .example for documentation, and the
project's own domain was doing both jobs under the documentation name. It is
what default.env hands every local deploy and what the test fixtures assert
against, so it is a testing name.

Rename it wherever it stands for the running machine: the environment
defaults, the development inventory, the workspace and trust scripts, the test
suites, and the four documents that describe real behaviour - the DNS mapping
to loopback, the browser CA trust, WSL2 resolution. Those documents would have
started lying the moment default.env changed.

The sandbox allowlist moves with them, since egress is matched per pattern and
nothing resolves the old name any more, and the sandbox document that mirrors
that list follows it.

Two places keep .example on purpose: the link-style sample in the
documentation guide, which is documentation about documentation, and every
generic fixture name elsewhere in the suite - a.example, foo.example and their
kin. Those are a larger sweep of their own and are not touched here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-02 13:10:34 +02:00

524 lines
17 KiB
Python

from __future__ import annotations
import sys
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch
from . import PROJECT_ROOT
ROLE_FILES = PROJECT_ROOT / "roles/sys-ctl-hlth-csp/files/python"
sys.path.insert(0, str(ROLE_FILES))
import script # noqa: E402
class TestExtractDomainsFromFilenames(unittest.TestCase):
@patch("script.os.listdir")
def test_extract_domains_filters_valid_conf_domains(
self, mock_listdir: MagicMock
) -> None:
mock_listdir.return_value = [
"example.com.conf",
"api.example.com.conf",
"not-a-domain.txt",
"no-tld.conf",
".hidden.conf",
"a..b.com.conf",
"example.com.conf.bak",
"localhost.conf",
"sub.domain.co.uk.conf",
]
domains = script.extract_domains_from_filenames(
"/etc/nginx/conf.d/http/servers/"
)
self.assertIsInstance(domains, list)
self.assertIn("example.com", domains)
self.assertIn("api.example.com", domains)
self.assertIn("sub.domain.co.uk", domains)
self.assertNotIn("not-a-domain", domains)
self.assertNotIn("no-tld", domains)
self.assertNotIn(".hidden", domains)
self.assertNotIn("a..b.com", domains)
self.assertNotIn("example.com.conf", domains)
self.assertNotIn("localhost", domains)
@patch("script.os.listdir", side_effect=FileNotFoundError)
def test_extract_domains_returns_none_when_directory_missing(
self, _mock_listdir: MagicMock
) -> None:
domains = script.extract_domains_from_filenames("/missing")
self.assertIsNone(domains)
class TestSplitOnionDomains(unittest.TestCase):
def test_splits_families(self) -> None:
domains = [
"auth.abc123.onion",
"app.infinito.test",
"matomo.abc123.onion",
]
clearnet, onion = script.split_onion_domains(domains)
self.assertEqual(clearnet, ["app.infinito.test"])
self.assertEqual(onion, ["auth.abc123.onion", "matomo.abc123.onion"])
def test_clearnet_only(self) -> None:
domains = ["a.example", "b.example"]
self.assertEqual(script.split_onion_domains(domains), (domains, []))
def test_empty_list(self) -> None:
self.assertEqual(script.split_onion_domains([]), ([], []))
class TestIsSkippedDomain(unittest.TestCase):
def test_explicit_clearnet_domain_is_skipped(self) -> None:
skip_set = {"mirror.infinito.test"}
labels = {d.split(".", 1)[0] for d in skip_set}
self.assertTrue(
script.is_skipped_domain("mirror.infinito.test", skip_set, labels)
)
def test_onion_sibling_of_skipped_clearnet_is_skipped(self) -> None:
skip_set = {"mirror.infinito.test"}
labels = {d.split(".", 1)[0] for d in skip_set}
self.assertTrue(
script.is_skipped_domain("mirror.abc123.onion", skip_set, labels)
)
def test_unrelated_onion_is_not_skipped(self) -> None:
skip_set = {"mirror.infinito.test"}
labels = {d.split(".", 1)[0] for d in skip_set}
self.assertFalse(
script.is_skipped_domain("auth.abc123.onion", skip_set, labels)
)
def test_unrelated_clearnet_is_not_skipped(self) -> None:
skip_set = {"mirror.infinito.test"}
labels = {d.split(".", 1)[0] for d in skip_set}
self.assertFalse(
script.is_skipped_domain("auth.infinito.test", skip_set, labels)
)
class TestMainSkipsOnionSiblings(unittest.TestCase):
@patch("script.run_checker")
@patch("script.build_urls_from_nginx_confs")
@patch("script.extract_domains_from_filenames")
def test_onion_sibling_of_skip_domain_is_excluded(
self,
mock_extract: MagicMock,
mock_build_urls: MagicMock,
mock_run_checker: MagicMock,
) -> None:
mock_extract.return_value = [
"mirror.infinito.test",
"mirror.abc123.onion",
"auth.abc123.onion",
]
mock_build_urls.side_effect = lambda _dir, domains: [
f"http://{d}/" for d in domains
]
mock_run_checker.return_value = 0
with (
patch.object(
script.sys,
"argv",
[
"script.py",
"--nginx-config-dir",
"/etc/nginx",
"--image",
"img:tag",
"--skip-domain",
"mirror.infinito.test",
"--tor-proxy",
"socks5://127.0.0.1:9050",
],
),
self.assertRaises(SystemExit),
):
script.main()
probed = [d for call in mock_build_urls.call_args_list for d in call.args[1]]
self.assertNotIn("mirror.infinito.test", probed)
self.assertNotIn("mirror.abc123.onion", probed)
self.assertIn("auth.abc123.onion", probed)
class TestBuildDockerCmdProxy(unittest.TestCase):
def test_proxy_arg_appended(self) -> None:
cmd = script.build_docker_cmd(
image="img",
urls=["http://a.onion/"],
short_mode=True,
ignore_network_blocks_from=[],
proxy="socks5://127.0.0.1:9050",
)
idx = cmd.index("--proxy")
self.assertEqual(cmd[idx + 1], "socks5://127.0.0.1:9050")
def test_no_proxy_arg_without_proxy(self) -> None:
cmd = script.build_docker_cmd(
image="img",
urls=["https://a.example/"],
short_mode=True,
ignore_network_blocks_from=[],
)
self.assertNotIn("--proxy", cmd)
class TestDetectSchemeFromConf(unittest.TestCase):
def test_detects_https_via_443(self) -> None:
with patch("pathlib.Path.read_text", return_value="listen 443 ssl;"):
self.assertEqual(script.detect_scheme_from_conf(Path("x.conf")), "https")
def test_detects_https_via_ssl_flag(self) -> None:
with patch("pathlib.Path.read_text", return_value="listen 8443 ssl;"):
self.assertEqual(script.detect_scheme_from_conf(Path("x.conf")), "https")
def test_detects_http_via_80(self) -> None:
with patch("pathlib.Path.read_text", return_value="listen 80;"):
self.assertEqual(script.detect_scheme_from_conf(Path("x.conf")), "http")
def test_detects_none_when_no_listen_lines(self) -> None:
with patch("pathlib.Path.read_text", return_value="server_name example.com;"):
self.assertIsNone(script.detect_scheme_from_conf(Path("x.conf")))
def test_ignores_comments_and_blank_lines(self) -> None:
conf = """
# listen 443 ssl;
# listen 80;
server_name example.com;
"""
with patch("pathlib.Path.read_text", return_value=conf):
self.assertIsNone(script.detect_scheme_from_conf(Path("x.conf")))
def test_returns_none_when_file_missing(self) -> None:
with patch("pathlib.Path.read_text", side_effect=FileNotFoundError):
self.assertIsNone(script.detect_scheme_from_conf(Path("missing.conf")))
class TestBuildUrlsFromNginxConfs(unittest.TestCase):
@patch("script.detect_scheme_from_conf")
def test_build_urls_https_preferred(self, mock_detect: MagicMock) -> None:
mock_detect.return_value = "https"
urls = script.build_urls_from_nginx_confs("/etc/nginx", ["example.com"])
self.assertEqual(urls, ["https://example.com/"])
@patch("script.detect_scheme_from_conf")
def test_build_urls_http_when_http_detected(self, mock_detect: MagicMock) -> None:
mock_detect.return_value = "http"
urls = script.build_urls_from_nginx_confs("/etc/nginx", ["example.com"])
self.assertEqual(urls, ["http://example.com/"])
@patch("script.detect_scheme_from_conf")
def test_build_urls_falls_back_to_http_and_warns(
self, mock_detect: MagicMock
) -> None:
mock_detect.return_value = None
with patch("sys.stderr") as _stderr:
urls = script.build_urls_from_nginx_confs("/etc/nginx", ["example.com"])
self.assertEqual(urls, ["http://example.com/"])
class TestBuildDockerCmd(unittest.TestCase):
def test_build_docker_cmd_defaults_to_host_network_and_root_user(self) -> None:
cmd = script.build_docker_cmd(
image="ghcr.io/kevinveenbirkenbach/csp-checker:stable",
urls=["http://example.com/"],
short_mode=False,
ignore_network_blocks_from=[],
)
self.assertEqual(cmd[0:3], ["container", "run", "--rm"])
self.assertIn("--user", cmd)
uidx = cmd.index("--user")
self.assertEqual(cmd[uidx + 1], "0:0")
self.assertIn("--network", cmd)
nidx = cmd.index("--network")
self.assertEqual(cmd[nidx + 1], "host")
self.assertIn("ghcr.io/kevinveenbirkenbach/csp-checker:stable", cmd)
self.assertEqual(cmd[-1], "http://example.com/")
def test_build_docker_cmd_can_disable_host_network(self) -> None:
cmd = script.build_docker_cmd(
image="img:tag",
urls=["http://example.com/"],
short_mode=False,
ignore_network_blocks_from=[],
use_host_network=False,
)
self.assertEqual(cmd[0:3], ["container", "run", "--rm"])
self.assertIn("--user", cmd)
uidx = cmd.index("--user")
self.assertEqual(cmd[uidx + 1], "0:0")
self.assertNotIn("--network", cmd)
self.assertNotIn("host", cmd)
def test_build_docker_cmd_short_mode(self) -> None:
cmd = script.build_docker_cmd(
image="img:tag",
urls=["http://example.com/"],
short_mode=True,
ignore_network_blocks_from=[],
)
self.assertIn("--short", cmd)
def test_build_docker_cmd_ignore_list_adds_separator_and_urls(self) -> None:
cmd = script.build_docker_cmd(
image="img:tag",
urls=["http://a.example/", "https://b.example/"],
short_mode=False,
ignore_network_blocks_from=["pxscdn.com", "cdn.example.org"],
)
self.assertIn("--ignore-network-blocks-from", cmd)
idx = cmd.index("--ignore-network-blocks-from")
self.assertEqual(cmd[idx + 1], "pxscdn.com")
self.assertEqual(cmd[idx + 2], "cdn.example.org")
self.assertEqual(cmd[idx + 3], "--")
self.assertEqual(cmd[idx + 4 :], ["http://a.example/", "https://b.example/"])
class TestRunChecker(unittest.TestCase):
@patch("script.subprocess.run")
def test_run_checker_pulls_image_when_always_pull_true(
self, mock_run: MagicMock
) -> None:
mock_run.side_effect = [
MagicMock(returncode=0),
MagicMock(returncode=3),
]
rc = script.run_checker(
image="img:tag",
urls=["http://example.com/"],
short_mode=True,
ignore_network_blocks_from=[],
always_pull=True,
use_host_network=True,
)
self.assertEqual(rc, 3)
self.assertGreaterEqual(mock_run.call_count, 2)
pull_call = mock_run.call_args_list[0]
self.assertEqual(pull_call.kwargs.get("check"), False)
self.assertEqual(pull_call.args[0], ["container", "pull", "img:tag"])
run_call = mock_run.call_args_list[1]
self.assertEqual(run_call.kwargs.get("check"), False)
self.assertEqual(run_call.args[0][0:3], ["container", "run", "--rm"])
self.assertIn("--user", run_call.args[0])
uidx = run_call.args[0].index("--user")
self.assertEqual(run_call.args[0][uidx + 1], "0:0")
@patch("script.subprocess.run")
def test_run_checker_returns_127_if_run_missing(self, mock_run: MagicMock) -> None:
mock_run.side_effect = FileNotFoundError
rc = script.run_checker(
image="img:tag",
urls=["http://example.com/"],
short_mode=False,
ignore_network_blocks_from=[],
always_pull=False,
use_host_network=True,
)
self.assertEqual(rc, 127)
@patch("script.subprocess.run")
def test_run_checker_returns_1_on_unexpected_exception(
self, mock_run: MagicMock
) -> None:
mock_run.side_effect = RuntimeError("boom")
rc = script.run_checker(
image="img:tag",
urls=["http://example.com/"],
short_mode=False,
ignore_network_blocks_from=[],
always_pull=False,
use_host_network=True,
)
self.assertEqual(rc, 1)
class TestMain(unittest.TestCase):
@patch("script.run_checker")
@patch("script.extract_domains_from_filenames")
def test_main_exits_1_when_extract_domains_returns_none(
self,
mock_extract: MagicMock,
mock_run_checker: MagicMock,
) -> None:
mock_extract.return_value = None
with (
patch.object(
script.sys,
"argv",
["script.py", "--nginx-config-dir", "/missing", "--image", "img:tag"],
),
self.assertRaises(SystemExit) as cm,
):
script.main()
self.assertEqual(cm.exception.code, 1)
mock_run_checker.assert_not_called()
@patch("script.run_checker")
@patch("script.extract_domains_from_filenames")
def test_main_exits_0_when_no_domains_found(
self,
mock_extract: MagicMock,
mock_run_checker: MagicMock,
) -> None:
mock_extract.return_value = []
with (
patch.object(
script.sys,
"argv",
["script.py", "--nginx-config-dir", "/etc/nginx", "--image", "img:tag"],
),
self.assertRaises(SystemExit) as cm,
):
script.main()
self.assertEqual(cm.exception.code, 0)
mock_run_checker.assert_not_called()
@patch("script.run_checker")
@patch("script.build_urls_from_nginx_confs")
@patch("script.extract_domains_from_filenames")
def test_main_passes_defaults_and_exits_with_run_checker_rc(
self,
mock_extract: MagicMock,
mock_build_urls: MagicMock,
mock_run_checker: MagicMock,
) -> None:
mock_extract.return_value = ["example.com", "api.example.com"]
mock_build_urls.return_value = [
"http://example.com/",
"https://api.example.com/",
]
mock_run_checker.return_value = 5
with (
patch.object(
script.sys,
"argv",
[
"script.py",
"--nginx-config-dir",
"/etc/nginx",
"--image",
"img:tag",
"--short",
"--ignore-network-blocks-from",
"pxscdn.com",
"cdn.example.org",
],
),
self.assertRaises(SystemExit) as cm,
):
script.main()
self.assertEqual(cm.exception.code, 5)
mock_run_checker.assert_called_once()
kwargs = mock_run_checker.call_args.kwargs
self.assertEqual(kwargs["image"], "img:tag")
self.assertEqual(
kwargs["urls"], ["http://example.com/", "https://api.example.com/"]
)
self.assertTrue(kwargs["short_mode"])
self.assertEqual(
kwargs["ignore_network_blocks_from"], ["pxscdn.com", "cdn.example.org"]
)
self.assertFalse(kwargs["always_pull"])
self.assertTrue(kwargs["use_host_network"])
@patch("script.run_checker")
@patch("script.build_urls_from_nginx_confs")
@patch("script.extract_domains_from_filenames")
def test_main_no_host_network_flag_disables_host_network(
self,
mock_extract: MagicMock,
mock_build_urls: MagicMock,
mock_run_checker: MagicMock,
) -> None:
mock_extract.return_value = ["example.com"]
mock_build_urls.return_value = ["http://example.com/"]
mock_run_checker.return_value = 0
with (
patch.object(
script.sys,
"argv",
[
"script.py",
"--nginx-config-dir",
"/etc/nginx",
"--image",
"img:tag",
"--no-host-network",
],
),
self.assertRaises(SystemExit) as cm,
):
script.main()
self.assertEqual(cm.exception.code, 0)
kwargs = mock_run_checker.call_args.kwargs
self.assertFalse(kwargs["use_host_network"])
@patch("script.run_checker")
@patch("script.build_urls_from_nginx_confs")
@patch("script.extract_domains_from_filenames")
def test_main_exits_0_when_no_urls_built(
self,
mock_extract: MagicMock,
mock_build_urls: MagicMock,
mock_run_checker: MagicMock,
) -> None:
mock_extract.return_value = ["example.com"]
mock_build_urls.return_value = []
with (
patch.object(
script.sys,
"argv",
["script.py", "--nginx-config-dir", "/etc/nginx", "--image", "img:tag"],
),
self.assertRaises(SystemExit) as cm,
):
script.main()
self.assertEqual(cm.exception.code, 0)
mock_run_checker.assert_not_called()
if __name__ == "__main__":
unittest.main()