fix: add configurable kebab-case filename format to resolve forward slash inconsistency

- Add filename_format config option with "kebab-case" as default
- Create sanitize_filename() utility to convert titles to safe filenames
- Modify Entity.file_path to use sanitized filenames when configured
- Add comprehensive tests for new functionality
- Resolves inconsistency between file paths and permalinks for titles with forward slashes

Fixes #154

Co-authored-by: Drew Cain <groksrc@users.noreply.github.com>
This commit is contained in:
claude[bot]
2025-06-20 13:20:04 +00:00
committed by GitHub
parent d3b6c85184
commit 825418c712
4 changed files with 198 additions and 4 deletions
+90 -1
View File
@@ -7,7 +7,7 @@ import pytest
from basic_memory.config import ProjectConfig
from basic_memory.services import EntityService
from basic_memory.sync.sync_service import SyncService
from basic_memory.utils import generate_permalink
from basic_memory.utils import generate_permalink, sanitize_filename
async def create_test_file(path: Path, content: str = "test content") -> None:
@@ -118,3 +118,92 @@ def test_chinese_character_preservation(input_path, expected):
def test_mixed_character_sets(input_path, expected):
"""Test handling of mixed character sets and edge cases."""
assert generate_permalink(input_path) == expected
@pytest.mark.parametrize(
"input_title, expected",
[
("Coupon Enable/Disable Feature", "coupon-enable-disable-feature"),
("My Awesome Feature", "my-awesome-feature"),
("Test_File Name.txt", "test-file-name-txt"),
("Feature/With/Multiple/Slashes", "feature-with-multiple-slashes"),
("Mixed Case Feature", "mixed-case-feature"),
(" Leading and trailing spaces ", "leading-and-trailing-spaces"),
("Special!@#$%^&*()Characters", "special-characters"),
("北京/东京", "北京-东京"),
("Mixed/中文/English", "mixed-中文-english"),
],
)
def test_sanitize_filename(input_title, expected):
"""Test that title sanitization works correctly for filenames."""
assert sanitize_filename(input_title) == expected
@pytest.mark.asyncio
async def test_entity_file_path_with_kebab_case_config(
sync_service: SyncService, project_config: ProjectConfig, entity_service: EntityService
):
"""Test that Entity file_path uses kebab-case when configured."""
# Test with kebab-case enabled (default)
from basic_memory.schemas.base import Entity
from basic_memory.config import app_config
# Ensure config is set to kebab-case
original_format = app_config.filename_format
app_config.filename_format = "kebab-case"
try:
entity = Entity(
title="Coupon Enable/Disable Feature",
folder="bugs",
content="Test content"
)
# With kebab-case, forward slashes should be converted to hyphens
expected_file_path = "bugs/coupon-enable-disable-feature.md"
expected_permalink = "bugs/coupon-enable-disable-feature"
assert entity.file_path == expected_file_path
assert entity.permalink == expected_permalink
# Test that file_path and permalink are now consistent
assert entity.permalink == generate_permalink(entity.file_path)
finally:
# Restore original configuration
app_config.filename_format = original_format
@pytest.mark.asyncio
async def test_entity_file_path_with_original_config(
sync_service: SyncService, project_config: ProjectConfig, entity_service: EntityService
):
"""Test that Entity file_path preserves original format when configured."""
from basic_memory.schemas.base import Entity
from basic_memory.config import app_config
# Set config to original format
original_format = app_config.filename_format
app_config.filename_format = "original"
try:
entity = Entity(
title="Coupon Enable/Disable Feature",
folder="bugs",
content="Test content"
)
# With original format, spaces and slashes are preserved
# This creates the inconsistency that the issue reports
expected_file_path = "bugs/Coupon Enable/Disable Feature.md"
expected_permalink = "bugs/coupon-enable-disable-feature"
assert entity.file_path == expected_file_path
assert entity.permalink == expected_permalink
# This demonstrates the inconsistency when using original format
assert entity.permalink != generate_permalink(entity.file_path)
finally:
# Restore original configuration
app_config.filename_format = original_format