Files
basicmachines-co-basic-memory/tests/sync/test_knowledge_sync_service.py
T
2025-01-11 00:49:11 -06:00

197 lines
6.4 KiB
Python

"""Tests for EntitySyncService."""
from datetime import datetime
import pytest
import pytest_asyncio
from basic_memory.markdown.schemas import (
EntityMarkdown,
EntityContent,
EntityFrontmatter,
Observation as MarkdownObservation,
Relation as MarkdownRelation,
)
from basic_memory.models import Entity as EntityModel
from basic_memory.sync.entity_sync_service import EntitySyncService
@pytest_asyncio.fixture
def test_frontmatter() -> EntityFrontmatter:
"""Create test frontmatter."""
return EntityFrontmatter(
title="Test Entity",
type="knowledge",
id="concept/test_entity",
created=datetime.now(),
modified=datetime.now(),
tags=["test", "sync"],
)
@pytest_asyncio.fixture
def test_content() -> EntityContent:
"""Create test content with observations and relations."""
return EntityContent(
content="A test entity description",
observations=[
MarkdownObservation(content="First observation"),
MarkdownObservation(content="Second observation"),
],
relations=[
MarkdownRelation(type="depends_on", target="concept/other_entity"),
MarkdownRelation(type="related_to", target="concept/another_entity"),
],
)
@pytest_asyncio.fixture
def test_markdown(test_frontmatter, test_content) -> EntityMarkdown:
"""Create complete test markdown entity."""
return EntityMarkdown(
frontmatter=test_frontmatter, content=test_content
)
@pytest.mark.asyncio
async def test_create_entity_without_relations(
knowledge_sync_service: EntitySyncService, test_markdown: EntityMarkdown
):
"""Test first pass creation without relations."""
# Create entity first pass
entity = await knowledge_sync_service.create_entity_from_markdown("test.md", test_markdown)
# Check basic fields
assert entity.name == "Test Entity"
assert entity.entity_type == "knowledge"
assert entity.path_id == "concept/test_entity"
assert entity.summary == "A test entity description"
# Check observations
assert len(entity.observations) == 2
assert entity.observations[0].content == "First observation"
assert entity.observations[1].content == "Second observation"
# Check no relations added
assert len(entity.relations) == 0
# Check checksum is None (indicating incomplete sync)
assert entity.checksum is None
@pytest.mark.asyncio
async def test_update_entity_without_relations(
knowledge_sync_service: EntitySyncService, test_markdown: EntityMarkdown
):
"""Test first pass update."""
# First create entity
entity = await knowledge_sync_service.create_entity_from_markdown("test.md", test_markdown)
# Modify markdown content
test_markdown.frontmatter.title = "Updated Title"
test_markdown.content.content = "Updated description"
test_markdown.content.observations = [MarkdownObservation(content="Updated observation")]
# Update entity
updated = await knowledge_sync_service.update_entity_and_observations(
entity.path_id, test_markdown
)
# Check fields updated
assert updated.name == "Updated Title"
assert updated.summary == "Updated description"
assert len(updated.observations) == 1
assert updated.observations[0].content == "Updated observation"
# Check checksum cleared
assert updated.checksum is None
@pytest.mark.asyncio
async def test_update_entity_relations(
knowledge_sync_service: EntitySyncService, test_markdown: EntityMarkdown
):
"""Test second pass relation updates."""
# Create main entity first
entity = await knowledge_sync_service.create_entity_from_markdown("test.md", test_markdown)
# Create target entities that relations point to
other_entity = EntityModel(
name="Other Entity",
entity_type="test",
path_id="concept/other_entity",
file_path="concept/other_entity.md",
content_type="text/markdown",
)
another_entity = EntityModel(
name="Another Entity",
entity_type="test",
path_id="concept/another_entity",
file_path="concept/another_entity.md",
content_type="text/markdown",
)
await knowledge_sync_service.entity_repository.add(other_entity)
await knowledge_sync_service.entity_repository.add(another_entity)
# Update relations and set checksum
test_checksum = "test-checksum-123"
updated = await knowledge_sync_service.update_entity_relations(test_markdown, test_checksum)
# Check relations
assert len(updated.relations) == 2
# Check relation details
relations = sorted(updated.relations, key=lambda r: r.relation_type)
assert relations[0].relation_type == "depends_on"
assert relations[0].from_id == entity.id
assert relations[0].to_id == other_entity.id
assert relations[1].relation_type == "related_to"
assert relations[1].from_id == entity.id
assert relations[1].to_id == another_entity.id
# Check checksum set
assert updated.checksum == test_checksum
@pytest.mark.asyncio
async def test_two_pass_sync_flow(
knowledge_sync_service: EntitySyncService, test_markdown: EntityMarkdown
):
"""Test complete two-pass sync flow."""
# Create target entities first
other_entity = EntityModel(
name="Other Entity",
entity_type="test",
path_id="concept/other_entity",
file_path="concept/other_entity.md",
content_type="text/markdown",
)
another_entity = EntityModel(
name="Another Entity",
entity_type="test",
path_id="concept/another_entity",
file_path="concept/another_entity.md",
content_type="text/markdown",
)
await knowledge_sync_service.entity_repository.add(other_entity)
await knowledge_sync_service.entity_repository.add(another_entity)
# First pass - create without relations
entity = await knowledge_sync_service.create_entity_from_markdown("test.md", test_markdown)
assert len(entity.relations) == 0
assert entity.checksum is None
# Second pass - add relations
checksum = "final-checksum-456"
updated = await knowledge_sync_service.update_entity_relations(test_markdown, checksum)
# Verify final state
assert len(updated.relations) == 2
assert updated.checksum == checksum
relations = sorted(updated.relations, key=lambda r: r.relation_type)
assert relations[0].to_id == other_entity.id
assert relations[1].to_id == another_entity.id