"""ORCH-073 FR-2 — pr_already_merged distinguishes code-PR from docs-PR. Covers TC-05..07. pr_already_merged is now an idempotency-guard: it counts a PR as "merged" ONLY when it carries the code of THIS feature-branch into main (merged & head.ref==branch & base.ref=="main"), excluding auto docs-PRs. Gitea HTTP is mocked; never-raise -> False (INV-1). """ import httpx import pytest from src import merge_gate BRANCH = "feature/ORCH-073-x" class _Resp: def __init__(self, status_code, payload=None): self.status_code = status_code self._payload = payload if payload is not None else [] def json(self): return self._payload @pytest.fixture(autouse=True) def _settings(monkeypatch): monkeypatch.setattr(merge_gate.settings, "gitea_url", "http://gitea.test") monkeypatch.setattr(merge_gate.settings, "gitea_owner", "admin") monkeypatch.setattr(merge_gate.settings, "gitea_token", "tok") # --------------------------------------------------------------------------- # TC-05: a merged docs-PR (head=docs/*, base=main) is NOT counted as code-merge. # --------------------------------------------------------------------------- def test_tc05_merged_docs_pr_not_counted(monkeypatch): payload = [ {"merged": True, "head": {"ref": "docs/ORCH-073-staging-log"}, "base": {"ref": "main"}}, {"merged": False, "head": {"ref": BRANCH}, "base": {"ref": "main"}}, ] monkeypatch.setattr(httpx, "get", lambda *a, **k: _Resp(200, payload)) assert merge_gate.pr_already_merged("orchestrator", BRANCH) is False # --------------------------------------------------------------------------- # TC-06: a merged code-PR (head==branch, base==main) IS recognised. # --------------------------------------------------------------------------- def test_tc06_merged_code_pr_recognised(monkeypatch): payload = [ {"merged": True, "head": {"ref": BRANCH}, "base": {"ref": "main"}}, ] monkeypatch.setattr(httpx, "get", lambda *a, **k: _Resp(200, payload)) assert merge_gate.pr_already_merged("orchestrator", BRANCH) is True def test_tc06_merged_code_pr_onto_non_main_base_not_counted(monkeypatch): # Right head but a foreign base (not main) must NOT count. payload = [ {"merged": True, "head": {"ref": BRANCH}, "base": {"ref": "develop"}}, ] monkeypatch.setattr(httpx, "get", lambda *a, **k: _Resp(200, payload)) assert merge_gate.pr_already_merged("orchestrator", BRANCH) is False # --------------------------------------------------------------------------- # TC-07: HTTP error / non-200 -> False (never-raise, conservative). # --------------------------------------------------------------------------- def test_tc07_non_200_is_false(monkeypatch): monkeypatch.setattr(httpx, "get", lambda *a, **k: _Resp(500, [])) assert merge_gate.pr_already_merged("orchestrator", BRANCH) is False def test_tc07_http_exception_is_false(monkeypatch): def boom(*a, **k): raise httpx.ConnectError("gitea unreachable") monkeypatch.setattr(httpx, "get", boom) assert merge_gate.pr_already_merged("orchestrator", BRANCH) is False