from __future__ import annotations import subprocess from pathlib import Path import pytest from agent_harness.runner import RunResult, run_task from agent_harness.taskspec import TaskSpec, TaskSpecError def _make_repo(tmp_path: Path) -> Path: repo = tmp_path / "sandbox" repo.mkdir() subprocess.run(["git", "init", "-q"], cwd=repo, check=True) (repo / "README.md").write_text("sandbox\n") subprocess.run(["git", "add", "."], cwd=repo, check=True) subprocess.run( ["git", "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "init"], cwd=repo, check=True, ) return repo class CommittingAdapter: """Fake adapter that simulates a session which commits.""" def __init__(self, repo: Path): self.repo = repo self.prompts: list[str] = [] def execute_prompt(self, prompt, config): self.prompts.append(prompt) (self.repo / "HELLO.md").write_text("hello\n") subprocess.run(["git", "add", "."], cwd=self.repo, check=True) subprocess.run( ["git", "-c", "user.email=t@t", "-c", "user.name=t", "commit", "-qm", "task done"], cwd=self.repo, check=True, ) from llm_connect.models import LLMResponse return LLMResponse(content="done", model="fake", usage={}, finish_reason="stop") class IdleAdapter: def execute_prompt(self, prompt, config): from llm_connect.models import LLMResponse return LLMResponse(content="nothing to do", model="fake", usage={}, finish_reason="stop") def _spec(repo: Path) -> TaskSpec: return TaskSpec(title="write hello", description="create HELLO.md", target_repo=repo) def test_run_task_success_when_session_commits(tmp_path) -> None: repo = _make_repo(tmp_path) adapter = CommittingAdapter(repo) result = run_task(_spec(repo), adapter=adapter, report_to_hub=False) assert isinstance(result, RunResult) assert result.ok is True assert result.committed is True assert result.head_before != result.head_after assert "write hello" in adapter.prompts[0] assert "Never push" in adapter.prompts[0] def test_run_task_fails_without_commit(tmp_path) -> None: repo = _make_repo(tmp_path) result = run_task(_spec(repo), adapter=IdleAdapter(), report_to_hub=False) assert result.ok is False assert result.committed is False assert result.reason == "session completed without committing" def test_taskspec_rejects_non_repo(tmp_path) -> None: spec_file = tmp_path / "task.json" spec_file.write_text( '{"title": "x", "description": "y", "target_repo": "%s"}' % tmp_path ) with pytest.raises(TaskSpecError, match="not a git repository"): TaskSpec.from_file(spec_file)