"""ORCH-110 TC-07: kill-switches + non-self repo => byte-for-byte pre-ORCH-110. Covers NFR-2 / FR-5 / AC-7. Each ORCH-110 behaviour is an INDEPENDENT kill-switch; with a flag off the affected path reverts to the prior behaviour. A non-self-hosting repo (enduro-trails) never reaches the infra-retry / tree-kill paths at all (the merge-gate is N/A for it). """ import os import tempfile os.environ.setdefault("ORCH_DB_PATH", os.path.join(tempfile.gettempdir(), "test_orch110_killswitch.db")) os.environ.setdefault("ORCH_REPOS_DIR", tempfile.gettempdir()) os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token") os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token") import pytest # noqa: E402 from src import merge_gate # noqa: E402 from src.qg import checks as qg # noqa: E402 from src.proc_group import ProcResult # noqa: E402 _REPO = "orchestrator" _BRANCH = "feature/ORCH-110-x" _WI = "ORCH-110" # --------------------------------------------------------------------------- # D1 kill-switch: subprocess_tree_kill_enabled=False -> the fallback path. # --------------------------------------------------------------------------- def test_tc07_tree_kill_off_passes_tree_kill_false(tmp_path, monkeypatch): wt = tmp_path / "wt" wt.mkdir() monkeypatch.setattr(merge_gate, "get_worktree_path", lambda r, b: str(wt)) monkeypatch.setattr(merge_gate.settings, "subprocess_tree_kill_enabled", False, raising=False) captured = {} def _fake(cmd, *, cwd, timeout, env=None, grace_s=5.0, tree_kill=True): captured["tree_kill"] = tree_kill return ProcResult(returncode=0, stdout="1 passed", stderr="", timed_out=False) monkeypatch.setattr(merge_gate, "run_in_process_group", _fake) ok, reason = merge_gate.retest_branch(_REPO, _BRANCH) assert ok is True and reason == "re-test green" assert captured["tree_kill"] is False # -> run_in_process_group degrades to subprocess.run # --------------------------------------------------------------------------- # D4 kill-switch: merge_retest_skip_when_current_enabled=False -> always re-test # after a rebase (even a proven no-op). # --------------------------------------------------------------------------- @pytest.fixture def gate_primitives(monkeypatch): calls = {"retest": 0} monkeypatch.setattr(qg.settings, "merge_gate_enabled", True, raising=False) monkeypatch.setattr(qg.settings, "merge_gate_repos", "", raising=False) monkeypatch.setattr(qg.settings, "premerge_rebase_always", True, raising=False) monkeypatch.setattr(merge_gate, "acquire_merge_lease", lambda *a, **k: (True, "lease acquired"), raising=False) monkeypatch.setattr(merge_gate, "branch_is_behind_main", lambda r, b: True, raising=False) monkeypatch.setattr(merge_gate, "auto_rebase_onto_main", lambda r, b: (True, "rebased"), raising=False) monkeypatch.setattr(merge_gate, "release_merge_lease", lambda *a, **k: None, raising=False) # A PROVEN no-op rebase: HEAD is identical before/after. monkeypatch.setattr(merge_gate, "head_sha", lambda r, b: "deadbeefcafe", raising=False) def _retest(r, b): calls["retest"] += 1 return True, "re-test green" monkeypatch.setattr(merge_gate, "retest_branch", _retest, raising=False) return calls def test_tc07_skip_when_current_off_always_retests(gate_primitives, monkeypatch): monkeypatch.setattr(qg.settings, "merge_retest_skip_when_current_enabled", False, raising=False) ok, reason = qg.check_branch_mergeable(_REPO, _WI, _BRANCH) assert ok is True assert reason == "rebased onto main, re-test green" assert gate_primitives["retest"] == 1 # re-test STILL runs (D4 off) def test_tc07_skip_when_current_on_skips_noop_retest(gate_primitives, monkeypatch): """Mirror sanity: with the flag ON, the proven no-op rebase skips the re-test.""" monkeypatch.setattr(qg.settings, "merge_retest_skip_when_current_enabled", True, raising=False) ok, reason = qg.check_branch_mergeable(_REPO, _WI, _BRANCH) assert ok is True assert "re-test skipped" in reason assert gate_primitives["retest"] == 0 # re-test NOT run on the no-op # --------------------------------------------------------------------------- # Non-self repo (enduro-trails): the merge-gate is N/A -> never the new paths. # --------------------------------------------------------------------------- def test_tc07_non_self_repo_is_noop(monkeypatch): monkeypatch.setattr(qg.settings, "merge_gate_enabled", True, raising=False) monkeypatch.setattr(qg.settings, "merge_gate_repos", "", raising=False) # Guard: if the gate wrongly engaged it would touch the lease -> fail loudly. monkeypatch.setattr( merge_gate, "acquire_merge_lease", lambda *a, **k: pytest.fail("merge-gate must be N/A for enduro-trails"), raising=False, ) ok, reason = qg.check_branch_mergeable("enduro-trails", "ET-1", "feature/ET-1-x") assert ok is True assert "N/A" in reason