test_markdown_plugins.py•7.41 kB
"""Tests for markdown plugins."""
from textwrap import dedent
from markdown_it import MarkdownIt
from markdown_it.token import Token
from basic_memory.markdown.plugins import (
    observation_plugin,
    relation_plugin,
    is_observation,
    is_explicit_relation,
    parse_relation,
    parse_inline_relations,
)
def test_observation_plugin():
    """Test observation plugin."""
    # Set up markdown-it instance
    md = MarkdownIt().use(observation_plugin)
    # Test basic observation with all features
    content = dedent("""
        - [design] Basic observation #tag1 #tag2 (with context)
        """)
    tokens = md.parse(content)
    token = [t for t in tokens if t.type == "inline"][0]
    assert "observation" in token.meta
    obs = token.meta["observation"]
    assert obs["category"] == "design"
    assert obs["content"] == "Basic observation #tag1 #tag2"
    assert set(obs["tags"]) == {"tag1", "tag2"}
    assert obs["context"] == "with context"
    # Test without category
    content = "- Basic observation #tag1 (context)"
    token = [t for t in md.parse(content) if t.type == "inline"][0]
    obs = token.meta["observation"]
    assert obs["category"] is None
    assert obs["content"] == "Basic observation #tag1"
    assert obs["tags"] == ["tag1"]
    assert obs["context"] == "context"
    # Test without tags
    content = "- [note] Basic observation (context)"
    token = [t for t in md.parse(content) if t.type == "inline"][0]
    obs = token.meta["observation"]
    assert obs["category"] == "note"
    assert obs["content"] == "Basic observation"
    assert obs["tags"] is None
    assert obs["context"] == "context"
def test_observation_edge_cases():
    """Test observation parsing edge cases."""
    # Test non-inline token
    token = Token("paragraph", "", 0)
    assert not is_observation(token)
    # Test empty content
    token = Token("inline", "", 0)
    assert not is_observation(token)
    # Test markdown task
    token = Token("inline", "[ ] Task item", 0)
    assert not is_observation(token)
    # Test completed task
    token = Token("inline", "[x] Done task", 0)
    assert not is_observation(token)
    # Test in-progress task
    token = Token("inline", "[-] Ongoing task", 0)
    assert not is_observation(token)
def test_observation_excludes_markdown_and_wiki_links():
    """Test that markdown links and wiki links are NOT parsed as observations.
    This test validates the fix for issue #247 where:
    - [text](url) markdown links were incorrectly parsed as observations
    - [[text]] wiki links were incorrectly parsed as observations
    """
    # Test markdown links are NOT observations
    token = Token("inline", "[Click here](https://example.com)", 0)
    assert not is_observation(token), "Markdown links should not be parsed as observations"
    token = Token("inline", "[Documentation](./docs/readme.md)", 0)
    assert not is_observation(token), "Relative markdown links should not be parsed as observations"
    token = Token("inline", "[Empty link]()", 0)
    assert not is_observation(token), "Empty markdown links should not be parsed as observations"
    # Test wiki links are NOT observations
    token = Token("inline", "[[SomeWikiPage]]", 0)
    assert not is_observation(token), "Wiki links should not be parsed as observations"
    token = Token("inline", "[[Multi Word Page]]", 0)
    assert not is_observation(token), "Multi-word wiki links should not be parsed as observations"
    # Test nested brackets are NOT observations
    token = Token("inline", "[[Nested [[Inner]] Link]]", 0)
    assert not is_observation(token), "Nested wiki links should not be parsed as observations"
    # Test valid observations still work (should return True)
    token = Token("inline", "[category] This is a valid observation", 0)
    assert is_observation(token), "Valid observations should still be parsed correctly"
    token = Token("inline", "[design] Valid observation #tag", 0)
    assert is_observation(token), "Valid observations with tags should still work"
    token = Token("inline", "Just some text #tag", 0)
    assert is_observation(token), "Tag-only observations should still work"
    # Test edge cases that should NOT be observations
    token = Token("inline", "[]Empty brackets", 0)
    assert not is_observation(token), "Empty category brackets should not be observations"
    token = Token("inline", "[category]No space after category", 0)
    assert not is_observation(token), "No space after category should not be valid observation"
def test_relation_plugin():
    """Test relation plugin."""
    md = MarkdownIt().use(relation_plugin)
    # Test explicit relation with all features
    content = dedent("""
        - implements [[Component]] (with context)
        """)
    tokens = md.parse(content)
    token = [t for t in tokens if t.type == "inline"][0]
    assert "relations" in token.meta
    rel = token.meta["relations"][0]
    assert rel["type"] == "implements"
    assert rel["target"] == "Component"
    assert rel["context"] == "with context"
    # Test implicit relations in text
    content = "Some text with a [[Link]] and [[Another Link]]"
    token = [t for t in md.parse(content) if t.type == "inline"][0]
    rels = token.meta["relations"]
    assert len(rels) == 2
    assert rels[0]["type"] == "links to"
    assert rels[0]["target"] == "Link"
    assert rels[1]["target"] == "Another Link"
def test_relation_edge_cases():
    """Test relation parsing edge cases."""
    # Test non-inline token
    token = Token("paragraph", "", 0)
    assert not is_explicit_relation(token)
    # Test empty content
    token = Token("inline", "", 0)
    assert not is_explicit_relation(token)
    # Test incomplete relation (missing target)
    token = Token("inline", "relates_to [[]]", 0)
    result = parse_relation(token)
    assert result is None
    # Test non-relation content
    token = Token("inline", "Just some text", 0)
    result = parse_relation(token)
    assert result is None
    # Test invalid inline link (empty target)
    assert not parse_inline_relations("Text with [[]] empty link")
    # Test nested links (avoid duplicates)
    result = parse_inline_relations("Text with [[Outer [[Inner]] Link]]")
    assert len(result) == 1
    assert result[0]["target"] == "Outer [[Inner]] Link"
def test_combined_plugins():
    """Test both plugins working together."""
    md = MarkdownIt().use(observation_plugin).use(relation_plugin)
    content = dedent("""
        # Section
        - [design] Observation with [[Link]] #tag (context)
        - implements [[Component]] (details)
        - Just a [[Reference]] in text
        
        Some text with a [[Link]] reference.
        """)
    tokens = md.parse(content)
    inline_tokens = [t for t in tokens if t.type == "inline"]
    # First token has both observation and relation
    obs_token = inline_tokens[1]
    assert "observation" in obs_token.meta
    assert "relations" in obs_token.meta
    # Second token has explicit relation
    rel_token = inline_tokens[2]
    assert "relations" in rel_token.meta
    rel = rel_token.meta["relations"][0]
    assert rel["type"] == "implements"
    # Third token has implicit relation
    text_token = inline_tokens[4]
    assert "relations" in text_token.meta
    link = text_token.meta["relations"][0]
    assert link["type"] == "links to"