Files
orchestrator/tests/test_analyst_comment_regression.py

127 lines
4.7 KiB
Python

"""ORCH-016 / TC-11 + AC-6: analyst status-comment regression.
Status-only verdict model from PR #12 / #13 must be preserved exactly:
- the analyst comment still asks the stakeholder for the **Approved** status,
- it still rejects the obsolete ``:approved:`` reaction and "move to In Progress",
- it still links the documents that actually exist (BRD / TRZ / AC / Test Plan,
skipping anything not on disk),
- it now also carries the new «Длительность: …» line when an agent_runs row
exists for (task_id, analyst).
"""
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_orch016_analyst_regression.db")
os.environ["ORCH_DB_PATH"] = _test_db
import pytest # noqa: E402
from src import db as db_module # noqa: E402
from src.db import init_db, get_db # noqa: E402
@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 _seed_task_and_analyst_run(task_id=42, agent="analyst", duration_seconds=180):
"""Insert a task and a finished analyst run with a measurable duration."""
conn = get_db()
conn.execute(
"INSERT INTO tasks (id, repo, branch, stage, work_item_id) "
"VALUES (?, 'orchestrator', 'feature/ORCH-016', 'analysis', 'ORCH-016')",
(task_id,),
)
conn.execute(
"INSERT INTO agent_runs (task_id, agent, started_at, finished_at) "
"VALUES (?, ?, datetime('now', ?), datetime('now'))",
(task_id, agent, f"-{duration_seconds} seconds"),
)
conn.commit()
conn.close()
def test_tc11_analyst_text_preserved_with_links(monkeypatch, tmp_path):
"""Analyst comment must keep all existing assertions from PR #12 / #13."""
from src import stage_engine as SE
from src.config import settings
wt = tmp_path / "wt"
docs = wt / "docs" / "work-items" / "ET-011"
docs.mkdir(parents=True)
for fname in (
"00-business-request.md", "01-brd.md", "02-trz.md",
"03-acceptance-criteria.md", "04-test-plan.yaml",
):
(docs / fname).write_text("x")
# 04b-ui-test-cases.md intentionally absent
monkeypatch.setattr(SE, "get_worktree_path", lambda repo, branch: str(wt))
monkeypatch.setattr(settings, "gitea_url", "http://localhost:3000", raising=False)
monkeypatch.setattr(
settings, "gitea_public_url", "https://git.mva154.duckdns.org", raising=False
)
monkeypatch.setattr(settings, "gitea_owner", "admin", raising=False)
html = SE._build_analyst_ready_comment(
"enduro-trails", "ET-011", "feature/ET-011-gpx-upload-feature",
)
# Status-only verdict text (PR #12 contract).
assert "Approved" in html
assert "Rejected" in html
assert ":approved:" not in html
assert "In Progress" not in html
# Clickable links via public URL only.
assert "<a href=" in html
base = ("https://git.mva154.duckdns.org/admin/enduro-trails/src/branch/"
"feature/ET-011-gpx-upload-feature/docs/work-items/ET-011/")
assert base + "01-brd.md" in html
assert base + "04-test-plan.yaml" in html
# Missing file NOT linked.
assert "04b-ui-test-cases.md" not in html
# Internal URL must NOT leak into clickable links.
assert "localhost:3000" not in html
def test_tc11_analyst_includes_duration_when_db_has_run(monkeypatch, tmp_path):
"""When an agent_runs row exists for (task_id, analyst), the comment carries
a «Длительность:» line populated via the DB fallback (AC-14)."""
from src import stage_engine as SE
from src.config import settings
wt = tmp_path / "wt"
(wt / "docs" / "work-items" / "ORCH-016").mkdir(parents=True)
(wt / "docs" / "work-items" / "ORCH-016" / "01-brd.md").write_text("x")
_seed_task_and_analyst_run(task_id=42, agent="analyst", duration_seconds=125)
monkeypatch.setattr(SE, "get_worktree_path", lambda repo, branch: str(wt))
monkeypatch.setattr(settings, "gitea_url", "http://localhost:3000", raising=False)
monkeypatch.setattr(settings, "gitea_public_url", "", raising=False)
monkeypatch.setattr(settings, "gitea_owner", "admin", raising=False)
html = SE._build_analyst_ready_comment(
"orchestrator", "ORCH-016", "feature/ORCH-016", task_id=42,
)
# Two-digit seconds rounding may shave ~1s — accept either neighbour.
assert any(
s in html
for s in ("Длительность: 2m 05s", "Длительность: 2m 04s", "Длительность: 2m 06s")
), html