"""Recipe preparation tests; generated contracts are never completion receipts.""" import json from pathlib import Path import tempfile import unittest from wbv2_local_planner import LocalPlanner class LocalPlannerTests(unittest.TestCase): def setUp(self): self.temporary = tempfile.TemporaryDirectory() self.addCleanup(self.temporary.cleanup) self.root = Path(self.temporary.name) self.directory = self.root / "tests/golden/WBV2/autopilot" self.planner = LocalPlanner(self.root, self.directory) self.context = {"task": "WBV2-P42-001", "parentSha256": "a" * 64} self.provider = "SERVER-OPERATOR-HTTP-PROVIDER" self.job = {"id": "SERVER-OPERATOR-SCHEMA-PALETTE", "ownedPaths": ["app.tsx"], "module": "RUNTIME"} def prepare_base(self): source = self.directory / "local-plans" / (self.provider + ".json") source.parent.mkdir(parents=True) contract = {**self.context, "job": self.provider, "result": {"checks": [{"id": "chromium-main", "argv": ["node", "test.js"], "kinds": ["chromium"]}], "cases": [{"id": "positive", "checkId": "chromium-main", "unitIds": ["operator:TEST"]}], "dependencies": ["test.js"], "changedFiles": ["test.js"]}} source.write_text(json.dumps(contract)) target = self.root / "web/tests/e2e/server-operator-schema-palette.spec.ts" target.parent.mkdir(parents=True) target.write_text("// synthetic test-entrypoint fixture\n") return source, target def test_missing_dependency_is_not_dispatched(self): self.prepare_base() result = self.planner.refresh(self.context, [self.job], set()) self.assertEqual(0, result["readyCount"]) self.assertEqual([], result["created"]) def test_missing_test_is_a_specific_preparation_task(self): _, target = self.prepare_base() target.unlink() self.planner.refresh(self.context, [self.job], {self.provider}) report = json.loads((self.directory / "local-task-assignments.json").read_text()) self.assertEqual("needs_test_entrypoint", report["assignments"][0]["state"]) def test_complete_recipe_is_prepared_but_not_marked_verified(self): self.prepare_base() result = self.planner.refresh(self.context, [self.job], {self.provider}) self.assertEqual([self.job["id"]], result["created"]) recipe = json.loads((self.directory / "local-plans" / (self.job["id"] + ".json")).read_text()) self.assertEqual("pending local behavior verification", recipe["acceptance"]) self.assertEqual("chromium-schema-palette", recipe["result"]["cases"][0]["checkId"]) self.assertIn("tests/e2e/server-operator-schema-palette.spec.ts", recipe["result"]["checks"][0]["argv"]) def test_stable_assignments_do_not_rewrite_every_poll(self): self.prepare_base() self.planner.refresh(self.context, [self.job], {self.provider}) report = self.directory / "local-task-assignments.json" before = report.stat().st_mtime_ns result = self.planner.refresh(self.context, [self.job], {self.provider}) self.assertFalse(result["changed"]) self.assertEqual(before, report.stat().st_mtime_ns) def test_parent_mismatch_fails_without_preparing_a_recipe(self): source, _ = self.prepare_base() contract = json.loads(source.read_text()) contract["parentSha256"] = "b" * 64 source.write_text(json.dumps(contract)) with self.assertRaisesRegex(ValueError, "parent mismatch"): self.planner.refresh(self.context, [self.job], {self.provider}) def test_unknown_modules_are_not_falsely_accepted(self): result = self.planner.refresh(self.context, [{"id": "OTHER", "ownedPaths": []}], set()) self.assertEqual(1, result["blockedCount"]) self.assertEqual(0, result["readyCount"]) if __name__ == "__main__": unittest.main()