Files
wehub-resource-sync c3bf08ac8d
K8s Workspace Integration Tests / k8s-workspace-tests (push) Has been cancelled
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Web UI / check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:27 +08:00

189 lines
6.1 KiB
Python

# -*- coding: utf-8 -*-
"""Read tool test case."""
import os
import tempfile
from unittest.async_case import IsolatedAsyncioTestCase
from agentscope.tool import ToolChunk, Read
from agentscope.permission import (
PermissionContext,
PermissionBehavior,
PermissionRule,
)
from agentscope.message import TextBlock
class ReadToolTest(IsolatedAsyncioTestCase):
"""The read tool test case."""
async def asyncSetUp(self) -> None:
"""The async setup method."""
self.read_tool = Read()
# Create a temporary file for testing
self.temp_file = tempfile.NamedTemporaryFile(
mode="w",
delete=False,
suffix=".txt",
)
# Write multiple lines
for i in range(1, 11):
self.temp_file.write(f"Line {i}\n")
self.temp_file.close()
async def asyncTearDown(self) -> None:
"""Clean up temporary files."""
if os.path.exists(self.temp_file.name):
os.unlink(self.temp_file.name)
async def test_tool_properties(self) -> None:
"""Test read tool properties."""
self.assertEqual(self.read_tool.name, "Read")
self.assertIsInstance(self.read_tool.description, str)
self.assertIsInstance(self.read_tool.input_schema, dict)
self.assertFalse(self.read_tool.is_mcp)
self.assertTrue(self.read_tool.is_read_only)
self.assertTrue(self.read_tool.is_concurrency_safe)
async def test_check_permissions(self) -> None:
"""Test read tool permission checking."""
context = PermissionContext()
tool_input = {"file_path": "/tmp/test.txt"}
decision = await self.read_tool.check_permissions(tool_input, context)
# Read/Glob/Grep are read-only, return PASSTHROUGH
self.assertEqual(decision.behavior, PermissionBehavior.PASSTHROUGH)
async def test_simple_read(self) -> None:
"""Test simple file reading."""
chunk = await self.read_tool(file_path=self.temp_file.name)
self.assertIsInstance(chunk, ToolChunk)
self.assertEqual(chunk.state, "running")
self.assertEqual(len(chunk.content), 1)
self.assertIsInstance(chunk.content[0], TextBlock)
content = chunk.content[0].text
# Should contain all lines with line numbers
self.assertIn("Line 1", content)
self.assertIn("Line 10", content)
async def test_read_with_offset(self) -> None:
"""Test reading with offset."""
chunk = await self.read_tool(
file_path=self.temp_file.name,
offset=5,
)
self.assertEqual(chunk.state, "running")
content = chunk.content[0].text
# Should start from line 5
self.assertIn("Line 5", content)
# Line 1 should not appear (but Line 10 contains "1",
# so check more specifically)
lines = content.split("\n")
line_numbers = [
int(line.split("\t")[0].strip()) for line in lines if line.strip()
]
self.assertNotIn(1, line_numbers)
self.assertIn(5, line_numbers)
async def test_read_with_limit(self) -> None:
"""Test reading with limit."""
chunk = await self.read_tool(
file_path=self.temp_file.name,
offset=1,
limit=3,
)
self.assertEqual(chunk.state, "running")
content = chunk.content[0].text
# Should only read 3 lines
self.assertIn("Line 1", content)
self.assertIn("Line 2", content)
self.assertIn("Line 3", content)
self.assertNotIn("Line 4", content)
async def test_read_nonexistent_file(self) -> None:
"""Test reading a non-existent file."""
chunk = await self.read_tool(file_path="/nonexistent/file.txt")
self.assertEqual(chunk.state, "error")
self.assertIn("does not exist", chunk.content[0].text)
async def test_read_directory(self) -> None:
"""Test reading a directory (should fail)."""
temp_dir = tempfile.mkdtemp()
try:
chunk = await self.read_tool(file_path=temp_dir)
self.assertEqual(chunk.state, "error")
self.assertIn("directory", chunk.content[0].text.lower())
finally:
os.rmdir(temp_dir)
async def test_match_rule_glob_pattern(self) -> None:
"""Test match_rule with glob patterns."""
# Test exact match
self.assertTrue(
await self.read_tool.match_rule(
"test.py",
{"file_path": "test.py"},
),
)
# Test wildcard pattern
self.assertTrue(
await self.read_tool.match_rule(
"*.py",
{"file_path": "test.py"},
),
)
# Test directory pattern
self.assertTrue(
await self.read_tool.match_rule(
"/tmp/**",
{"file_path": "/tmp/test.py"},
),
)
# Test non-matching pattern
self.assertFalse(
await self.read_tool.match_rule(
"*.txt",
{"file_path": "test.py"},
),
)
# Test empty file_path
self.assertFalse(
await self.read_tool.match_rule(
"*.py",
{"file_path": ""},
),
)
async def test_generate_suggestions(self) -> None:
"""Test generate_suggestions for file operations."""
# Test suggestion for file in subdirectory
suggestions = await self.read_tool.generate_suggestions(
{"file_path": "/tmp/project/src/main.py"},
)
self.assertIsInstance(suggestions, list)
self.assertGreater(len(suggestions), 0)
self.assertIsInstance(suggestions[0], PermissionRule)
# Should suggest parent directory pattern
suggestion_contents = [s.rule_content for s in suggestions]
self.assertIn("/tmp/project/src/**", suggestion_contents)
# Test suggestion for file in root
suggestions = await self.read_tool.generate_suggestions(
{"file_path": "/test.py"},
)
self.assertGreater(len(suggestions), 0)