"""Tests for knowledge entity writer.""" from datetime import datetime, UTC import pytest from basic_memory.markdown.knowledge_writer import KnowledgeWriter from basic_memory.models import ( Entity as EntityModel, Observation, Relation, ObservationCategory, ) @pytest.fixture def writer(): """Create writer instance.""" return KnowledgeWriter() @pytest.fixture def test_entity(): """Create test entity with observations and relations.""" now = datetime.now(UTC) # Create main entity entity = EntityModel( id=1, path_id="test/test_entity", name="TestEntity", entity_type="test", description="A test entity", created_at=now, updated_at=now ) # Add observations with categories and context entity.observations = [ Observation( content="Technical implementation detail", category=ObservationCategory.TECH.value, context="Initial implementation" ), Observation( content="Design pattern choice", category=ObservationCategory.DESIGN.value ), ] # Create related entity other_entity = EntityModel( id=2, path_id="test/other_entity", name="OtherEntity", entity_type="test", created_at=now, updated_at=now ) # Create relation from main entity to other relation = Relation(from_entity=entity, to_entity=other_entity, relation_type="relates_to") entity.from_relations = [relation] return entity @pytest.mark.asyncio async def test_format_frontmatter(writer: KnowledgeWriter, test_entity: EntityModel): """Test frontmatter generation.""" frontmatter = await writer.format_frontmatter(test_entity) assert frontmatter["type"] == "test" assert frontmatter["id"] == "test/test_entity" assert isinstance(frontmatter["created"], str) assert isinstance(frontmatter["modified"], str) @pytest.mark.asyncio async def test_format_content_with_categories(writer: KnowledgeWriter, test_entity: EntityModel): """Test content formatting with categorized observations.""" content = await writer.format_content(test_entity) # Check observations section header and format comment assert "## Observations" in content assert "" in content # Check formatted observations assert "- [tech] Technical implementation detail (Initial implementation)" in content assert "- [design] Design pattern choice" in content @pytest.mark.asyncio async def test_format_content_default_category(writer: KnowledgeWriter): """Test formatting observation with default category.""" entity = EntityModel(id=1, name="Test", entity_type="test") entity.observations = [ Observation(content="Simple note", category=ObservationCategory.NOTE.value) ] content = await writer.format_content(entity) assert "- [note] Simple note" in content @pytest.mark.asyncio async def test_format_content_context_handling(writer: KnowledgeWriter): """Test formatting observations with different context scenarios.""" entity = EntityModel(id=1, name="Test", entity_type="test") entity.observations = [ # With context Observation( content="With context", category=ObservationCategory.TECH.value, context="Important context" ), # Without context Observation( content="No context", category=ObservationCategory.TECH.value ), ] content = await writer.format_content(entity) assert "- [tech] With context (Important context)" in content assert "- [tech] No context" in content assert "No context ()" not in content # Shouldn't have empty parentheses @pytest.mark.asyncio async def test_format_content_sections_order(writer: KnowledgeWriter, test_entity: EntityModel): """Test proper order and spacing of sections with new format.""" content = await writer.format_content(test_entity) lines = content.split("\n") # Find key sections title_idx = next(i for i, line in enumerate(lines) if line.startswith("# ")) obs_idx = next(i for i, line in enumerate(lines) if line.strip() == "## Observations") format_idx = next(i for i, line in enumerate(lines) if "" in content assert "```yml" in content assert "ai_generated: true" in content.lower() assert "confidence: 0.95" in content assert "tags:" in content assert "- test" in content assert "- example" in content @pytest.mark.asyncio async def test_format_metadata_only(writer: KnowledgeWriter): """Test metadata formatting alone.""" metadata = {"test": "value", "nested": {"key": "value"}} content = await writer.format_metadata(metadata) assert content.startswith("# Metadata\n") assert "" in content assert "```yml" in content assert "test: value" in content assert "nested:" in content assert " key: value" in content assert content.strip().endswith("```") @pytest.mark.asyncio async def test_format_metadata_empty(writer: KnowledgeWriter): """Test metadata formatting with empty/none metadata.""" assert await writer.format_metadata(None) == "" assert await writer.format_metadata({}) == "" @pytest.mark.asyncio async def test_format_content_minimal_entity(writer: KnowledgeWriter): """Test formatting with minimal entity.""" entity = EntityModel(id=1, name="Minimal", entity_type="test") content = await writer.format_content(entity) # Should have title only assert content.strip() == "# Minimal"