from pathlib import Path import pytest from agentci.engine.events import ( JobProgress, RuntimeSessionLinked, WorkflowCreated, WorkflowLinked, ) from agentci.engine.model import Workflow, WorkflowKind from agentci.engine.run import JobRun class RecordingRepository: def __init__(self, *, fail_event_ids: set[str] | None = None) -> None: self.events: list[tuple[str, object]] = [] self.fail_event_ids = fail_event_ids or set() async def apply(self, event_id: str, event: object) -> None: self.events.append((event_id, event)) if event_id in self.fail_event_ids: raise RuntimeError("write failed") async def test_job_run_emits_each_report_payload_with_monotonic_ids() -> None: repository = RecordingRepository() run = JobRun(repository, "job-1", 10) # type: ignore[arg-type] workflow = Workflow( id="workflow-1", kind=WorkflowKind.PLAN, repo_owner="alice", repo_name="repo", issue_number=3, workspace_path=Path("/work/repo"), base_sha="abc", ) await run.stage("cloning") await run.create_workflow(workflow, "planning") await run.link_workflow("workflow-2", "discussing") await run.link_session("session-1") assert repository.events == [ ("task:10:report:1", JobProgress(job_id="job-1", stage="cloning")), ( "task:10:report:2", WorkflowCreated(job_id="job-1", workflow=workflow, stage="planning"), ), ( "task:10:report:3", WorkflowLinked(job_id="job-1", workflow_id="workflow-2", stage="discussing"), ), ( "task:10:report:4", RuntimeSessionLinked(job_id="job-1", session_id="session-1"), ), ] async def test_failed_report_consumes_only_its_run_sequence_number() -> None: repository = RecordingRepository(fail_event_ids={"task:10:report:1"}) first = JobRun(repository, "job-1", 10) # type: ignore[arg-type] second = JobRun(repository, "job-2", 20) # type: ignore[arg-type] with pytest.raises(RuntimeError, match="write failed"): await first.stage("cloning") await second.stage("fixing") await first.link_session("session-1") assert [event_id for event_id, _ in repository.events] == [ "task:10:report:1", "task:20:report:1", "task:10:report:2", ]