mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
c44291830c
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
259 lines
9.3 KiB
Python
259 lines
9.3 KiB
Python
"""Tests for directory service."""
|
|
|
|
import pytest
|
|
|
|
from basic_memory.services.directory_service import DirectoryService
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_directory_tree_empty(directory_service: DirectoryService):
|
|
"""Test getting empty directory tree."""
|
|
|
|
# When no entities exist, result should just be the root
|
|
result = await directory_service.get_directory_tree()
|
|
assert result is not None
|
|
assert len(result.children) == 0
|
|
|
|
assert result.name == "Root"
|
|
assert result.directory_path == "/"
|
|
assert result.has_children is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_directory_tree(directory_service: DirectoryService, test_graph):
|
|
# test_graph files:
|
|
# /
|
|
# ├── test
|
|
# │ ├── Connected Entity 1.md
|
|
# │ ├── Connected Entity 2.md
|
|
# │ ├── Deep Entity.md
|
|
# │ ├── Deeper Entity.md
|
|
# │ └── Root.md
|
|
|
|
result = await directory_service.get_directory_tree()
|
|
assert result is not None
|
|
assert len(result.children) == 1
|
|
|
|
node_0 = result.children[0]
|
|
assert node_0.name == "test"
|
|
assert node_0.type == "directory"
|
|
assert node_0.content_type is None
|
|
assert node_0.entity_id is None
|
|
assert node_0.note_type is None
|
|
assert node_0.title is None
|
|
assert node_0.directory_path == "/test"
|
|
assert node_0.has_children is True
|
|
assert len(node_0.children) == 5
|
|
|
|
# assert one file node
|
|
node_file = node_0.children[0]
|
|
assert node_file.name == "Deeper Entity.md"
|
|
assert node_file.type == "file"
|
|
assert node_file.content_type == "text/markdown"
|
|
assert node_file.entity_id == 1
|
|
assert node_file.note_type == "deeper"
|
|
assert node_file.title == "Deeper Entity"
|
|
assert node_file.permalink == "test-project/test/deeper-entity"
|
|
assert node_file.directory_path == "/test/Deeper Entity.md"
|
|
assert node_file.file_path == "test/Deeper Entity.md"
|
|
assert node_file.has_children is False
|
|
assert len(node_file.children) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_empty(directory_service: DirectoryService):
|
|
"""Test listing directory with no entities."""
|
|
result = await directory_service.list_directory()
|
|
assert result == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_root(directory_service: DirectoryService, test_graph):
|
|
"""Test listing root directory contents."""
|
|
result = await directory_service.list_directory(dir_name="/")
|
|
|
|
# Should return immediate children of root (the "test" directory)
|
|
assert len(result) == 1
|
|
assert result[0].name == "test"
|
|
assert result[0].type == "directory"
|
|
assert result[0].directory_path == "/test"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_specific_path(directory_service: DirectoryService, test_graph):
|
|
"""Test listing specific directory contents."""
|
|
result = await directory_service.list_directory(dir_name="/test")
|
|
|
|
# Should return the 5 files in the test directory
|
|
assert len(result) == 5
|
|
file_names = {node.name for node in result}
|
|
expected_files = {
|
|
"Connected Entity 1.md",
|
|
"Connected Entity 2.md",
|
|
"Deep Entity.md",
|
|
"Deeper Entity.md",
|
|
"Root.md",
|
|
}
|
|
assert file_names == expected_files
|
|
|
|
# All should be files
|
|
for node in result:
|
|
assert node.type == "file"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_nonexistent_path(directory_service: DirectoryService, test_graph):
|
|
"""Test listing nonexistent directory."""
|
|
result = await directory_service.list_directory(dir_name="/nonexistent")
|
|
assert result == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_with_glob_filter(directory_service: DirectoryService, test_graph):
|
|
"""Test listing directory with glob pattern filtering."""
|
|
# Filter for files containing "Connected"
|
|
result = await directory_service.list_directory(dir_name="/test", file_name_glob="*Connected*")
|
|
|
|
assert len(result) == 2
|
|
file_names = {node.name for node in result}
|
|
assert file_names == {"Connected Entity 1.md", "Connected Entity 2.md"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_with_markdown_filter(directory_service: DirectoryService, test_graph):
|
|
"""Test listing directory with markdown file filter."""
|
|
result = await directory_service.list_directory(dir_name="/test", file_name_glob="*.md")
|
|
|
|
# All files in test_graph are markdown files
|
|
assert len(result) == 5
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_with_specific_file_filter(
|
|
directory_service: DirectoryService, test_graph
|
|
):
|
|
"""Test listing directory with specific file pattern."""
|
|
result = await directory_service.list_directory(dir_name="/test", file_name_glob="Root.*")
|
|
|
|
assert len(result) == 1
|
|
assert result[0].name == "Root.md"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_depth_control(directory_service: DirectoryService, test_graph):
|
|
"""Test listing directory with depth control."""
|
|
# Depth 1 should only return immediate children
|
|
result_depth_1 = await directory_service.list_directory(dir_name="/", depth=1)
|
|
assert len(result_depth_1) == 1 # Just the "test" directory
|
|
|
|
# Depth 2 should return directory + its contents
|
|
result_depth_2 = await directory_service.list_directory(dir_name="/", depth=2)
|
|
assert len(result_depth_2) == 6 # "test" directory + 5 files in it
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_path_normalization(directory_service: DirectoryService, test_graph):
|
|
"""Test that directory paths are normalized correctly."""
|
|
# Test various path formats that should all be equivalent
|
|
paths_to_test = ["/test", "test", "/test/", "test/"]
|
|
|
|
base_result = await directory_service.list_directory(dir_name="/test")
|
|
|
|
for path in paths_to_test:
|
|
result = await directory_service.list_directory(dir_name=path)
|
|
assert len(result) == len(base_result)
|
|
# Compare by name since the objects might be different instances
|
|
result_names = {node.name for node in result}
|
|
base_names = {node.name for node in base_result}
|
|
assert result_names == base_names
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_dot_slash_prefix_normalization(
|
|
directory_service: DirectoryService, test_graph
|
|
):
|
|
"""Test that ./ prefixed directory paths are normalized correctly."""
|
|
# This test reproduces the bug report issue where ./dirname fails
|
|
base_result = await directory_service.list_directory(dir_name="/test")
|
|
|
|
# Test paths with ./ prefix that should be equivalent to /test
|
|
dot_paths_to_test = ["./test", "./test/"]
|
|
|
|
for path in dot_paths_to_test:
|
|
result = await directory_service.list_directory(dir_name=path)
|
|
assert len(result) == len(base_result), (
|
|
f"Path '{path}' returned {len(result)} results, expected {len(base_result)}"
|
|
)
|
|
# Compare by name since the objects might be different instances
|
|
result_names = {node.name for node in result}
|
|
base_names = {node.name for node in base_result}
|
|
assert result_names == base_names, f"Path '{path}' returned different files than expected"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_glob_no_matches(directory_service: DirectoryService, test_graph):
|
|
"""Test listing directory with glob that matches nothing."""
|
|
result = await directory_service.list_directory(
|
|
dir_name="/test", file_name_glob="*.nonexistent"
|
|
)
|
|
assert result == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_directory_default_parameters(directory_service: DirectoryService, test_graph):
|
|
"""Test listing directory with default parameters."""
|
|
# Should default to root directory, depth 1, no glob filter
|
|
result = await directory_service.list_directory()
|
|
|
|
assert len(result) == 1
|
|
assert result[0].name == "test"
|
|
assert result[0].type == "directory"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_directory_structure_empty(directory_service: DirectoryService):
|
|
"""Test getting empty directory structure."""
|
|
# When no entities exist, result should just be the root
|
|
result = await directory_service.get_directory_structure()
|
|
assert result is not None
|
|
assert len(result.children) == 0
|
|
|
|
assert result.name == "Root"
|
|
assert result.directory_path == "/"
|
|
assert result.type == "directory"
|
|
assert result.has_children is False
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_directory_structure(directory_service: DirectoryService, test_graph):
|
|
"""Test getting directory structure with folders only (no files)."""
|
|
# test_graph files:
|
|
# /
|
|
# ├── test
|
|
# │ ├── Connected Entity 1.md
|
|
# │ ├── Connected Entity 2.md
|
|
# │ ├── Deep Entity.md
|
|
# │ ├── Deeper Entity.md
|
|
# │ └── Root.md
|
|
|
|
result = await directory_service.get_directory_structure()
|
|
assert result is not None
|
|
assert len(result.children) == 1
|
|
|
|
# Should only have the "test" directory, not the files
|
|
node_0 = result.children[0]
|
|
assert node_0.name == "test"
|
|
assert node_0.type == "directory"
|
|
assert node_0.directory_path == "/test"
|
|
assert node_0.has_children is False # No subdirectories, only files
|
|
|
|
# Verify no file metadata is present
|
|
assert node_0.content_type is None
|
|
assert node_0.entity_id is None
|
|
assert node_0.note_type is None
|
|
assert node_0.title is None
|
|
assert node_0.permalink is None
|
|
|
|
# No file nodes should be present
|
|
assert len(node_0.children) == 0
|