Files
agentci/tests/test_context.py
T
2026-07-22 23:49:44 +02:00

216 lines
6.4 KiB
Python

from typing import cast
from agentci.engine.repository import Repository
from agentci.integrations.gitea.client import Gitea
from agentci.integrations.gitea.models import CommentInfo, IssueInfo, PullRequestInfo
from agentci.workflows.context import build_issue_context, build_pull_request_context
class FakeIssueGitea:
async def issue(self, *_args):
return IssueInfo(number=2, title="Broken widget", body="It fails.", state="open")
async def issue_comments(self, *_args):
return [
CommentInfo(1, "alice", "Details", "2026-01-01"),
CommentInfo(2, "agentci", "Agent job queued", "2026-01-02"),
]
class FakeRepository:
async def operational_comment_ids(self, *_args):
return {2}
class FakePullRequestGitea:
async def pull_request(self, *_args):
return PullRequestInfo(
number=3,
title="Fix widget",
body="Fixes the failure.",
state="open",
merged=False,
base_branch="main",
head_branch="fix-widget",
head_sha="abcdef1234567890",
head_owner="alice",
head_repo="repo",
)
async def issue_comments(self, *_args):
return [CommentInfo(3, "bob", "Please add a test.", "2026-01-03")]
async def pull_reviews(self, *_args):
return [
{
"id": 4,
"user": {"login": "carol"},
"state": "REQUEST_CHANGES",
"body": "One issue remains.",
}
]
async def review_comments(self, *_args):
return [
{
"path": "src/widget.py",
"new_position": 12,
"body": "Handle the empty value.",
}
]
async def pull_commits(self, *_args):
return [{"sha": "abcdef1234567890", "commit": {"message": "Fix widget"}}]
class EmptyIssueGitea:
async def issue(self, *_args):
return IssueInfo(number=5, title="Empty issue", body="", state="open")
async def issue_comments(self, *_args):
return []
class EmptyRepository:
async def operational_comment_ids(self, *_args):
return set()
class EmptyPullRequestGitea:
async def pull_request(self, *_args):
return PullRequestInfo(
number=6,
title="Empty pull request",
body="",
state="open",
merged=False,
base_branch="main",
head_branch="empty",
head_sha="1234567890abcdef",
head_owner="alice",
head_repo="repo",
)
async def issue_comments(self, *_args):
return []
async def pull_reviews(self, *_args):
return []
async def pull_commits(self, *_args):
return []
async def review_comments(self, *_args):
raise AssertionError("review comments should not be requested without reviews")
class OrderedPullRequestGitea(FakePullRequestGitea):
async def issue_comments(self, *_args):
return [
CommentInfo(1, "alice", "First timeline comment", "2026-01-01"),
CommentInfo(2, "bob", "Second timeline comment", "2026-01-02"),
]
async def pull_reviews(self, *_args):
return [
{
"id": 10,
"user": {"login": "carol"},
"state": "REQUEST_CHANGES",
"body": "First formal review",
},
{
"id": 11,
"user": {"login": "dave"},
"state": "APPROVED",
"body": "Second formal review",
},
]
async def review_comments(self, *_args):
review_id = _args[-1]
if review_id == 10:
return [
{
"path": "src/old.py",
"new_position": None,
"old_position": 21,
"body": "Old-side position",
}
]
return [
{
"path": "src/new.py",
"new_position": 34,
"body": "New-side position",
}
]
async def pull_commits(self, *_args):
return [
{"sha": "111111111111aaaa", "commit": {"message": "First commit"}},
{"sha": "222222222222bbbb", "commit": {"message": "Second commit"}},
]
async def test_issue_context_excludes_operational_comments() -> None:
context = await build_issue_context(
cast(Gitea, FakeIssueGitea()),
cast(Repository, FakeRepository()),
"org",
"repo",
2,
)
assert "Broken widget" in context
assert "Details" in context
assert "Agent job queued" not in context
async def test_pull_request_context_includes_feedback_and_commits() -> None:
pull, context = await build_pull_request_context(
cast(Gitea, FakePullRequestGitea()), "org", "repo", 3
)
assert pull.number == 3
assert "Please add a test." in context
assert "One issue remains." in context
assert "`src/widget.py:12`: Handle the empty value." in context
assert "abcdef123456 Fix widget" in context
async def test_issue_context_labels_empty_body_and_discussion() -> None:
context = await build_issue_context(
cast(Gitea, EmptyIssueGitea()),
cast(Repository, EmptyRepository()),
"org",
"repo",
5,
)
assert "## Issue body\n(empty)" in context
assert "## Discussion\n(none)" in context
async def test_pull_request_context_labels_empty_sections() -> None:
_, context = await build_pull_request_context(
cast(Gitea, EmptyPullRequestGitea()), "org", "repo", 6
)
assert "## Pull request body\n(empty)" in context
assert "## Commits\n(none)" in context
assert "## Timeline discussion\n(none)" in context
assert "## Formal and inline reviews\n(none)" in context
async def test_pull_request_context_preserves_source_order_and_positions() -> None:
_, context = await build_pull_request_context(
cast(Gitea, OrderedPullRequestGitea()), "org", "repo", 3
)
assert context.index("First timeline comment") < context.index("Second timeline comment")
assert context.index("111111111111 First commit") < context.index("222222222222 Second commit")
assert context.index("Review 10 by carol") < context.index("Review 11 by dave")
assert "`src/old.py:21`: Old-side position" in context
assert "`src/new.py:34`: New-side position" in context