154 lines
5.8 KiB
Python
154 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
import sys
|
|
import unittest
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
RUNTIME = ROOT / "harness/runtime"
|
|
sys.path.insert(0, str(RUNTIME))
|
|
import workflow_dispatch # noqa: E402
|
|
|
|
|
|
class WorkflowDispatchTest(unittest.TestCase):
|
|
def test_all_manifest_workflows_resolve(self) -> None:
|
|
result = workflow_dispatch.check_all(ROOT)
|
|
self.assertEqual(result["status"], "PASS")
|
|
self.assertEqual(result["workflow_count"], 24)
|
|
self.assertEqual(result["findings"], [])
|
|
|
|
def test_agentic_workflow_emits_review_and_output_plan(self) -> None:
|
|
result = workflow_dispatch.build_plan(ROOT, "branch-spec")
|
|
self.assertEqual(result["status"], "PLANNED")
|
|
self.assertEqual(result["execution"]["kind"], "orchestrated")
|
|
self.assertEqual(result["dispatch"]["semantic_review"], "dispatch")
|
|
self.assertEqual(result["output_contract"]["mode"], "decision-risk-summary")
|
|
self.assertEqual(result["phase"], "baseline")
|
|
self.assertEqual(result["mandatory_gates"]["typed_contract"], "required")
|
|
|
|
def test_final_resolution_reacts_to_findings_and_claims(self) -> None:
|
|
result = workflow_dispatch.build_plan(
|
|
ROOT,
|
|
"branch-spec",
|
|
phase="final",
|
|
finding_count=6,
|
|
claims_present=True,
|
|
public_claims_present=True,
|
|
risk="high",
|
|
)
|
|
self.assertEqual(result["phase"], "final")
|
|
self.assertEqual(result["mandatory_gates"]["proof_manifest"], "required")
|
|
self.assertEqual(result["dispatch"]["adversarial_review"], "dispatch")
|
|
|
|
def test_baseline_rejects_post_work_context(self) -> None:
|
|
with self.assertRaises(workflow_dispatch.DispatchError):
|
|
workflow_dispatch.build_plan(ROOT, "branch-spec", finding_count=1)
|
|
|
|
def test_deterministic_execute_binds_apply_to_dry_run_hash(self) -> None:
|
|
plan = workflow_dispatch.build_plan(ROOT, "branch-from-project")
|
|
digest = "a" * 64
|
|
calls: list[list[str]] = []
|
|
|
|
def runner(argv, **_kwargs):
|
|
calls.append(list(argv))
|
|
status = "DRY_RUN" if "--dry-run" in argv else "APPLIED"
|
|
stdout = json.dumps({
|
|
"schema_version": "branch-from-project-result/v1",
|
|
"status": status,
|
|
"plan_sha256": digest,
|
|
})
|
|
return subprocess.CompletedProcess(argv, 0, stdout, "")
|
|
|
|
result, exit_code = workflow_dispatch.execute_deterministic(
|
|
ROOT,
|
|
plan,
|
|
["demo", "WI-DEMO-001"],
|
|
runner=runner,
|
|
)
|
|
self.assertEqual(exit_code, 0)
|
|
self.assertEqual(result["status"], "APPLIED")
|
|
self.assertEqual(len(calls), 2)
|
|
self.assertIn("--dry-run", calls[0])
|
|
self.assertEqual(calls[1][-2:], ["--expected-plan-sha256", digest])
|
|
|
|
def test_required_review_blocks_deterministic_execution(self) -> None:
|
|
plan = workflow_dispatch.build_plan(ROOT, "branch-from-project", risk="high")
|
|
result, exit_code = workflow_dispatch.execute_deterministic(
|
|
ROOT,
|
|
plan,
|
|
["demo", "WI-DEMO-001"],
|
|
runner=lambda *_args, **_kwargs: self.fail("runner must not execute"),
|
|
)
|
|
self.assertEqual(exit_code, 1)
|
|
self.assertEqual(result["status"], "REVIEW_REQUIRED")
|
|
|
|
def test_invalid_child_schema_is_environment_error(self) -> None:
|
|
plan = workflow_dispatch.build_plan(ROOT, "branch-from-project")
|
|
|
|
def runner(argv, **_kwargs):
|
|
return subprocess.CompletedProcess(argv, 0, '{"schema_version":"wrong"}', "")
|
|
|
|
with self.assertRaises(workflow_dispatch.DispatchError):
|
|
workflow_dispatch.execute_deterministic(ROOT, plan, ["demo", "WI-DEMO-001"], runner=runner)
|
|
|
|
def test_child_exit_envelope_preserves_quality_and_environment_failures(self) -> None:
|
|
plan = workflow_dispatch.build_plan(ROOT, "branch-from-project")
|
|
|
|
def result(code: int, status: str):
|
|
def runner(argv, **_kwargs):
|
|
return subprocess.CompletedProcess(
|
|
argv,
|
|
code,
|
|
json.dumps({
|
|
"schema_version": "branch-from-project-result/v1",
|
|
"status": status,
|
|
}),
|
|
"",
|
|
)
|
|
|
|
return runner
|
|
|
|
quality, quality_code = workflow_dispatch.execute_deterministic(
|
|
ROOT,
|
|
plan,
|
|
["demo", "WI-DEMO-001"],
|
|
runner=result(1, "FAIL"),
|
|
)
|
|
environment, environment_code = workflow_dispatch.execute_deterministic(
|
|
ROOT,
|
|
plan,
|
|
["demo", "WI-DEMO-001"],
|
|
runner=result(2, "ERROR"),
|
|
)
|
|
self.assertEqual((quality["status"], quality_code), ("FAIL", 1))
|
|
self.assertEqual((environment["status"], environment_code), ("ERROR", 2))
|
|
|
|
def test_successful_apply_must_echo_dry_run_plan_hash(self) -> None:
|
|
plan = workflow_dispatch.build_plan(ROOT, "branch-from-project")
|
|
calls = 0
|
|
|
|
def runner(argv, **_kwargs):
|
|
nonlocal calls
|
|
calls += 1
|
|
document = {
|
|
"schema_version": "branch-from-project-result/v1",
|
|
"status": "DRY_RUN" if calls == 1 else "APPLIED",
|
|
"plan_sha256": ("a" if calls == 1 else "b") * 64,
|
|
}
|
|
return subprocess.CompletedProcess(argv, 0, json.dumps(document), "")
|
|
|
|
with self.assertRaises(workflow_dispatch.DispatchError):
|
|
workflow_dispatch.execute_deterministic(
|
|
ROOT,
|
|
plan,
|
|
["demo", "WI-DEMO-001"],
|
|
runner=runner,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|