Skip to main content
Glama
test_vfs_tools.py11.9 kB
""" Tests for VFS Tools """ import pytest from chuk_mcp_vfs.models import ( CopyRequest, FindRequest, GrepRequest, ProviderType, WriteRequest, ) from chuk_mcp_vfs.vfs_tools import VFSTools from chuk_mcp_vfs.workspace_manager import WorkspaceManager @pytest.fixture def setup_workspace(): """Fixture to create a workspace for testing.""" async def _setup(): manager = WorkspaceManager() await manager.create_workspace(name="test", provider_type=ProviderType.MEMORY) tools = VFSTools(manager) return tools return _setup @pytest.mark.asyncio async def test_write_and_read(setup_workspace): """Test writing and reading a file.""" tools = await setup_workspace() # Write file write_resp = await tools.write( WriteRequest(path="/test.txt", content="Hello World") ) assert write_resp.success assert write_resp.path == "/test.txt" # Read file read_resp = await tools.read("/test.txt") assert read_resp.content == "Hello World" assert read_resp.path == "/test.txt" @pytest.mark.asyncio async def test_mkdir_and_ls(setup_workspace): """Test creating directory and listing.""" tools = await setup_workspace() # Create directory mkdir_resp = await tools.mkdir("/testdir") assert mkdir_resp.success assert mkdir_resp.path == "/testdir" # List root ls_resp = await tools.ls("/") # Should have testdir and possibly .workspace assert len(ls_resp.entries) >= 1 testdir_entries = [e for e in ls_resp.entries if e.name == "testdir"] assert len(testdir_entries) == 1 assert testdir_entries[0].type.value == "directory" @pytest.mark.asyncio async def test_cd_and_pwd(setup_workspace): """Test changing directory and getting working directory.""" tools = await setup_workspace() # Create directory await tools.mkdir("/testdir") # Change directory cd_resp = await tools.cd("/testdir") assert cd_resp.success assert cd_resp.cwd == "/testdir" # Get current directory pwd_resp = await tools.pwd() assert pwd_resp.cwd == "/testdir" @pytest.mark.asyncio async def test_tree(setup_workspace): """Test tree structure.""" tools = await setup_workspace() # Create directory structure await tools.mkdir("/dir1") await tools.mkdir("/dir1/subdir") await tools.write(WriteRequest(path="/dir1/file.txt", content="test")) # Get tree tree_resp = await tools.tree("/") assert tree_resp.root.name == "/" assert tree_resp.root.children is not None assert len(tree_resp.root.children) >= 1 @pytest.mark.asyncio async def test_rm_file(setup_workspace): """Test removing a file.""" tools = await setup_workspace() # Create file await tools.write(WriteRequest(path="/remove_me.txt", content="delete this")) # Remove file rm_resp = await tools.rm("/remove_me.txt") assert rm_resp.success assert rm_resp.path == "/remove_me.txt" # Verify removed ls_resp = await tools.ls("/") filenames = [e.name for e in ls_resp.entries] assert "remove_me.txt" not in filenames @pytest.mark.asyncio async def test_rm_empty_directory(setup_workspace): """Test removing an empty directory.""" tools = await setup_workspace() # Create empty directory await tools.mkdir("/remove_dir") # Remove directory recursively rm_resp = await tools.rm("/remove_dir", recursive=True) assert rm_resp.success @pytest.mark.asyncio async def test_mv(setup_workspace): """Test moving/renaming files.""" tools = await setup_workspace() # Create file await tools.write(WriteRequest(path="/old_name.txt", content="move me")) # Move file mv_resp = await tools.mv("/old_name.txt", "/new_name.txt") assert mv_resp.success assert mv_resp.source == "/old_name.txt" assert mv_resp.dest == "/new_name.txt" # Verify moved ls_resp = await tools.ls("/") filenames = [e.name for e in ls_resp.entries] assert "old_name.txt" not in filenames assert "new_name.txt" in filenames @pytest.mark.asyncio async def test_cp(setup_workspace): """Test copying files.""" tools = await setup_workspace() # Create file await tools.write(WriteRequest(path="/original.txt", content="copy me")) # Copy file cp_req = CopyRequest(source="/original.txt", dest="/copy.txt", recursive=False) cp_resp = await tools.cp(cp_req) assert cp_resp.success assert cp_resp.source == "/original.txt" assert cp_resp.dest == "/copy.txt" # Verify both exist ls_resp = await tools.ls("/") filenames = [e.name for e in ls_resp.entries] assert "original.txt" in filenames assert "copy.txt" in filenames @pytest.mark.asyncio async def test_find(setup_workspace): """Test finding files by pattern.""" tools = await setup_workspace() # Create test files await tools.mkdir("/search") await tools.write(WriteRequest(path="/search/file1.txt", content="test")) await tools.write(WriteRequest(path="/search/file2.txt", content="test")) await tools.write(WriteRequest(path="/search/other.md", content="test")) # Find txt files find_req = FindRequest(pattern="*.txt", path="/search", max_results=100) find_resp = await tools.find(find_req) assert len(find_resp.matches) == 2 assert "/search/file1.txt" in find_resp.matches assert "/search/file2.txt" in find_resp.matches assert not find_resp.truncated @pytest.mark.asyncio async def test_grep(setup_workspace): """Test searching file contents.""" tools = await setup_workspace() # Create files with content await tools.mkdir("/grep_test") await tools.write(WriteRequest(path="/grep_test/file1.txt", content="hello world")) await tools.write( WriteRequest(path="/grep_test/file2.txt", content="foo bar\nhello there") ) # Search for "hello" grep_req = GrepRequest(pattern="hello", path="/grep_test", max_results=100) grep_resp = await tools.grep(grep_req) assert len(grep_resp.matches) == 2 files_matched = {m.file for m in grep_resp.matches} assert "/grep_test/file1.txt" in files_matched assert "/grep_test/file2.txt" in files_matched @pytest.mark.asyncio async def test_write_with_nested_path(setup_workspace): """Test writing to nested paths.""" tools = await setup_workspace() # Create parent first await tools.mkdir("/deep") await tools.mkdir("/deep/nested") # Write to nested path write_resp = await tools.write( WriteRequest(path="/deep/nested/file.txt", content="test") ) assert write_resp.success # Verify file exists read_resp = await tools.read("/deep/nested/file.txt") assert read_resp.content == "test" @pytest.mark.asyncio async def test_cd_invalid_directory(setup_workspace): """Test cd to invalid directory raises error.""" tools = await setup_workspace() with pytest.raises(ValueError, match="Not a directory"): await tools.cd("/nonexistent") @pytest.mark.asyncio async def test_rm_nonexistent_path(setup_workspace): """Test removing non-existent path raises error.""" tools = await setup_workspace() with pytest.raises(ValueError, match="Path not found"): await tools.rm("/nonexistent.txt") @pytest.mark.asyncio async def test_rm_directory_without_recursive(setup_workspace): """Test removing directory without recursive flag raises error.""" tools = await setup_workspace() await tools.mkdir("/somedir") with pytest.raises(ValueError, match="Cannot remove directory"): await tools.rm("/somedir", recursive=False) @pytest.mark.asyncio async def test_ls_default_path(setup_workspace): """Test ls with default path (.).""" tools = await setup_workspace() # List current directory (defaults to /) ls_resp = await tools.ls() assert ls_resp.path == "/" assert isinstance(ls_resp.entries, list) @pytest.mark.asyncio async def test_tree_max_depth(setup_workspace): """Test tree with max depth limit.""" tools = await setup_workspace() # Create deep structure await tools.mkdir("/a") await tools.mkdir("/a/b") await tools.mkdir("/a/b/c") await tools.mkdir("/a/b/c/d") # Get tree with depth limit tree_resp = await tools.tree("/", max_depth=2) assert tree_resp.root.name == "/" assert tree_resp.root.children is not None @pytest.mark.asyncio async def test_find_truncated(setup_workspace): """Test find with result truncation.""" tools = await setup_workspace() # Create many files await tools.mkdir("/many") for i in range(10): await tools.write(WriteRequest(path=f"/many/file{i}.txt", content=f"file {i}")) # Find with small max_results find_req = FindRequest(pattern="*.txt", path="/many", max_results=5) find_resp = await tools.find(find_req) assert len(find_resp.matches) == 5 assert find_resp.truncated is True @pytest.mark.asyncio async def test_grep_truncated(setup_workspace): """Test grep with result truncation.""" tools = await setup_workspace() # Create files with pattern await tools.mkdir("/greptest") for i in range(10): await tools.write( WriteRequest(path=f"/greptest/file{i}.txt", content=f"match pattern {i}") ) # Grep with small max_results grep_req = GrepRequest(pattern="pattern", path="/greptest", max_results=5) grep_resp = await tools.grep(grep_req) assert len(grep_resp.matches) == 5 assert grep_resp.truncated is True @pytest.mark.asyncio async def test_grep_single_file(setup_workspace): """Test grep on a single file.""" tools = await setup_workspace() await tools.write( WriteRequest(path="/single.txt", content="line 1\nline 2\nline 3") ) grep_req = GrepRequest(pattern="line", path="/single.txt", max_results=100) grep_resp = await tools.grep(grep_req) assert len(grep_resp.matches) == 3 @pytest.mark.asyncio async def test_grep_nonexistent_path(setup_workspace): """Test grep on non-existent path raises error.""" tools = await setup_workspace() grep_req = GrepRequest(pattern="test", path="/nonexistent", max_results=100) with pytest.raises(ValueError, match="Path not found"): await tools.grep(grep_req) @pytest.mark.asyncio async def test_cd_to_file(setup_workspace): """Test cd to a file raises error.""" tools = await setup_workspace() await tools.write(WriteRequest(path="/file.txt", content="test")) with pytest.raises(ValueError, match="Not a directory"): await tools.cd("/file.txt") @pytest.mark.asyncio async def test_ls_with_empty_entries(setup_workspace): """Test ls on empty directory.""" tools = await setup_workspace() await tools.mkdir("/empty") ls_resp = await tools.ls("/empty") assert ls_resp.path == "/empty" assert len(ls_resp.entries) == 0 @pytest.mark.asyncio async def test_write_creates_parent_directory(setup_workspace): """Test that write creates parent directory if it doesn't exist.""" tools = await setup_workspace() # Write to nested path without creating parents first write_resp = await tools.write( WriteRequest(path="/auto/created/file.txt", content="test content") ) assert write_resp.success assert write_resp.path == "/auto/created/file.txt" # Verify we can read it back read_resp = await tools.read("/auto/created/file.txt") assert read_resp.content == "test content" @pytest.mark.asyncio async def test_read_nonexistent_file(setup_workspace): """Test reading non-existent file raises error.""" tools = await setup_workspace() with pytest.raises(ValueError, match="Could not read file"): await tools.read("/nonexistent.txt")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrishayuk/chuk-mcp-vfs'

If you have feedback or need assistance with the MCP directory API, please join our Discord server