import pytest from duck_core.tools.file_read import FileReadTool from duck_core.tools.file_write import FileWriteTool from duck_core.tools.gateway import ToolGateway from duck_core.tools.shell_exec_safe import ShellExecSafeTool @pytest.mark.asyncio async def test_file_tools_stay_inside_workspace(tmp_path): write = FileWriteTool(str(tmp_path)) read = FileReadTool(str(tmp_path)) result = await write.run({"path": "tmp/note.txt", "content": "hello duck"}) loaded = await read.run({"path": "tmp/note.txt"}) escaped = await read.run({"path": "../outside.txt"}) assert result.ok is True assert loaded.output == "hello duck" assert escaped.ok is False @pytest.mark.asyncio async def test_shell_tool_blocks_dangerous_commands(tmp_path): shell = ShellExecSafeTool(str(tmp_path)) allowed = await shell.run({"command": "pwd"}) blocked = await shell.run({"command": "rm -rf ."}) assert allowed.ok is True assert blocked.ok is False @pytest.mark.asyncio async def test_tool_gateway_runs_allowed_directive(tmp_path): gateway = ToolGateway.default(str(tmp_path)) result = await gateway.run_action( {"tool": "file_write", "args": {"path": "a.txt", "content": "x"}} ) assert result.ok is True assert result.metadata["path"].endswith("a.txt") @pytest.mark.asyncio async def test_tool_gateway_lists_workspace_directory(tmp_path): (tmp_path / "src").mkdir() (tmp_path / "src" / "app.py").write_text("print('duck')") (tmp_path / "README.md").write_text("hello") gateway = ToolGateway.default(str(tmp_path)) result = await gateway.run_action({"tool": "list_dir", "args": {"path": "."}}) escaped = await gateway.run_action({"tool": "list_dir", "args": {"path": ".."}}) assert result.ok is True assert "README.md" in result.output assert "src/" in result.output assert escaped.ok is False @pytest.mark.asyncio async def test_tool_gateway_searches_file_contents(tmp_path): (tmp_path / "src").mkdir() (tmp_path / "src" / "app.py").write_text("duck tool gateway\\n") (tmp_path / "notes.txt").write_text("other content\\n") gateway = ToolGateway.default(str(tmp_path)) result = await gateway.run_action( {"tool": "search_files", "args": {"query": "duck tool", "path": "."}} ) escaped = await gateway.run_action( {"tool": "search_files", "args": {"query": "duck", "path": ".."}} ) assert result.ok is True assert "src/app.py:1:duck tool gateway" in result.output assert result.metadata["matches"] == 1 assert escaped.ok is False