This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
orchestrator/agent-team/tests/test_github_intake.py
Adam Moussa 0842ff778b feat(agent-team): P4 live github/claude_code transports + GitHub-issue intake
Live github (issue-comment poster) and claude_code (file-drop) transports, plus GithubIntake (labeled issue -> coordinator.start_task, de-duped). run-team _build_transport now wires github/claude_code live (was SystemExit) + adds the intake-github subcommand. claude_code drop-path also neutralizes backslash (defense-in-depth).
2026-06-18 13:23:02 -04:00

233 lines
7.8 KiB
Python

"""Unit tests for agent_team.transport.github_intake (§3.3.1, INTAKE poller).
Fully hermetic: both the GitHub issue client and the coordinator are injected
in-memory fakes, so no network call, token, GitHub SDK, or model is exercised.
The tests pin the poller's contract: a labeled issue creates exactly one task,
a re-poll does not double-ingest, unlabeled issues are never seen (the client
filters by label), and the intake text is the issue title + body.
"""
from __future__ import annotations
from typing import Any
import pytest
from agent_team.transport.github_intake import (
GITHUB_TRANSPORT_NAME,
GithubIntake,
issue_task_text,
)
INTAKE_LABEL = "agent-team"
# --------------------------------------------------------------------------- #
# Fakes
# --------------------------------------------------------------------------- #
class FakeIssueClient:
"""In-memory ``GithubIssueClient`` returning only issues carrying ``label``.
Mirrors the production client's contract: ``list_open_issues(label=...)``
returns the subset of the configured issues whose ``labels`` include the
requested label. Records each requested label so a test can assert the
poller queries with the configured label.
"""
def __init__(self, issues: list[dict[str, Any]]) -> None:
self.issues = issues
self.requested_labels: list[str] = []
def list_open_issues(self, *, label: str) -> list[dict[str, Any]]:
self.requested_labels.append(label)
return [issue for issue in self.issues if label in (issue.get("labels") or [])]
class FakeCoordinator:
"""In-memory coordinator double recording every ``start_task`` call.
Captures the keyword arguments of each call so a test can assert exactly one
task was started, with the expected ``task_text`` / ``transport_name``.
Returns a synthetic ``thread_id`` like the real coordinator.
"""
def __init__(self) -> None:
self.calls: list[dict[str, Any]] = []
def start_task(self, *, task_text: str, transport_name: str) -> str:
self.calls.append({"task_text": task_text, "transport_name": transport_name})
return f"thread-{len(self.calls)}"
def _issue(
issue_id: int,
*,
title: str = "Do the thing",
body: str = "with details",
labels: list[str] | None = None,
) -> dict[str, Any]:
"""Build a minimal GitHub-issue-shaped mapping for the fakes."""
return {
"id": issue_id,
"number": issue_id,
"title": title,
"body": body,
"labels": [INTAKE_LABEL] if labels is None else labels,
}
# --------------------------------------------------------------------------- #
# issue_task_text
# --------------------------------------------------------------------------- #
def test_issue_task_text_joins_title_and_body() -> None:
text = issue_task_text(_issue(1, title="Add poller", body="for GitHub intake"))
assert text == "Add poller\n\nfor GitHub intake"
def test_issue_task_text_title_only_when_body_empty() -> None:
assert issue_task_text(_issue(1, title="Title only", body="")) == "Title only"
assert issue_task_text(_issue(1, title="Title only", body=" ")) == "Title only"
def test_issue_task_text_falls_back_to_id_when_title_empty() -> None:
text = issue_task_text(_issue(42, title="", body=""))
assert text == "issue #42"
def test_issue_task_text_strips_surrounding_whitespace() -> None:
text = issue_task_text(_issue(1, title=" Trim me ", body="\n body \n"))
assert text == "Trim me\n\nbody"
# --------------------------------------------------------------------------- #
# GithubIntake construction
# --------------------------------------------------------------------------- #
def test_empty_label_is_rejected() -> None:
with pytest.raises(ValueError):
GithubIntake(
client=FakeIssueClient([]), coordinator=FakeCoordinator(), label=""
)
# --------------------------------------------------------------------------- #
# poll_once: the core contract
# --------------------------------------------------------------------------- #
def test_labeled_issue_creates_exactly_one_task() -> None:
client = FakeIssueClient([_issue(1, title="Build it", body="now")])
coordinator = FakeCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
ingested = intake.poll_once()
assert ingested == ["1"]
assert len(coordinator.calls) == 1
call = coordinator.calls[0]
assert call["task_text"] == "Build it\n\nnow"
assert call["transport_name"] == GITHUB_TRANSPORT_NAME
assert client.requested_labels == [INTAKE_LABEL]
def test_repoll_does_not_double_ingest() -> None:
client = FakeIssueClient([_issue(1)])
coordinator = FakeCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
first = intake.poll_once()
second = intake.poll_once()
assert first == ["1"]
assert second == [] # already ingested -> no new task
assert len(coordinator.calls) == 1
assert intake.ingested_ids == frozenset({"1"})
def test_unlabeled_issues_are_ignored() -> None:
client = FakeIssueClient(
[
_issue(1, labels=[INTAKE_LABEL]),
_issue(2, labels=["bug"]),
_issue(3, labels=[]),
]
)
coordinator = FakeCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
ingested = intake.poll_once()
assert ingested == ["1"]
assert len(coordinator.calls) == 1
assert coordinator.calls[0]["task_text"].startswith("Do the thing")
def test_new_issue_on_second_poll_is_ingested() -> None:
issues = [_issue(1)]
client = FakeIssueClient(issues)
coordinator = FakeCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
first = intake.poll_once()
issues.append(_issue(2, title="Second", body="task"))
second = intake.poll_once()
assert first == ["1"]
assert second == ["2"]
assert len(coordinator.calls) == 2
assert coordinator.calls[1]["task_text"] == "Second\n\ntask"
def test_multiple_labeled_issues_each_create_one_task() -> None:
client = FakeIssueClient([_issue(1), _issue(2), _issue(3)])
coordinator = FakeCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
ingested = intake.poll_once()
assert ingested == ["1", "2", "3"]
assert len(coordinator.calls) == 3
def test_id_falls_back_to_number_when_id_absent() -> None:
issue = {"number": 7, "title": "No id", "body": "", "labels": [INTAKE_LABEL]}
client = FakeIssueClient([issue])
coordinator = FakeCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
ingested = intake.poll_once()
assert ingested == ["7"]
assert intake.poll_once() == [] # de-dup on number-derived id
def test_failed_start_task_leaves_issue_eligible_for_retry() -> None:
"""A raising start_task must NOT mark the issue ingested (no silent drop)."""
class FlakyCoordinator:
def __init__(self) -> None:
self.attempts = 0
def start_task(self, *, task_text: str, transport_name: str) -> str:
self.attempts += 1
if self.attempts == 1:
raise RuntimeError("transient intake failure")
return "thread-ok"
client = FakeIssueClient([_issue(1)])
coordinator = FlakyCoordinator()
intake = GithubIntake(client=client, coordinator=coordinator, label=INTAKE_LABEL)
with pytest.raises(RuntimeError):
intake.poll_once()
assert intake.ingested_ids == frozenset() # not recorded -> retryable
# The retry succeeds and ingests the issue exactly once.
ingested = intake.poll_once()
assert ingested == ["1"]
assert coordinator.attempts == 2