mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 11:46:35 +00:00
The assertion checks the file apply() created one line above. Routed through the shared cache it would compare against the state before that write, so the read stays direct and says why. The marker sits on a hoisted variable rather than inside the assertEqual call: the formatter wraps the call across lines, and an inline nocheck only counts within the line span of the call it belongs to, so the one-liner form strands it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
147 lines
6.1 KiB
Python
147 lines
6.1 KiB
Python
"""Unit tests for library/stack_host_files.py.
|
|
|
|
Exercises the building blocks (`render`, `survey`, `plan`, `apply`)
|
|
directly. The `main()` entrypoint is integration-tested through the
|
|
dashboard role's playbook — mocking AnsibleModule stdin/exit_json
|
|
end-to-end adds churn without catching additional logic bugs.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib.util
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from typing import TYPE_CHECKING
|
|
|
|
from . import PROJECT_ROOT
|
|
|
|
if TYPE_CHECKING:
|
|
from types import ModuleType
|
|
|
|
MODULE_PATH = PROJECT_ROOT / "library" / "stack_host_files.py"
|
|
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
|
|
def _load_stack_host_files() -> ModuleType:
|
|
spec = importlib.util.spec_from_file_location(
|
|
"library_stack_host_files", str(MODULE_PATH)
|
|
)
|
|
if spec is None or spec.loader is None:
|
|
raise ImportError(f"Could not load: {MODULE_PATH}")
|
|
mod = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(mod) # type: ignore[attr-defined]
|
|
return mod
|
|
|
|
|
|
_MOD = _load_stack_host_files()
|
|
|
|
|
|
class TestRender(unittest.TestCase):
|
|
def test_every_document_becomes_its_own_suffixed_file(self) -> None:
|
|
out = _MOD.render({"de": {"Home": "Startseite"}, "en": {}}, ".yaml", 4, 100)
|
|
self.assertEqual(sorted(out), ["de.yaml", "en.yaml"])
|
|
|
|
def test_the_document_is_serialised_as_block_yaml(self) -> None:
|
|
out = _MOD.render({"de": {"Home": "Startseite"}}, ".yaml", 4, 100)
|
|
self.assertEqual(out["de.yaml"], "Home: Startseite\n")
|
|
|
|
def test_non_ascii_survives_unescaped(self) -> None:
|
|
out = _MOD.render({"de": {"Mail": "E-Mäil"}}, ".yaml", 4, 100)
|
|
self.assertIn("E-Mäil", out["de.yaml"])
|
|
|
|
def test_a_wide_width_keeps_a_long_scalar_on_one_line(self) -> None:
|
|
long_value = "word " * 40
|
|
out = _MOD.render({"de": {"K": long_value}}, ".yaml", 4, 100_000)
|
|
self.assertEqual(len(out["de.yaml"].splitlines()), 1)
|
|
|
|
|
|
class TestSurvey(unittest.TestCase):
|
|
def test_a_missing_directory_surveys_as_empty(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
self.assertEqual(_MOD.survey(Path(tmp) / "absent", ".yaml"), {})
|
|
|
|
def test_only_files_carrying_the_suffix_are_reported(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
directory = Path(tmp)
|
|
(directory / "de.yaml").write_text("a\n", encoding="utf-8")
|
|
(directory / "notes.txt").write_text("b\n", encoding="utf-8")
|
|
self.assertEqual(sorted(_MOD.survey(directory, ".yaml")), ["de.yaml"])
|
|
|
|
|
|
class TestPlan(unittest.TestCase):
|
|
def _directory(self, tmp: str, files: dict[str, str]) -> dict[str, Path]:
|
|
directory = Path(tmp)
|
|
for name, content in files.items():
|
|
(directory / name).write_text(content, encoding="utf-8")
|
|
return _MOD.survey(directory, ".yaml")
|
|
|
|
def test_a_missing_file_is_written(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
present = self._directory(tmp, {})
|
|
written, removed = _MOD.plan(present, {"de.yaml": "a\n"}, True)
|
|
self.assertEqual((written, removed), (["de.yaml"], []))
|
|
|
|
def test_an_identical_file_is_left_alone(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
present = self._directory(tmp, {"de.yaml": "a\n"})
|
|
written, removed = _MOD.plan(present, {"de.yaml": "a\n"}, True)
|
|
self.assertEqual((written, removed), ([], []))
|
|
|
|
def test_a_differing_file_is_rewritten(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
present = self._directory(tmp, {"de.yaml": "old\n"})
|
|
written, removed = _MOD.plan(present, {"de.yaml": "new\n"}, True)
|
|
self.assertEqual((written, removed), (["de.yaml"], []))
|
|
|
|
def test_an_unwanted_file_is_removed_when_pruning(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
present = self._directory(tmp, {"de.yaml": "a\n", "en.yaml": "b\n"})
|
|
written, removed = _MOD.plan(present, {"en.yaml": "b\n"}, True)
|
|
self.assertEqual((written, removed), ([], ["de.yaml"]))
|
|
|
|
def test_an_unwanted_file_survives_without_pruning(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
present = self._directory(tmp, {"de.yaml": "a\n"})
|
|
written, removed = _MOD.plan(present, {}, False)
|
|
self.assertEqual((written, removed), ([], []))
|
|
|
|
|
|
class TestApply(unittest.TestCase):
|
|
def test_it_creates_the_directory_and_writes_with_the_given_modes(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
directory = Path(tmp) / "content"
|
|
wanted = {"de.yaml": "Home: Startseite\n"}
|
|
_MOD.apply(directory, {}, wanted, ["de.yaml"], [], "0640", "0750")
|
|
target = directory / "de.yaml"
|
|
produced = target.read_text(
|
|
encoding="utf-8"
|
|
) # nocheck: cache-read apply() wrote this file a line above; a cached read would serve the pre-write state
|
|
self.assertEqual(produced, wanted["de.yaml"])
|
|
self.assertEqual(target.stat().st_mode & 0o777, 0o640)
|
|
self.assertEqual(directory.stat().st_mode & 0o777, 0o750)
|
|
|
|
def test_it_drops_the_files_the_plan_marked_removed(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
directory = Path(tmp)
|
|
stale = directory / "de.yaml"
|
|
stale.write_text("a\n", encoding="utf-8")
|
|
present = _MOD.survey(directory, ".yaml")
|
|
_MOD.apply(directory, present, {}, [], ["de.yaml"], "0644", "0755")
|
|
self.assertFalse(stale.exists())
|
|
|
|
def test_a_full_cycle_converges(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
directory = Path(tmp) / "content"
|
|
wanted = _MOD.render({"de": {"Home": "Startseite"}}, ".yaml", 4, 100)
|
|
written, removed = _MOD.plan(_MOD.survey(directory, ".yaml"), wanted, True)
|
|
_MOD.apply(directory, {}, wanted, written, removed, "0644", "0755")
|
|
again = _MOD.plan(_MOD.survey(directory, ".yaml"), wanted, True)
|
|
self.assertEqual(again, ([], []))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|