Files
computer-playbook/tests/unit/python/roles/sys-ctl-hlth-disc-space/files/test_script.py
T
kevinveenbirkenbachandClaude Opus 5 74afec37c3 fix(health): exit with a status, not a failure count
Four probes returned the number of failures as the process exit code. An exit status is 8 bits wide, so exactly 256 or 512 failures arrive as 0 and the probe reports success. sys-service installs these as Type=oneshot units (roles/sys-service/vars/main.yml:28) and sys-service-terminator starts them with until: result is not failed, so that exit code is the whole health verdict.

The count is not lost: each of the four prints it to stdout a line earlier. Only the exit status stops carrying it, because it cannot.

This makes no CI job green and is not meant to. For any count other than 256 or 512 nothing changes; at those two it turns a false success into an honest failure.

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

106 lines
3.5 KiB
Python
Executable File

#!/usr/bin/env python3
import contextlib
import importlib.util
import io
import sys
from types import SimpleNamespace
from unittest import TestCase, main, mock
from . import PROJECT_ROOT
def load_target_module():
repo_root = PROJECT_ROOT
script_path = (
repo_root
/ "roles"
/ "sys-ctl-hlth-disc-space"
/ "files"
/ "python"
/ "script.py"
)
if not script_path.is_file():
raise FileNotFoundError(f"Target script not found at: {script_path}")
spec = importlib.util.spec_from_file_location("target_script", script_path)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
spec.loader.exec_module(module)
return module
SCRIPT_MODULE = load_target_module()
class TestDiskSpaceScript(TestCase):
def test_get_disk_usage_percentages_parses_output(self):
fake_df_output = "Use%\n 10%\n 50%\n100%\n"
with mock.patch.object(
SCRIPT_MODULE.subprocess,
"run",
return_value=SimpleNamespace(stdout=fake_df_output, returncode=0),
):
result = SCRIPT_MODULE.get_disk_usage_percentages()
self.assertEqual(result, [10, 50, 100])
def test_main_exits_zero_when_below_threshold(self):
df_print_cp = SimpleNamespace(stdout="Filesystem ...\n", returncode=0)
df_pcent_cp = SimpleNamespace(stdout="Use%\n 10%\n 50%\n 80%\n", returncode=0)
def fake_run(args, capture_output=False, text=False, check=False):
if args == ["df", "--output=pcent"]:
return df_pcent_cp
if args == ["df"]:
return df_print_cp
raise AssertionError(f"Unexpected subprocess.run args: {args}")
with (
mock.patch.object(SCRIPT_MODULE.subprocess, "run", side_effect=fake_run),
mock.patch.object(sys, "argv", ["script.py", "80"]),
mock.patch.object(
SCRIPT_MODULE.sys, "exit", side_effect=SystemExit
) as mock_exit,
contextlib.redirect_stdout(io.StringIO()),
self.assertRaises(SystemExit),
):
SCRIPT_MODULE.main()
mock_exit.assert_called_once_with(0)
def test_main_exits_with_error_count_and_prints_warnings(self):
df_print_cp = SimpleNamespace(stdout="Filesystem ...\n", returncode=0)
df_pcent_cp = SimpleNamespace(stdout="Use%\n 60%\n 90%\n 95%\n", returncode=0)
def fake_run(args, capture_output=False, text=False, check=False):
if args == ["df", "--output=pcent"]:
return df_pcent_cp
if args == ["df"]:
return df_print_cp
raise AssertionError(f"Unexpected subprocess.run args: {args}")
with (
mock.patch.object(SCRIPT_MODULE.subprocess, "run", side_effect=fake_run),
mock.patch.object(sys, "argv", ["script.py", "80"]),
mock.patch.object(
SCRIPT_MODULE.sys, "exit", side_effect=SystemExit
) as mock_exit,
):
buffer = io.StringIO()
with contextlib.redirect_stdout(buffer), self.assertRaises(SystemExit):
SCRIPT_MODULE.main()
mock_exit.assert_called_once_with(1)
output = buffer.getvalue()
self.assertIn("Checking disk space usage...", output)
self.assertIn("WARNING: 90% exceeds the limit of 80%.", output)
self.assertIn("WARNING: 95% exceeds the limit of 80%.", output)
self.assertNotIn("60% exceeds the limit of 80%.", output)
if __name__ == "__main__":
main()