"""ORCH-067 — Group C: clickable issue number in the live card (AC-10/AC-11/AC-14). The issue number in the card header is now a Plane hyperlink (``ORCH-NNN``) when a usable browser URL can be built, and degrades fail-safe to the html-escaped raw number when any piece is missing (web base / non-loopback / workspace / project_id / plane_issue_id). The card must NEVER break under parse_mode=HTML: a title with '<'/'&'/'>' stays escaped while the markup stays valid. Network is isolated (no HTTP from the render path here); the DB is a temp SQLite. Test ids TC-10, TC-11, TC-16 from 04-test-plan.yaml. """ import os import tempfile os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token") os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token") _test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_card_link.db") os.environ["ORCH_DB_PATH"] = _test_db from types import SimpleNamespace # noqa: E402 import pytest # noqa: E402 import src.db as db_module # noqa: E402 import src.projects as projects_mod # noqa: E402 from src.db import init_db, get_db # noqa: E402 from src import notifications as N # noqa: E402 # orchestrator repo -> default project registry uuid (src/projects.py). _ORCH_PROJECT_ID = "8da6aa25-a60e-44d6-a1e2-d8ae59aa7d6a" @pytest.fixture(autouse=True) def setup_db(monkeypatch): monkeypatch.setattr(db_module.settings, "db_path", _test_db, raising=False) if os.path.exists(_test_db): os.unlink(_test_db) init_db() # Keep the render path fully offline (no live overlay HTTP). monkeypatch.setattr(N._get_settings(), "tracker_live_status", False, raising=False) # Pin the repo->project resolution so cross-file tests that reload the # ORCH_PROJECTS_JSON registry can't strip 'orchestrator' out from under us. monkeypatch.setattr( projects_mod, "get_project_by_repo", lambda repo: (SimpleNamespace(plane_project_id=_ORCH_PROJECT_ID) if repo == "orchestrator" else None), ) yield if os.path.exists(_test_db): os.unlink(_test_db) def _set(monkeypatch, **kw): s = N._get_settings() for k, v in kw.items(): monkeypatch.setattr(s, k, v, raising=False) def _mk_task(wid="ORCH-067", repo="orchestrator", title="card link", plane_issue_id="issue-uuid-1", stage="development"): conn = get_db() cur = conn.execute( "INSERT INTO tasks (plane_id, work_item_id, repo, branch, stage, title, " "plane_issue_id) VALUES (?, ?, ?, ?, ?, ?, ?)", ("p1", wid, repo, "feature/ORCH-067-x", stage, title, plane_issue_id), ) tid = cur.lastrowid conn.commit() conn.close() return tid # --------------------------------------------------------------------------- # # TC-10 / AC-10 — full data -> clickable wrapping the issue number # --------------------------------------------------------------------------- # def test_tc10_card_number_is_clickable(monkeypatch): _set(monkeypatch, plane_web_url="https://plane.example.org", plane_api_url="http://localhost:8091", plane_workspace_slug="acme") tid = _mk_task(plane_issue_id="abcd-issue-uuid") text = N.render_task_tracker(tid) expected_url = ( f"https://plane.example.org/acme/projects/{_ORCH_PROJECT_ID}" f"/issues/abcd-issue-uuid/" ) assert f'ORCH-067' in text # --------------------------------------------------------------------------- # # TC-11 / AC-11 — fail-safe: any missing piece -> escaped number, no , no crash # --------------------------------------------------------------------------- # @pytest.mark.parametrize("override,reason", [ ({"plane_web_url": "", "plane_api_url": ""}, "no web base"), ({"plane_web_url": "http://localhost:8091", "plane_api_url": ""}, "loopback base"), ({"plane_workspace_slug": ""}, "no workspace"), ]) def test_tc11_card_number_degrades_settings(monkeypatch, override, reason): _set(monkeypatch, plane_web_url="https://plane.example.org", plane_api_url="http://localhost:8091", plane_workspace_slug="acme") _set(monkeypatch, **override) tid = _mk_task(plane_issue_id="abcd-issue-uuid") text = N.render_task_tracker(tid) assert "ORCH-067" in text # raw number still shown assert " the number is shown unlinked, render survives. _set(monkeypatch, plane_web_url="https://plane.example.org", plane_workspace_slug="acme") tid = _mk_task(plane_issue_id=None) text = N.render_task_tracker(tid) assert "ORCH-067" in text assert "drop & table >", plane_issue_id="iss-1") text = N.render_task_tracker(tid) # Raw title markup is escaped -> cannot break parse_mode=HTML. assert "" not in text assert "<b>" in text assert "&" in text # The card's own anchor markup stays well-formed (balanced tags). assert text.count("") assert text.count("