Files
basicmachines-co-basic-memory/tests/services/test_link_resolver.py
T
2025-01-26 00:00:37 -06:00

152 lines
5.0 KiB
Python

"""Tests for link resolution service."""
import pytest
from datetime import datetime, timezone
import pytest_asyncio
from basic_memory.models.knowledge import Entity
from basic_memory.services.link_resolver import LinkResolver
@pytest_asyncio.fixture
async def test_entities(entity_repository, file_service):
"""Create a set of test entities."""
entities = [
Entity(
title="Core Service",
entity_type="component",
permalink="components/core-service",
file_path="components/core-service.md",
content_type="text/markdown",
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
),
Entity(
title="Service Config",
entity_type="config",
permalink="config/service-config",
file_path="config/service-config.md",
content_type="text/markdown",
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
),
Entity(
title="Auth Service",
entity_type="component",
permalink="components/auth/service",
file_path="components/auth/service.md",
content_type="text/markdown",
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
),
Entity(
title="Core Features",
entity_type="specs",
permalink="specs/features/core",
file_path="specs/features/core.md",
content_type="text/markdown",
created_at=datetime.now(timezone.utc),
updated_at=datetime.now(timezone.utc),
),
]
for entity in entities:
await file_service.write_entity_file(entity)
# Add to repository
return await entity_repository.add_all(entities)
@pytest_asyncio.fixture
async def link_resolver(entity_repository, search_service, test_entities):
"""Create LinkResolver instance with indexed test data."""
# Index all test entities
for entity in test_entities:
await search_service.index_entity(entity)
return LinkResolver(entity_repository, search_service)
@pytest.mark.asyncio
async def test_exact_permalink_match(link_resolver, test_entities):
"""Test resolving a link that exactly matches a permalink."""
entity = await link_resolver.resolve_link("components/core-service")
assert entity.permalink == "components/core-service"
@pytest.mark.asyncio
async def test_exact_title_match(link_resolver, test_entities):
"""Test resolving a link that matches an entity title."""
entity = await link_resolver.resolve_link("Core Service")
assert entity.permalink == "components/core-service"
@pytest.mark.skip(reason="Fuzzy misspelling not yet implemented")
@pytest.mark.asyncio
async def test_fuzzy_title_match_misspelling(link_resolver):
# Test slight misspelling
result = await link_resolver.resolve_link("Core Servise")
assert result.permalink == "components/core-service"
@pytest.mark.asyncio
async def test_fuzzy_title_partial_match(link_resolver):
# Test partial match
result = await link_resolver.resolve_link("Auth Serv")
assert result.permalink == "components/auth/service"
@pytest.mark.asyncio
async def test_link_text_normalization(link_resolver):
"""Test link text normalization."""
# Basic normalization
text, alias = link_resolver._normalize_link_text("[[Core Service]]")
assert text == "Core Service"
assert alias is None
# With alias
text, alias = link_resolver._normalize_link_text("[[Core Service|Main Service]]")
assert text == "Core Service"
assert alias == "Main Service"
# Extra whitespace
text, alias = link_resolver._normalize_link_text(" [[ Core Service | Main Service ]] ")
assert text == "Core Service"
assert alias == "Main Service"
@pytest.mark.asyncio
async def test_resolve_none(link_resolver):
"""Test resolving non-existent entity."""
# Basic new entity
assert await link_resolver.resolve_link("New Feature") is None
@pytest.mark.skip("Advanced relevance scoring not yet implemented")
@pytest.mark.asyncio
async def test_multiple_matches_resolution(link_resolver):
"""Test resolution when multiple potential matches exist."""
# Add some similar entities
test_cases = [
{
"link": "Service", # Ambiguous
"expected_prefix": "components/", # Should prefer component directory match
},
{
"link": "Core", # Ambiguous
"expected_prefix": "specs/", # Should prefer specs directory match
},
{
"link": "Service",
"expected": "components/core-service", # Should pick shortest/highest scored
},
]
for case in test_cases:
result = await link_resolver.resolve_link(case["link"])
if "expected_prefix" in case:
assert result.startswith(case["expected_prefix"])
else:
assert result == case["expected"]