feat(plane): unified status-comment format with duration line (ORCH-016) (#34)
This commit was merged in pull request #34.
This commit is contained in:
199
tests/test_post_usage_comments_integration.py
Normal file
199
tests/test_post_usage_comments_integration.py
Normal file
@@ -0,0 +1,199 @@
|
||||
"""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"]
|
||||
Reference in New Issue
Block a user