73 lines
4.0 KiB
Python
73 lines
4.0 KiB
Python
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from business_continuation_context import retained_results
|
|
import scheduler_maintenance as maintenance
|
|
|
|
|
|
class RetainedResultTests(unittest.TestCase):
|
|
def setUp(self):
|
|
directory = tempfile.TemporaryDirectory()
|
|
self.addCleanup(directory.cleanup)
|
|
self.root = Path(directory.name)
|
|
self.current = self.root / "docs/current"
|
|
(self.current / "business-evidence").mkdir(parents=True)
|
|
self.source = self.root / "native.cpp"
|
|
self.source.write_text("changed after evidence")
|
|
self.path = self.current / "business-evidence/partial.json"
|
|
report = {"results": [{"capabilityId": "Part:case", "status": "not_implemented",
|
|
"cases": [{"assertions": [{"name": "geometry done", "passed": True}, {"name": "UI missing", "passed": False}]}],
|
|
"remaining": ["native task cancel"], "evidenceFiles": {"native.cpp": "old-hash", "../outside.cpp": "old-hash"}}]}
|
|
self.path.write_text(json.dumps(report))
|
|
self.ledger = {"records": {"Part:case": {"status": "failed", "lastFailureEvidencePath": str(self.path.relative_to(self.root)),
|
|
"lastFailureEvidenceSha256": hashlib.sha256(self.path.read_bytes()).hexdigest()}}}
|
|
(self.current / "business-acceptance.json").write_text(json.dumps(self.ledger))
|
|
|
|
def test_partial_assertions_are_retained_without_claiming_current_acceptance(self):
|
|
rows, paths = retained_results(["Part:case"], root=self.root)
|
|
proof = rows[0]["evidence"][0]
|
|
self.assertEqual(proof["completedAssertions"], ["geometry done"])
|
|
self.assertEqual(proof["remaining"], ["native task cancel"])
|
|
self.assertEqual(proof["statusAtExecution"], "not_implemented")
|
|
self.assertFalse(proof["currentInputsRevalidated"])
|
|
self.assertIn(self.source, paths)
|
|
self.assertIn(self.path, paths)
|
|
self.assertTrue(all(path.is_relative_to(self.root) for path in paths))
|
|
|
|
def test_tampered_evidence_is_not_injected_as_completed_work(self):
|
|
self.path.write_text('{"results": []}')
|
|
self.assertEqual(retained_results(["Part:case"], root=self.root), ([], []))
|
|
|
|
def test_unselected_business_evidence_is_not_loaded(self):
|
|
self.assertEqual(retained_results(["Sketcher:other"], root=self.root), ([], []))
|
|
|
|
def test_maintenance_skips_historically_completed_batch_without_new_fault(self):
|
|
batch = {"status": "ready", "batchId": "old", "selected": {"module": "Part", "recordCount": 1}, "records": [["Part:case"]]}
|
|
with patch.object(maintenance, "deferred_validation_ids", return_value={"Part:case"}), \
|
|
patch.object(maintenance.subprocess, "run", side_effect=AssertionError("completed batch prepared again")):
|
|
result = maintenance.prepare_work({"batch": batch, "runtimeIssues": []}, self.current, "review")
|
|
self.assertEqual(result["status"], "historical_work_deferred")
|
|
self.assertEqual(len(batch["records"]), 1)
|
|
|
|
def test_historical_inspection_does_not_launch_model_build_or_verification(self):
|
|
with patch.object(maintenance, "ROOT", self.root), \
|
|
patch.object(maintenance, "STATE_PATH", self.current / "maintenance.json"), \
|
|
patch.object(maintenance, "prepare_work", return_value={"status": "historical_work_deferred", "nextAction": "return to work"}):
|
|
maintenance.configure(True, now=1000)
|
|
request = maintenance.claim_due(now=2800)
|
|
def forbidden(*args):
|
|
raise AssertionError("No model should start for an already completed batch")
|
|
result = maintenance.execute(request, forbidden, self.current / "cycle.log", now=lambda: 2801, context={})
|
|
self.assertEqual(result["status"], "checked_no_new_work")
|
|
self.assertEqual(result["commandsCompleted"], 0)
|
|
self.assertFalse(result["modelResponded"])
|
|
self.assertEqual(result["fileChanges"], 0)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|