"""ORCH-073 FR-1 — verify_merged_to_main: SHA-in-main is the SINGLE criterion. Covers TC-01..04 / AC-2 / AC-6. The former OR-branch `pr_already_merged` was the phantom-merge root cause and is removed: a merged docs-PR must NOT confirm a merge. git/HTTP are mocked; the verifier honours the never-raise contract (INV-1). """ import pytest from src import merge_gate class _R: """Minimal completed-subprocess stand-in (returncode only).""" def __init__(self, rc): self.returncode = rc self.stdout = "" self.stderr = "" @pytest.fixture(autouse=True) def _settings(monkeypatch): monkeypatch.setattr(merge_gate.settings, "merge_verify_enabled", True) monkeypatch.setattr(merge_gate.settings, "merge_verify_timeout_s", 5) # --------------------------------------------------------------------------- # TC-01 (AC-6): sha is an ancestor of origin/main (merge-base rc=0) -> True. # --------------------------------------------------------------------------- def test_tc01_true_when_sha_is_ancestor(monkeypatch): monkeypatch.setattr(merge_gate, "ensure_worktree", lambda r, b: "/wt") calls = [] def fake_run(cmd, *a, **k): calls.append(cmd) return _R(0) # fetch ok; merge-base --is-ancestor -> 0 (ancestor) monkeypatch.setattr(merge_gate.subprocess, "run", fake_run) assert merge_gate.verify_merged_to_main("orchestrator", "feature/ORCH-073-x", "abc123") is True assert any( "merge-base" in c and "--is-ancestor" in c and "origin/main" in c for c in calls ) # --------------------------------------------------------------------------- # TC-02 (AC-2): sha NOT in main AND a merged docs-PR exists -> False. # This is the exact ORCH-067/069 bug: a merged docs-PR must not confirm. # --------------------------------------------------------------------------- def test_tc02_false_when_sha_not_in_main_even_with_merged_docs_pr(monkeypatch): # A merged docs-PR is present (mock returns True), but it must be IGNORED. called = {"pr": False} def fake_pr_already_merged(r, b): called["pr"] = True return True monkeypatch.setattr(merge_gate, "pr_already_merged", fake_pr_already_merged) monkeypatch.setattr(merge_gate, "ensure_worktree", lambda r, b: "/wt") monkeypatch.setattr( merge_gate.subprocess, "run", lambda cmd, *a, **k: _R(1) if "merge-base" in cmd else _R(0), ) assert merge_gate.verify_merged_to_main("orchestrator", "feature/ORCH-073-x", "abc123") is False # The merged-PR signal is no longer consulted by the verifier at all. assert called["pr"] is False # --------------------------------------------------------------------------- # TC-03: empty sha -> inconclusive -> False (fail-closed), no git consulted. # --------------------------------------------------------------------------- def test_tc03_empty_sha_is_false(monkeypatch): def boom(*a, **k): raise AssertionError("git must NOT run for an empty SHA") monkeypatch.setattr(merge_gate, "ensure_worktree", boom) monkeypatch.setattr(merge_gate.subprocess, "run", boom) assert merge_gate.verify_merged_to_main("orchestrator", "feature/ORCH-073-x", "") is False # --------------------------------------------------------------------------- # TC-04 (INV-1): a git/OS error -> False, exception never propagated. # --------------------------------------------------------------------------- def test_tc04_never_raises_on_git_error(monkeypatch): monkeypatch.setattr(merge_gate, "ensure_worktree", lambda r, b: "/wt") def boom(*a, **k): raise OSError("git exploded") monkeypatch.setattr(merge_gate.subprocess, "run", boom) assert merge_gate.verify_merged_to_main("orchestrator", "feature/ORCH-073-x", "abc123") is False def test_tc04_never_raises_on_worktree_error(monkeypatch): def boom(*a, **k): raise RuntimeError("worktree down") monkeypatch.setattr(merge_gate, "ensure_worktree", boom) assert merge_gate.verify_merged_to_main("orchestrator", "feature/ORCH-073-x", "abc123") is False