Files
pulse/scripts/tests/test_failed_upgrade_intake_docs.py
pulse-triage[bot] 6809ca2b23 Separate failed-upgrade attempts from observed recovery in issue intake
Keep the failing runtime, intended release, original updater result, host stop and later recovery separate. Accept unknowns and existing redacted evidence without a repeat update, reboot, restore or loss of rollback backups. Guard both forms and preserve version classification when attempted and recovered versions differ.

Change-source: pulse-maintainer
Contract-Neutral: Community intake guidance and regression tests only; installer, updater, runtime and triage automation contracts are unchanged.
2026-10-10 11:39:23 +01:00

60 lines
2.6 KiB
Python

#!/usr/bin/env python3
"""Guard passive failed-upgrade intake, not installer or workload recovery."""
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[2]
class FailedUpgradeIntakeDocsTest(unittest.TestCase):
def guidance(self):
text = (ROOT / "docs/ISSUE_TRIAGE.md").read_text()
start = "An interrupted upgrade needs a timeline, not one inferred version."
self.assertEqual(text.count(start), 1)
return " ".join(text.split(start, 1)[1].split("\nThe optional", 1)[0].split())
def test_shipped_triage_guide_matches_source(self):
self.assertEqual((ROOT / "docs/ISSUE_TRIAGE.md").read_bytes(),
(ROOT / "frontend-modern/public/docs/ISSUE_TRIAGE.md").read_bytes())
def test_attempt_failure_and_recovery_are_separate_observations(self):
guide = self.guidance()
for distinction in (
"starting version, intended version or asset, route, original time and timezone",
"last recorded updater or installer result, what stopped",
"version running at failure separate from the version observed after recovery",
"accept unknowns and use evidence already supplied anywhere in the thread",
"Version labels are intake metadata, not proof of the failing executable",
):
with self.subTest(distinction=distinction):
self.assertIn(distinction, guide)
def test_signature_and_later_boot_do_not_establish_completion_or_stop_cause(self):
guide = self.guidance()
for limit in (
"artifact authenticity, not completed installation, successful rollback",
"`Broken pipe` alone does not establish why a service or its host stopped",
"A later boot marks a new run, not the stop's time or cause",
"Distinguish Pulse stopping from its whole LXC, VM or host stopping",
):
with self.subTest(limit=limit):
self.assertIn(limit, guide)
def test_collection_does_not_replay_incident_or_destroy_recovery_evidence(self):
guide = self.guidance()
for boundary in (
"existing host stop record only when available and consequential",
"Ask for only the missing distinction, not full journals",
"another version already supplied",
"Do not rerun the update, reboot, restore, run Diagnostics or delete rollback backups",
"keep backups private and preserve the original evidence",
):
with self.subTest(boundary=boundary):
self.assertIn(boundary, guide)
if __name__ == "__main__":
unittest.main()