240 lines
7.7 KiB
Python
240 lines
7.7 KiB
Python
"""Tests for MemoryTool20250818."""
|
|
|
|
import pytest
|
|
from pathlib import Path
|
|
from nanobot.agent.tools.anthropic import MemoryTool20250818
|
|
from nanobot.agent.tools.anthropic.base import CLIResult
|
|
|
|
|
|
@pytest.fixture
|
|
def temp_workspace(tmp_path):
|
|
"""Create temporary workspace."""
|
|
return tmp_path
|
|
|
|
|
|
@pytest.fixture
|
|
def memory_tool(temp_workspace):
|
|
"""Create MemoryTool instance."""
|
|
return MemoryTool20250818(workspace=temp_workspace)
|
|
|
|
|
|
def test_memory_tool_initialization(memory_tool, temp_workspace):
|
|
"""Test that MemoryTool initializes correctly."""
|
|
assert memory_tool.api_type == "memory_20250818"
|
|
assert memory_tool.name == "memory"
|
|
assert memory_tool.beta_flag == "context-management-2025-06-27"
|
|
assert (temp_workspace / "memories").exists()
|
|
|
|
|
|
def test_memory_tool_to_params(memory_tool):
|
|
"""Test that to_params returns correct format."""
|
|
params = memory_tool.to_params()
|
|
assert params == {
|
|
"type": "memory_20250818",
|
|
"name": "memory"
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_view_file(memory_tool, temp_workspace):
|
|
"""Test viewing a file with line numbers."""
|
|
# Create test file
|
|
test_file = temp_workspace / "memories" / "notes.txt"
|
|
test_file.write_text("Line 1\nLine 2\nLine 3\n")
|
|
|
|
result = await memory_tool(command="view", path="/memories/notes.txt")
|
|
|
|
assert result.exit_code == 0
|
|
assert result.error == ""
|
|
assert "Here's the content of /memories/notes.txt with line numbers:" in result.output
|
|
assert " 1\tLine 1" in result.output
|
|
assert " 2\tLine 2" in result.output
|
|
assert " 3\tLine 3" in result.output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_view_file_with_range(memory_tool, temp_workspace):
|
|
"""Test viewing a file with line range."""
|
|
# Create test file with 10 lines
|
|
test_file = temp_workspace / "memories" / "test.txt"
|
|
test_file.write_text("\n".join([f"Line {i}" for i in range(1, 11)]))
|
|
|
|
result = await memory_tool(command="view", path="/memories/test.txt", view_range=[3, 5])
|
|
|
|
assert result.exit_code == 0
|
|
assert " 3\tLine 3" in result.output
|
|
assert " 4\tLine 4" in result.output
|
|
assert " 5\tLine 5" in result.output
|
|
assert "Line 1" not in result.output
|
|
assert "Line 10" not in result.output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_view_file_not_exists(memory_tool):
|
|
"""Test viewing a nonexistent file."""
|
|
result = await memory_tool(command="view", path="/memories/nonexistent.txt")
|
|
|
|
assert result.exit_code == 1
|
|
assert result.output == ""
|
|
assert "The path /memories/nonexistent.txt does not exist" in result.error
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_view_directory(memory_tool, temp_workspace):
|
|
"""Test viewing a directory listing."""
|
|
# Create test directory structure
|
|
memories = temp_workspace / "memories"
|
|
(memories / "notes.txt").write_text("content")
|
|
(memories / "project").mkdir()
|
|
(memories / "project" / "status.xml").write_text("<status>ok</status>")
|
|
(memories / ".hidden").write_text("hidden") # Should be excluded
|
|
|
|
result = await memory_tool(command="view", path="/memories")
|
|
|
|
assert result.exit_code == 0
|
|
assert result.error == ""
|
|
assert "Here're the files and directories up to 2 levels deep in /memories" in result.output
|
|
assert "/memories" in result.output
|
|
assert "/memories/notes.txt" in result.output
|
|
assert "/memories/project" in result.output
|
|
assert "/memories/project/status.xml" in result.output
|
|
assert ".hidden" not in result.output # Hidden files excluded
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_view_empty_directory(memory_tool):
|
|
"""Test viewing an empty directory."""
|
|
result = await memory_tool(command="view", path="/memories")
|
|
|
|
assert result.exit_code == 0
|
|
assert "/memories" in result.output
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_file(memory_tool, temp_workspace):
|
|
"""Test creating a new file."""
|
|
result = await memory_tool(
|
|
command="create",
|
|
path="/memories/notes.txt",
|
|
file_text="My notes\nLine 2\n"
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert result.error == ""
|
|
assert "File created successfully at: /memories/notes.txt" in result.output
|
|
|
|
# Verify file was created
|
|
created_file = temp_workspace / "memories" / "notes.txt"
|
|
assert created_file.exists()
|
|
assert created_file.read_text() == "My notes\nLine 2\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_file_nested_directory(memory_tool, temp_workspace):
|
|
"""Test creating a file in a nested directory (auto-creates parent dirs)."""
|
|
result = await memory_tool(
|
|
command="create",
|
|
path="/memories/project/status.xml",
|
|
file_text="<status>ok</status>"
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert "File created successfully at: /memories/project/status.xml" in result.output
|
|
|
|
# Verify file and parent directory were created
|
|
created_file = temp_workspace / "memories" / "project" / "status.xml"
|
|
assert created_file.exists()
|
|
assert created_file.read_text() == "<status>ok</status>"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_file_already_exists(memory_tool, temp_workspace):
|
|
"""Test creating a file that already exists."""
|
|
# Create file first
|
|
existing = temp_workspace / "memories" / "existing.txt"
|
|
existing.write_text("existing content")
|
|
|
|
result = await memory_tool(
|
|
command="create",
|
|
path="/memories/existing.txt",
|
|
file_text="new content"
|
|
)
|
|
|
|
assert result.exit_code == 1
|
|
assert result.output == ""
|
|
assert "Error: File /memories/existing.txt already exists" in result.error
|
|
|
|
# Verify original content unchanged
|
|
assert existing.read_text() == "existing content"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_file_missing_text(memory_tool):
|
|
"""Test creating a file without file_text parameter."""
|
|
result = await memory_tool(
|
|
command="create",
|
|
path="/memories/notes.txt"
|
|
)
|
|
|
|
assert result.exit_code == 1
|
|
assert result.output == ""
|
|
assert "Error: file_text is required for create command" in result.error
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_str_replace_success(memory_tool, temp_workspace):
|
|
"""Test replacing unique string in a file."""
|
|
test_file = temp_workspace / "memories" / "config.txt"
|
|
test_file.write_text("color: blue\nsize: large\n")
|
|
|
|
result = await memory_tool(
|
|
command="str_replace",
|
|
path="/memories/config.txt",
|
|
old_str="blue",
|
|
new_str="green"
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert result.error == ""
|
|
assert "The memory file has been edited." in result.output
|
|
|
|
# Verify file was modified
|
|
assert test_file.read_text() == "color: green\nsize: large\n"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_str_replace_not_found(memory_tool, temp_workspace):
|
|
"""Test replacing string that doesn't exist."""
|
|
test_file = temp_workspace / "memories" / "config.txt"
|
|
test_file.write_text("color: blue\n")
|
|
|
|
result = await memory_tool(
|
|
command="str_replace",
|
|
path="/memories/config.txt",
|
|
old_str="red",
|
|
new_str="green"
|
|
)
|
|
|
|
assert result.exit_code == 1
|
|
assert result.output == ""
|
|
assert "No replacement was performed, old_str `red` did not appear verbatim" in result.error
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_str_replace_duplicate(memory_tool, temp_workspace):
|
|
"""Test replacing string that appears multiple times."""
|
|
test_file = temp_workspace / "memories" / "config.txt"
|
|
test_file.write_text("color: blue\nbackground: blue\n")
|
|
|
|
result = await memory_tool(
|
|
command="str_replace",
|
|
path="/memories/config.txt",
|
|
old_str="blue",
|
|
new_str="green"
|
|
)
|
|
|
|
assert result.exit_code == 1
|
|
assert result.output == ""
|
|
assert "Multiple occurrences of old_str `blue`" in result.error
|
|
assert "Please ensure it is unique" in result.error
|