Skip to main content
Glama

MCP server for LogSeq

by ergut
test_tool_handlers.py14.7 kB
import pytest from unittest.mock import patch, Mock from mcp.types import TextContent from mcp_logseq.tools import ( CreatePageToolHandler, ListPagesToolHandler, GetPageContentToolHandler, DeletePageToolHandler, UpdatePageToolHandler, SearchToolHandler ) class TestCreatePageToolHandler: """Test cases for CreatePageToolHandler.""" def test_get_tool_description(self): """Test tool description schema.""" handler = CreatePageToolHandler() tool = handler.get_tool_description() assert tool.name == "create_page" assert "Create a new page in LogSeq" in tool.description assert tool.inputSchema["required"] == ["title", "content"] @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success(self, mock_logseq_class): """Test successful page creation.""" # Setup mock mock_api = Mock() mock_logseq_class.return_value = mock_api handler = CreatePageToolHandler() args = {"title": "Test Page", "content": "Test content"} result = handler.run_tool(args) # Verify API was called correctly mock_api.create_page.assert_called_once_with("Test Page", "Test content") # Verify result assert len(result) == 1 assert isinstance(result[0], TextContent) assert "Successfully created page 'Test Page'" in result[0].text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) def test_run_tool_missing_args(self): """Test tool with missing required arguments.""" handler = CreatePageToolHandler() with pytest.raises(RuntimeError, match="title and content arguments required"): handler.run_tool({"title": "Test"}) # Missing content with pytest.raises(RuntimeError, match="title and content arguments required"): handler.run_tool({"content": "Test"}) # Missing title @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_api_error(self, mock_logseq_class): """Test tool with API error.""" # Setup mock to raise exception mock_api = Mock() mock_api.create_page.side_effect = Exception("API Error") mock_logseq_class.return_value = mock_api handler = CreatePageToolHandler() args = {"title": "Test Page", "content": "Test content"} with pytest.raises(Exception, match="API Error"): handler.run_tool(args) class TestListPagesToolHandler: """Test cases for ListPagesToolHandler.""" def test_get_tool_description(self): """Test tool description schema.""" handler = ListPagesToolHandler() tool = handler.get_tool_description() assert tool.name == "list_pages" assert "Lists all pages in a LogSeq graph" in tool.description assert tool.inputSchema["required"] == [] @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success_exclude_journals(self, mock_logseq_class): """Test successful page listing excluding journals.""" # Setup mock mock_api = Mock() mock_api.list_pages.return_value = [ {"originalName": "Regular Page", "journal?": False}, {"originalName": "Journal Page", "journal?": True}, {"name": "Another Page", "journal?": False} ] mock_logseq_class.return_value = mock_api handler = ListPagesToolHandler() result = handler.run_tool({"include_journals": False}) # Verify result assert len(result) == 1 assert isinstance(result[0], TextContent) text = result[0].text assert "Regular Page" in text assert "Another Page" in text assert "Journal Page" not in text assert "Total pages: 2" in text assert "(excluding journal pages)" in text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success_include_journals(self, mock_logseq_class): """Test successful page listing including journals.""" # Setup mock mock_api = Mock() mock_api.list_pages.return_value = [ {"originalName": "Regular Page", "journal?": False}, {"originalName": "Journal Page", "journal?": True} ] mock_logseq_class.return_value = mock_api handler = ListPagesToolHandler() result = handler.run_tool({"include_journals": True}) # Verify result text = result[0].text assert "Regular Page" in text assert "Journal Page" in text assert "Total pages: 2" in text assert "(including journal pages)" in text class TestGetPageContentToolHandler: """Test cases for GetPageContentToolHandler.""" def test_get_tool_description(self): """Test tool description schema.""" handler = GetPageContentToolHandler() tool = handler.get_tool_description() assert tool.name == "get_page_content" assert "Get the content of a specific page" in tool.description assert tool.inputSchema["required"] == ["page_name"] @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success_text_format(self, mock_logseq_class): """Test successful page content retrieval in text format.""" # Setup mock mock_api = Mock() mock_api.get_page_content.return_value = { "page": { "originalName": "Test Page", "properties": {"tags": ["test"], "priority": "high"} }, "blocks": [ {"content": "Block 1 content"}, {"content": "Block 2 content"} ] } mock_logseq_class.return_value = mock_api handler = GetPageContentToolHandler() result = handler.run_tool({"page_name": "Test Page", "format": "text"}) # Verify result assert len(result) == 1 text = result[0].text assert "# Test Page" in text assert "tags: ['test']" in text assert "priority: high" in text assert "Block 1 content" in text assert "Block 2 content" in text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success_json_format(self, mock_logseq_class): """Test successful page content retrieval in JSON format.""" # Setup mock mock_data = {"page": {"name": "Test"}, "blocks": []} mock_api = Mock() mock_api.get_page_content.return_value = mock_data mock_logseq_class.return_value = mock_api handler = GetPageContentToolHandler() result = handler.run_tool({"page_name": "Test Page", "format": "json"}) # Verify result assert str(mock_data) in result[0].text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_page_not_found(self, mock_logseq_class): """Test page content retrieval for non-existent page.""" # Setup mock mock_api = Mock() mock_api.get_page_content.return_value = None mock_logseq_class.return_value = mock_api handler = GetPageContentToolHandler() result = handler.run_tool({"page_name": "Non-existent"}) # Verify result assert "Page 'Non-existent' not found" in result[0].text class TestDeletePageToolHandler: """Test cases for DeletePageToolHandler.""" def test_get_tool_description(self): """Test tool description schema.""" handler = DeletePageToolHandler() tool = handler.get_tool_description() assert tool.name == "delete_page" assert "Delete a page from LogSeq" in tool.description assert tool.inputSchema["required"] == ["page_name"] @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success(self, mock_logseq_class): """Test successful page deletion.""" # Setup mock mock_api = Mock() mock_api.delete_page.return_value = {"success": True} mock_logseq_class.return_value = mock_api handler = DeletePageToolHandler() result = handler.run_tool({"page_name": "Test Page"}) # Verify API was called mock_api.delete_page.assert_called_once_with("Test Page") # Verify result text = result[0].text assert "✅ Successfully deleted page 'Test Page'" in text assert "🗑️ Page 'Test Page' has been permanently removed" in text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_page_not_found(self, mock_logseq_class): """Test deletion of non-existent page.""" # Setup mock to raise ValueError mock_api = Mock() mock_api.delete_page.side_effect = ValueError("Page 'Test' does not exist") mock_logseq_class.return_value = mock_api handler = DeletePageToolHandler() result = handler.run_tool({"page_name": "Test"}) # Verify error handling text = result[0].text assert "❌ Error: Page 'Test' does not exist" in text class TestUpdatePageToolHandler: """Test cases for UpdatePageToolHandler.""" def test_get_tool_description(self): """Test tool description schema.""" handler = UpdatePageToolHandler() tool = handler.get_tool_description() assert tool.name == "update_page" assert "Update a page in LogSeq" in tool.description assert tool.inputSchema["required"] == ["page_name"] @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success(self, mock_logseq_class): """Test successful page update.""" # Setup mock mock_api = Mock() mock_api.update_page.return_value = { "updates": [("properties", {"success": True}), ("content", {"success": True})], "page": "Test Page" } mock_logseq_class.return_value = mock_api handler = UpdatePageToolHandler() result = handler.run_tool({ "page_name": "Test Page", "content": "New content", "properties": {"priority": "high"} }) # Verify API was called mock_api.update_page.assert_called_once_with( "Test Page", content="New content", properties={"priority": "high"} ) # Verify result text = result[0].text assert "✅ Successfully updated page 'Test Page'" in text assert "📝 Properties updated" in text assert "📄 Content appended" in text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) def test_run_tool_no_updates(self): """Test update with no content or properties.""" handler = UpdatePageToolHandler() result = handler.run_tool({"page_name": "Test Page"}) # Verify error handling text = result[0].text assert "❌ Error: Either 'content' or 'properties' must be provided" in text class TestSearchToolHandler: """Test cases for SearchToolHandler.""" def test_get_tool_description(self): """Test tool description schema.""" handler = SearchToolHandler() tool = handler.get_tool_description() assert tool.name == "search" assert "Search for content across LogSeq pages" in tool.description assert tool.inputSchema["required"] == ["query"] @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_success(self, mock_logseq_class): """Test successful search.""" # Setup mock mock_api = Mock() mock_api.search_content.return_value = { "blocks": [{"block/content": "Found content"}], "pages": ["Matching Page"], "pages-content": [{"block/snippet": "Snippet content"}], "files": [], "has-more?": False } mock_logseq_class.return_value = mock_api handler = SearchToolHandler() result = handler.run_tool({"query": "test"}) # Verify API was called mock_api.search_content.assert_called_once_with("test", {"limit": 20}) # Verify result text = result[0].text assert "# Search Results for 'test'" in text assert "📄 Content Blocks (1 found)" in text assert "Found content" in text assert "📑 Matching Pages (1 found)" in text assert "Matching Page" in text assert "Total results found: 2" in text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_no_results(self, mock_logseq_class): """Test search with no results.""" # Setup mock mock_api = Mock() mock_api.search_content.return_value = None mock_logseq_class.return_value = mock_api handler = SearchToolHandler() result = handler.run_tool({"query": "nothing"}) # Verify result text = result[0].text assert "No search results found for 'nothing'" in text @patch.dict('os.environ', {'LOGSEQ_API_TOKEN': 'test_token'}) @patch('mcp_logseq.tools.logseq.LogSeq') def test_run_tool_with_options(self, mock_logseq_class): """Test search with custom options.""" # Setup mock mock_api = Mock() mock_api.search_content.return_value = {"blocks": [], "pages": [], "files": []} mock_logseq_class.return_value = mock_api handler = SearchToolHandler() result = handler.run_tool({ "query": "test", "limit": 5, "include_blocks": False, "include_files": True }) # Verify API was called with correct options mock_api.search_content.assert_called_once_with("test", {"limit": 5})

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/ergut/mcp-logseq-server'

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