71 lines
2.3 KiB
Python
71 lines
2.3 KiB
Python
import asyncio
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from clink.agents.base import CLIAgentError
|
|
from clink.agents.codex import CodexAgent
|
|
from clink.models import ResolvedCLIClient, ResolvedCLIRole
|
|
|
|
|
|
class DummyProcess:
|
|
def __init__(self, *, stdout: bytes = b"", stderr: bytes = b"", returncode: int = 0):
|
|
self._stdout = stdout
|
|
self._stderr = stderr
|
|
self.returncode = returncode
|
|
|
|
async def communicate(self, _input):
|
|
return self._stdout, self._stderr
|
|
|
|
|
|
@pytest.fixture()
|
|
def codex_agent():
|
|
prompt_path = Path("systemprompts/clink/codex_default.txt").resolve()
|
|
role = ResolvedCLIRole(name="default", prompt_path=prompt_path, role_args=[])
|
|
client = ResolvedCLIClient(
|
|
name="codex",
|
|
executable=["codex"],
|
|
internal_args=["exec"],
|
|
config_args=["--json", "--dangerously-bypass-approvals-and-sandbox"],
|
|
env={},
|
|
timeout_seconds=30,
|
|
parser="codex_jsonl",
|
|
roles={"default": role},
|
|
output_to_file=None,
|
|
working_dir=None,
|
|
)
|
|
return CodexAgent(client), role
|
|
|
|
|
|
async def _run_agent_with_process(monkeypatch, agent, role, process):
|
|
async def fake_create_subprocess_exec(*_args, **_kwargs):
|
|
return process
|
|
|
|
monkeypatch.setattr(asyncio, "create_subprocess_exec", fake_create_subprocess_exec)
|
|
return await agent.run(role=role, prompt="do something", files=[], images=[])
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_codex_agent_recovers_jsonl(monkeypatch, codex_agent):
|
|
agent, role = codex_agent
|
|
stdout = b"""
|
|
{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"Hello from Codex"}}
|
|
{"type":"turn.completed","usage":{"input_tokens":10,"output_tokens":5}}
|
|
"""
|
|
process = DummyProcess(stdout=stdout, returncode=124)
|
|
result = await _run_agent_with_process(monkeypatch, agent, role, process)
|
|
|
|
assert result.returncode == 124
|
|
assert "Hello from Codex" in result.parsed.content
|
|
assert result.parsed.metadata["usage"]["output_tokens"] == 5
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_codex_agent_propagates_invalid_json(monkeypatch, codex_agent):
|
|
agent, role = codex_agent
|
|
stdout = b"not json"
|
|
process = DummyProcess(stdout=stdout, returncode=1)
|
|
|
|
with pytest.raises(CLIAgentError):
|
|
await _run_agent_with_process(monkeypatch, agent, role, process)
|