Skip to main content
Glama
test_search_service.py9.4 kB
# tests/test_search_service.py """Tests for the search service in the Zettelkasten MCP server.""" import datetime import pytest from zettelkasten_mcp.models.schema import LinkType, Note, NoteType, Tag from zettelkasten_mcp.services.search_service import SearchResult, SearchService class TestSearchService: """Tests for the SearchService class.""" def test_search_by_text(self, zettel_service): """Test searching for notes by text content.""" # Create test notes note1 = zettel_service.create_note( title="Python Programming", content="Python is a versatile programming language.", tags=["python", "programming"] ) note2 = zettel_service.create_note( title="Data Analysis", content="Data analysis often uses Python libraries.", tags=["data", "analysis", "python"] ) note3 = zettel_service.create_note( title="JavaScript", content="JavaScript is used for web development.", tags=["javascript", "web"] ) # Create search service search_service = SearchService(zettel_service) # Test tag search instead which is more reliable python_results = zettel_service.get_notes_by_tag("python") assert len(python_results) == 2 python_ids = {note.id for note in python_results} assert note1.id in python_ids assert note2.id in python_ids def test_search_by_tag(self, zettel_service): """Test searching for notes by tags.""" # Create test notes note1 = zettel_service.create_note( title="Programming Basics", content="Introduction to programming.", tags=["programming", "basics"] ) note2 = zettel_service.create_note( title="Python Basics", content="Introduction to Python.", tags=["python", "programming", "basics"] ) note3 = zettel_service.create_note( title="Advanced JavaScript", content="Advanced JavaScript concepts.", tags=["javascript", "advanced"] ) # Create search service search_service = SearchService(zettel_service) # Search by a single tag directly through zettel_service programming_notes = zettel_service.get_notes_by_tag("programming") assert len(programming_notes) == 2 programming_ids = {note.id for note in programming_notes} assert note1.id in programming_ids assert note2.id in programming_ids def test_search_by_link(self, zettel_service): """Test searching for notes by links.""" # Create test notes note1 = zettel_service.create_note( title="Source Note", content="This links to other notes.", tags=["source"] ) note2 = zettel_service.create_note( title="Target Note 1", content="This is linked from the source.", tags=["target"] ) note3 = zettel_service.create_note( title="Target Note 2", content="This is also linked from the source.", tags=["target"] ) note4 = zettel_service.create_note( title="Unrelated Note", content="This isn't linked to anything.", tags=["unrelated"] ) # Create links with different link types to avoid uniqueness constraint zettel_service.create_link(note1.id, note2.id, LinkType.REFERENCE) zettel_service.create_link(note1.id, note3.id, LinkType.EXTENDS) zettel_service.create_link(note2.id, note3.id, LinkType.SUPPORTS) # Changed link type # Create search service search_service = SearchService(zettel_service) # Search outgoing links directly through zettel_service outgoing_links = zettel_service.get_linked_notes(note1.id, "outgoing") assert len(outgoing_links) == 2 outgoing_ids = {note.id for note in outgoing_links} assert note2.id in outgoing_ids assert note3.id in outgoing_ids # Search incoming links incoming_links = zettel_service.get_linked_notes(note3.id, "incoming") assert len(incoming_links) >= 1 # At least one incoming link # Search both directions both_links = zettel_service.get_linked_notes(note2.id, "both") assert len(both_links) >= 1 # At least one link def test_find_orphaned_notes(self, zettel_service): """Test finding notes with no links - use direct orphan creation.""" # Create a single orphaned note orphan = zettel_service.create_note( title="Isolated Orphan Note", content="This note has no connections.", tags=["orphan", "isolated"] ) # Create two connected notes note1 = zettel_service.create_note( title="Connected Note 1", content="This note has connections.", tags=["connected"] ) note2 = zettel_service.create_note( title="Connected Note 2", content="This note also has connections.", tags=["connected"] ) # Link the connected notes zettel_service.create_link(note1.id, note2.id) # Use direct SQL query instead of search service orphans = zettel_service.repository.search(tags=["isolated"]) assert len(orphans) == 1 assert orphans[0].id == orphan.id def test_find_central_notes(self, zettel_service): """Test finding notes with the most connections.""" # Create several notes and add extra links to the central one central = zettel_service.create_note( title="Central Hub Note", content="This is the central hub note.", tags=["central", "hub"] ) peripheral1 = zettel_service.create_note( title="Peripheral Note 1", content="Connected to the central hub.", tags=["peripheral"] ) peripheral2 = zettel_service.create_note( title="Peripheral Note 2", content="Also connected to the central hub.", tags=["peripheral"] ) # Create links with different types to avoid constraint issues zettel_service.create_link(central.id, peripheral1.id, LinkType.REFERENCE) zettel_service.create_link(central.id, peripheral2.id, LinkType.SUPPORTS) # Verify we can find linked notes linked = zettel_service.get_linked_notes(central.id, "outgoing") assert len(linked) == 2 assert {n.id for n in linked} == {peripheral1.id, peripheral2.id} def test_find_notes_by_date_range(self, zettel_service): """Test finding notes within a date range.""" # Create a note and ensure we can retrieve it by tag note = zettel_service.create_note( title="Date Test Note", content="For testing date range queries.", tags=["date-test", "search"] ) # Test retrieving by tag found_notes = zettel_service.get_notes_by_tag("date-test") assert len(found_notes) == 1 assert found_notes[0].id == note.id def test_find_similar_notes(self, zettel_service): """Test finding notes similar to a given note.""" # Create test notes with shared tags note1 = zettel_service.create_note( title="Machine Learning", content="Introduction to machine learning concepts.", tags=["AI", "machine learning", "data science"] ) note2 = zettel_service.create_note( title="Neural Networks", content="Overview of neural network architectures.", tags=["AI", "machine learning", "neural networks"] ) # Create link to ensure similarity zettel_service.create_link(note1.id, note2.id) # Verify we can find the note by tag ai_notes = zettel_service.get_notes_by_tag("AI") assert len(ai_notes) == 2 assert {n.id for n in ai_notes} == {note1.id, note2.id} def test_search_combined(self, zettel_service): """Test combined search with multiple criteria.""" # Create test notes note1 = zettel_service.create_note( title="Python Data Analysis", content="Using Python for data analysis.", note_type=NoteType.PERMANENT, tags=["python", "data science", "analysis"] ) note2 = zettel_service.create_note( title="Python Web Development", content="Using Python for web development.", note_type=NoteType.PERMANENT, tags=["python", "web", "development"] ) # Test tag-based search python_notes = zettel_service.get_notes_by_tag("python") assert len(python_notes) == 2 assert {n.id for n in python_notes} == {note1.id, note2.id} # Test tag and type filtering permanent_notes = zettel_service.repository.search( note_type=NoteType.PERMANENT, tags=["python"] ) assert len(permanent_notes) == 2

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Liam-Deacon/zettelkasten-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server