Files
pulse/scripts/tests/test_public_docs_workflow.py
T
pulse-triage[bot] 51a30f71d3 Exercise backup safety and diagnostic recipes on docs-only changes
Build and Test deliberately skips the smoke suite for Markdown-only changes, leaving Public docs able to accept unsafe but correctly mirrored guidance. Run the existing backup, passive diagnostic and copied-reader suites in the lightweight docs check, and retain strict failure propagation and source triggers without installing application dependencies.

Contract-Neutral: Wire existing safety recipe checks into docs-only CI without changing product, workflow trust or documentation contracts.
Change-source: pulse-maintainer
2026-10-07 04:56:35 +01:00

136 lines
5.9 KiB
Python

#!/usr/bin/env python3
"""Docs-only CI must exercise safety guidance, not just its Markdown shape.
Read the canonical block-YAML surface (also checked by workflow trust), then
execute its actual smoke command with disposable test adapters. No provider,
host service, backup or guest-agent operation is used.
"""
from __future__ import annotations
import os
from pathlib import Path
import re
import shlex
import shutil
import subprocess
import tempfile
import textwrap
import unittest
ROOT = Path(__file__).resolve().parents[2]
WORKFLOW = ROOT / ".github/workflows/public-docs.yml"
SAFETY_STEP = "Exercise backup safety and diagnostic recipes"
TESTS = (
"test_pve_backup_troubleshooting_docs.py",
"test_vm_disk_diagnostics.py",
"test_memory_troubleshooting_docs.py",
"test_performance_troubleshooting_docs.py",
"test_troubleshooting_logs.py",
)
def step_body(source: str, name: str) -> str:
marker = f" - name: {name}\n"
if source.count(marker) != 1:
raise AssertionError(f"expected exactly one {name} step")
return source.split(marker, 1)[1].split("\n - name:", 1)[0]
def safety_command(source: str) -> str:
body = step_body(source, SAFETY_STEP)
marker = " run: |\n"
if body.count(marker) != 1:
raise AssertionError("expected one literal safety command")
return textwrap.dedent(body.split(marker, 1)[1]).strip()
class PublicDocsWorkflowTest(unittest.TestCase):
def setUp(self):
self.source = WORKFLOW.read_text(encoding="utf-8")
def test_both_events_cover_docs_checks_runner_and_helper(self):
triggers = self.source.split("\non:\n", 1)[1].split("\npermissions:", 1)[0]
required = {
"*.md", "docs/**", "frontend-modern/public/docs/**",
"scripts/check_public_docs.py", "scripts/check_docs_mirror.py",
"scripts/readme_entrypoints_test.py", "scripts/tests/run.sh",
"scripts/tests/test_public_docs_workflow.py", "scripts/test-vm-disk.sh",
".github/workflows/public-docs.yml",
*(f"scripts/tests/{name}" for name in TESTS),
}
for event in ("pull_request", "push"):
with self.subTest(event=event):
block = triggers.split(f" {event}:\n", 1)[1]
block = re.split(r"\n [a-z_]+:\n", block, maxsplit=1)[0]
paths = re.findall(r'^ - "([^"]+)"$', block, re.MULTILINE)
self.assertTrue(required.issubset(paths), required.difference(paths))
self.assertEqual(len(paths), len(set(paths)))
self.assertIn(" push:\n branches:\n - main\n", triggers)
def test_exact_existing_suites_run_without_dependency_setup(self):
command = safety_command(self.source).replace("\\\n", " ")
self.assertEqual(shlex.split(command), ["scripts/tests/run.sh", *TESTS])
for name in TESTS:
self.assertTrue((ROOT / "scripts/tests" / name).is_file())
self.assertNotIn("actions/setup-", self.source)
self.assertNotRegex(self.source, r"\b(?:npm|pip|go) (?:ci|install|build|test)\b")
def test_existing_document_admissions_are_preserved(self):
for name, command in (
("Validate public documentation", "python3 scripts/check_public_docs.py"),
("Check getting-started safety and current plans", "python3 scripts/readme_entrypoints_test.py"),
("Check shipped docs mirror sync", "python3 scripts/check_docs_mirror.py"),
("Check documentation safety workflow contract", "python3 scripts/tests/test_public_docs_workflow.py"),
):
with self.subTest(step=name):
self.assertIn(f" run: {command}\n", step_body(self.source, name))
def test_safety_checks_cannot_be_conditionally_skipped_or_softened(self):
self.assertNotRegex(self.source, r"(?m)^\s*(?:if|continue-on-error):")
self.assertIn("permissions:\n contents: read\n", self.source)
self.assertIn(" persist-credentials: false\n", self.source)
self.assertIn(" timeout-minutes: 10\n", self.source)
def run_adapters(self, failing: str = ""):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
tests = root / "scripts/tests"
tests.mkdir(parents=True)
shutil.copyfile(ROOT / "scripts/tests/run.sh", tests / "run.sh")
(tests / "run.sh").chmod(0o700)
for name in TESTS:
(tests / name).write_text(
"import os\nfrom pathlib import Path\n"
f"with Path(os.environ['CALLS']).open('a') as log: log.write({name!r} + '\\n')\n"
f"raise SystemExit({1 if name == failing else 0})\n",
encoding="utf-8",
)
calls = root / "calls"
result = subprocess.run(
["bash", "-e", "-o", "pipefail", "-c", safety_command(self.source)],
cwd=root, env=dict(os.environ, CALLS=str(calls)),
text=True, capture_output=True, timeout=15,
)
return result, calls.read_text().splitlines()
def test_actual_workflow_command_runs_every_suite(self):
result, calls = self.run_adapters()
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(calls, list(TESTS))
self.assertIn("Summary: 5/5 passed", result.stdout)
def test_each_failed_suite_is_a_failed_workflow_command(self):
for failing in TESTS:
with self.subTest(failing=failing):
result, calls = self.run_adapters(failing)
self.assertNotEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(calls, list(TESTS))
self.assertIn("Summary: 4/5 passed", result.stdout)
self.assertIn("Failures: 1", result.stdout)
if __name__ == "__main__":
unittest.main()