"""ORCH-019 — src/bug_fast_track.py: bug-fast-track pure logic (never-raise, fail-safe). Covers (04-test-plan.yaml): TC-01 is_bug_task() True for an issue carrying the `Bug` label (label read from the Plane API via labels.has_label, NOT the webhook payload). TC-02 is_bug_task() False on missing/ambiguous label or labels=None (fail-safe). TC-03 bug_fast_track_applies(): the LOCAL scope (enabled + CSV repos) is checked FIRST, before any network; disabled flag -> False without has_label. TC-04 never-raise: an exception in the label apparatus degrades is_bug_task to False (full cycle), never propagates. """ import os import tempfile import pytest os.environ.setdefault( "ORCH_DB_PATH", os.path.join(tempfile.gettempdir(), "test_bug_fast_track.db") ) os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token") os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token") from src import bug_fast_track # noqa: E402 from src import plane_sync # noqa: E402 from src import config as cfg # noqa: E402 @pytest.fixture(autouse=True) def enabled_self_hosting(monkeypatch): monkeypatch.setattr(cfg.settings, "bug_fast_track_enabled", True, raising=False) monkeypatch.setattr(cfg.settings, "bug_fast_track_label", "Bug", raising=False) monkeypatch.setattr(cfg.settings, "bug_fast_track_repos", "", raising=False) # Keep _resolve_project_id offline-deterministic (mirrors test_labels.py). monkeypatch.setattr(plane_sync, "_resolve_project_id", lambda w=None, p=None: "proj-1") yield # --- TC-01: classification True -------------------------------------------- def test_tc01_is_bug_task_true(monkeypatch): monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-BUG"]) monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"}) assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is True def test_tc01_label_from_plane_api_not_payload(monkeypatch): """The decision comes from labels.has_label (Plane API), independent of any webhook payload field — a payload `type` is irrelevant.""" seen = {"fetch": 0} def fetch(w, p=None): seen["fetch"] += 1 return ["uuid-BUG"] monkeypatch.setattr(plane_sync, "fetch_issue_labels", fetch) monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"}) assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is True assert seen["fetch"] == 1 # the Plane API WAS consulted # --- TC-02: fail-safe on absent / ambiguous / None ------------------------- def test_tc02_label_absent(monkeypatch): monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-OTHER"]) monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"}) assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False def test_tc02_labels_none(monkeypatch): monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: None) monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"}) assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False def test_tc02_label_ambiguous(monkeypatch): monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-BUG"]) monkeypatch.setattr( plane_sync, "get_project_labels", lambda pid: {"bug": "__AMBIGUOUS__"} ) assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False def test_tc02_empty_label_config(monkeypatch): monkeypatch.setattr(cfg.settings, "bug_fast_track_label", "", raising=False) monkeypatch.setattr(plane_sync, "fetch_issue_labels", lambda w, p=None: ["uuid-BUG"]) monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"}) assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False # --- TC-03: local scope first (CSV + self-hosting + kill-switch) ------------ def test_tc03_empty_csv_self_hosting_only(monkeypatch): monkeypatch.setattr(cfg.settings, "bug_fast_track_repos", "", raising=False) assert bug_fast_track.bug_fast_track_applies("orchestrator") is True assert bug_fast_track.bug_fast_track_applies("enduro-trails") is False def test_tc03_csv_membership(monkeypatch): monkeypatch.setattr(cfg.settings, "bug_fast_track_repos", "enduro-trails, foo", raising=False) assert bug_fast_track.bug_fast_track_applies("enduro-trails") is True assert bug_fast_track.bug_fast_track_applies("foo") is True # orchestrator is NOT in the explicit CSV -> out of scope. assert bug_fast_track.bug_fast_track_applies("orchestrator") is False def test_tc03_killswitch_off_no_network(monkeypatch): """The gate idiom `applies(repo) and is_bug_task(...)` short-circuits before any network call when the kill-switch is off (AC-6).""" monkeypatch.setattr(cfg.settings, "bug_fast_track_enabled", False, raising=False) called = {"fetch": 0} def spy(*a, **k): called["fetch"] += 1 return ["uuid-BUG"] monkeypatch.setattr(plane_sync, "fetch_issue_labels", spy) repo = "orchestrator" fired = bug_fast_track.bug_fast_track_applies(repo) and bug_fast_track.is_bug_task( "ORCH-1", "proj-1" ) assert fired is False assert called["fetch"] == 0 # is_bug_task never reached -> zero network # --- TC-04: never-raise ----------------------------------------------------- def test_tc04_is_bug_task_never_raises(monkeypatch): def boom(*a, **k): raise RuntimeError("plane down") monkeypatch.setattr(plane_sync, "fetch_issue_labels", boom) monkeypatch.setattr(plane_sync, "get_project_labels", lambda pid: {"bug": "uuid-BUG"}) # Degrades to False (full cycle), no exception. assert bug_fast_track.is_bug_task("ORCH-1", "proj-1") is False def test_tc04_applies_never_raises(monkeypatch): # A repos config whose access explodes still yields False, not a crash. class _Poisoned: bug_fast_track_enabled = True @property def bug_fast_track_repos(self): raise RuntimeError("boom") monkeypatch.setattr(bug_fast_track, "settings", _Poisoned(), raising=False) assert bug_fast_track.bug_fast_track_applies("orchestrator") is False # --- skips_architecture predicate ------------------------------------------ def test_skips_architecture_bug(monkeypatch): assert bug_fast_track.skips_architecture("bug") is True assert bug_fast_track.skips_architecture("BUG") is True def test_skips_architecture_full(monkeypatch): assert bug_fast_track.skips_architecture("full") is False assert bug_fast_track.skips_architecture(None) is False assert bug_fast_track.skips_architecture("") is False def test_skips_architecture_killswitch_off(monkeypatch): monkeypatch.setattr(cfg.settings, "bug_fast_track_enabled", False, raising=False) # Even a stored 'bug' track is inert when the kill-switch is off (1:1 routing). assert bug_fast_track.skips_architecture("bug") is False # --- snapshot --------------------------------------------------------------- def test_snapshot_never_raises(): snap = bug_fast_track.snapshot() assert set(snap) >= { "enabled", "label", "repos", "active_bug_tasks", "total_bug_tasks", "est_saved_architecture_runs", }