notify_approve_requested now embeds two HTML <a> links into the single notifying approve-gate message: a Gitea branch-view link to 01-brd.md and a Plane issue browser link. Adds ORCH_PLANE_WEB_URL (external Plane web URL, fallback to plane_api_url) with a loopback-guard that omits the Plane link when the resolved base is localhost/empty (no broken localhost URLs in prod). Each link is built independently and omitted on missing data; the message and the "flip to Approved" call to action are always sent as exactly one ping. The shared send_telegram helper is left untouched (min blast radius for the self-hosting prod container). Dynamic labels are html.escaped; parse_mode=HTML preserved. QG registry / stages / approve handler unchanged. Docs updated in-PR: CHANGELOG, .env.example, INFRA env map. Tests: test_notify_approve_links.py, test_analysis_approve_flow_links.py. Refs: ORCH-017 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
101 lines
3.7 KiB
Python
101 lines
3.7 KiB
Python
"""ORCH-017 / TC-10: analysis-approved flow wires DB fields into the approve ping.
|
|
|
|
When the analyst's artifacts are ready, `_handle_analysis_approved_flow` sets the
|
|
issue In Review, posts the analyst comment, and calls `notify_approve_requested`.
|
|
This test drives that flow with all network side-effects mocked and asserts the
|
|
resulting Telegram ping carries the BRD + Plane links built from the task's DB
|
|
row (repo / branch / plane_issue_id), while the approval gate name and the
|
|
no-self-advance contract are unchanged (AC-1 / AC-2 / AC-8).
|
|
"""
|
|
|
|
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_approve_flow.db")
|
|
os.environ["ORCH_DB_PATH"] = _test_db
|
|
|
|
import pytest # noqa: E402
|
|
|
|
import src.db as db_module # noqa: E402
|
|
from src.db import init_db, get_db # noqa: E402
|
|
from src import notifications as N # noqa: E402
|
|
from src import stage_engine as SE # noqa: E402
|
|
|
|
_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()
|
|
yield
|
|
if os.path.exists(_test_db):
|
|
os.unlink(_test_db)
|
|
|
|
|
|
def _mk_task(monkeypatch):
|
|
conn = get_db()
|
|
cur = conn.execute(
|
|
"INSERT INTO tasks (plane_id, work_item_id, repo, branch, stage, title, "
|
|
"plane_issue_id) VALUES (?, ?, ?, ?, ?, ?, ?)",
|
|
("p1", "ORCH-017", "orchestrator",
|
|
"feature/ORCH-017-brd-plane-telegram", "analysis",
|
|
"Approve flow", "issue-uuid-7"),
|
|
)
|
|
tid = cur.lastrowid
|
|
conn.commit()
|
|
conn.close()
|
|
return tid
|
|
|
|
|
|
def test_tc10_approved_flow_builds_links_from_db(monkeypatch):
|
|
tid = _mk_task(monkeypatch)
|
|
|
|
# Settings that make both links resolvable.
|
|
s = N._get_settings()
|
|
monkeypatch.setattr(s, "gitea_public_url", "https://git.example.org", raising=False)
|
|
monkeypatch.setattr(s, "gitea_owner", "orchteam", raising=False)
|
|
monkeypatch.setattr(s, "plane_web_url", "https://plane.example.org", raising=False)
|
|
monkeypatch.setattr(s, "plane_workspace_slug", "acme", raising=False)
|
|
|
|
# Isolate every network/fs side-effect of the flow.
|
|
monkeypatch.setitem(SE.QG_CHECKS, "check_analysis_complete",
|
|
lambda repo, wid, branch: (True, "ok"))
|
|
monkeypatch.setattr(SE, "set_issue_in_review", lambda wid: None)
|
|
monkeypatch.setattr(SE, "plane_add_comment", lambda *a, **k: None)
|
|
monkeypatch.setattr(SE, "_build_analyst_ready_comment", lambda *a, **k: "c")
|
|
|
|
# Capture the approve ping; stub the tracker refresh.
|
|
calls = []
|
|
monkeypatch.setattr(N, "send_telegram",
|
|
lambda text, disable_notification=False: calls.append(text) or 1)
|
|
monkeypatch.setattr(N, "update_task_tracker", lambda task_id: None)
|
|
|
|
result = SE.AdvanceResult()
|
|
SE._handle_analysis_approved_flow(
|
|
tid, "analysis", "orchestrator", "ORCH-017",
|
|
"feature/ORCH-017-brd-plane-telegram", "analyst", result,
|
|
)
|
|
|
|
# Gate name + no-self-advance contract unchanged (AC-8).
|
|
assert result.qg_name == "check_analysis_approved"
|
|
assert result.note == "analysis-in-review"
|
|
assert result.advanced is False
|
|
|
|
# Exactly one ping carrying both links built from the DB row (AC-1 / AC-2).
|
|
assert len(calls) == 1
|
|
text = calls[0]
|
|
assert (
|
|
"https://git.example.org/orchteam/orchestrator/src/branch/"
|
|
"feature/ORCH-017-brd-plane-telegram/docs/work-items/ORCH-017/01-brd.md"
|
|
) in text
|
|
assert (
|
|
f"https://plane.example.org/acme/projects/{_ORCH_PROJECT_ID}"
|
|
f"/issues/issue-uuid-7/"
|
|
) in text
|