ai-agent-book 精选快照(<2MB 代码与文档,来自 github.com/bojieli/ai-agent-book)
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
Build latest book artifacts / build (push) Canceled after 0s
dependency resolution / resolve (3.11) (push) Canceled after 0s
dependency resolution / resolve (3.13) (push) Canceled after 0s
deploy-pages / build (push) Canceled after 0s
deploy-pages / deploy (push) Canceled after 0s
i18n consistency check / check (push) Canceled after 0s
provider adoption tests / test (chapter2/context-compression) (push) Canceled after 0s
provider adoption tests / test (chapter2/prompt-injection) (push) Canceled after 0s
provider adoption tests / test (chapter2/system-hint) (push) Canceled after 0s
provider adoption tests / test (chapter3/log-sanitization) (push) Canceled after 0s
web-search-agent tests / test (push) Canceled after 0s
web-search-agent tests / agentbook (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
"""
|
||||
Real tests for enhanced filesystem tools.
|
||||
These tests perform actual file operations to verify functionality.
|
||||
"""
|
||||
import asyncio
|
||||
import json
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
# Add current directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
from config import Config
|
||||
from filesystem_enhanced import FilesystemEnhanced
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def fs(tmp_path, monkeypatch):
|
||||
"""Create filesystem instance with temp workspace."""
|
||||
monkeypatch.setattr(Config, "WORKSPACE_DIR", tmp_path)
|
||||
return FilesystemEnhanced()
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
def test_files(fs):
|
||||
"""Create test files for testing."""
|
||||
# Create some test files
|
||||
try:
|
||||
(Config.WORKSPACE_DIR / "test1.txt").write_text("Hello World")
|
||||
(Config.WORKSPACE_DIR / "test2.txt").write_text("Python Testing\nLine 2\nLine 3")
|
||||
(Config.WORKSPACE_DIR / "data.json").write_text('{"key": "value"}')
|
||||
(Config.WORKSPACE_DIR / "subdir").mkdir()
|
||||
(Config.WORKSPACE_DIR / "subdir" / "nested.txt").write_text("Nested file")
|
||||
except Exception as e:
|
||||
print(f"Error creating test files: {e}")
|
||||
return fs
|
||||
|
||||
|
||||
class TestReadOperations:
|
||||
"""Tests for file reading operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_text_file(self, test_files):
|
||||
"""Test reading a text file."""
|
||||
result = await test_files.read_text_file("test1.txt")
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["content"] == "Hello World"
|
||||
assert result["file_size"] > 0
|
||||
assert result["lines"] == 1
|
||||
|
||||
print("✅ Read text file successfully")
|
||||
print(f" Content: {result['content']}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_multiline_file(self, test_files):
|
||||
"""Test reading multiline file."""
|
||||
result = await test_files.read_text_file("test2.txt")
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["lines"] == 3
|
||||
assert "Python Testing" in result["content"]
|
||||
|
||||
print("✅ Read multiline file")
|
||||
print(f" Lines: {result['lines']}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_nonexistent_file(self, fs):
|
||||
"""Test reading non-existent file."""
|
||||
result = await fs.read_text_file("nonexistent.txt")
|
||||
|
||||
assert result["success"] is False
|
||||
assert "does not exist" in result["error"]
|
||||
|
||||
print("✅ Correctly handled nonexistent file")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_multiple_files(self, test_files):
|
||||
"""Test reading multiple files at once."""
|
||||
result = await test_files.read_multiple_files([
|
||||
"test1.txt",
|
||||
"test2.txt",
|
||||
"data.json"
|
||||
])
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["files_read"] == 3
|
||||
assert result["files_failed"] == 0
|
||||
assert "test1.txt" in result["results"]
|
||||
assert "test2.txt" in result["results"]
|
||||
|
||||
print(f"✅ Read {result['files_read']} files")
|
||||
print(f" Files: {', '.join(result['results'].keys())}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_multiple_files_with_errors(self, test_files):
|
||||
"""Test reading multiple files with some missing."""
|
||||
result = await test_files.read_multiple_files([
|
||||
"test1.txt",
|
||||
"nonexistent.txt",
|
||||
"test2.txt"
|
||||
])
|
||||
|
||||
assert result["success"] is True # At least some succeeded
|
||||
assert result["files_read"] == 2
|
||||
assert result["files_failed"] == 1
|
||||
assert len(result["errors"]) == 1
|
||||
|
||||
print(f"✅ Read {result['files_read']} files, {result['files_failed']} failed")
|
||||
|
||||
|
||||
class TestListOperations:
|
||||
"""Tests for directory listing operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_directory_with_sizes(self, test_files):
|
||||
"""Test listing directory with file sizes."""
|
||||
result = await test_files.list_directory_with_sizes(".")
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["total_items"] >= 4 # At least 3 files + 1 dir
|
||||
assert result["total_size"] > 0
|
||||
assert len(result["contents"]) >= 4
|
||||
|
||||
# Check structure
|
||||
item = result["contents"][0]
|
||||
assert "name" in item
|
||||
assert "type" in item
|
||||
assert "size" in item
|
||||
assert "size_human" in item
|
||||
|
||||
print(f"✅ Listed {result['total_items']} items")
|
||||
print(f" Total size: {result['total_size_human']}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_directory_tree(self, test_files):
|
||||
"""Test generating directory tree."""
|
||||
result = await test_files.directory_tree(".", max_depth=3)
|
||||
|
||||
assert result["success"] is True
|
||||
assert "tree" in result
|
||||
assert result["tree"]["type"] == "directory"
|
||||
assert "children" in result["tree"]
|
||||
assert len(result["tree"]["children"]) >= 4
|
||||
|
||||
print(f"✅ Generated directory tree")
|
||||
print(f" Root: {result['root']}")
|
||||
print(f" Items: {result['tree']['count']}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_directory_tree_depth_limit(self, test_files):
|
||||
"""Test directory tree with depth limit."""
|
||||
# Create deeper structure
|
||||
(Config.WORKSPACE_DIR / "deep" / "level2" / "level3").mkdir(parents=True)
|
||||
|
||||
result = await test_files.directory_tree(".", max_depth=2)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["max_depth"] == 2
|
||||
|
||||
print("✅ Directory tree respects depth limit")
|
||||
|
||||
|
||||
class TestSearchOperations:
|
||||
"""Tests for file search operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_files_pattern(self, test_files):
|
||||
"""Test searching files by pattern."""
|
||||
result = await test_files.search_files("*.txt", ".", recursive=True)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["matches"] >= 3 # test1.txt, test2.txt, nested.txt
|
||||
assert result["pattern"] == "*.txt"
|
||||
|
||||
# Check results structure
|
||||
if len(result["files"]) > 0:
|
||||
file_info = result["files"][0]
|
||||
assert "path" in file_info
|
||||
assert "size" in file_info
|
||||
assert "size_human" in file_info
|
||||
|
||||
print(f"✅ Found {result['matches']} files matching *.txt")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_files_nonrecursive(self, test_files):
|
||||
"""Test non-recursive search."""
|
||||
result = await test_files.search_files("*.txt", ".", recursive=False)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["recursive"] is False
|
||||
# Should not find nested.txt
|
||||
paths = [f["path"] for f in result["files"]]
|
||||
assert not any("subdir" in p for p in paths)
|
||||
|
||||
print(f"✅ Non-recursive search: {result['matches']} files")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_search_files_json(self, test_files):
|
||||
"""Test searching for specific file type."""
|
||||
result = await test_files.search_files("*.json", ".", recursive=True)
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["matches"] >= 1
|
||||
|
||||
print(f"✅ Found {result['matches']} JSON files")
|
||||
|
||||
|
||||
class TestFileInfo:
|
||||
"""Tests for file information retrieval."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_file_info(self, test_files):
|
||||
"""Test getting file information."""
|
||||
result = await test_files.get_file_info("test2.txt")
|
||||
|
||||
assert result["success"] is True
|
||||
info = result["file_info"]
|
||||
|
||||
assert info["name"] == "test2.txt"
|
||||
assert info["extension"] == ".txt"
|
||||
assert info["is_file"] is True
|
||||
assert info["is_directory"] is False
|
||||
assert info["lines"] == 3
|
||||
assert info["size"] > 0
|
||||
|
||||
print("✅ File info retrieved")
|
||||
print(f" Name: {info['name']}")
|
||||
print(f" Size: {info['size_human']}")
|
||||
print(f" Lines: {info['lines']}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_directory_info(self, test_files):
|
||||
"""Test getting directory information."""
|
||||
result = await test_files.get_file_info("subdir")
|
||||
|
||||
assert result["success"] is True
|
||||
info = result["file_info"]
|
||||
|
||||
assert info["is_directory"] is True
|
||||
assert info["is_file"] is False
|
||||
|
||||
print("✅ Directory info retrieved")
|
||||
|
||||
|
||||
class TestMoveOperations:
|
||||
"""Tests for move and copy operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_move_file(self, test_files):
|
||||
"""Test moving a file."""
|
||||
result = await test_files.move_file("test1.txt", "moved.txt")
|
||||
|
||||
assert result["success"] is True
|
||||
assert Path(Config.WORKSPACE_DIR / "moved.txt").exists()
|
||||
assert not Path(Config.WORKSPACE_DIR / "test1.txt").exists()
|
||||
|
||||
print("✅ File moved successfully")
|
||||
print(f" From: test1.txt")
|
||||
print(f" To: moved.txt")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_move_file_no_overwrite(self, test_files):
|
||||
"""Test move without overwrite."""
|
||||
result = await test_files.move_file("test1.txt", "test2.txt", overwrite=False)
|
||||
|
||||
assert result["success"] is False
|
||||
assert "already exists" in result["error"]
|
||||
|
||||
print("✅ Correctly prevented overwrite")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_copy_file(self, test_files):
|
||||
"""Test copying a file."""
|
||||
result = await test_files.copy_file("test1.txt", "copied.txt")
|
||||
|
||||
assert result["success"] is True
|
||||
assert Path(Config.WORKSPACE_DIR / "copied.txt").exists()
|
||||
assert Path(Config.WORKSPACE_DIR / "test1.txt").exists() # Original still exists
|
||||
|
||||
print("✅ File copied successfully")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_copy_directory(self, test_files):
|
||||
"""Test copying a directory."""
|
||||
result = await test_files.copy_file("subdir", "subdir_copy")
|
||||
|
||||
assert result["success"] is True
|
||||
assert Path(Config.WORKSPACE_DIR / "subdir_copy").exists()
|
||||
assert Path(Config.WORKSPACE_DIR / "subdir_copy" / "nested.txt").exists()
|
||||
|
||||
print("✅ Directory copied recursively")
|
||||
|
||||
|
||||
class TestDeleteOperations:
|
||||
"""Tests for delete operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_file(self, test_files):
|
||||
"""Test deleting a file."""
|
||||
result = await test_files.delete_file("test1.txt")
|
||||
|
||||
assert result["success"] is True
|
||||
assert not Path(Config.WORKSPACE_DIR / "test1.txt").exists()
|
||||
|
||||
print("✅ File deleted")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_directory_recursive(self, test_files):
|
||||
"""Test deleting directory recursively."""
|
||||
result = await test_files.delete_file("subdir", recursive=True)
|
||||
|
||||
assert result["success"] is True
|
||||
assert not Path(Config.WORKSPACE_DIR / "subdir").exists()
|
||||
|
||||
print("✅ Directory deleted recursively")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delete_directory_without_recursive(self, test_files):
|
||||
"""Test that directory delete requires recursive flag."""
|
||||
result = await test_files.delete_file("subdir", recursive=False)
|
||||
|
||||
assert result["success"] is False
|
||||
assert "recursive" in result["error"].lower()
|
||||
|
||||
print("✅ Correctly required recursive flag")
|
||||
|
||||
|
||||
class TestCreateOperations:
|
||||
"""Tests for create operations."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_directory(self, fs):
|
||||
"""Test creating a directory."""
|
||||
result = await fs.create_directory("newdir")
|
||||
|
||||
assert result["success"] is True
|
||||
assert Path(Config.WORKSPACE_DIR / "newdir").exists()
|
||||
|
||||
print("✅ Directory created")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_create_nested_directory(self, fs):
|
||||
"""Test creating nested directories."""
|
||||
result = await fs.create_directory("parent/child/grandchild", parents=True)
|
||||
|
||||
assert result["success"] is True
|
||||
assert Path(Config.WORKSPACE_DIR / "parent" / "child" / "grandchild").exists()
|
||||
|
||||
print("✅ Nested directories created")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_list_allowed_directories(self, fs):
|
||||
"""Test listing allowed directories."""
|
||||
result = await fs.list_allowed_directories()
|
||||
|
||||
assert result["success"] is True
|
||||
assert result["count"] >= 1
|
||||
assert len(result["allowed_directories"]) >= 1
|
||||
|
||||
print(f"✅ Listed {result['count']} allowed directories")
|
||||
|
||||
|
||||
# Run tests
|
||||
if __name__ == "__main__":
|
||||
print("=" * 70)
|
||||
print("Running Enhanced Filesystem Tools Tests")
|
||||
print("=" * 70)
|
||||
print()
|
||||
|
||||
pytest.main([__file__, "-v", "-s"])
|
||||
Reference in New Issue
Block a user