200 lines
6.8 KiB
Python
200 lines
6.8 KiB
Python
"""ORCH-016 / TC-13..TC-15: _post_usage_comments integration tests.
|
|
|
|
End-to-end (DB + filesystem worktree, no network) verification that
|
|
AgentLauncher._post_usage_comments:
|
|
- resolves the task by (repo, branch),
|
|
- threads the explicit duration_s into build_status_comment,
|
|
- posts exactly ONE status comment authored by the finishing agent,
|
|
- for deployer: ALSO posts the per-task usage summary (deployer authorship).
|
|
|
|
The actual Plane HTTP call (plane_sync.add_comment) is patched out; we only
|
|
check the (work_item_id, body, author) tuples the launcher passes to it.
|
|
"""
|
|
|
|
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_post_usage.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
|
|
from src.agents.launcher import AgentLauncher # noqa: E402
|
|
|
|
|
|
REPO = "enduro-trails"
|
|
BRANCH = "feature/ET-016-x"
|
|
WID = "ET-016"
|
|
|
|
|
|
@pytest.fixture
|
|
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()
|
|
conn = get_db()
|
|
conn.execute(
|
|
"INSERT INTO tasks (id, repo, branch, stage, work_item_id) "
|
|
"VALUES (1, ?, ?, 'review', ?)",
|
|
(REPO, BRANCH, WID),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
yield
|
|
if os.path.exists(_test_db):
|
|
os.unlink(_test_db)
|
|
|
|
|
|
@pytest.fixture
|
|
def fake_worktree(monkeypatch, tmp_path):
|
|
"""Stub get_worktree_path inside the launcher module to a tmp_path location."""
|
|
wt = tmp_path / "wt"
|
|
(wt / "docs" / "work-items" / WID).mkdir(parents=True)
|
|
|
|
def _get_wt(repo, branch):
|
|
return str(wt)
|
|
|
|
# The launcher imports get_worktree_path lazily inside the function body
|
|
# (`from ..git_worktree import get_worktree_path`); patch the source module.
|
|
monkeypatch.setattr("src.git_worktree.get_worktree_path", _get_wt)
|
|
monkeypatch.setattr("src.usage._input_total", lambda u: 0) # quiet <sub> tail
|
|
return wt
|
|
|
|
|
|
@pytest.fixture
|
|
def capture_comments(monkeypatch):
|
|
posts = []
|
|
|
|
def _spy(work_item_id, body, author=None, **kwargs):
|
|
posts.append({"wid": work_item_id, "body": body, "author": author})
|
|
|
|
monkeypatch.setattr("src.agents.launcher.plane_add_comment", _spy)
|
|
return posts
|
|
|
|
|
|
@pytest.fixture
|
|
def public_url(monkeypatch):
|
|
from src.config import settings
|
|
monkeypatch.setattr(
|
|
settings, "gitea_public_url", "https://git.mva154.duckdns.org", raising=False
|
|
)
|
|
monkeypatch.setattr(settings, "gitea_url", "http://localhost:3000", raising=False)
|
|
monkeypatch.setattr(settings, "gitea_owner", "admin", raising=False)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TC-13: reviewer comment.
|
|
# ---------------------------------------------------------------------------
|
|
def test_tc13_reviewer_posts_one_status_comment(
|
|
setup_db, fake_worktree, capture_comments, public_url
|
|
):
|
|
(fake_worktree / "docs" / "work-items" / WID / "12-review.md").write_text(
|
|
"---\nverdict: APPROVE\n---\nReviewed.",
|
|
)
|
|
|
|
AgentLauncher()._post_usage_comments(
|
|
run_id=99, agent="reviewer", repo=REPO, branch=BRANCH,
|
|
usage={"input_tokens": 1, "output_tokens": 1, "cost_usd": 0.01},
|
|
duration_s=180,
|
|
)
|
|
|
|
assert len(capture_comments) == 1
|
|
post = capture_comments[0]
|
|
assert post["wid"] == WID
|
|
assert post["author"] == "reviewer"
|
|
body = post["body"]
|
|
assert "\U0001f50e Reviewer" in body
|
|
assert "Verdict: APPROVE" in body
|
|
assert "Длительность: 3m 00s" in body
|
|
assert "12-review.md" in body
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TC-14: tester comment.
|
|
# ---------------------------------------------------------------------------
|
|
def test_tc14_tester_posts_one_status_comment(
|
|
setup_db, fake_worktree, capture_comments, public_url
|
|
):
|
|
(fake_worktree / "docs" / "work-items" / WID / "13-test-report.md").write_text(
|
|
"---\nverdict: PASS\n---\n",
|
|
)
|
|
|
|
AgentLauncher()._post_usage_comments(
|
|
run_id=100, agent="tester", repo=REPO, branch=BRANCH,
|
|
usage=None, duration_s=42,
|
|
)
|
|
|
|
assert len(capture_comments) == 1
|
|
post = capture_comments[0]
|
|
assert post["author"] == "tester"
|
|
body = post["body"]
|
|
assert "\U0001f9ea Tester" in body
|
|
assert "Verdict: PASS" in body
|
|
assert "Длительность: 42s" in body
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TC-15: deployer comment + per-task summary (two comments, both from deployer).
|
|
# ---------------------------------------------------------------------------
|
|
def test_tc15_deployer_posts_status_then_summary(
|
|
setup_db, fake_worktree, capture_comments, public_url
|
|
):
|
|
# Task stage = 'deploy' so build_status_comment uses 14-deploy-log.md.
|
|
conn = get_db()
|
|
conn.execute("UPDATE tasks SET stage='deploy' WHERE id=1")
|
|
conn.commit()
|
|
conn.close()
|
|
(fake_worktree / "docs" / "work-items" / WID / "14-deploy-log.md").write_text(
|
|
"---\ndeploy_status: SUCCESS\n---\nDeployed.",
|
|
)
|
|
|
|
AgentLauncher()._post_usage_comments(
|
|
run_id=101, agent="deployer", repo=REPO, branch=BRANCH,
|
|
usage={"input_tokens": 1, "output_tokens": 1, "cost_usd": 0.01},
|
|
duration_s=300,
|
|
)
|
|
|
|
# 2 comments: status + per-task summary.
|
|
assert len(capture_comments) == 2
|
|
status, summary = capture_comments
|
|
assert status["author"] == "deployer"
|
|
assert "Status: SUCCESS" in status["body"]
|
|
assert "Длительность: 5m 00s" in status["body"]
|
|
assert "14-deploy-log.md" in status["body"]
|
|
|
|
assert summary["author"] == "deployer"
|
|
# task_summary_comment header (Russian "Итого по задаче").
|
|
assert "\U0001f4ca" in summary["body"]
|
|
assert "Итого" in summary["body"]
|
|
|
|
|
|
def test_deployer_staging_picks_15_log(
|
|
setup_db, fake_worktree, capture_comments, public_url
|
|
):
|
|
conn = get_db()
|
|
conn.execute("UPDATE tasks SET stage='deploy-staging' WHERE id=1")
|
|
conn.commit()
|
|
conn.close()
|
|
(fake_worktree / "docs" / "work-items" / WID / "15-staging-log.md").write_text(
|
|
"---\nstaging_status: SUCCESS\n---\n",
|
|
)
|
|
|
|
AgentLauncher()._post_usage_comments(
|
|
run_id=102, agent="deployer", repo=REPO, branch=BRANCH,
|
|
usage=None, duration_s=10,
|
|
)
|
|
|
|
# deployer always also posts the summary; check the FIRST comment is status.
|
|
assert len(capture_comments) == 2
|
|
status = capture_comments[0]
|
|
assert "Status: SUCCESS" in status["body"]
|
|
assert "15-staging-log.md" in status["body"]
|
|
assert "14-deploy-log.md" not in status["body"]
|
|
assert "staging-деплой" in status["body"]
|