afe89964df
CI / push-validation (pull_request) Successful in 26s
CI / helm (pull_request) Successful in 50s
CI / lint (pull_request) Successful in 52s
CI / build (pull_request) Successful in 51s
CI / quality (pull_request) Successful in 52s
CI / security (pull_request) Successful in 1m15s
CI / typecheck (pull_request) Successful in 1m35s
CI / unit_tests (pull_request) Successful in 5m25s
CI / docker (pull_request) Successful in 1m34s
CI / integration_tests (pull_request) Failing after 10m26s
CI / coverage (pull_request) Failing after 18m51s
CI / status-check (pull_request) Has been cancelled
The tdd_expected_fail listener (robot/tdd_expected_fail_listener.py and features/environment.py) inverts test results: a test tagged @tdd_expected_fail that PASSES is forced to FAIL with the message "Bug appears to be fixed. Remove the tdd_expected_fail tag…". After rebasing onto master, 22 scenarios across 11 files were triggering that forced-failure path because the underlying bugs (#4199, #4201, #4202, #4203, #4205, #4206, #4243, #4252, #4301, #4303, #4304) have been fixed on master but the @tdd_expected_fail tags were never removed. This commit removes the stale tag (keeping @tdd_issue and @tdd_issue_<N> for traceability, per the master-side pattern in robot/tdd_skill_add_regression.robot:9 "tag removed after bug fix"). Files touched (lines: where the stale tag was): features/plan_cli_spec_alignment.feature (121, 128, 136) robot/a2a_facade.robot (40) robot/actor_cli_show.robot (13, 30) robot/actor_configuration.robot (8) robot/actor_context_export_import.robot (20, 71, 92) robot/cli_extensions.robot (61) robot/cli_formats.robot (14, 23, 30, 48, 57, 85) robot/cli_lifecycle_e2e.robot (77, 87) robot/config_project_scope.robot (37) robot/config_resolution.robot (13) robot/container_tool_exec.robot (137) Verified locally: targeted unit_tests run on features/plan_cli_spec_alignment.feature now passes 20/20 (was 17/20 with 3 scenarios forced-failed by the inversion). ISSUES CLOSED: #3677
162 lines
9.0 KiB
Plaintext
162 lines
9.0 KiB
Plaintext
*** 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
|