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

213 lines
6.9 KiB
Python

# -*- coding: utf-8 -*-
"""Write tool test case."""
import os
import tempfile
from unittest.async_case import IsolatedAsyncioTestCase
from agentscope.tool import Write
from agentscope.permission import (
PermissionContext,
PermissionBehavior,
PermissionRule,
)
from agentscope.state import AgentState
from agentscope.message import ToolResultState
class WriteToolTest(IsolatedAsyncioTestCase):
"""The write tool test case."""
async def asyncSetUp(self) -> None:
"""The async setup method."""
self.write_tool = Write()
self.temp_dir = tempfile.mkdtemp()
async def asyncTearDown(self) -> None:
"""Clean up temporary files."""
import shutil
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
async def test_tool_properties(self) -> None:
"""Test write tool properties."""
self.assertEqual(self.write_tool.name, "Write")
self.assertIsInstance(self.write_tool.description, str)
self.assertIsInstance(self.write_tool.input_schema, dict)
self.assertFalse(self.write_tool.is_mcp)
self.assertFalse(self.write_tool.is_read_only)
self.assertFalse(self.write_tool.is_concurrency_safe)
async def test_check_permissions(self) -> None:
"""Test write tool permission checking.
Write tool should return PASSTHROUGH for non-dangerous paths,
allowing PermissionEngine to check allow rules.
"""
context = PermissionContext()
tool_input = {"file_path": "/tmp/test.txt"}
decision = await self.write_tool.check_permissions(tool_input, context)
self.assertEqual(decision.behavior, PermissionBehavior.PASSTHROUGH)
async def test_simple_write(self) -> None:
"""Test simple file writing."""
file_path = os.path.join(self.temp_dir, "test.txt")
content = "Hello World\nThis is a test\n"
chunk = await self.write_tool(
file_path=file_path,
content=content,
)
self.assertEqual(chunk.state, "running")
self.assertTrue(chunk.is_last)
# Verify file was created and content is correct
self.assertTrue(os.path.exists(file_path))
with open(file_path, "r", encoding="utf-8") as f:
written_content = f.read()
self.assertEqual(written_content, content)
async def test_write_creates_directory(self) -> None:
"""Test that write creates parent directories."""
file_path = os.path.join(self.temp_dir, "subdir", "test.txt")
content = "Test content"
chunk = await self.write_tool(
file_path=file_path,
content=content,
)
self.assertEqual(chunk.state, "running")
# Verify directory and file were created
self.assertTrue(os.path.exists(file_path))
with open(file_path, "r", encoding="utf-8") as f:
written_content = f.read()
self.assertEqual(written_content, content)
async def test_write_overwrites_existing(self) -> None:
"""Test that write overwrites existing files."""
file_path = os.path.join(self.temp_dir, "test.txt")
# Write initial content
with open(file_path, "w", encoding="utf-8") as f:
f.write("Initial content")
# Overwrite with new content
new_content = "New content"
chunk = await self.write_tool(
file_path=file_path,
content=new_content,
)
self.assertEqual(len([chunk]), 1)
# Verify content was overwritten
with open(file_path, "r", encoding="utf-8") as f:
written_content = f.read()
self.assertEqual(written_content, new_content)
self.assertNotIn("Initial", written_content)
async def test_write_empty_content(self) -> None:
"""Test writing empty content."""
file_path = os.path.join(self.temp_dir, "empty.txt")
chunk = await self.write_tool(
file_path=file_path,
content="",
)
self.assertEqual(len([chunk]), 1)
self.assertTrue(os.path.exists(file_path))
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
self.assertEqual(content, "")
async def test_overwrite_existing_without_prior_read_errors(self) -> None:
"""Overwriting an existing file via state-injected call requires
the file to have been read first (cached in tool_context).
"""
file_path = os.path.join(self.temp_dir, "existing.txt")
with open(file_path, "w", encoding="utf-8") as f:
f.write("original")
state = AgentState()
chunk = await self.write_tool(
file_path=file_path,
content="new",
_agent_state=state,
)
self.assertEqual(chunk.state, ToolResultState.ERROR)
self.assertIn("has not been read", chunk.content[0].text)
# File must not have been mutated
with open(file_path, "r", encoding="utf-8") as f:
self.assertEqual(f.read(), "original")
async def test_match_rule_glob_pattern(self) -> None:
"""Test match_rule with glob patterns."""
# Test exact match
self.assertTrue(
await self.write_tool.match_rule(
"test.py",
{"file_path": "test.py"},
),
)
# Test wildcard pattern
self.assertTrue(
await self.write_tool.match_rule(
"*.py",
{"file_path": "test.py"},
),
)
# Test directory pattern
self.assertTrue(
await self.write_tool.match_rule(
"/tmp/**",
{"file_path": "/tmp/test.py"},
),
)
# Test non-matching pattern
self.assertFalse(
await self.write_tool.match_rule(
"*.txt",
{"file_path": "test.py"},
),
)
# Test empty file_path
self.assertFalse(
await self.write_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.write_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.write_tool.generate_suggestions(
{"file_path": "/test.py"},
)
self.assertGreater(len(suggestions), 0)