"""Unit tests for terminal tool."""
import asyncio
import pytest
from navi.tools.terminal import TerminalTool
from navi.tools.terminal_manager import TerminalManager
class TestTerminalTool:
@pytest.fixture
def tool(self):
return TerminalTool()
async def test_run_echo(self, tool):
result = await tool.execute({"action": "run", "command": "echo hello"})
assert result.success
assert "hello" in result.output
async def test_run_pwd(self, tool):
result = await tool.execute({"action": "run", "command": "pwd"})
assert result.success
assert "/" in result.output
async def test_run_empty_command(self, tool):
result = await tool.execute({"action": "run", "command": " "})
assert not result.success
assert "empty" in result.output.lower()
async def test_run_invalid_command(self, tool):
result = await tool.execute({"action": "run", "command": "this_command_does_not_exist_12345"})
assert not result.success
async def test_missing_action(self, tool):
result = await tool.execute({"command": "echo hi"})
assert not result.success
assert "missing" in result.output.lower()
async def test_unknown_action(self, tool):
result = await tool.execute({"action": "fly", "command": "echo hi"})
assert not result.success
assert "unknown" in result.output.lower()
class TestTerminalToolPersistent:
@pytest.fixture
async def manager(self):
tm = TerminalManager(max_idle_seconds=1)
tm.start()
yield tm
# cleanup
await tm.shutdown()
@pytest.fixture
def tool(self, manager):
return TerminalTool(terminal_manager=manager)
async def test_open_list_close(self, tool, manager):
from navi.tools._internal.base import ToolContext
ctx = ToolContext(session_id="s1", stop_event=None, model="test")
result = await tool.execute(
{
"action": "open",
"terminal_name": "t1",
"description": "test terminal",
"command": "echo hello",
},
ctx=ctx,
)
assert result.success
assert result.metadata["name"] == "t1"
assert "hello" in result.output
result = await tool.execute({"action": "list"}, ctx=ctx)
assert result.success
assert "t1" in result.output
assert "test terminal" in result.output
result = await tool.execute(
{"action": "close", "terminal_name": "t1"},
ctx=ctx,
)
assert result.success
result = await tool.execute({"action": "list"}, ctx=ctx)
assert result.success
assert "No active terminals" in result.output
async def test_open_background(self, tool, manager):
from navi.tools._internal.base import ToolContext
ctx = ToolContext(session_id="s2", stop_event=None, model="test")
result = await tool.execute(
{
"action": "open",
"terminal_name": "bg1",
"description": "bg test",
"command": "sleep 10",
"background": True,
},
ctx=ctx,
)
assert result.success
assert "background" in result.output.lower()
assert result.metadata["name"] == "bg1"
# status should show busy
result = await tool.execute(
{"action": "status", "terminal_name": "bg1"},
ctx=ctx,
)
assert result.success
assert "busy" in result.output.lower()
# close it
result = await tool.execute(
{"action": "close", "terminal_name": "bg1"},
ctx=ctx,
)
assert result.success
async def test_send_input(self, tool, manager):
from navi.tools._internal.base import ToolContext
ctx = ToolContext(session_id="s3", stop_event=None, model="test")
result = await tool.execute(
{
"action": "open",
"terminal_name": "in1",
"description": "input test",
"command": "cat",
"background": True,
},
ctx=ctx,
)
assert result.success
result = await tool.execute(
{"action": "send_input", "terminal_name": "in1", "input": "hello\n"},
ctx=ctx,
)
assert result.success
# give cat a moment to echo back
await asyncio.sleep(0.2)
result = await tool.execute(
{"action": "status", "terminal_name": "in1"},
ctx=ctx,
)
assert result.success
assert "hello" in result.output
await tool.execute({"action": "close", "terminal_name": "in1"}, ctx=ctx)
async def test_no_manager(self):
tool = TerminalTool(terminal_manager=None)
from navi.tools._internal.base import ToolContext
ctx = ToolContext(session_id="s4", stop_event=None, model="test")
result = await tool.execute(
{"action": "open", "terminal_name": "x", "description": "d", "command": "echo hi"},
ctx=ctx,
)
assert not result.success
assert "manager" in result.output.lower()
async def test_open_missing_name(self, tool):
from navi.tools._internal.base import ToolContext
ctx = ToolContext(session_id="s5", stop_event=None, model="test")
result = await tool.execute(
{"action": "open", "description": "d", "command": "echo hi"},
ctx=ctx,
)
assert not result.success
assert "missing" in result.output.lower()