"""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("