feat: close issues after merged agent PRs

This commit is contained in:
2026-05-06 16:19:38 +08:00
parent 6d1a6d037e
commit aa8afa1a63
6 changed files with 177 additions and 10 deletions

View File

@@ -8,7 +8,7 @@ import httpx
from agent_gitea.config import AppConfig
from agent_gitea.gitea import GiteaClient
from agent_gitea.models import AgentResult, TaskState
from agent_gitea.service import TaskRunner, scan_issues, sync_repositories
from agent_gitea.service import TaskRunner, close_issues_for_merged_pull_requests, scan_issues, sync_repositories
def make_config(tmp_path: Path, **overrides: object) -> AppConfig:
@@ -265,12 +265,94 @@ def test_run_task_success_posts_review_comments(db, tmp_path):
pull_requests = [payload for _, path, payload in requests if path == "/api/v1/repos/acme/service/pulls"]
assert pull_requests[0]["title"] == "代理实现Ready issue"
assert "代理实现报告" in pull_requests[0]["body"]
assert "Closes #1" in pull_requests[0]["body"]
command = json.loads(db.list_agent_runs(task.id)[0]["command_json"])
assert command[1] == "--cd"
assert Path(command[2]).is_absolute()
assert [path for _, path, _ in requests].count("/api/v1/repos/acme/service/issues/5/comments") == 2
def test_close_issues_for_merged_pull_requests_closes_linked_issue(db):
repo = db.upsert_repository(
owner="acme",
name="service",
clone_url="https://gitea.test/acme/service.git",
default_branch="main",
enabled=True,
)
db.upsert_issue(
repo_id=repo.id,
issue_number=1,
title="Ready issue",
body="Body",
labels=["agent:ready"],
state="open",
html_url="https://gitea.test/acme/service/issues/1",
)
task = db.create_task(repo.id, 1)
db.transition(task.id, TaskState.CLAIMED)
db.transition(task.id, TaskState.PLANNING)
db.transition(task.id, TaskState.IMPLEMENTING)
db.transition(task.id, TaskState.TESTING)
db.transition(task.id, TaskState.PR_OPENED, pr_number=5)
db.transition(task.id, TaskState.REVIEWING)
db.transition(task.id, TaskState.HUMAN_REVIEW_READY, clear_lease=True)
requests: list[tuple[str, str, dict]] = []
def handler(request: httpx.Request) -> httpx.Response:
payload = json.loads(request.content.decode() or "{}")
requests.append((request.method, request.url.path, payload))
if request.url.path == "/api/v1/repos/acme/service/pulls/5":
return httpx.Response(200, json={"number": 5, "state": "closed", "merged": True})
if request.url.path == "/api/v1/repos/acme/service/issues/1/comments":
return httpx.Response(201, json={"id": 1})
if request.url.path == "/api/v1/repos/acme/service/issues/1":
return httpx.Response(200, json={"number": 1, "state": "closed"})
return httpx.Response(404)
closed = close_issues_for_merged_pull_requests(db, make_client(handler))
assert closed == 1
assert db.get_issue(repo.id, 1).state == "closed" # type: ignore[union-attr]
assert ("PATCH", "/api/v1/repos/acme/service/issues/1", {"state": "closed"}) in requests
def test_close_issues_for_merged_pull_requests_skips_unmerged_pr(db):
repo = db.upsert_repository(
owner="acme",
name="service",
clone_url="https://gitea.test/acme/service.git",
default_branch="main",
enabled=True,
)
db.upsert_issue(
repo_id=repo.id,
issue_number=1,
title="Ready issue",
body="Body",
labels=["agent:ready"],
state="open",
html_url="https://gitea.test/acme/service/issues/1",
)
task = db.create_task(repo.id, 1)
db.transition(task.id, TaskState.CLAIMED)
db.transition(task.id, TaskState.PLANNING)
db.transition(task.id, TaskState.IMPLEMENTING)
db.transition(task.id, TaskState.TESTING)
db.transition(task.id, TaskState.PR_OPENED, pr_number=5)
db.transition(task.id, TaskState.REVIEWING)
db.transition(task.id, TaskState.HUMAN_REVIEW_READY, clear_lease=True)
def handler(request: httpx.Request) -> httpx.Response:
assert request.url.path == "/api/v1/repos/acme/service/pulls/5"
return httpx.Response(200, json={"number": 5, "state": "open", "merged": False})
closed = close_issues_for_merged_pull_requests(db, make_client(handler))
assert closed == 0
assert db.get_issue(repo.id, 1).state == "open" # type: ignore[union-attr]
def test_run_task_no_diff_becomes_blocked(db, tmp_path):
config = make_config(tmp_path)
seed_task(db)

View File

@@ -40,6 +40,7 @@ def test_prompt_and_pr_body_include_contract_sections(db):
assert ".agent-output/AGENT_IMPLEMENTATION_REPORT.md" in prompt
assert "关联 Issue#7" in body
assert "Closes #7" in body
assert "人工审核" in body