Skip to content

Commit 34f6b6b

Browse files
committed
test(app-server): use deterministic command exec integration
1 parent 822a05c commit 34f6b6b

1 file changed

Lines changed: 36 additions & 56 deletions

File tree

tests/test_integration_real_binary.py

Lines changed: 36 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import asyncio
4+
import base64
45
import os
56
import subprocess
67
from pathlib import Path
@@ -9,14 +10,9 @@
910

1011
from codex import Codex, CodexOptions, ThreadStartOptions, TurnOptions
1112
from codex.app_server import AsyncAppServerClient
12-
from codex.app_server.options import (
13-
AppServerProcessOptions,
14-
AppServerThreadStartOptions,
15-
AppServerTurnOptions,
16-
)
13+
from codex.app_server.options import AppServerProcessOptions
1714
from codex.protocol import types as protocol
1815

19-
_STREAM_TIMEOUT_SECONDS = 90.0
2016
_COMMAND_UNDER_TEST = "git diff --no-color HEAD~1...HEAD"
2117

2218

@@ -120,8 +116,8 @@ def test_run_with_real_codex_binary_and_api_key(tmp_path: Path) -> None:
120116
assert result == {"answer": "OK"}
121117

122118

123-
def test_streamed_git_command_events_with_real_codex_binary(tmp_path: Path) -> None:
124-
binary, api_key, child_env = _integration_binary_and_env(tmp_path)
119+
def test_streamed_command_exec_events_with_real_codex_binary(tmp_path: Path) -> None:
120+
binary, _api_key, child_env = _integration_binary_and_env(tmp_path)
125121
repo = _create_git_repo(tmp_path / "repo")
126122

127123
async def scenario() -> None:
@@ -132,65 +128,49 @@ async def scenario() -> None:
132128
)
133129
)
134130
try:
135-
await client.account.login_api_key(api_key=api_key)
136-
thread = await client.start_thread(
137-
AppServerThreadStartOptions(
138-
model="gpt-5-mini",
131+
process_id = "codex-python-integration-git-diff"
132+
subscription = client.events.subscribe({"command/exec/outputDelta"})
133+
command_task = asyncio.create_task(
134+
client.command.execute(
135+
command=["/bin/sh", "-lc", _COMMAND_UNDER_TEST],
139136
cwd=str(repo),
140-
approval_policy=protocol.AskForApproval("never"),
141-
sandbox=protocol.SandboxMode("workspace-write"),
142-
config={
143-
"skip_git_repo_check": True,
144-
"web_search": "disabled",
145-
},
137+
process_id=process_id,
138+
stream_stdout_stderr=True,
139+
timeout_ms=5000,
146140
)
147141
)
148-
stream = await thread.run(
149-
(
150-
f'Use the shell tool to run `/bin/sh -lc "{_COMMAND_UNDER_TEST}"` exactly '
151-
"once. After the command completes, reply with the single word OK."
152-
),
153-
AppServerTurnOptions(effort=protocol.ReasoningEffort("low")),
154-
)
155142

156-
saw_command_start = False
157-
saw_command_completion = False
143+
stdout_chunks: list[str] = []
158144
observed_events: list[str] = []
159145

160146
while True:
161147
try:
162-
event = await asyncio.wait_for(
163-
stream.__anext__(), timeout=_STREAM_TIMEOUT_SECONDS
164-
)
165-
except StopAsyncIteration:
166-
break
148+
event = await asyncio.wait_for(subscription.next(), timeout=0.2)
149+
except TimeoutError:
150+
if command_task.done():
151+
break
152+
continue
167153

168154
method = getattr(getattr(event, "method", None), "root", type(event).__name__)
169-
if isinstance(
170-
event,
171-
protocol.ItemStartedNotificationModel | protocol.ItemCompletedNotificationModel,
172-
):
173-
item = event.params.item.root
174-
observed_events.append(
175-
f"{method}: {type(item).__name__}: "
176-
f"command={getattr(item, 'command', None)!r}"
177-
)
178-
if not isinstance(item, protocol.CommandExecutionThreadItem):
179-
continue
180-
if _COMMAND_UNDER_TEST not in item.command:
181-
continue
182-
if isinstance(event, protocol.ItemStartedNotificationModel):
183-
saw_command_start = True
184-
if isinstance(event, protocol.ItemCompletedNotificationModel):
185-
saw_command_completion = True
186-
else:
187-
observed_events.append(f"{method}: {type(event).__name__}")
188-
155+
observed_events.append(f"{method}: {type(event).__name__}")
156+
if not isinstance(event, protocol.CommandExecOutputDeltaNotificationModel):
157+
continue
158+
if event.params.processId != process_id:
159+
continue
160+
if event.params.stream.root != "stdout":
161+
continue
162+
stdout_chunks.append(base64.b64decode(event.params.deltaBase64).decode())
163+
164+
result = await command_task
165+
await subscription.close()
189166
event_summary = "\n".join(observed_events)
190-
assert saw_command_start is True, event_summary
191-
assert saw_command_completion is True, event_summary
192-
assert stream.final_turn is not None
193-
assert stream.final_turn.status.root == "completed"
167+
streamed_stdout = "".join(stdout_chunks)
168+
assert result.exit_code == 0
169+
assert result.stderr == ""
170+
assert result.stdout == ""
171+
assert "-one" in streamed_stdout, event_summary
172+
assert "+two" in streamed_stdout, event_summary
173+
assert "+three" in streamed_stdout, event_summary
194174
finally:
195175
await client.close()
196176

0 commit comments

Comments
 (0)