Accumulated uncommitted work: GHOST Web backend, WebGL/GPU web layer, web workflow engine and protocol, tooling, docs and task records. Large generated evidence, user-testing archives, build snapshots and imported test assets remain untracked. Co-Authored-By: Claude Code <noreply@anthropic.com>
59 lines
2.7 KiB
Python
59 lines
2.7 KiB
Python
"""Synthetic recovery metadata tests, not Blender acceptance evidence."""
|
|
import hashlib
|
|
import json
|
|
import pathlib
|
|
import tempfile
|
|
import unittest
|
|
from recover_behavior_contracts import candidate, KINDS
|
|
|
|
|
|
class RecoveryTests(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.root = pathlib.Path(self.temp.name)
|
|
self.worker = self.root / "run/worker-result.json"
|
|
self.worker.parent.mkdir()
|
|
self.contract = {"job": "SCULPT", "task": "TASK", "result": {"sourceUnits": ["stroke", "remesh"]}}
|
|
self.cases = [{"id": kind, "kind": kind, "unitIds": ["stroke", "remesh"],
|
|
"receipt": f"run/{kind}.json"} for kind in sorted(KINDS)]
|
|
self.worker.write_text(json.dumps({"cases": self.cases, "sourceUnits": ["stroke", "remesh"]}))
|
|
(self.root / "source.cc").write_text("synthetic source")
|
|
def digest(path):
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
artifacts = []
|
|
for case in self.cases:
|
|
path = self.root / case["receipt"]
|
|
path.write_text(json.dumps({"status": "PASS", "runtime": "CHROMIUM_WEBENGINE",
|
|
"caseId": case["id"], "kind": case["kind"], "unitIds": case["unitIds"]}))
|
|
artifacts.append({"path": case["receipt"], "sha256": digest(path)})
|
|
self.proof = self.root / "run/verified-receipt.json"
|
|
self.proof.write_text(json.dumps({"status": "verified", "job": "SCULPT", "queueTask": "TASK",
|
|
"sourceUnits": ["stroke", "remesh"], "artifacts": artifacts,
|
|
"inputs": {"source.cc": digest(self.root / "source.cc")}}))
|
|
|
|
def test_valid_metadata_is_rebound_for_fresh_execution(self):
|
|
result = candidate(self.worker, self.contract, self.root)
|
|
self.assertIsNotNone(result)
|
|
self.assertTrue(all(case["receipt"].startswith("unallocated/") for case in result["cases"]))
|
|
|
|
def test_case_count_without_verified_receipt_is_rejected(self):
|
|
self.proof.unlink()
|
|
self.assertIsNone(candidate(self.worker, self.contract, self.root))
|
|
|
|
def test_scope_cannot_drop_remesh(self):
|
|
self.worker.write_text(json.dumps({"cases": self.cases, "sourceUnits": ["stroke"]}))
|
|
self.assertIsNone(candidate(self.worker, self.contract, self.root))
|
|
|
|
def test_missing_receipt_is_rejected(self):
|
|
(self.root / self.cases[0]["receipt"]).unlink()
|
|
self.assertIsNone(candidate(self.worker, self.contract, self.root))
|
|
|
|
def test_changed_source_is_rejected(self):
|
|
(self.root / "source.cc").write_text("modified source")
|
|
self.assertIsNone(candidate(self.worker, self.contract, self.root))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|