Skip to content

Commit 70b367c

Browse files
test(code_execution): add tests for post-processor API rejection recovery
1 parent 817b264 commit 70b367c

1 file changed

Lines changed: 71 additions & 0 deletions

File tree

tests/unittests/flows/llm_flows/test_code_execution.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,3 +279,74 @@ async def test_pre_processor_does_not_inject_instruction_for_builtin_executor():
279279

280280
system_instruction = str(llm_request.config.system_instruction or '')
281281
assert _NON_BUILTIN_EXECUTOR_INSTRUCTION not in system_instruction
282+
283+
284+
# ---------------------------------------------------------------------------
285+
# Post-processor: API rejection recovery path
286+
# ---------------------------------------------------------------------------
287+
288+
289+
@pytest.mark.asyncio
290+
@patch('google.adk.flows.llm_flows._code_execution.logger')
291+
async def test_post_processor_recovers_from_unexpected_tool_call(mock_logger):
292+
mock_executor = MagicMock(spec=BaseCodeExecutor)
293+
mock_executor.code_block_delimiters = [('```tool_code\n', '\n```')]
294+
mock_executor.error_retry_attempts = 2
295+
mock_executor.stateful = False
296+
mock_executor.execute_code.return_value = CodeExecutionResult(stdout='42')
297+
298+
agent = Agent(name='test_agent', code_executor=mock_executor)
299+
invocation_context = await testing_utils.create_invocation_context(
300+
agent=agent, user_content='run some code'
301+
)
302+
invocation_context.artifact_service = MagicMock()
303+
invocation_context.artifact_service.save_artifact = AsyncMock(
304+
return_value='v1'
305+
)
306+
307+
llm_response = LlmResponse(
308+
content=None,
309+
error_code='UNEXPECTED_TOOL_CALL',
310+
error_message='Unexpected tool call: print(6*7)',
311+
)
312+
313+
events = [
314+
event
315+
async for event in response_processor.run_async(
316+
invocation_context, llm_response
317+
)
318+
]
319+
320+
mock_executor.execute_code.assert_called_once()
321+
call_input = mock_executor.execute_code.call_args[0][1]
322+
assert call_input.code == 'print(6*7)'
323+
assert len(events) == 2
324+
mock_logger.info.assert_called_once()
325+
326+
327+
@pytest.mark.asyncio
328+
async def test_post_processor_skips_recovery_for_builtin_executor():
329+
code_executor = BuiltInCodeExecutor()
330+
agent = Agent(name='test_agent', code_executor=code_executor)
331+
invocation_context = await testing_utils.create_invocation_context(
332+
agent=agent, user_content='run some code'
333+
)
334+
invocation_context.artifact_service = MagicMock()
335+
invocation_context.artifact_service.save_artifact = AsyncMock()
336+
337+
llm_response = LlmResponse(
338+
content=None,
339+
error_code='UNEXPECTED_TOOL_CALL',
340+
error_message='Unexpected tool call: print(1)',
341+
)
342+
343+
events = [
344+
event
345+
async for event in response_processor.run_async(
346+
invocation_context, llm_response
347+
)
348+
]
349+
350+
# BuiltInCodeExecutor path bails out early — no events, no artifact saves.
351+
assert events == []
352+
invocation_context.artifact_service.save_artifact.assert_not_called()

0 commit comments

Comments
 (0)