mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
c44291830c
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
164 lines
6.2 KiB
Python
164 lines
6.2 KiB
Python
"""Tests for markdown/utils.py - entity model conversion utilities."""
|
|
|
|
from datetime import datetime, timezone
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from basic_memory.markdown.schemas import EntityMarkdown, EntityFrontmatter, Observation
|
|
from basic_memory.markdown.utils import entity_model_from_markdown
|
|
from basic_memory.models import Entity
|
|
|
|
|
|
class TestEntityModelFromMarkdown:
|
|
"""Tests for entity_model_from_markdown function."""
|
|
|
|
def _create_markdown(
|
|
self,
|
|
title: str = "Test Entity",
|
|
note_type: str = "note",
|
|
permalink: str = "test/test-entity",
|
|
created: datetime | None = None,
|
|
modified: datetime | None = None,
|
|
observations: list[Observation] | None = None,
|
|
) -> EntityMarkdown:
|
|
"""Helper to create test EntityMarkdown objects."""
|
|
now = datetime.now(timezone.utc)
|
|
return EntityMarkdown(
|
|
frontmatter=EntityFrontmatter(
|
|
title=title,
|
|
type=note_type,
|
|
permalink=permalink,
|
|
),
|
|
content=f"# {title}\n\nTest content.",
|
|
observations=observations or [],
|
|
relations=[],
|
|
created=created or now,
|
|
modified=modified or now,
|
|
)
|
|
|
|
def test_new_entity_has_external_id(self):
|
|
"""Test that a new entity always gets an external_id set.
|
|
|
|
This is a regression test for GitHub issue #512 where SQLite failed
|
|
with NOT NULL constraint on external_id because SQLAlchemy's Python-side
|
|
default wasn't always evaluated.
|
|
"""
|
|
markdown = self._create_markdown()
|
|
file_path = Path("test/test-entity.md")
|
|
|
|
entity = entity_model_from_markdown(file_path, markdown)
|
|
|
|
# external_id must be set (non-None, non-empty)
|
|
assert entity.external_id is not None
|
|
assert entity.external_id != ""
|
|
# Should be a valid UUID format (36 chars with hyphens)
|
|
assert len(entity.external_id) == 36
|
|
assert entity.external_id.count("-") == 4
|
|
|
|
def test_existing_entity_preserves_external_id(self):
|
|
"""Test that an existing entity's external_id is preserved."""
|
|
markdown = self._create_markdown()
|
|
file_path = Path("test/test-entity.md")
|
|
|
|
# Create existing entity with known external_id
|
|
existing_external_id = "12345678-1234-1234-1234-123456789012"
|
|
existing_entity = Entity()
|
|
existing_entity.external_id = existing_external_id
|
|
|
|
entity = entity_model_from_markdown(file_path, markdown, entity=existing_entity)
|
|
|
|
# Should preserve the existing external_id
|
|
assert entity.external_id == existing_external_id
|
|
|
|
def test_entity_with_empty_external_id_gets_new_one(self):
|
|
"""Test that an entity with empty string external_id gets a new UUID."""
|
|
markdown = self._create_markdown()
|
|
file_path = Path("test/test-entity.md")
|
|
|
|
# Create existing entity with empty external_id
|
|
existing_entity = Entity()
|
|
existing_entity.external_id = ""
|
|
|
|
entity = entity_model_from_markdown(file_path, markdown, entity=existing_entity)
|
|
|
|
# Should have a new external_id
|
|
assert entity.external_id is not None
|
|
assert entity.external_id != ""
|
|
assert len(entity.external_id) == 36
|
|
|
|
def test_entity_with_none_external_id_gets_new_one(self):
|
|
"""Test that an entity with None external_id gets a new UUID."""
|
|
markdown = self._create_markdown()
|
|
file_path = Path("test/test-entity.md")
|
|
|
|
# Create existing entity with None external_id
|
|
existing_entity = Entity()
|
|
# Explicitly set to None to test this case
|
|
object.__setattr__(existing_entity, "external_id", None)
|
|
|
|
entity = entity_model_from_markdown(file_path, markdown, entity=existing_entity)
|
|
|
|
# Should have a new external_id
|
|
assert entity.external_id is not None
|
|
assert entity.external_id != ""
|
|
assert len(entity.external_id) == 36
|
|
|
|
def test_multiple_calls_generate_unique_ids(self):
|
|
"""Test that multiple new entities get unique external_ids."""
|
|
markdown1 = self._create_markdown(title="Entity 1", permalink="test/entity-1")
|
|
markdown2 = self._create_markdown(title="Entity 2", permalink="test/entity-2")
|
|
|
|
entity1 = entity_model_from_markdown(Path("test/entity-1.md"), markdown1)
|
|
entity2 = entity_model_from_markdown(Path("test/entity-2.md"), markdown2)
|
|
|
|
# Both should have external_ids
|
|
assert entity1.external_id is not None
|
|
assert entity2.external_id is not None
|
|
|
|
# They should be unique
|
|
assert entity1.external_id != entity2.external_id
|
|
|
|
def test_entity_model_fields_populated_with_external_id(self):
|
|
"""Test that entity fields are populated correctly, including external_id.
|
|
|
|
This is a basic sanity check that entity_model_from_markdown sets
|
|
the key fields we care about for the #512 fix.
|
|
"""
|
|
markdown = self._create_markdown(
|
|
title="My Test Entity",
|
|
note_type="component",
|
|
permalink="components/my-test",
|
|
)
|
|
file_path = Path("components/my-test.md")
|
|
|
|
entity = entity_model_from_markdown(file_path, markdown, project_id=1)
|
|
|
|
# The key assertion: external_id must always be set
|
|
assert entity.external_id is not None
|
|
assert len(entity.external_id) == 36 # UUID format
|
|
|
|
# Verify file_path is set correctly (uses posix format)
|
|
assert entity.file_path == "components/my-test.md"
|
|
|
|
# Timestamps should be set from markdown
|
|
assert entity.created_at is not None
|
|
assert entity.updated_at is not None
|
|
|
|
def test_missing_dates_raises_error(self):
|
|
"""Test that missing created/modified dates raise ValueError."""
|
|
markdown = EntityMarkdown(
|
|
frontmatter=EntityFrontmatter(
|
|
title="Test",
|
|
type="note",
|
|
),
|
|
content="# Test",
|
|
observations=[],
|
|
relations=[],
|
|
created=None,
|
|
modified=None,
|
|
)
|
|
|
|
with pytest.raises(ValueError, match="Both created and modified dates are required"):
|
|
entity_model_from_markdown(Path("test.md"), markdown)
|