fixes for sync

This commit is contained in:
phernandez
2025-01-18 20:04:45 -06:00
parent 7322bb5350
commit d65fca0a3c
8 changed files with 140 additions and 13 deletions
+1 -1
View File
@@ -14,7 +14,7 @@ __all__ = ["init", "status", "sync"]
from basic_memory.config import config
def setup_logging(home_dir: str = config.home, log_file: str = "basic-memory-tools.log"):
def setup_logging(home_dir: str = config.home, log_file: str = "./basic-memory/basic-memory-tools.log"):
"""Configure logging for the application."""
# Remove default handler and any existing handlers
-1
View File
@@ -10,7 +10,6 @@ from basic_memory.schemas.memory import GraphContext, MemoryUrl
@mcp.tool(
name="Build Context",
description="Build context from a memory:// URI to continue conversations naturally.",
)
async def build_context(
+12 -6
View File
@@ -30,13 +30,16 @@ def generate_permalink(file_path: str) -> str:
file_path: Original file path
Returns:
Normalized permalink that matches validation rules
Normalized permalink that matches validation rules. Converts spaces and underscores
to hyphens for consistency.
Examples:
>>> generate_permalink("docs/My Feature.md")
'docs/my-feature'
>>> generate_permalink("specs/API (v2).md")
'specs/api-v2'
>>> generate_permalink("design/unified_model_refactor.md")
'design/unified-model-refactor'
"""
# Remove extension
base = os.path.splitext(file_path)[0]
@@ -47,8 +50,11 @@ def generate_permalink(file_path: str) -> str:
# Convert to lowercase
lower_text = ascii_text.lower()
# Replace spaces and invalid chars with hyphens
clean_text = re.sub(r'[^a-z0-9/\-_]', '-', lower_text)
# First replace underscores with hyphens
text_with_hyphens = lower_text.replace('_', '-')
# Replace remaining invalid chars with hyphens
clean_text = re.sub(r'[^a-z0-9/\-]', '-', text_with_hyphens)
# Collapse multiple hyphens
clean_text = re.sub(r'-+', '-', clean_text)
@@ -130,17 +136,17 @@ class Entity(Base):
Requirements:
1. Must be valid URI path component
2. Only lowercase letters, numbers, hyphens, and underscores
2. Only lowercase letters, numbers, and hyphens (no underscores)
3. Path segments separated by forward slashes
4. No leading/trailing hyphens in segments
"""
if not value:
raise ValueError("Permalink must not be None")
if not re.match(r'^[a-z0-9][a-z0-9\-_/]*[a-z0-9]$', value):
if not re.match(r'^[a-z0-9][a-z0-9\-/]*[a-z0-9]$', value):
raise ValueError(
f"Invalid permalink format: {value}. "
"Use only lowercase letters, numbers, hyphens, and underscores."
"Use only lowercase letters, numbers, and hyphens."
)
return value
@@ -74,10 +74,16 @@ class SearchRepository:
await session.commit()
def _quote_search_term(self, term: str) -> str:
"""Add quotes if term contains special characters or /.
For FTS5, phrases with / need to be quoted to be treated as a single token.
"""Add quotes if term contains special characters.
For FTS5, special characters and phrases need to be quoted to be treated as a single token.
"""
if '/' in term or '*' in term or any(c in term for c in "-"):
# List of special characters that need quoting
special_chars = ['/', '*', '-', '.', ' ', '(', ')', '[', ']', '"', "'"]
# Check if term contains any special characters
if any(c in term for c in special_chars):
# If the term already contains quotes, escape them
term = term.replace('"', '""')
return f'"{term}"'
return term
@@ -229,4 +235,4 @@ class SearchRepository:
else:
result = await session.execute(query)
logger.debug("Query executed successfully")
return result
return result
+1 -1
View File
@@ -123,5 +123,5 @@ class LinkResolver():
scored_results.append((score, result))
# Sort by score (lowest first) and return best
scored_results.sort()
scored_results.sort(key=lambda x: x[0], reverse=True)
return scored_results[0][1]
+17
View File
@@ -0,0 +1,17 @@
"""Test file for experimenting with edit_file."""
def function_one():
"""First test function."""
print("Hello from function one")
# Some code here
x = 1 + 2
return x
def function_two():
"""Second test function."""
print("Hello from function two")
# Some more code
y = 3 * 4
return y
+37
View File
@@ -420,7 +420,44 @@ modified: 2024-01-01
assert doc is not None
# File should have a checksum, even if it's from either version
assert doc.checksum is not None
@pytest.mark.asyncio
async def test_permalink_formatting(sync_service: SyncService, test_config: ProjectConfig, entity_service: EntityService):
"""Test that permalinks are properly formatted during sync."""
# Test cases with different filename formats
test_files = {
# filename -> expected permalink
"my_awesome_feature.md": "my-awesome-feature",
"MIXED_CASE_NAME.md": "mixed-case-name",
"spaces and_underscores.md": "spaces-and-underscores",
"design/model_refactor.md": "design/model-refactor",
"test/multiple_word_directory/feature_name.md": "test/multiple-word-directory/feature-name",
}
# Create test files
for filename, _ in test_files.items():
content: str = """
---
type: knowledge
created: 2024-01-01
modified: 2024-01-01
---
# Test File
Testing permalink generation.
"""
await create_test_file(test_config.home / filename, content)
# Run sync
await sync_service.sync(test_config.home)
# Verify permalinks
entities = await entity_service.repository.find_all()
for filename, expected_permalink in test_files.items():
# Find entity for this file
entity = next(e for e in entities if e.file_path == filename)
assert entity.permalink == expected_permalink, f"File {filename} should have permalink {expected_permalink}"
@pytest.mark.asyncio
async def test_sync_null_checksum_cleanup(
+62
View File
@@ -0,0 +1,62 @@
"""Test permalink formatting during sync."""
import pytest
from pathlib import Path
from basic_memory.config import ProjectConfig
from basic_memory.services import EntityService
from basic_memory.sync.sync_service import SyncService
async def create_test_file(path: Path, content: str = "test content") -> None:
"""Create a test file with given content."""
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content)
@pytest.mark.asyncio
async def test_permalink_formatting(
sync_service: SyncService, test_config: ProjectConfig, entity_service: EntityService
):
"""Test that permalinks are properly formatted during sync.
This ensures:
- Underscores are converted to hyphens
- Spaces are converted to hyphens
- Mixed case is lowercased
- Directory structure is preserved
- Multiple directories work correctly
"""
project_dir = test_config.home
# Test cases with different filename formats
test_cases = [
# filename -> expected permalink
("my_awesome_feature.md", "my-awesome-feature"),
("MIXED_CASE_NAME.md", "mixed-case-name"),
("spaces and_underscores.md", "spaces-and-underscores"),
("design/model_refactor.md", "design/model-refactor"),
("test/multiple_word_directory/feature_name.md", "test/multiple-word-directory/feature-name"),
]
# Create test files
for filename, _ in test_cases:
content = f"""
---
type: knowledge
created: 2024-01-01
modified: 2024-01-01
---
# Test File
Testing permalink generation.
"""
await create_test_file(project_dir / filename, content)
# Run sync
await sync_service.sync(test_config.home)
# Verify permalinks
for filename, expected_permalink in test_cases:
entity = await entity_service.repository.get_by_file_path(filename)
assert entity.permalink == expected_permalink, \
f"File {filename} should have permalink {expected_permalink}"