issue.updated ships only the changed fields, so name was absent and the branch slug became feature/<id>-untitled. Add fetch_issue_fields (single issue-detail GET returning name+description, reusing the endpoint/token of fetch_issue_description) and pull the name above the slug build. Empty name still falls back to untitled.
139 lines
5.8 KiB
Python
139 lines
5.8 KiB
Python
"""Tests for fix/taskmd-description (3 bugs at the analyst pipeline entry/exit):
|
|
|
|
BUG A: start_pipeline built the analyst .task.md WITHOUT the description body
|
|
(only Title), so analyst received a ~101-byte file and reported the
|
|
"business request is empty". task_desc must now carry the description.
|
|
|
|
BUG B: issue.updated ships only changed fields, so `name` is usually absent ->
|
|
slug/branch became "untitled". start_pipeline must pull the real name
|
|
from the Plane API (single fetch_issue_fields GET, above the slug build)
|
|
so the branch slug is NOT "untitled".
|
|
|
|
BUG C: the analyst "artifacts ready" comment used the obsolete ":approved:"
|
|
wording. Under the status-only model it must ask for the **Approved**
|
|
status (not ":approved:", not "In Progress") and link the docs that
|
|
actually exist.
|
|
"""
|
|
|
|
import os
|
|
import tempfile
|
|
|
|
_test_db = os.path.join(tempfile.gettempdir(), "test_orchestrator_taskmd_desc.db")
|
|
os.environ["ORCH_DB_PATH"] = _test_db
|
|
os.environ.setdefault("ORCH_PLANE_WEBHOOK_SECRET", "")
|
|
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
|
|
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
|
|
|
|
import pytest # noqa: E402
|
|
from unittest.mock import patch, AsyncMock # noqa: E402
|
|
from fastapi.testclient import TestClient # noqa: E402
|
|
|
|
from src.main import app # noqa: E402
|
|
from src.db import init_db, get_db # noqa: E402
|
|
from src import projects as P # noqa: E402
|
|
from src.projects import reload_projects # noqa: E402
|
|
|
|
ENDURO_PLANE_ID = "7a79f0a9-5278-49cd-9007-9a338f238f9c"
|
|
IN_PROGRESS = "b873d9eb-993c-48cd-97ac-99a9b1623967"
|
|
BACKLOG = "113b24f6-cce8-4be9-9a22-a359b9cf0122"
|
|
|
|
client = TestClient(app)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def setup(monkeypatch):
|
|
monkeypatch.setattr(P.settings, "db_path", _test_db)
|
|
import src.db as _db
|
|
monkeypatch.setattr(_db.settings, "db_path", _test_db)
|
|
if os.path.exists(_test_db):
|
|
os.unlink(_test_db)
|
|
init_db()
|
|
monkeypatch.setattr("src.webhooks.plane.verify_plane_signature", lambda body, sig: True)
|
|
registry_json = (
|
|
f'[{{"plane_project_id": "{ENDURO_PLANE_ID}", "repo": "enduro-trails",'
|
|
f' "work_item_prefix": "ET", "name": "enduro-trails"}}]'
|
|
)
|
|
monkeypatch.setattr(P.settings, "projects_json", registry_json)
|
|
reload_projects()
|
|
yield
|
|
reload_projects()
|
|
if os.path.exists(_test_db):
|
|
os.unlink(_test_db)
|
|
|
|
|
|
def _task(plane_id):
|
|
conn = get_db()
|
|
row = conn.execute("SELECT * FROM tasks WHERE plane_id=?", (plane_id,)).fetchone()
|
|
conn.close()
|
|
return row
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# BUG A: description reaches the analyst .task.md
|
|
# --------------------------------------------------------------------------- #
|
|
@patch("src.webhooks.plane.enqueue_job", return_value=1)
|
|
@patch("src.webhooks.plane._create_initial_docs", new_callable=AsyncMock)
|
|
@patch("src.webhooks.plane._create_gitea_branch", new_callable=AsyncMock)
|
|
@patch("src.plane_sync.fetch_issue_sequence_id", return_value=11)
|
|
@patch("src.plane_sync.fetch_issue_fields",
|
|
return_value=("ET-011 real title",
|
|
"REAL BUSINESS REQUEST BODY: user wants GPX upload with "
|
|
"validation and a results map."))
|
|
def test_taskdesc_includes_description(
|
|
mock_fields, mock_seq, mock_branch, mock_docs, mock_enqueue
|
|
):
|
|
resp = client.post("/webhook/plane", json={
|
|
"event": "issue", "action": "updated",
|
|
"data": {
|
|
"id": "taskA",
|
|
# status change payload: NO name, NO description (only changed field)
|
|
"project": ENDURO_PLANE_ID,
|
|
"state": {"id": IN_PROGRESS, "name": "In Progress", "group": "started"},
|
|
},
|
|
"activity": {"field": "state", "new_value": IN_PROGRESS, "old_value": BACKLOG},
|
|
})
|
|
assert resp.status_code == 200
|
|
mock_enqueue.assert_called_once()
|
|
# task_desc is the 3rd positional arg of enqueue_job(agent, repo, task_desc, ...)
|
|
task_desc = mock_enqueue.call_args.args[2]
|
|
assert "Description:" in task_desc
|
|
# the actual description body (not just the Title) is in the file
|
|
assert "REAL BUSINESS REQUEST BODY" in task_desc
|
|
assert "results map" in task_desc
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# BUG B: name fetched from Plane API when payload is empty -> slug not untitled
|
|
# --------------------------------------------------------------------------- #
|
|
@patch("src.webhooks.plane.enqueue_job", return_value=1)
|
|
@patch("src.webhooks.plane._create_initial_docs", new_callable=AsyncMock)
|
|
@patch("src.webhooks.plane._create_gitea_branch", new_callable=AsyncMock)
|
|
@patch("src.plane_sync.fetch_issue_sequence_id", return_value=11)
|
|
@patch("src.plane_sync.fetch_issue_fields",
|
|
return_value=("GPX upload feature",
|
|
"A sufficiently long description so QG-0 passes cleanly."))
|
|
def test_name_fetched_when_payload_empty(
|
|
mock_fields, mock_seq, mock_branch, mock_docs, mock_enqueue
|
|
):
|
|
resp = client.post("/webhook/plane", json={
|
|
"event": "issue", "action": "updated",
|
|
"data": {
|
|
"id": "taskB",
|
|
# NO name, NO description in the payload (Plane status-change shape)
|
|
"project": ENDURO_PLANE_ID,
|
|
"state": {"id": IN_PROGRESS, "name": "In Progress", "group": "started"},
|
|
},
|
|
"activity": {"field": "state", "new_value": IN_PROGRESS, "old_value": BACKLOG},
|
|
})
|
|
assert resp.status_code == 200
|
|
mock_fields.assert_called_once()
|
|
row = _task("taskB")
|
|
assert row is not None
|
|
branch = row["branch"]
|
|
# slug derived from the fetched name -> "gpx-upload-feature", NOT untitled
|
|
assert "untitled" not in branch
|
|
assert "gpx-upload-feature" in branch
|
|
# Title in the analyst task file is the fetched name, not "untitled"
|
|
task_desc = mock_enqueue.call_args.args[2]
|
|
assert "Title: GPX upload feature" in task_desc
|