| """Test cases for Bash Tool.""" |
|
|
| import asyncio |
|
|
| import pytest |
|
|
| from mini_agent.tools.bash_tool import BackgroundShellManager, BashKillTool, BashOutputTool, BashTool |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_foreground_command(): |
| """Test executing a simple foreground command.""" |
| print("\n=== Testing Foreground Command ===") |
|
|
| bash_tool = BashTool() |
| result = await bash_tool.execute(command="echo 'Hello from foreground'") |
|
|
| assert result.success |
| assert "Hello from foreground" in result.stdout |
| assert result.exit_code == 0 |
| print(f"Output: {result.content}") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_foreground_command_with_stderr(): |
| """Test command that outputs to both stdout and stderr.""" |
| print("\n=== Testing Stdout/Stderr Separation ===") |
|
|
| bash_tool = BashTool() |
| result = await bash_tool.execute(command="echo 'stdout message' && echo 'stderr message' >&2") |
|
|
| assert result.success |
| assert "stdout message" in result.stdout |
| assert "stderr message" in result.stderr |
| print(f"Stdout: {result.stdout}") |
| print(f"Stderr: {result.stderr}") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_command_failure(): |
| """Test command that fails with non-zero exit code.""" |
| print("\n=== Testing Command Failure ===") |
|
|
| bash_tool = BashTool() |
| result = await bash_tool.execute(command="ls /nonexistent_directory_12345") |
|
|
| assert not result.success |
| assert result.exit_code != 0 |
| assert result.error is not None |
| print(f"Error: {result.error}") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_command_timeout(): |
| """Test command timeout.""" |
| print("\n=== Testing Command Timeout ===") |
|
|
| bash_tool = BashTool() |
| result = await bash_tool.execute(command="sleep 10", timeout=1) |
|
|
| assert not result.success |
| assert "timed out" in result.error.lower() |
| assert result.exit_code == -1 |
| print(f"Timeout error: {result.error}") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_background_command(): |
| """Test running a command in the background.""" |
| print("\n=== Testing Background Command ===") |
|
|
| bash_tool = BashTool() |
| result = await bash_tool.execute( |
| command="for i in 1 2 3; do echo 'Line '$i; sleep 0.5; done", run_in_background=True |
| ) |
|
|
| assert result.success |
| assert result.bash_id is not None |
| assert "Background command started" in result.stdout |
|
|
| bash_id = result.bash_id |
| print(f"Background command started with ID: {bash_id}") |
|
|
| |
| await asyncio.sleep(1) |
|
|
| |
| bash_output_tool = BashOutputTool() |
| output_result = await bash_output_tool.execute(bash_id=bash_id) |
|
|
| assert output_result.success |
| print(f"Output:\n{output_result.content}") |
|
|
| |
| bash_kill_tool = BashKillTool() |
| kill_result = await bash_kill_tool.execute(bash_id=bash_id) |
| assert kill_result.success |
| print("Background process terminated") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_bash_output_monitoring(): |
| """Test monitoring background command output.""" |
| print("\n=== Testing Output Monitoring ===") |
|
|
| bash_tool = BashTool() |
|
|
| |
| result = await bash_tool.execute( |
| command="for i in 1 2 3 4 5; do echo 'Line '$i; sleep 0.5; done", run_in_background=True |
| ) |
|
|
| assert result.success |
| bash_id = result.bash_id |
| print(f"Started background command: {bash_id}") |
|
|
| bash_output_tool = BashOutputTool() |
|
|
| |
| for i in range(3): |
| await asyncio.sleep(1) |
| output_result = await bash_output_tool.execute(bash_id=bash_id) |
| assert output_result.success |
| print(f"\n--- Check #{i + 1} ---") |
| print(f"Output:\n{output_result.content}") |
|
|
| |
| bash_kill_tool = BashKillTool() |
| await bash_kill_tool.execute(bash_id=bash_id) |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_bash_output_with_filter(): |
| """Test bash_output with regex filter.""" |
| print("\n=== Testing Output Filter ===") |
|
|
| bash_tool = BashTool() |
|
|
| |
| result = await bash_tool.execute( |
| command="for i in 1 2 3 4 5; do echo 'Line '$i; sleep 0.3; done", run_in_background=True |
| ) |
|
|
| assert result.success |
| bash_id = result.bash_id |
|
|
| |
| await asyncio.sleep(2) |
|
|
| |
| bash_output_tool = BashOutputTool() |
| output_result = await bash_output_tool.execute(bash_id=bash_id, filter_str="Line [24]") |
|
|
| assert output_result.success |
| lines = output_result.content |
| print(f"Filtered output:\n{output_result.content}") |
|
|
| |
| bash_kill_tool = BashKillTool() |
| await bash_kill_tool.execute(bash_id=bash_id) |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_bash_kill(): |
| """Test terminating a background command.""" |
| print("\n=== Testing Bash Kill ===") |
|
|
| bash_tool = BashTool() |
|
|
| |
| result = await bash_tool.execute(command="sleep 100", run_in_background=True) |
|
|
| assert result.success |
| bash_id = result.bash_id |
| print(f"Started long-running command: {bash_id}") |
|
|
| |
| await asyncio.sleep(0.5) |
| bg_shell = BackgroundShellManager.get(bash_id) |
| assert bg_shell is not None |
| assert bg_shell.status == "running" |
|
|
| |
| bash_kill_tool = BashKillTool() |
| kill_result = await bash_kill_tool.execute(bash_id=bash_id) |
|
|
| assert kill_result.success |
| |
| assert kill_result.exit_code == -15 or kill_result.bash_id == bash_id |
| print(f"Kill result:\n{kill_result.content}") |
|
|
| |
| bg_shell = BackgroundShellManager.get(bash_id) |
| assert bg_shell is None |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_bash_kill_nonexistent(): |
| """Test killing a non-existent bash process.""" |
| print("\n=== Testing Kill Non-existent Process ===") |
|
|
| bash_kill_tool = BashKillTool() |
| result = await bash_kill_tool.execute(bash_id="nonexistent123") |
|
|
| assert not result.success |
| assert "not found" in result.error.lower() |
| print(f"Expected error: {result.error}") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_bash_output_nonexistent(): |
| """Test getting output from non-existent bash process.""" |
| print("\n=== Testing Output From Non-existent Process ===") |
|
|
| bash_output_tool = BashOutputTool() |
| result = await bash_output_tool.execute(bash_id="nonexistent123") |
|
|
| assert not result.success |
| assert "not found" in result.error.lower() |
| print(f"Expected error: {result.error}") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_multiple_background_commands(): |
| """Test running multiple background commands simultaneously.""" |
| print("\n=== Testing Multiple Background Commands ===") |
|
|
| bash_tool = BashTool() |
|
|
| |
| bash_ids = [] |
| for i in range(3): |
| result = await bash_tool.execute( |
| command=f"for j in 1 2 3; do echo 'Command {i + 1} Line '$j; sleep 0.5; done", run_in_background=True |
| ) |
| assert result.success |
| bash_ids.append(result.bash_id) |
| print(f"Started command {i + 1}: {result.bash_id}") |
|
|
| |
| await asyncio.sleep(1) |
|
|
| bash_output_tool = BashOutputTool() |
| for bash_id in bash_ids: |
| output_result = await bash_output_tool.execute(bash_id=bash_id) |
| assert output_result.success |
| print(f"\nOutput for {bash_id}:\n{output_result.content[:100]}...") |
|
|
| |
| bash_kill_tool = BashKillTool() |
| for bash_id in bash_ids: |
| await bash_kill_tool.execute(bash_id=bash_id) |
|
|
| print("All background processes cleaned up") |
|
|
|
|
| @pytest.mark.asyncio |
| async def test_timeout_validation(): |
| """Test timeout parameter validation.""" |
| print("\n=== Testing Timeout Validation ===") |
|
|
| bash_tool = BashTool() |
|
|
| |
| result = await bash_tool.execute(command="echo 'test'", timeout=1000) |
| assert result.success |
| print("Timeout > 600 handled correctly") |
|
|
| |
| result = await bash_tool.execute(command="echo 'test'", timeout=0) |
| assert result.success |
| print("Timeout < 1 handled correctly") |
|
|