Skip to content

Commit a4708eb

Browse files
authored
test(providers): fix kiro/q integration tests (mock_db signature + event-loop starvation) (#333)
* test(providers): fix mock_db._create signature to match db_create_terminal The integration-test mock_db fixture's _create() side-effect lagged behind db_create_terminal(), which now takes allowed_tools (positional) and caller_id (keyword). create_terminal() therefore raised "TypeError: _create() got an unexpected keyword argument 'caller_id'" at fixture setup, erroring all 7 kiro/q CLI integration tests. Add allowed_tools and caller_id (both default None) to the mock signature and record them in the stored metadata. test_real_kiro_initialization_and_idle now passes; the remaining kiro status-detection timing assertions are unrelated. * test(kiro): await asyncio.sleep in async integration tests so StatusMonitor runs The async kiro integration tests polled status with blocking time.sleep(), which starves the asyncio StatusMonitor task on the same event loop: the FIFO buffer never drains, status stays latched at IDLE, and the PROCESSING/COMPLETED assertions fail (while the permission-prompt waits never see output and skip). Make _wait_for_status / _wait_for_permission async and replace the in-test blocking sleeps with await asyncio.sleep() so the monitor coroutine runs. Verified against real kiro-cli 2.10.0: the file now reports 3 passed, 4 skipped (was 2 failed plus skips masking the same starvation). Root cause confirmed via a probe — blocking sleep reproduces IDLE-forever with an empty buffer; awaiting yields IDLE -> PROCESSING -> COMPLETED.
1 parent 462fa2f commit a4708eb

2 files changed

Lines changed: 31 additions & 15 deletions

File tree

test/providers/conftest.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,23 @@ def mock_db():
5959
"""
6060
terminals = {}
6161

62-
def _create(terminal_id, session_name, window_name, provider, agent_profile):
62+
def _create(
63+
terminal_id,
64+
session_name,
65+
window_name,
66+
provider,
67+
agent_profile,
68+
allowed_tools=None,
69+
caller_id=None,
70+
):
6371
terminals[terminal_id] = {
6472
"id": terminal_id,
6573
"tmux_session": session_name,
6674
"tmux_window": window_name,
6775
"provider": provider,
6876
"agent_profile": agent_profile,
77+
"allowed_tools": allowed_tools,
78+
"caller_id": caller_id,
6979
"last_active": datetime.now(),
7080
}
7181
return terminals[terminal_id]

test/providers/test_kiro_cli_integration.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
CAO_TEST_WATCH=1 pytest test/providers/test_kiro_cli_integration.py -v -o "addopts="
1212
"""
1313

14+
import asyncio
1415
import json
1516
import os
1617
import re
@@ -165,23 +166,28 @@ def _clean(terminal_id):
165166
return ANSI_RE.sub("", _get_output(terminal_id))
166167

167168

168-
def _wait_for_permission(terminal_id, timeout=15):
169+
async def _wait_for_permission(terminal_id, timeout=15):
169170
elapsed = 0
170171
while elapsed < timeout:
171172
if PERM_RE.search(_clean(terminal_id)):
172173
return True
173-
time.sleep(1)
174+
# await (not time.sleep) so the asyncio StatusMonitor task on this same
175+
# event loop keeps draining the FIFO and updating the buffer. A blocking
176+
# sleep starves the monitor, leaving the buffer empty and status latched.
177+
await asyncio.sleep(1)
174178
elapsed += 1
175179
return False
176180

177181

178-
def _wait_for_status(terminal_id, target, timeout=30):
182+
async def _wait_for_status(terminal_id, target, timeout=30):
179183
elapsed = 0
180184
while elapsed < timeout:
181185
s = status_monitor.get_status(terminal_id)
182186
if s == target:
183187
return s
184-
time.sleep(1)
188+
# await (not time.sleep) — see _wait_for_permission: yielding lets the
189+
# StatusMonitor coroutine run so get_status() reflects fresh output.
190+
await asyncio.sleep(1)
185191
elapsed += 1
186192
return status_monitor.get_status(terminal_id)
187193

@@ -214,7 +220,7 @@ async def test_real_kiro_simple_query_and_completed(self, terminal):
214220
_log("QUERY", "Sending: Say 'Hello, integration test!'")
215221
_send(terminal.id, "Say 'Hello, integration test!'")
216222
_log("QUERY", "Waiting for COMPLETED...")
217-
status = _wait_for_status(terminal.id, TerminalStatus.COMPLETED)
223+
status = await _wait_for_status(terminal.id, TerminalStatus.COMPLETED)
218224
_log("QUERY", f"Status: {status}")
219225
assert status == TerminalStatus.COMPLETED
220226

@@ -235,7 +241,7 @@ async def test_p1_p2_active_permission_prompt(self, terminal):
235241
_log("P1", "Sending: Run this command: echo 'test'")
236242
_send(terminal.id, "Run this command: echo 'test'")
237243
_log("P1", "Waiting for permission prompt...")
238-
if not _wait_for_permission(terminal.id, timeout=30):
244+
if not await _wait_for_permission(terminal.id, timeout=30):
239245
pytest.skip("Permission prompt not triggered (tool may be pre-approved)")
240246
status = status_monitor.get_status(terminal.id)
241247
_log("P1", f"Status: {status}")
@@ -246,13 +252,13 @@ async def test_p3_p4_injection_during_active_prompt(self, terminal):
246252
"""P3/P4: Invalid answer submitted during active prompt."""
247253
_log("P3", "Sending: Run: whoami")
248254
_send(terminal.id, "Run: whoami")
249-
if not _wait_for_permission(terminal.id):
255+
if not await _wait_for_permission(terminal.id):
250256
pytest.skip("Permission prompt not triggered")
251257
status = status_monitor.get_status(terminal.id)
252258
_log("P3", f"Status before injection: {status}")
253259
assert status == TerminalStatus.WAITING_USER_ANSWER
254260
_send(terminal.id, "[Test injection]")
255-
time.sleep(1)
261+
await asyncio.sleep(1)
256262
status = status_monitor.get_status(terminal.id)
257263
_log("P3", f"Status after injection: {status}")
258264
assert status == TerminalStatus.WAITING_USER_ANSWER
@@ -261,10 +267,10 @@ async def test_p3_p4_injection_during_active_prompt(self, terminal):
261267
async def test_p5_p6_stale_permission_after_answer(self, terminal):
262268
"""P5/P6: Answered prompt — must NOT be WAITING_USER_ANSWER."""
263269
_send(terminal.id, "Run this bash command: echo 'stale test'")
264-
if not _wait_for_permission(terminal.id):
270+
if not await _wait_for_permission(terminal.id):
265271
pytest.skip("Permission prompt not triggered")
266272
_send(terminal.id, "y")
267-
status = _wait_for_status(terminal.id, TerminalStatus.COMPLETED)
273+
status = await _wait_for_status(terminal.id, TerminalStatus.COMPLETED)
268274
_log("P5", f"Status after answer: {status}")
269275
assert status != TerminalStatus.WAITING_USER_ANSWER
270276
assert PERM_RE.search(_clean(terminal.id))
@@ -273,10 +279,10 @@ async def test_p5_p6_stale_permission_after_answer(self, terminal):
273279
async def test_p7_multiple_permission_prompts(self, terminal):
274280
"""P7: Second unanswered prompt after first answered."""
275281
_send(terminal.id, "Run: echo 'first'")
276-
if not _wait_for_permission(terminal.id):
282+
if not await _wait_for_permission(terminal.id):
277283
pytest.skip("Permission prompt not triggered")
278284
_send(terminal.id, "y")
279-
status = _wait_for_status(terminal.id, TerminalStatus.COMPLETED, timeout=30)
285+
status = await _wait_for_status(terminal.id, TerminalStatus.COMPLETED, timeout=30)
280286
assert status == TerminalStatus.COMPLETED, f"First command didn't complete ({status})"
281287
before_count = len(PERM_RE.findall(_clean(terminal.id)))
282288
_send(terminal.id, "Run: echo 'second'")
@@ -287,7 +293,7 @@ async def test_p7_multiple_permission_prompts(self, terminal):
287293
if after_count > before_count:
288294
found_new = True
289295
break
290-
time.sleep(1)
296+
await asyncio.sleep(1)
291297
elapsed += 1
292298
if not found_new:
293299
pytest.skip("Second permission prompt not triggered (tool may be session-approved)")
@@ -301,7 +307,7 @@ async def test_n4_n5_processing_state(self, terminal):
301307
elapsed = 0
302308
status = status_monitor.get_status(terminal.id)
303309
while status == TerminalStatus.IDLE and elapsed < 10:
304-
time.sleep(0.5)
310+
await asyncio.sleep(0.5)
305311
elapsed += 0.5
306312
status = status_monitor.get_status(terminal.id)
307313
_log("N4", f"Status after {elapsed}s: {status}")

0 commit comments

Comments
 (0)