8ea00f5185
CI / unit_tests (push) Has been cancelled
CI / benchmark-publish (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / security (push) Has been cancelled
CI / quality (push) Has been cancelled
CI / integration_tests (push) Has been cancelled
CI / e2e_tests (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / benchmark-regression (push) Has been cancelled
CI / build (push) Has been cancelled
CI / push-validation (push) Has been cancelled
CI / status-check (push) Has been cancelled
CI / docker (push) Has been cancelled
CI / helm (push) Has been cancelled
Co-authored-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me> Co-committed-by: Jeffrey Phillips Freeman <the@jeffreyfreeman.me>
528 lines
26 KiB
Gherkin
528 lines
26 KiB
Gherkin
@container-tool-exec
|
|
Feature: Container-aware tool execution and I/O forwarding
|
|
As a CleverAgents developer
|
|
I want tools to execute inside containers when the execution environment is CONTAINER
|
|
So that file-based tools operate on the container filesystem transparently
|
|
|
|
Background:
|
|
Given I have the container tool execution module imported
|
|
|
|
# -- PathMapper ---------------------------------------------------------
|
|
|
|
Scenario: PathMapper maps host path to container path
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map host path "/tmp/sandbox/src/main.py" to container
|
|
Then the container path should be "/workspace/src/main.py"
|
|
|
|
Scenario: PathMapper maps container path to host path
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map container path "/workspace/src/main.py" to host
|
|
Then the host path should be "/tmp/sandbox/src/main.py"
|
|
|
|
Scenario: PathMapper returns unmapped path when outside root
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map host path "/usr/lib/libc.so" to container
|
|
Then the container path should be "/usr/lib/libc.so"
|
|
|
|
Scenario: PathMapper maps root path exactly
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map host path "/tmp/sandbox" to container
|
|
Then the container path should be "/workspace"
|
|
|
|
Scenario: PathMapper is_host_path returns true for host paths
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
Then "/tmp/sandbox/file.txt" should be a host path
|
|
And "/usr/lib/libc.so" should not be a host path
|
|
|
|
Scenario: PathMapper is_container_path returns true for container paths
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
Then "/workspace/file.txt" should be a container path
|
|
And "/tmp/other/file.txt" should not be a container path
|
|
|
|
# -- ContainerConfig ----------------------------------------------------
|
|
|
|
Scenario: ContainerConfig has sensible defaults
|
|
When I create a default ContainerConfig
|
|
Then the workspace_folder should be "/workspace"
|
|
And the container timeout_seconds should be 120
|
|
|
|
# -- ContainerMetadata --------------------------------------------------
|
|
|
|
Scenario: ContainerMetadata is frozen and holds execution details
|
|
When I create a ContainerMetadata with container_id "abc123" and image "node:20"
|
|
Then the container_metadata container_id should be "abc123"
|
|
And the container_metadata image should be "node:20"
|
|
And the container_metadata timed_out should be false
|
|
|
|
# -- ContainerToolExecutor basic ----------------------------------------
|
|
|
|
Scenario: ContainerToolExecutor can be instantiated with config
|
|
Given I have a ContainerConfig with workspace "/workspace" and container_id "test-ctr"
|
|
When I create a ContainerToolExecutor with that config
|
|
Then the executor config container_id should be "test-ctr"
|
|
And the executor should have a path mapper
|
|
|
|
Scenario: ContainerToolExecutor maps input paths before execution
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map tool inputs with path "/tmp/sandbox/src/file.py"
|
|
Then the mapped input path should be "/workspace/src/file.py"
|
|
|
|
Scenario: ContainerToolExecutor maps output paths after execution
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map tool output with path "/workspace/build/output.txt"
|
|
Then the mapped output path should be "/tmp/sandbox/build/output.txt"
|
|
|
|
Scenario: ContainerToolExecutor handles nested input path mapping
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map tool inputs with nested paths under "/tmp/sandbox"
|
|
Then all nested paths should be mapped to container paths
|
|
|
|
# -- Timeout handling ---------------------------------------------------
|
|
|
|
Scenario: ContainerToolExecutor reports timeout as structured error
|
|
Given I have a ContainerToolExecutor with a mock that times out
|
|
When I execute tool "file_read" with inputs in the container
|
|
Then the container tool result should indicate failure
|
|
And the container tool result error should mention "timed out"
|
|
And the tool result metadata should contain container info with timed_out true
|
|
|
|
# -- Error reporting ----------------------------------------------------
|
|
|
|
Scenario: ContainerToolExecutor reports non-zero exit as structured error
|
|
Given I have a ContainerToolExecutor with a mock that returns exit code 1
|
|
When I execute tool "file_read" with inputs in the container
|
|
Then the container tool result should indicate failure
|
|
And the container tool result error should mention "exit code 1"
|
|
|
|
Scenario: ContainerToolExecutor reports OSError as structured error
|
|
Given I have a ContainerToolExecutor with subprocess raising OSError
|
|
When I execute tool "compile" with inputs in the container
|
|
Then the container tool result should indicate failure
|
|
|
|
# -- Successful execution -----------------------------------------------
|
|
|
|
Scenario: ContainerToolExecutor returns parsed JSON output on success
|
|
Given I have a ContainerToolExecutor with a mock that returns JSON output
|
|
When I execute tool "lint" with inputs in the container
|
|
Then the container tool result should indicate success
|
|
And the tool result output should contain the parsed JSON
|
|
And the tool result metadata should contain container info
|
|
|
|
Scenario: ContainerToolExecutor returns raw output when not JSON
|
|
Given I have a ContainerToolExecutor with a mock that returns plain text
|
|
When I execute tool "echo" with inputs in the container
|
|
Then the container tool result should indicate success
|
|
And the tool result output should contain raw_output
|
|
|
|
# -- Command building ---------------------------------------------------
|
|
|
|
Scenario: _build_exec_command places --container-id flag adjacent to its value
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I build an exec command for tool "test_tool" with timeout 30
|
|
Then the command should have "--container-id" adjacent to its value
|
|
|
|
# -- ToolRunner container routing ---------------------------------------
|
|
|
|
# @tdd_issue @tdd_issue_4243 @tdd_expected_fail @skip
|
|
@skip
|
|
Scenario: ToolRunner delegates to ContainerToolExecutor when env is CONTAINER
|
|
Given I have a ToolRunner with a ContainerToolExecutor mock
|
|
When I execute a tool with container execution environment
|
|
Then the ContainerToolExecutor should have been called
|
|
And the resolver should have been called with the correct arguments
|
|
|
|
# @tdd_issue @tdd_issue_4243 @tdd_expected_fail @skip
|
|
@skip
|
|
Scenario: ToolRunner returns error when container executor is not configured
|
|
Given I have a ToolRunner without a ContainerToolExecutor
|
|
When I execute a tool with container execution environment
|
|
Then the container tool result error should mention "ContainerToolExecutor must be configured"
|
|
|
|
# -- ToolInvocation audit trail -----------------------------------------
|
|
|
|
Scenario: ToolInvocation has container_metadata field
|
|
When I create a ToolInvocation with container_metadata
|
|
Then the ToolInvocation container_metadata should contain "container_id"
|
|
And the ToolInvocation container_metadata should contain "image"
|
|
|
|
Scenario: ToolInvocation container_metadata defaults to None
|
|
When I create a ToolInvocation without container_metadata
|
|
Then the ToolInvocation container_metadata should be None
|
|
|
|
# -- ContainerExecutionError --------------------------------------------
|
|
|
|
Scenario: ContainerExecutionError carries exit_code and stderr
|
|
When I raise a ContainerExecutionError with exit_code 2 and stderr "oops"
|
|
Then the error exit_code should be 2
|
|
And the error stderr should be "oops"
|
|
|
|
Scenario: ContainerTimeoutError carries timeout_seconds
|
|
When I raise a ContainerTimeoutError with timeout 30
|
|
Then the timeout error message should mention "30"
|
|
And the timeout error timed_out should be true
|
|
|
|
# -- Signal exit code preservation ----------------------------------------
|
|
|
|
Scenario: ContainerToolExecutor preserves negative exit codes from signal kills
|
|
Given I have a ContainerToolExecutor with a mock that returns exit code -9
|
|
When I execute tool "compile" with inputs in the container
|
|
Then the container tool result should indicate failure
|
|
And the tool result metadata should have exit_code -9
|
|
|
|
# -- Path mapping with spaces --------------------------------------------
|
|
|
|
Scenario: _looks_like_path accepts paths with spaces
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map tool inputs with path "/tmp/sandbox/My Documents/file.py"
|
|
Then the mapped input path should be "/workspace/My Documents/file.py"
|
|
|
|
# -- Output truncation ---------------------------------------------------
|
|
|
|
Scenario: ContainerToolExecutor truncates output exceeding max bytes
|
|
Given I have a ContainerToolExecutor with a mock that returns oversized output
|
|
When I execute tool "big_output" with inputs in the container
|
|
Then the container tool result stdout should be truncated
|
|
|
|
# -- Path traversal protection -------------------------------------------
|
|
|
|
Scenario: sync_results_to_host rejects path traversal attempts
|
|
Given I have a ContainerToolExecutor with a mock that succeeds on sync using a temp dir
|
|
When I attempt to sync a path that traverses outside the sandbox
|
|
Then a ContainerExecutionError should be raised for path traversal
|
|
|
|
# -- Sync results -------------------------------------------------------
|
|
|
|
Scenario: sync_results_to_host maps container path to host
|
|
Given I have a ContainerToolExecutor with a mock that succeeds on sync using a temp dir
|
|
When I sync container path "/workspace/output.txt" to host
|
|
Then the synced host path should be under the host sandbox root
|
|
|
|
# -- execute_tool input validation (P2-19) --------------------------------
|
|
|
|
Scenario: execute_tool rejects empty tool_name
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I execute tool with empty tool_name
|
|
Then a ValueError should be raised with message "non-empty"
|
|
|
|
Scenario: execute_tool rejects non-dict inputs
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I execute tool with non-dict inputs
|
|
Then a container TypeError should be raised with message "must be a dict"
|
|
|
|
Scenario: execute_tool rejects negative timeout_seconds
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I execute tool with negative timeout
|
|
Then a ValueError should be raised with message "must be > 0"
|
|
|
|
# -- extract_container_metadata helper (P1-9) -----------------------------
|
|
|
|
Scenario: extract_container_metadata returns container dict from ToolResult
|
|
When I create a ToolResult with container metadata in metadata dict
|
|
Then extract_container_metadata should return the container dict
|
|
|
|
# =========================================================================
|
|
# Safe initialization — PathMapper validation guards
|
|
# =========================================================================
|
|
|
|
Scenario: PathMapper rejects null bytes in host_root
|
|
When I try to create a PathMapper with null bytes in host_root
|
|
Then a PathMapper ValueError should be raised mentioning "null"
|
|
|
|
Scenario: PathMapper rejects null bytes in container_root
|
|
When I try to create a PathMapper with null bytes in container_root
|
|
Then a PathMapper ValueError should be raised mentioning "null"
|
|
|
|
Scenario: PathMapper rejects empty host_root
|
|
When I try to create a PathMapper with empty host_root
|
|
Then a PathMapper ValueError should be raised mentioning "empty"
|
|
|
|
Scenario: PathMapper rejects filesystem-root host_root
|
|
When I try to create a PathMapper with host_root "/"
|
|
Then a PathMapper ValueError should be raised mentioning "/"
|
|
|
|
Scenario: PathMapper rejects filesystem-root container_root
|
|
When I try to create a PathMapper with container_root "/"
|
|
Then a PathMapper ValueError should be raised mentioning "/"
|
|
|
|
Scenario: PathMapper rejects overlapping roots
|
|
When I try to create a PathMapper with overlapping roots
|
|
Then a PathMapper ValueError should be raised mentioning "overlap"
|
|
|
|
Scenario: PathMapper is_host_path returns false for null-byte paths
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
Then a path with null bytes should not be a host path
|
|
|
|
Scenario: PathMapper is_container_path returns false for null-byte paths
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
Then a path with null bytes should not be a container path
|
|
|
|
# =========================================================================
|
|
# Safe initialization — ContainerConfig workspace_folder guards
|
|
# =========================================================================
|
|
|
|
Scenario: ContainerConfig rejects relative workspace_folder
|
|
When I try to create a ContainerConfig with workspace_folder "relative/path"
|
|
Then a ContainerConfig ValueError should be raised mentioning "absolute"
|
|
|
|
Scenario: ContainerConfig rejects workspace_folder that normalises to root
|
|
When I try to create a ContainerConfig with workspace_folder "/."
|
|
Then a ContainerConfig ValueError should be raised mentioning "root"
|
|
|
|
Scenario: ContainerConfig rejects workspace_folder with dotdot traversal
|
|
When I try to create a ContainerConfig with workspace_folder "/workspace/../etc"
|
|
Then a ContainerConfig ValueError should be raised mentioning ".."
|
|
|
|
# =========================================================================
|
|
# Safe initialization — ContainerToolExecutor binary resolution
|
|
# =========================================================================
|
|
|
|
Scenario: ContainerToolExecutor without devcontainer binary uses workspace-folder target args
|
|
Given I have a ContainerToolExecutor with no container_id configured
|
|
When I request the target CLI arguments
|
|
Then the target args should use "--workspace-folder"
|
|
|
|
Scenario: ContainerToolExecutor raises when devcontainer binary is missing at execution
|
|
Given I have a ContainerToolExecutor with no devcontainer binary
|
|
When I try to require the devcontainer binary
|
|
Then a ContainerExecutionError should be raised about missing binary
|
|
|
|
# =========================================================================
|
|
# Cleanup — sync_results_to_host error paths
|
|
# =========================================================================
|
|
|
|
Scenario: sync_results_to_host rejects null bytes in container_path
|
|
Given I have a ContainerToolExecutor with a mock that succeeds on sync using a temp dir
|
|
When I try to sync a path containing null bytes
|
|
Then a sync ValueError should be raised mentioning "null"
|
|
|
|
Scenario: sync_results_to_host rejects relative container_path
|
|
Given I have a ContainerToolExecutor with a mock that succeeds on sync using a temp dir
|
|
When I try to sync a relative container path
|
|
Then a sync ValueError should be raised mentioning "absolute"
|
|
|
|
Scenario: sync_results_to_host raises on timeout during file sync
|
|
Given I have a ContainerToolExecutor with a mock that times out on sync
|
|
When I try to sync container path to host with timeout
|
|
Then a ContainerTimeoutError should be raised from sync
|
|
|
|
Scenario: sync_results_to_host raises on non-zero exit during file sync
|
|
Given I have a ContainerToolExecutor with a mock that returns non-zero exit on sync
|
|
When I try to sync container path to host with failure
|
|
Then a ContainerExecutionError should be raised from sync failure
|
|
|
|
Scenario: sync_results_to_host raises on OSError during file write
|
|
Given I have a ContainerToolExecutor with a mock that triggers OSError on write
|
|
When I try to sync container path to host triggering write error
|
|
Then a ContainerExecutionError should be raised from write failure
|
|
|
|
# =========================================================================
|
|
# Safe execution — _looks_like_path heuristic boundary checks
|
|
# =========================================================================
|
|
|
|
Scenario: _looks_like_path rejects strings with newlines
|
|
When I check whether a string with newlines looks like a path
|
|
Then it should not look like a path
|
|
|
|
Scenario: _looks_like_path rejects strings with null bytes
|
|
When I check whether a string with null bytes looks like a path
|
|
Then it should not look like a path
|
|
|
|
Scenario: _looks_like_path rejects protocol-relative URIs
|
|
When I check whether a protocol-relative URI looks like a path
|
|
Then it should not look like a path
|
|
|
|
Scenario: _looks_like_path rejects strings with query parameters
|
|
When I check whether a path with query string looks like a path
|
|
Then it should not look like a path
|
|
|
|
Scenario: _looks_like_path rejects strings with fragment identifiers
|
|
When I check whether a path with fragment identifier looks like a path
|
|
Then it should not look like a path
|
|
|
|
Scenario: _looks_like_path rejects strings with scheme separators
|
|
When I check whether a URL-like string looks like a path
|
|
Then it should not look like a path
|
|
|
|
# =========================================================================
|
|
# Safe execution — recursion depth guards on path mapping
|
|
# =========================================================================
|
|
|
|
Scenario: _map_input_paths stops at max recursion depth
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map deeply nested input paths exceeding the recursion limit
|
|
Then the input mapping should return without error
|
|
|
|
Scenario: _map_output_paths stops at max recursion depth
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map deeply nested output paths exceeding the recursion limit
|
|
Then the output mapping should return without error
|
|
|
|
# =========================================================================
|
|
# Safe execution — _parse_output edge cases
|
|
# =========================================================================
|
|
|
|
Scenario: _parse_output returns result key for non-dict JSON
|
|
When I parse stdout containing a JSON array
|
|
Then the parsed output should wrap it under "result" key
|
|
|
|
Scenario: _parse_output returns empty dict for blank stdout
|
|
When I parse empty stdout
|
|
Then the parsed output should be an empty dict
|
|
|
|
# =========================================================================
|
|
# Safe execution — _map_output_paths skips raw_output key
|
|
# =========================================================================
|
|
|
|
Scenario: _map_output_paths preserves raw_output without path mapping
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map output containing a raw_output key with container path
|
|
Then the raw_output value should remain unmapped
|
|
|
|
# =========================================================================
|
|
# Safe execution — container tool non-JSON-serialisable inputs
|
|
# =========================================================================
|
|
|
|
Scenario: execute_tool returns error for non-JSON-serialisable inputs
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I execute a tool with non-serialisable inputs
|
|
Then the container tool result should indicate failure
|
|
And the container tool result error should mention "JSON-serialisable"
|
|
|
|
# =========================================================================
|
|
# ToolRunner full lifecycle — discover, activate, deactivate
|
|
# =========================================================================
|
|
|
|
Scenario: ToolRunner discover returns tools from registry
|
|
Given I have a ToolRunner with a populated registry
|
|
When I call discover on the runner
|
|
Then the discovered tools list should not be empty
|
|
|
|
Scenario: ToolRunner activate marks a tool as ready
|
|
Given I have a ToolRunner with a populated registry
|
|
When I activate a tool on the runner
|
|
Then the activated tool spec should be returned
|
|
|
|
Scenario: ToolRunner activate raises ToolError for unknown tool
|
|
Given I have a ToolRunner with a populated registry
|
|
When I try to activate an unknown tool
|
|
Then a ToolError should be raised for activation
|
|
|
|
Scenario: ToolRunner deactivate cleans up active tool
|
|
Given I have a ToolRunner with a populated registry
|
|
When I activate and then deactivate a tool
|
|
Then deactivate should return true
|
|
And a second deactivate should return false
|
|
|
|
# =========================================================================
|
|
# ToolRunner host execution path
|
|
# =========================================================================
|
|
|
|
Scenario: ToolRunner executes tool on host and returns success
|
|
Given I have a ToolRunner with a host-routed tool
|
|
When I execute the host tool with valid inputs
|
|
Then the host tool result should indicate success
|
|
And the host tool result output should contain handler data
|
|
|
|
Scenario: ToolRunner normalises non-dict handler output
|
|
Given I have a ToolRunner with a tool returning non-dict output
|
|
When I execute the non-dict-output tool
|
|
Then the result output should wrap the value under "result"
|
|
|
|
Scenario: ToolRunner catches handler exception and returns error
|
|
Given I have a ToolRunner with a tool that raises an exception
|
|
When I execute the raising tool
|
|
Then the tool result should indicate failure with exception info
|
|
|
|
Scenario: ToolRunner returns error for non-JSON-serialisable host output
|
|
Given I have a ToolRunner with a tool returning non-serialisable output
|
|
When I execute the non-serialisable-output tool
|
|
Then the tool result should indicate failure mentioning "not JSON-serialisable"
|
|
|
|
Scenario: ToolRunner returns error for non-JSON-serialisable host inputs
|
|
Given I have a ToolRunner with a host-routed tool
|
|
When I execute the host tool with non-serialisable inputs
|
|
Then the tool result should indicate failure mentioning "not JSON-serialisable"
|
|
|
|
Scenario: ToolRunner raises ToolError for unregistered tool in execute
|
|
Given I have a ToolRunner with an empty registry
|
|
When I try to execute an unregistered tool
|
|
Then a ToolError should be raised for execution
|
|
|
|
# =========================================================================
|
|
# ToolRunner container routing — additional edge cases
|
|
# =========================================================================
|
|
|
|
# @tdd_issue @tdd_issue_4243 @tdd_expected_fail @skip
|
|
@skip
|
|
Scenario: ToolRunner returns error when container env resolver raises ContainerUnavailableError
|
|
Given I have a ToolRunner with an unavailable-container resolver
|
|
When I execute a tool through the unavailable-container runner
|
|
Then the tool result should indicate container unavailable
|
|
|
|
# @tdd_issue @tdd_issue_4243 @tdd_expected_fail @skip
|
|
@skip
|
|
Scenario: ToolRunner validates missing required input fields for container tools
|
|
Given I have a ToolRunner with a container executor and required-field tool
|
|
When I execute the container tool with missing required fields
|
|
Then the tool result error should mention "Missing required input fields"
|
|
|
|
Scenario: ToolRunner returns error for non-JSON-serialisable container inputs
|
|
Given I have a ToolRunner with a container executor for JSON validation
|
|
When I execute a container tool with non-serialisable inputs
|
|
Then the container tool result error should mention "not JSON-serialisable"
|
|
|
|
# @tdd_issue @tdd_issue_4243 @tdd_expected_fail @skip
|
|
@skip
|
|
Scenario: ToolRunner catches container executor exception
|
|
Given I have a ToolRunner with a failing container executor
|
|
When I execute a tool through the failing container runner
|
|
Then the tool result error should mention "Container execution error"
|
|
|
|
# =========================================================================
|
|
# Safe execution — list-type value mapping branches
|
|
# =========================================================================
|
|
|
|
Scenario: _map_input_paths handles list values containing host paths
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map tool inputs containing a list of host paths
|
|
Then the list values should be mapped to container paths
|
|
|
|
Scenario: _map_output_paths handles list values containing container paths
|
|
Given I have a ContainerToolExecutor with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I map tool output containing a list of container paths
|
|
Then the list values should be mapped to host paths
|
|
|
|
# =========================================================================
|
|
# Safe execution — PathMapper null byte rejection in mapping methods
|
|
# =========================================================================
|
|
|
|
Scenario: PathMapper host_to_container rejects null bytes
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I try to map a host path with null bytes to container
|
|
Then a mapping ValueError should be raised about null bytes
|
|
|
|
Scenario: PathMapper container_to_host rejects null bytes
|
|
Given I have a PathMapper with host_root "/tmp/sandbox" and container_root "/workspace"
|
|
When I try to map a container path with null bytes to host
|
|
Then a mapping ValueError should be raised about null bytes
|
|
|
|
# =========================================================================
|
|
# ToolResult validation — success/error consistency
|
|
# =========================================================================
|
|
|
|
Scenario: ToolResult rejects success=True with error message
|
|
When I try to create a ToolResult with success true and an error
|
|
Then a ToolResult validation error should be raised
|
|
|
|
Scenario: ToolResult rejects success=False without error message
|
|
When I try to create a ToolResult with success false and no error
|
|
Then a ToolResult validation error should be raised
|
|
|
|
# =========================================================================
|
|
# ToolError structured attributes
|
|
# =========================================================================
|
|
|
|
Scenario: ToolError carries structured context attributes
|
|
When I create a ToolError with known attributes
|
|
Then the ToolError should expose tool_name, error_type, and details
|