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>
116 lines
6.4 KiB
Python
116 lines
6.4 KiB
Python
"""Local supervisor regressions; fixtures do not claim Blender coverage."""
|
|
import contextlib
|
|
import io
|
|
import unittest
|
|
from unittest.mock import patch
|
|
|
|
import test_wbv2_autopilot as fixtures
|
|
from wbv2_autopilot import sha, write_json
|
|
|
|
|
|
class LocalWorkflowTests(unittest.TestCase):
|
|
setUp = fixtures.SupervisorTests.setUp
|
|
file = fixtures.SupervisorTests.file
|
|
ready_result = fixtures.SupervisorTests.ready_result
|
|
|
|
def local_contract(self):
|
|
self.policy["modelExecution"] = "disabled"
|
|
result = self.ready_result(self.runner.directory / "old")
|
|
contract_path = "local-contract.json"
|
|
self.file("my-tools/wbv2_validation.py", "# fixture\n")
|
|
self.policy["localWorkflows"] = {self.job["id"]: contract_path}
|
|
contract = {"task": self.task, "job": self.job["id"], "parentSha256": self.context["parentSha256"], "result": result}
|
|
write_json(self.root / contract_path, contract)
|
|
return contract
|
|
|
|
def test_local_workflow_never_calls_model_and_writes_fresh_receipts(self):
|
|
self.local_contract()
|
|
with patch.object(self.runner, "worker", side_effect=AssertionError("model forbidden")), patch.object(self.runner, "worker_prompt", side_effect=AssertionError("prompt forbidden")), contextlib.redirect_stdout(io.StringIO()):
|
|
self.assertTrue(self.runner.attempt(self.context, self.job))
|
|
self.assertTrue(self.runner.receipt_valid(self.runner.state["jobs"][self.job["id"]]))
|
|
self.assertEqual(0, self.runner.state["tokensUsed"])
|
|
|
|
def test_model_execution_is_denied_at_all_entrypoints(self):
|
|
self.policy["modelExecution"] = "disabled"
|
|
with self.assertRaisesRegex(ValueError, "disabled"):
|
|
self.runner.worker("source", self.runner.directory)
|
|
with self.assertRaisesRegex(ValueError, "disabled"):
|
|
self.runner.worker_prompt(self.context, self.job, self.runner.directory, {})
|
|
with self.assertRaisesRegex(ValueError, "disabled"):
|
|
self.runner.run_process(["codex", "exec"], self.root, self.runner.directory / "model.log", 1)
|
|
|
|
def test_local_contract_cannot_advance_or_change_parent(self):
|
|
contract = self.local_contract()
|
|
contract["parentSha256"] = "0" * 64
|
|
write_json(self.root / "local-contract.json", contract)
|
|
with self.assertRaisesRegex(ValueError, "parent changed"):
|
|
self.runner.local_result(self.context, self.job, self.runner.directory / "fresh")
|
|
|
|
def test_scheduler_selects_only_registered_unverified_local_jobs(self):
|
|
self.local_contract()
|
|
unknown = {"id": "NO-LOCAL-CONTRACT"}
|
|
self.assertEqual(self.job, self.runner.choose_job([unknown, self.job]))
|
|
self.runner.state["jobs"][self.job["id"]] = {"state": "blocked", "localContractSha256": sha(self.root / "local-contract.json")}
|
|
self.assertIsNone(self.runner.choose_job([unknown, self.job]))
|
|
|
|
def test_local_plan_discovery_and_changed_contract_retry(self):
|
|
contract = self.local_contract()
|
|
self.policy["localWorkflows"] = {}
|
|
location = self.runner.directory / "local-plans" / (self.job["id"] + ".json")
|
|
write_json(location, contract)
|
|
self.assertEqual(self.job, self.runner.choose_job([self.job]))
|
|
self.runner.state["jobs"][self.job["id"]] = {"state": "blocked", "localContractSha256": sha(location)}
|
|
self.assertIsNone(self.runner.choose_job([self.job]))
|
|
contract["maintenanceRevision"] = 2
|
|
write_json(location, contract)
|
|
self.assertEqual(self.job, self.runner.choose_job([self.job]))
|
|
|
|
def test_behavior_pending_does_not_starve_the_next_workflow(self):
|
|
contract = self.local_contract()
|
|
next_job = {"id": "NEXT-JOB", "ownedPaths": []}
|
|
self.policy["localWorkflows"]["NEXT-JOB"] = "next-contract.json"
|
|
write_json(self.root / "next-contract.json", {**contract, "job": "NEXT-JOB"})
|
|
self.runner.state["jobs"][self.job["id"]] = {
|
|
"state": "behavior_pending", "localContractSha256": sha(self.root / "local-contract.json")}
|
|
self.assertEqual(next_job, self.runner.choose_job([self.job, next_job]))
|
|
contract["revision"] = 2
|
|
write_json(self.root / "local-contract.json", contract)
|
|
self.assertEqual(self.job, self.runner.choose_job([self.job, next_job]))
|
|
|
|
def test_model_process_is_denied_even_with_legacy_policy(self):
|
|
self.policy["modelExecution"] = "enabled"
|
|
with self.assertRaisesRegex(ValueError, "disabled"):
|
|
self.runner.run_process(["codex", "exec"], self.root, self.runner.directory / "model.log", 1)
|
|
|
|
def test_receipt_producer_mismatch_is_reported_before_cache(self):
|
|
self.local_contract()
|
|
script = (self.root / "check.py").read_text()
|
|
self.file("check.py", script.replace("'unitIds': ['operator:TEST']", "'unitIds': ['operator:WRONG']"))
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
self.assertFalse(self.runner.attempt(self.context, self.job))
|
|
reason = self.runner.state["jobs"][self.job["id"]]["reason"]
|
|
self.assertIn("binding mismatch before cache", reason)
|
|
self.assertIn("operator:WRONG", reason)
|
|
|
|
def test_missing_receipt_names_the_producing_check(self):
|
|
self.local_contract()
|
|
self.file("check.py", "print('No receipt emitted')\n")
|
|
with contextlib.redirect_stdout(io.StringIO()):
|
|
self.assertFalse(self.runner.attempt(self.context, self.job))
|
|
reason = self.runner.state["jobs"][self.job["id"]]["reason"]
|
|
self.assertIn("missing runtime receipt: case=positive check=native", reason)
|
|
|
|
def test_local_environment_is_applied_and_supervisor_receipts_win(self):
|
|
self.local_contract()
|
|
contract = self.runner.local_result(self.context, self.job, self.runner.directory / "copy")
|
|
contract["checks"][0]["argv"] = ["env", "WEB_TEST_PORT=5297", "WBV2_RECEIPT_DIR=/tmp/stale", "python3", "check.py"]
|
|
write_json(self.root / "local-contract.json", {"task": self.task, "job": self.job["id"], "parentSha256": self.context["parentSha256"], "result": contract})
|
|
with patch.object(self.runner, "run_process", wraps=self.runner.run_process) as run, contextlib.redirect_stdout(io.StringIO()):
|
|
self.assertTrue(self.runner.attempt(self.context, self.job))
|
|
self.assertEqual("5297", run.call_args.kwargs["env"]["WEB_TEST_PORT"])
|
|
self.assertNotEqual("/tmp/stale", run.call_args.kwargs["env"]["WBV2_RECEIPT_DIR"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|