refactor tests
Publish container image / Build and push (push) Successful in 32s

This commit is contained in:
2026-07-26 23:49:40 +02:00
parent 5ef10d28fe
commit ce9f1e3d20
32 changed files with 1546 additions and 1923 deletions
+60 -160
View File
@@ -1,33 +1,15 @@
import json
from pathlib import Path
from types import SimpleNamespace
from typing import Any, cast
import pytest
from agentci.engine.model import Job, JobKind, Workflow, WorkflowKind, WorkflowStatus
from agentci.engine.run import JobRun
from agentci.integrations.gitea.models import CommentInfo, IssueInfo, PullRequestInfo
from agentci.workflows.implementation import implement
from agentci.workflows.model import AgentResult, ReviewReport
from agentci.workflows.render import JobRejected
from agentci.workflows.services import WorkflowServices
class RecordingRun(JobRun):
def __init__(self) -> None:
self.stages: list[str] = []
self.created_workflows: list[tuple[Workflow, str]] = []
self.linked_sessions: list[str] = []
async def stage(self, stage: str) -> None:
self.stages.append(stage)
async def create_workflow(self, workflow: Workflow, stage: str) -> None:
self.created_workflows.append((workflow, stage))
async def link_session(self, session_id: str) -> None:
self.linked_sessions.append(session_id)
from tests.workflow_support import make_workflow_harness
class FakeRepository:
@@ -85,71 +67,6 @@ class FakeGitea:
return pull(number=42, branch=values["head"])
class RecordingGit:
def __init__(self, *, changed: bool = True) -> None:
self.changed = changed
self.calls: list[tuple[Any, ...]] = []
async def clone(self, owner: str, repo: str, branch: str, destination: Path) -> str:
self.calls.append(("clone", owner, repo, branch, destination))
return "base-sha"
async def create_branch(self, workspace: Path, branch: str) -> None:
self.calls.append(("create_branch", workspace, branch))
async def has_changes(self, workspace: Path) -> bool:
self.calls.append(("has_changes", workspace))
return self.changed
async def diff_check(self, workspace: Path) -> None:
self.calls.append(("diff_check", workspace))
async def commit(self, workspace: Path, message: str) -> str:
self.calls.append(("commit", workspace, message))
return "commit-sha"
async def push(self, workspace: Path, branch: str, *, set_upstream: bool = False) -> None:
self.calls.append(("push", workspace, branch, set_upstream))
class RecordingDevelopment:
description = "Python 3.13"
def __init__(self) -> None:
self.workspaces: list[Path] = []
async def prepare(self, workspace: Path) -> None:
self.workspaces.append(workspace)
class RecordingPrompts:
def __init__(self) -> None:
self.calls: list[tuple[str, dict[str, str]]] = []
def render(self, name: str, **values: str) -> str:
self.calls.append((name, values))
return f"rendered {name}"
class RecordingOpenCode:
def __init__(self, result: AgentResult, report: ReviewReport) -> None:
self.result = result
self.report = report
self.created_sessions: list[tuple[Path, str]] = []
self.resume_calls: list[dict[str, Any]] = []
async def create_session(self, workspace: Path, title: str) -> str:
self.created_sessions.append((workspace, title))
return f"{title}-session"
async def resume(self, **values: Any) -> AgentResult | ReviewReport:
self.resume_calls.append(values)
if values["result_type"] is AgentResult:
return self.result
assert values["result_type"] is ReviewReport
return self.report
def job() -> Job:
return Job(
id="job-1",
@@ -203,95 +120,63 @@ def pull(
)
def make_services(
tmp_path: Path,
*,
changed: bool = True,
implementations: list[Workflow] | None = None,
existing_pulls: dict[int, PullRequestInfo] | None = None,
plan: Workflow | None = None,
) -> tuple[
WorkflowServices,
FakeRepository,
FakeGitea,
RecordingGit,
RecordingDevelopment,
RecordingPrompts,
RecordingOpenCode,
]:
repository = FakeRepository(implementations=implementations, plan=plan)
gitea = FakeGitea(existing_pulls=existing_pulls)
git = RecordingGit(changed=changed)
development = RecordingDevelopment()
prompts = RecordingPrompts()
opencode = RecordingOpenCode(
AgentResult(
summary_markdown="# Implement widget\n\nHandled empty input.",
tests=["pytest: passed"],
),
ReviewReport(summary="Ready", findings=[]),
def implementation_settings(tmp_path: Path) -> SimpleNamespace:
return SimpleNamespace(
branch_prefix="agent",
workspaces_dir=tmp_path / "workspaces",
gitea_url="https://git.example.test",
implement_model="provider/model",
implement_variant="high",
implement_review_rounds=2,
)
services = cast(
WorkflowServices,
SimpleNamespace(
settings=SimpleNamespace(
branch_prefix="agent",
workspaces_dir=tmp_path / "workspaces",
gitea_url="https://git.example.test",
implement_model="provider/model",
implement_variant="high",
implement_review_rounds=2,
),
repository=repository,
gitea=gitea,
git=git,
opencode=opencode,
prompts=prompts,
development=development,
),
)
return services, repository, gitea, git, development, prompts, opencode
async def test_initial_implementation_completes_review_commit_push_and_pr(
tmp_path: Path,
) -> None:
stale = workflow(pr_number=8)
services, repository, gitea, git, development, prompts, opencode = make_services(
tmp_path,
implementations=[stale],
existing_pulls={8: pull(state="closed")},
repository = FakeRepository(implementations=[stale])
gitea = FakeGitea(existing_pulls={8: pull(state="closed")})
harness = make_workflow_harness(
settings=implementation_settings(tmp_path),
repository=repository,
gitea=gitea,
responses=[
AgentResult(
summary_markdown="# Implement widget\n\nHandled empty input.",
tests=["pytest: passed"],
),
ReviewReport(summary="Ready", findings=[]),
],
)
run = RecordingRun()
body = await implement(job(), run, services)
body = await implement(job(), harness.run, harness.services)
assert len(run.created_workflows) == 1
created, created_stage = run.created_workflows[0]
assert len(harness.run.created_workflows) == 1
created, created_stage = harness.run.created_workflows[0]
assert created_stage == "installing development environment"
assert created.status is WorkflowStatus.ACTIVE
assert created.branch == f"agent/issue-7-{created.id[:8]}"
assert created.workspace_path == tmp_path / "workspaces" / created.id / "repo"
assert created.base_sha == "base-sha"
assert development.workspaces == [created.workspace_path]
assert run.linked_sessions == ["implementation-session"]
assert opencode.created_sessions == [
assert harness.development.workspaces == [created.workspace_path]
assert harness.run.linked_sessions == ["implementation-session"]
assert harness.opencode.created_sessions == [
(created.workspace_path, "implementation"),
(created.workspace_path, "implementation-review"),
]
assert [call["result_type"] for call in opencode.resume_calls] == [
assert [call["result_type"] for call in harness.opencode.resume_calls] == [
AgentResult,
ReviewReport,
]
initial_prompt = prompts.calls[0]
initial_prompt = harness.prompts.calls[0]
assert initial_prompt[0] == "implement_initial"
assert initial_prompt[1]["artifact"] == "(no canonical plan)"
assert initial_prompt[1]["request"] == "Keep the change focused."
assert "Please cover empty input." in initial_prompt[1]["context"]
assert "Job queued" not in initial_prompt[1]["context"]
assert initial_prompt[1]["context"].startswith("Repository: org/repo\n")
assert git.calls == [
assert harness.git.calls == [
("clone", "org", "repo", "main", created.workspace_path),
("create_branch", created.workspace_path, created.branch),
("has_changes", created.workspace_path),
@@ -338,16 +223,29 @@ async def test_initial_implementation_completes_review_commit_push_and_pr(
async def test_initial_implementation_rejects_clean_worktree_before_commit_or_pr(
tmp_path: Path,
) -> None:
services, repository, gitea, git, _, _, _ = make_services(tmp_path, changed=False)
run = RecordingRun()
repository = FakeRepository()
gitea = FakeGitea()
harness = make_workflow_harness(
settings=implementation_settings(tmp_path),
repository=repository,
gitea=gitea,
changed=False,
responses=[
AgentResult(
summary_markdown="# Implement widget\n\nHandled empty input.",
tests=["pytest: passed"],
),
ReviewReport(summary="Ready", findings=[]),
],
)
with pytest.raises(
JobRejected,
match="OpenCode completed without producing any file changes",
):
await implement(job(), run, services)
await implement(job(), harness.run, harness.services)
assert [call[0] for call in git.calls] == [
assert [call[0] for call in harness.git.calls] == [
"clone",
"create_branch",
"has_changes",
@@ -355,7 +253,7 @@ async def test_initial_implementation_rejects_clean_worktree_before_commit_or_pr
assert gitea.created_pulls == []
assert repository.saved_workflows
assert all(item.status is WorkflowStatus.ACTIVE for item in repository.saved_workflows)
assert "creating pull request" not in run.stages
assert "creating pull request" not in harness.run.stages
@pytest.mark.parametrize(
@@ -376,17 +274,19 @@ async def test_initial_implementation_rejects_duplicate_agent_pr_before_clone(
existing_pull: PullRequestInfo,
message: str,
) -> None:
services, _, gitea, git, development, _, opencode = make_services(
tmp_path,
implementations=[workflow(pr_number=8)],
existing_pulls={8: existing_pull},
repository = FakeRepository(implementations=[workflow(pr_number=8)])
gitea = FakeGitea(existing_pulls={8: existing_pull})
harness = make_workflow_harness(
settings=implementation_settings(tmp_path),
repository=repository,
gitea=gitea,
)
with pytest.raises(JobRejected) as error:
await implement(job(), RecordingRun(), services)
await implement(job(), harness.run, harness.services)
assert str(error.value) == message
assert git.calls == []
assert development.workspaces == []
assert opencode.resume_calls == []
assert harness.git.calls == []
assert harness.development.workspaces == []
assert harness.opencode.resume_calls == []
assert gitea.default_branch_calls == []