fixing markdown parsing tests

This commit is contained in:
phernandez
2024-12-22 17:06:06 -06:00
parent 5a78464a45
commit 1d98df895b
8 changed files with 185 additions and 165 deletions
+6 -8
View File
@@ -63,7 +63,7 @@ class MarkdownParser(ABC, Generic[T]):
try:
# Split into frontmatter and content
frontmatter, markdown = await parse_frontmatter(content)
# Extract metadata from frontmatter if present
metadata = frontmatter.pop("metadata", None)
@@ -83,9 +83,7 @@ class MarkdownParser(ABC, Generic[T]):
# Create final document
return await self.create_document(
frontmatter=parsed_frontmatter,
content=parsed_content,
metadata=parsed_metadata
frontmatter=parsed_frontmatter, content=parsed_content, metadata=parsed_metadata
)
except Exception as e:
@@ -106,7 +104,7 @@ class MarkdownParser(ABC, Generic[T]):
"""
# Initialize state
title = None
sections: Dict[str, List[str]] = {}
sections: Dict[str, str] = {}
current_section = None
current_lines: List[str] = []
@@ -122,7 +120,7 @@ class MarkdownParser(ABC, Generic[T]):
if current_section and current_lines:
sections[current_section] = "\n".join(current_lines).strip()
current_lines = []
# Start new section
current_section = line[3:].strip().lower()
continue
@@ -149,7 +147,7 @@ class MarkdownParser(ABC, Generic[T]):
pass
@abstractmethod
async def parse_content(self, title: str, sections: Dict[str, List[str]]) -> Any:
async def parse_content(self, title: str, sections: Dict[str, str]) -> Any:
"""Parse content sections."""
pass
@@ -161,4 +159,4 @@ class MarkdownParser(ABC, Generic[T]):
@abstractmethod
async def create_document(self, frontmatter: Any, content: Any, metadata: Optional[Any]) -> T:
"""Create document from parsed sections."""
pass
pass
+35 -52
View File
@@ -1,6 +1,6 @@
"""Parser for Basic Memory entity markdown files."""
from typing import Dict, Any, Optional, List
from typing import Dict, Any, Optional
from loguru import logger
@@ -11,7 +11,7 @@ from basic_memory.markdown.schemas import (
EntityContent,
EntityMetadata,
Observation,
Relation
Relation,
)
@@ -44,19 +44,19 @@ class EntityParser(MarkdownParser[Entity]):
try:
# Preprocess fields for schema validation
processed = frontmatter.copy()
# Ensure id is string
if 'id' in processed:
processed['id'] = str(processed['id'])
if "id" in processed:
processed["id"] = str(processed["id"])
# Handle tags field
if 'tags' in processed:
if isinstance(processed['tags'], str):
if "tags" in processed:
if isinstance(processed["tags"], str):
# Split comma-separated tags and strip whitespace
processed['tags'] = [tag.strip() for tag in processed['tags'].split(',')]
processed["tags"] = [tag.strip() for tag in processed["tags"].split(",")]
return EntityFrontmatter(**processed)
except Exception as e:
logger.error(f"Invalid entity frontmatter: {e}")
raise ParseError(f"Invalid entity frontmatter: {str(e)}") from e
@@ -85,10 +85,10 @@ class EntityParser(MarkdownParser[Entity]):
observations = []
if "observations" not in sections:
raise ParseError("Missing required observations section")
for line in sections["observations"].splitlines():
if line and not line.isspace():
observation = await self._parse_observation(line)
observation = await self.parse_observation(line)
if observation:
observations.append(observation)
@@ -97,15 +97,12 @@ class EntityParser(MarkdownParser[Entity]):
if "relations" in sections:
for line in sections["relations"].splitlines():
if line and not line.isspace():
relation = await self._parse_relation(line)
relation = await self.parse_relation(line)
if relation:
relations.append(relation)
return EntityContent(
title=title,
description=description,
observations=observations,
relations=relations
title=title, description=description, observations=observations, relations=relations
)
except ParseError:
@@ -114,7 +111,7 @@ class EntityParser(MarkdownParser[Entity]):
logger.error(f"Invalid entity content: {e}")
raise ParseError(f"Invalid entity content: {str(e)}") from e
async def _parse_observation(self, line: str) -> Optional[Observation]:
async def parse_observation(self, line: str) -> Optional[Observation]:
"""
Parse a single observation line.
@@ -127,47 +124,44 @@ class EntityParser(MarkdownParser[Entity]):
# Extract category if present [category]
category = None
content = line
if line.startswith('['):
end_bracket = line.find(']')
if line.startswith("["):
end_bracket = line.find("]")
if end_bracket != -1:
category = line[1:end_bracket].strip()
content = line[end_bracket + 1:].strip()
content = line[end_bracket + 1 :].strip()
# Extract context if present (context)
context = None
if content.endswith(')'):
context_start = content.rfind('(')
if content.endswith(")"):
context_start = content.rfind("(")
if context_start != -1:
context = content[context_start + 1:-1].strip()
context = content[context_start + 1 : -1].strip()
content = content[:context_start].strip()
# Extract tags #tag1 #tag2
tags = []
content_parts = []
for part in content.split():
if part.startswith('#'):
if part.startswith("#"):
tags.append(part[1:]) # Remove # prefix
else:
content_parts.append(part)
content = ' '.join(content_parts).strip()
content = " ".join(content_parts).strip()
if not content:
logger.warning(f"Skipping observation with no content: {line}")
return None
return Observation(
category=category,
content=content,
context=context,
tags=tags if tags else None
category=category, content=content, context=context, tags=tags if tags else None
)
except Exception as e:
logger.warning(f"Failed to parse observation '{line}': {e}")
return None
async def _parse_relation(self, line: str) -> Optional[Relation]:
async def parse_relation(self, line: str) -> Optional[Relation]:
"""
Parse a single relation line.
@@ -180,34 +174,30 @@ class EntityParser(MarkdownParser[Entity]):
# Extract context if present (context)
context = None
main_part = line
if line.endswith(')'):
context_start = line.rfind('(')
if line.endswith(")"):
context_start = line.rfind("(")
if context_start != -1:
context = line[context_start + 1:-1].strip()
context = line[context_start + 1 : -1].strip()
main_part = line[:context_start].strip()
# Extract relation type and target [[Entity]]
if '[[' not in main_part or ']]' not in main_part:
if "[[" not in main_part or "]]" not in main_part:
logger.warning(f"Invalid relation format (missing [[]]): {line}")
return None
# Split into relation type and target
relation_parts = main_part.split('[[', 1)
relation_parts = main_part.split("[[", 1)
relation_type = relation_parts[0].strip()
if not relation_type:
logger.warning(f"Missing relation type: {line}")
return None
target = relation_parts[1].split(']]')[0].strip()
target = relation_parts[1].split("]]")[0].strip()
if not target:
logger.warning(f"Missing target entity: {line}")
return None
return Relation(
type=relation_type,
target=target,
context=context
)
return Relation(type=relation_type, target=target, context=context)
except Exception as e:
logger.warning(f"Failed to parse relation '{line}': {e}")
@@ -235,10 +225,7 @@ class EntityParser(MarkdownParser[Entity]):
raise ParseError(f"Invalid entity metadata: {str(e)}") from e
async def create_document(
self,
frontmatter: EntityFrontmatter,
content: EntityContent,
metadata: EntityMetadata
self, frontmatter: EntityFrontmatter, content: EntityContent, metadata: EntityMetadata
) -> Entity:
"""
Create entity from parsed sections.
@@ -251,8 +238,4 @@ class EntityParser(MarkdownParser[Entity]):
Returns:
Complete entity
"""
return Entity(
frontmatter=frontmatter,
content=content,
metadata=metadata
)
return Entity(frontmatter=frontmatter, content=content, metadata=metadata)
+30 -23
View File
@@ -2,10 +2,13 @@
from textwrap import dedent
from basic_memory.markdown import EntityMetadata
import pytest
from basic_memory.utils.file_utils import parse_frontmatter
def test_parse_metadata():
@pytest.mark.asyncio
async def test_parse_metadata():
"""Test parsing basic metadata."""
text = dedent("""
owner: team-auth
@@ -13,22 +16,24 @@ def test_parse_metadata():
status: active
""")
result = EntityMetadata.from_text(text)
result, remaining = await parse_frontmatter(text)
assert result.metadata["owner"] == "team-auth"
assert result.metadata["priority"] == "high"
assert result.metadata["status"] == "active"
assert result["owner"] == "team-auth"
assert result["priority"] == "high"
assert result["status"] == "active"
def test_parse_metadata_empty():
@pytest.mark.asyncio
async def test_parse_metadata_empty():
"""Test parsing empty metadata."""
text = ""
result = EntityMetadata.from_text(text)
assert result.metadata == {}
result, remaining = await parse_frontmatter(text)
assert result == {}
def test_parse_metadata_whitespace():
@pytest.mark.asyncio
async def test_parse_metadata_whitespace():
"""Test handling of various whitespace in metadata."""
text = dedent("""
owner: team-auth
@@ -36,14 +41,15 @@ def test_parse_metadata_whitespace():
status: active
""")
result = EntityMetadata.from_text(text)
result, remaining = await parse_frontmatter(text)
assert result.metadata["owner"] == "team-auth"
assert result.metadata["priority"] == "high"
assert result.metadata["status"] == "active"
assert result["owner"] == "team-auth"
assert result["priority"] == "high"
assert result["status"] == "active"
def test_parse_metadata_multiline_values():
@pytest.mark.asyncio
async def test_parse_metadata_multiline_values():
"""Test handling of multiline metadata values."""
text = dedent("""
owner: team-auth
@@ -53,21 +59,22 @@ def test_parse_metadata_multiline_values():
status: active
""")
result = EntityMetadata.from_text(text)
result, _ = await parse_frontmatter(text)
assert result.metadata["owner"] == "team-auth"
assert result.metadata["status"] == "active"
assert len(result.metadata["description"].splitlines()) == 3
assert result["owner"] == "team-auth"
assert result["status"] == "active"
assert len(result["description"].splitlines()) == 3
def test_parse_metadata_invalid():
@pytest.mark.asyncio
async def test_parse_metadata_invalid():
"""Test handling of invalid metadata format."""
text = dedent("""
owner team-auth
priority: high
""")
result = EntityMetadata.from_text(text)
result, _ = await parse_frontmatter(text)
assert "priority" in result.metadata
assert "owner" not in result.metadata
assert "priority" in result
assert "owner" not in result
+50 -34
View File
@@ -2,87 +2,103 @@
import pytest
from basic_memory.markdown import ParseError
from basic_memory.markdown.schemas import Observation
from basic_memory.markdown import ParseError, EntityParser
def test_observation_empty_input():
@pytest.mark.asyncio
async def test_observation_empty_input():
"""Test handling of empty input."""
assert Observation.from_line("") is None
assert Observation.from_line(" ") is None
assert Observation.from_line("\n") is None
parser = EntityParser()
assert await parser.parse_observation("") is None
assert await parser.parse_observation(" ") is None
assert await parser.parse_observation("\n") is None
def test_observation_unicode():
@pytest.mark.asyncio
async def test_observation_unicode():
"""Test handling of Unicode content."""
# Invalid UTF-8 sequences
assert Observation.from_line("- [test] Bad UTF \xff") is None
assert Observation.from_line("- [test] Bad UTF \xfe") is None
parser = EntityParser()
assert await parser.parse_observation("- [test] Bad UTF \xff") is None
assert await parser.parse_observation("- [test] Bad UTF \xfe") is None
# Control characters
assert Observation.from_line("- [test] With \x00 null") is None
assert Observation.from_line("- [test] With \x01 ctrl-a") is None
assert Observation.from_line("- [test] With \x1b escape") is None
assert Observation.from_line("- [test] With \x7f delete") is None
assert Observation.from_line("- [test] With " + chr(0x1F) + " unit sep") is None
assert await parser.parse_observation("- [test] With \x00 null") is None
assert await parser.parse_observation("- [test] With \x01 ctrl-a") is None
assert await parser.parse_observation("- [test] With \x1b escape") is None
assert await parser.parse_observation("- [test] With \x7f delete") is None
assert await parser.parse_observation("- [test] With " + chr(0x1F) + " unit sep") is None
# Valid UTF-8
obs = Observation.from_line("- [测试] Unicode content #标签")
obs = await parser.parse_observation("- [测试] Unicode content #标签")
assert obs is not None
assert obs.category == "测试"
assert "标签" in obs.tags
assert "标签" in obs.tags # pyright: ignore [reportOperatorIssue]
def test_observation_invalid_context():
@pytest.mark.asyncio
async def test_observation_invalid_context():
"""Test handling of invalid context format."""
obs = Observation.from_line("- [test] Content (unclosed")
parser = EntityParser()
obs = await parser.parse_observation("- [test] Content (unclosed")
assert obs is not None
assert obs.content == "Content (unclosed"
assert obs.context is None
obs = Observation.from_line("- [test] Content (with) extra) parens)")
obs = await parser.parse_observation("- [test] Content (with) extra) parens)")
assert obs is not None
assert obs.content == "Content"
assert obs.context == "with) extra) parens"
def test_observation_complex_format():
@pytest.mark.asyncio
async def test_observation_complex_format():
"""Test parsing complex observation formats."""
# Test multiple nested tags and spaces
obs = Observation.from_line("- [complex test] This is #tag1#tag2 with #tag3 content")
parser = EntityParser()
obs = await parser.parse_observation("- [complex test] This is #tag1#tag2 with #tag3 content")
assert obs is not None
assert obs.category == "complex test"
assert set(obs.tags) == {"tag1", "tag2", "tag3"}
assert set(obs.tags) == {"tag1", "tag2", "tag3"} # pyright: ignore [reportArgumentType]
assert obs.content == "This is with content"
def test_observation_exception_handling():
@pytest.mark.asyncio
async def test_observation_exception_handling():
"""Test general error handling in observation parsing."""
# Test with a problematic regex pattern that could cause catastrophic backtracking
long_input = "[test] " + "a" * 1000000 # Very long input
assert Observation.from_line(long_input) is None
parser = EntityParser()
assert await parser.parse_observation(long_input) is None
# Test with invalid types
assert Observation.from_line(None) is None # type: ignore
assert Observation.from_line(123) is None # type: ignore
assert Observation.from_line(object()) is None # type: ignore
assert await parser.parse_observation(None) is None # type: ignore
assert await parser.parse_observation(123) is None # type: ignore
assert await parser.parse_observation(object()) is None # type: ignore
def test_observation_malformed_category():
@pytest.mark.asyncio
async def test_observation_malformed_category():
"""Test handling of malformed category brackets."""
parser = EntityParser()
with pytest.raises(ParseError, match="unclosed category"):
Observation.from_line("- [test Content")
await parser.parse_observation("- [test Content")
with pytest.raises(ParseError, match="missing category"):
Observation.from_line("- test] Content")
await parser.parse_observation("- test] Content")
assert Observation.from_line("- [] Empty category") is None
assert await parser.parse_observation("- [] Empty category") is None
def test_observation_whitespace():
@pytest.mark.asyncio
async def test_observation_whitespace():
"""Test handling of whitespace."""
# Valid whitespace cases
obs = Observation.from_line("- [test] Content")
parser = EntityParser()
obs = await parser.parse_observation("- [test] Content")
assert obs is not None
assert obs.content == "Content"
@@ -96,6 +112,6 @@ def test_observation_whitespace():
for char, name in test_chars.items():
content = f"- [test] Content{char}with{char}{name}"
obs = Observation.from_line(content)
obs = await parser.parse_observation(content)
assert obs is not None
assert obs.content == f"Content with {name}"
+17 -12
View File
@@ -2,15 +2,18 @@
from textwrap import dedent
import pytest
from basic_memory.markdown.parser import EntityParser
def test_parse_complete_file(tmp_path):
@pytest.mark.asyncio
async def test_parse_complete_file(tmp_path):
"""Test parsing a complete entity file."""
content = dedent("""
---
type: component
id: component/auth_service
id: 123
created: 2024-12-21T14:00:00Z
modified: 2024-12-21T14:00:00Z
tags: authentication, security, core
@@ -42,11 +45,11 @@ def test_parse_complete_file(tmp_path):
test_file.write_text(content)
parser = EntityParser()
entity = parser.parse_file(test_file)
entity = await parser.parse_file(test_file)
# Check frontmatter
assert entity.frontmatter.type == "component"
assert entity.frontmatter.id == "component/auth_service"
assert entity.frontmatter.id == "123"
assert "authentication" in entity.frontmatter.tags
# Check content
@@ -57,7 +60,7 @@ def test_parse_complete_file(tmp_path):
# Check specific observation
obs = entity.content.observations[0]
assert obs.category == "design"
assert "security" in obs.tags
assert "security" in obs.tags # pyright: ignore [reportOperatorIssue]
assert obs.context == "JWT based"
# Check specific relation
@@ -71,15 +74,16 @@ def test_parse_complete_file(tmp_path):
assert entity.metadata.metadata["priority"] == "high"
def test_parse_minimal_file(tmp_path):
@pytest.mark.asyncio
async def test_parse_minimal_file(tmp_path):
"""Test parsing a minimal valid entity file."""
content = dedent("""
---
type: component
id: minimal
id: 0
created: 2024-12-21T14:00:00Z
modified: 2024-12-21T14:00:00Z
tags: []
tags:
---
# Minimal Entity
@@ -95,16 +99,17 @@ def test_parse_minimal_file(tmp_path):
test_file.write_text(content)
parser = EntityParser()
entity = parser.parse_file(test_file)
entity = await parser.parse_file(test_file)
assert entity.frontmatter.type == "component"
assert entity.frontmatter.id == "minimal"
assert entity.frontmatter.id == "0"
assert len(entity.content.observations) == 1
assert len(entity.content.relations) == 1
assert not entity.metadata.metadata # Empty metadata
def test_file_with_metadata_only(tmp_path):
@pytest.mark.asyncio
async def test_file_with_metadata_only(tmp_path):
"""Test parsing a file that has metadata but no content."""
content = dedent("""
---
@@ -127,7 +132,7 @@ def test_file_with_metadata_only(tmp_path):
test_file.write_text(content)
parser = EntityParser()
entity = parser.parse_file(test_file)
entity = await parser.parse_file(test_file)
assert entity.metadata.metadata["owner"] == "test-team"
assert entity.metadata.metadata["status"] == "active"
+20 -17
View File
@@ -2,73 +2,76 @@
import pytest
from basic_memory.markdown import Observation
from basic_memory.markdown.parser import EntityParser, ParseError
def test_parse_observation_basic():
@pytest.mark.asyncio
async def test_parse_observation_basic():
"""Test basic observation parsing with category and tags."""
parser = EntityParser()
obs = Observation.from_line("- [design] Core feature #important #mvp")
obs = await parser.parse_observation("- [design] Core feature #important #mvp")
assert obs is not None
assert obs.category == "design"
assert obs.content == "Core feature"
assert set(obs.tags) == {"important", "mvp"}
assert set(obs.tags) == {"important", "mvp"} # pyright: ignore [reportArgumentType]
assert obs.context is None
def test_parse_observation_with_context():
@pytest.mark.asyncio
async def test_parse_observation_with_context():
"""Test observation parsing with context in parentheses."""
parser = EntityParser()
obs = Observation.from_line(
obs = await parser.parse_observation(
"- [feature] Authentication system #security #auth (Required for MVP)"
)
assert obs is not None
assert obs.category == "feature"
assert obs.content == "Authentication system"
assert set(obs.tags) == {"security", "auth"}
assert set(obs.tags) == {"security", "auth"} # pyright: ignore [reportArgumentType]
assert obs.context == "Required for MVP"
def test_parse_observation_edge_cases():
@pytest.mark.asyncio
async def test_parse_observation_edge_cases():
"""Test observation parsing edge cases."""
parser = EntityParser()
# Multiple word tags
obs = Observation.from_line("- [tech] Database #high-priority #needs-review")
obs = await parser.parse_observation("- [tech] Database #high-priority #needs-review")
assert obs is not None
assert set(obs.tags) == {"high-priority", "needs-review"}
assert set(obs.tags) == {"high-priority", "needs-review"} # pyright: ignore [reportArgumentType]
# Multiple word category
obs = Observation.from_line("- [user experience] Design #ux")
obs = await parser.parse_observation("- [user experience] Design #ux")
assert obs is not None
assert obs.category == "user experience"
# Parentheses in content shouldn't be treated as context
obs = Observation.from_line("- [code] Function (x) returns y #function")
obs = await parser.parse_observation("- [code] Function (x) returns y #function")
assert obs is not None
assert obs.content == "Function (x) returns y"
assert obs.context is None
# Multiple hashtags together
obs = Observation.from_line("- [test] Feature #important#urgent#now")
obs = await parser.parse_observation("- [test] Feature #important#urgent#now")
assert obs is not None
assert set(obs.tags) == {"important", "urgent", "now"}
assert set(obs.tags) == {"important", "urgent", "now"} # pyright: ignore [reportArgumentType]
def test_parse_observation_errors():
@pytest.mark.asyncio
async def test_parse_observation_errors():
"""Test error handling in observation parsing."""
parser = EntityParser()
# Missing category brackets
with pytest.raises(ParseError, match="missing category"):
Observation.from_line("- Design without brackets #test")
await parser.parse_observation("- Design without brackets #test")
# Unclosed category
with pytest.raises(ParseError, match="unclosed category"):
Observation.from_line("- [design Core feature #test")
await parser.parse_observation("- [design Core feature #test")
+15 -12
View File
@@ -2,61 +2,64 @@
import pytest
from basic_memory.markdown import Relation
from basic_memory.markdown.parser import EntityParser, ParseError
def test_parse_relation_basic():
@pytest.mark.asyncio
async def test_parse_relation_basic():
"""Test basic relation parsing."""
parser = EntityParser()
rel = Relation.from_line("- implements [[Auth Service]]")
rel = await parser.parse_relation("- implements [[Auth Service]]")
assert rel is not None
assert rel.type == "implements"
assert rel.target == "Auth Service"
assert rel.context is None
def test_parse_relation_with_context():
@pytest.mark.asyncio
async def test_parse_relation_with_context():
"""Test relation parsing with context."""
parser = EntityParser()
rel = Relation.from_line("- depends_on [[Database]] (Required for persistence)")
rel = await parser.parse_relation("- depends_on [[Database]] (Required for persistence)")
assert rel is not None
assert rel.type == "depends_on"
assert rel.target == "Database"
assert rel.context == "Required for persistence"
def test_parse_relation_edge_cases():
@pytest.mark.asyncio
async def test_parse_relation_edge_cases():
"""Test relation parsing edge cases."""
parser = EntityParser()
# Multiple word type
rel = Relation.from_line("- is used by [[Client App]] (Primary consumer)")
rel = await parser.parse_relation("- is used by [[Client App]] (Primary consumer)")
assert rel is not None
assert rel.type == "is used by"
# Brackets in context
rel = Relation.from_line("- implements [[API]] (Follows [OpenAPI] spec)")
rel = await parser.parse_relation("- implements [[API]] (Follows [OpenAPI] spec)")
assert rel is not None
assert rel.context == "Follows [OpenAPI] spec"
# Extra spaces
rel = Relation.from_line("- specifies [[Format]] (Documentation)")
rel = await parser.parse_relation("- specifies [[Format]] (Documentation)")
assert rel is not None
assert rel.type == "specifies"
assert rel.target == "Format"
def test_parse_relation_errors():
@pytest.mark.asyncio
async def test_parse_relation_errors():
"""Test error handling in relation parsing."""
parser = EntityParser()
# Missing target brackets
with pytest.raises(ParseError, match="missing \\[\\["):
Relation.from_line("- implements Auth Service")
await parser.parse_relation("- implements Auth Service")
# Unclosed target
with pytest.raises(ParseError, match="missing ]]"):
Relation.from_line("- implements [[Auth Service")
await parser.parse_relation("- implements [[Auth Service")
+12 -7
View File
@@ -16,15 +16,13 @@ from basic_memory.utils.file_utils import ParseError, FileError
@pytest.fixture
def sample_entity_content():
"""Sample entity file content."""
return """---
type: test
return """
---
id: 123
type: test
created: 2024-12-22T10:00:00Z
modified: 2024-12-22T10:00:00Z
tags: entity, test
metadata:
checksum: abc123
doc_id: 1
---
# Test Entity
@@ -37,6 +35,13 @@ A test entity for testing purposes.
## Relations
- depends_on [[Other Entity]] (Testing the relation parsing)
---
metadata:
checksum: abc123
doc_id: 1
---
"""
@@ -54,7 +59,7 @@ async def test_parse_valid_file(tmp_path: Path, sample_entity_content):
# Verify frontmatter
assert isinstance(entity.frontmatter, EntityFrontmatter)
assert entity.frontmatter.type == "test"
assert entity.frontmatter.id == "test/test_entity"
assert entity.frontmatter.id == "123"
# Verify content
assert isinstance(entity.content, EntityContent)
@@ -132,7 +137,7 @@ async def test_parse_content_str(sample_entity_content):
assert isinstance(entity, Entity)
assert entity.frontmatter.type == "test"
assert entity.frontmatter.id == "test/test_entity"
assert entity.frontmatter.id == "123"
assert entity.content.title == "Test Entity"
# Verify observations parsed correctly