*** Settings *** Documentation Integration tests for container-aware tool execution (#515) Resource ${CURDIR}/common.resource Suite Setup Setup Test Environment Suite Teardown Cleanup Test Environment *** Variables *** ${TIMEOUT} 60s *** Test Cases *** PathMapper Maps Host To Container [Documentation] Verify PathMapper translates host paths to container paths ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.path_mapper import PathMapper ... m = PathMapper('/tmp/sandbox', '/workspace') ... result = m.host_to_container('/tmp/sandbox/src/f.py') ... assert result == '/workspace/src/f.py', f"got {result}" ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 PathMapper host_to_container failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK PathMapper Maps Container To Host [Documentation] Verify PathMapper translates container paths to host paths ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.path_mapper import PathMapper ... m = PathMapper('/tmp/sandbox', '/workspace') ... result = m.container_to_host('/workspace/src/f.py') ... assert result == '/tmp/sandbox/src/f.py', f"got {result}" ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 PathMapper container_to_host failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK PathMapper Leaves External Paths Unmapped [Documentation] Paths outside root are returned unchanged ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.path_mapper import PathMapper ... m = PathMapper('/tmp/sandbox', '/workspace') ... result = m.host_to_container('/usr/lib/x') ... assert result == '/usr/lib/x', f"got {result}" ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 PathMapper external path mapping failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ContainerConfig Has Defaults [Documentation] ContainerConfig defaults to /workspace and 120s timeout ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.container_executor import ContainerConfig ... c = ContainerConfig() ... assert c.workspace_folder == '/workspace', f"got {c.workspace_folder}" ... assert c.timeout_seconds == 120, f"got {c.timeout_seconds}" ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 ContainerConfig defaults test failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ContainerMetadata Is Frozen [Documentation] ContainerMetadata is a frozen Pydantic model ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.container_executor import ContainerMetadata ... m = ContainerMetadata(container_id='x', image='y') ... assert m.container_id == 'x', f"got {m.container_id}" ... assert m.image == 'y', f"got {m.image}" ... assert m.timed_out is False ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 ContainerMetadata test failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ContainerToolExecutor Instantiation [Documentation] ContainerToolExecutor can be created with ContainerConfig ${script}= Catenate SEPARATOR=\n ... import structlog, sys; structlog.configure(logger_factory=structlog.PrintLoggerFactory(file=sys.stderr)) ... from cleveragents.tool.container_executor import ContainerConfig, ContainerToolExecutor ... c = ContainerConfig(container_id='t') ... e = ContainerToolExecutor(c) ... assert e.config.container_id == 't', f"got {e.config.container_id}" ... assert e.path_mapper is not None ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 Executor instantiation failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ContainerExecutionError Carries Details [Documentation] ContainerExecutionError stores exit_code and stderr ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.container_executor import ContainerExecutionError ... e = ContainerExecutionError('fail', exit_code=2, stderr='bad') ... assert e.exit_code == 2, f"got {e.exit_code}" ... assert e.stderr == 'bad', f"got {e.stderr}" ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 ContainerExecutionError test failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ContainerTimeoutError Carries Timeout [Documentation] ContainerTimeoutError records timeout_seconds ${script}= Catenate SEPARATOR=\n ... from cleveragents.tool.container_executor import ContainerTimeoutError ... e = ContainerTimeoutError(30) ... assert e.timeout_seconds == 30, f"got {e.timeout_seconds}" ... assert e.timed_out is True ... assert '30' in str(e) ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 ContainerTimeoutError test failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ToolInvocation Has ContainerMetadata Field [Documentation] ToolInvocation accepts container_metadata dict ${script}= Catenate SEPARATOR=\n ... from cleveragents.domain.models.core.change import ToolInvocation ... t = ToolInvocation( ... plan_id='01TESTPLANID000000000000000', ... tool_name='ns/t', ... container_metadata={'container_id': 'x'}, ... ) ... assert t.container_metadata['container_id'] == 'x' ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 ToolInvocation container_metadata test failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK ToolRunner Container Routing Without Executor Returns Error [Documentation] ToolRunner without ContainerToolExecutor returns error for container env [Tags] tdd_issue tdd_issue_4243 ${script}= Catenate SEPARATOR=\n ... from unittest.mock import MagicMock ... from cleveragents.tool.runner import ToolRunner ... from cleveragents.tool.registry import ToolRegistry ... from cleveragents.tool.runtime import ToolSpec ... from cleveragents.domain.models.core.plan import ExecutionEnvironment ... from cleveragents.application.services.execution_environment_resolver import ExecutionEnvironmentResolver ... r = MagicMock(spec=ToolRegistry) ... s = ToolSpec(name='t', description='d', input_schema={}, handler=lambda x: x) ... r.get.return_value = s ... ev = MagicMock(spec=ExecutionEnvironmentResolver) ... # ToolRunner uses resolve_with_precedence() (not resolve_and_validate()) ... ev.resolve_with_precedence.return_value = ExecutionEnvironment.CONTAINER ... ev.has_devcontainer.return_value = False ... ev.validate_container_available.return_value = None ... runner = ToolRunner(registry=r, execution_environment_resolver=ev) ... res = runner.execute('t', {}) ... assert not res.success, f"Expected failure but got success" ... assert 'ContainerToolExecutor' in res.error, f"Expected ContainerToolExecutor in error: {res.error}" ... print("OK") ${result}= Run Process ${PYTHON} -c ${script} ... timeout=${TIMEOUT} on_timeout=kill env:PYTHONPATH=${CURDIR}/../src Should Be Equal As Integers ${result.rc} 0 ToolRunner container routing test failed: ${result.stderr} Should Be Equal As Strings ${result.stdout.strip()} OK