"""Tests for canonical review workflow load proof (#389).""" import os import sys import unittest from unittest.mock import patch sys.path.insert(0, str(__import__("pathlib").Path(__file__).resolve().parent.parent)) import review_workflow_load import mcp_server class TestReviewWorkflowLoadModule(unittest.TestCase): def setUp(self): review_workflow_load.clear_review_workflow_load() mcp_server._save_review_decision_lock(None) def test_load_records_hash_and_schema(self): root = str(__import__("pathlib").Path(__file__).resolve().parent.parent) recorded = review_workflow_load.record_review_workflow_load(root) self.assertEqual( recorded["workflow_source"], review_workflow_load.WORKFLOW_REL_PATH, ) self.assertEqual(recorded["task_mode"], "review-merge-pr") self.assertRegex(recorded["workflow_hash"], r"^[0-9a-f]{12}$") self.assertEqual( recorded["final_report_schema_path"], review_workflow_load.SCHEMA_REL_PATH, ) status = review_workflow_load.workflow_load_status(root) self.assertTrue(status["workflow_load_proof_present"]) self.assertTrue(status["workflow_load_valid"]) def test_profile_identity_mismatch_blocks(self): """#559: different daemon PIDs are OK; profile identity mismatch is not.""" import tempfile from unittest.mock import patch import mcp_session_state root = str(__import__("pathlib").Path(__file__).resolve().parent.parent) with tempfile.TemporaryDirectory() as tmp: with patch.dict( os.environ, { mcp_session_state.STATE_DIR_ENV: tmp, mcp_session_state.SESSION_PROFILE_LOCK_ENV: "prgs-reviewer", }, clear=False, ): review_workflow_load.clear_review_workflow_load() review_workflow_load.record_review_workflow_load(root) # Corrupt the in-memory profile identity while keeping PID. review_workflow_load._REVIEW_WORKFLOW_LOAD[ "session_profile_lock" ] = "other-profile" review_workflow_load._REVIEW_WORKFLOW_LOAD[ "profile_identity" ] = "other-profile" blockers = review_workflow_load.review_workflow_load_blockers(root) self.assertTrue( any("profile identity mismatch" in b for b in blockers), msg=blockers, ) review_workflow_load.clear_review_workflow_load() def test_prompt_conflict_detected(self): conflict, reasons = review_workflow_load.assess_prompt_conflict( "Run work-issue author implementation only") self.assertTrue(conflict) self.assertTrue(reasons) class TestReviewWorkflowLoadGates(unittest.TestCase): def setUp(self): review_workflow_load.clear_review_workflow_load() mcp_server.init_review_decision_lock("prgs", "review_pr") mcp_server.record_preflight_check("whoami") mcp_server.record_preflight_check("capability", "reviewer") def _load_workflow(self): return mcp_server.gitea_load_review_workflow() def test_mcp_helper_returns_required_fields(self): res = self._load_workflow() self.assertTrue(res["success"]) self.assertTrue(res["loaded"]) self.assertIn("workflow_source", res) self.assertIn("workflow_hash", res) self.assertIn("final_report_schema_path", res) self.assertIn("final_report_schema_hash", res) def test_mark_final_blocked_without_load(self): res = mcp_server.gitea_mark_final_review_decision( 42, "approve", remote="prgs") self.assertFalse(res["marked_ready"]) self.assertTrue(any( "gitea_load_review_workflow" in r for r in res["reasons"])) self.assertTrue(any( "approve/merge replay" in r.lower() or "Do not call" in r for r in res["reasons"])) def test_submit_review_blocked_without_load(self): with patch("mcp_server.gitea_check_pr_eligibility") as elig: elig.return_value = { "eligible": True, "authenticated_user": "rev", "profile_name": "prgs-reviewer", "pr_author": "author", "head_sha": "abc123", "reasons": [], } res = mcp_server.gitea_submit_pr_review( 42, "approve", remote="prgs", final_review_decision_ready=True, ) self.assertFalse(res["performed"]) self.assertTrue(any( "gitea_load_review_workflow" in r for r in res["reasons"])) def test_merge_blocked_without_load(self): res = mcp_server.gitea_merge_pr( 42, confirmation="MERGE PR 42", remote="prgs", ) self.assertFalse(res["performed"]) self.assertTrue(any( "gitea_load_review_workflow" in r for r in res["reasons"])) def test_resolve_capability_reports_missing_load(self): with patch.object(mcp_server, "_ensure_matching_profile"): with patch.object( mcp_server.gitea_config, "is_runtime_switching_enabled", return_value=False): with patch.object( mcp_server, "_authenticated_username", return_value="rev"): res = mcp_server.gitea_resolve_task_capability( "review_pr", remote="prgs") proof = res.get("workflow_load_proof") or {} self.assertFalse(proof.get("workflow_load_valid")) self.assertTrue(any( "gitea_load_review_workflow" in g for g in res.get("task_role_guidance") or [])) def test_init_review_lock_clears_prior_load(self): self._load_workflow() mcp_server.init_review_decision_lock("prgs", "review_pr") blockers = review_workflow_load.review_workflow_load_blockers( str(__import__("pathlib").Path(__file__).resolve().parent.parent)) self.assertTrue(blockers) def test_dry_run_allowed_without_load(self): with patch("mcp_server.get_auth_header", return_value="Basic dGVzdA=="), \ patch("mcp_server._list_pr_lease_comments", return_value=[]), \ patch("mcp_server.gitea_check_pr_eligibility") as elig: elig.return_value = { "eligible": True, "authenticated_user": "rev", "profile_name": "prgs-reviewer", "pr_author": "author", "head_sha": "abc123", "reasons": [], } res = mcp_server.gitea_dry_run_pr_review( 42, "approve", remote="prgs") self.assertNotIn( "gitea_load_review_workflow", " ".join(res.get("reasons") or []), ) if __name__ == "__main__": unittest.main()