Skip to main content
Glama

Notion API MCP Server

test_markdown.py8.73 kB
""" Validation tests for markdown conversion and block formatting. """ import pytest from notion_api_mcp.utils.formatting import ( parse_markdown_to_blocks, blocks_to_markdown, create_rich_text, create_block, BlockType ) # Test Data: (markdown, expected_blocks, description) MARKDOWN_TEST_CASES = [ ( "# Heading 1\n## Heading 2\n### Heading 3", [ {"type": "heading_1", "heading_1": {"rich_text": [{"type": "text", "text": {"content": "Heading 1"}}]}}, {"type": "heading_2", "heading_2": {"rich_text": [{"type": "text", "text": {"content": "Heading 2"}}]}}, {"type": "heading_3", "heading_3": {"rich_text": [{"type": "text", "text": {"content": "Heading 3"}}]}} ], "Headers of different levels" ), ( "- Item 1\n- Item 2\n1. First\n2. Second", [ {"type": "bulleted_list_item", "bulleted_list_item": {"rich_text": [{"type": "text", "text": {"content": "Item 1"}}]}}, {"type": "bulleted_list_item", "bulleted_list_item": {"rich_text": [{"type": "text", "text": {"content": "Item 2"}}]}}, {"type": "numbered_list_item", "numbered_list_item": {"rich_text": [{"type": "text", "text": {"content": "First"}}]}}, {"type": "numbered_list_item", "numbered_list_item": {"rich_text": [{"type": "text", "text": {"content": "Second"}}]}} ], "Mixed list types" ), ( "[ ] Todo item\n[x] Completed item", [ {"type": "to_do", "to_do": {"rich_text": [{"type": "text", "text": {"content": "Todo item"}}], "checked": False}}, {"type": "to_do", "to_do": {"rich_text": [{"type": "text", "text": {"content": "Completed item"}}], "checked": True}} ], "Todo items with different states" ), ( "```python\nprint('hello')\n```\n> Quote block", [ {"type": "code", "code": {"rich_text": [{"type": "text", "text": {"content": "python"}}]}}, {"type": "quote", "quote": {"rich_text": [{"type": "text", "text": {"content": "Quote block"}}]}} ], "Code and quote blocks" ), ( "Normal paragraph\nwith multiple\nlines", [ {"type": "paragraph", "paragraph": {"rich_text": [{"type": "text", "text": {"content": "Normal paragraph"}}]}}, {"type": "paragraph", "paragraph": {"rich_text": [{"type": "text", "text": {"content": "with multiple"}}]}}, {"type": "paragraph", "paragraph": {"rich_text": [{"type": "text", "text": {"content": "lines"}}]}} ], "Multi-line paragraphs" ) ] # Complex formatting cases COMPLEX_MARKDOWN = """ # Project Overview ## Features - Basic functionality - **Advanced** features - *Optional* components ### Implementation Details 1. First step 2. Second step - Sub-item 1 - Sub-item 2 ```python def example(): return "Hello World" ``` > Important note > Continued quote [ ] Task pending [x] Task completed Regular paragraph with **bold** and *italic* text. """ class TestMarkdownParsing: """Test markdown parsing functionality.""" @pytest.mark.parametrize("markdown,expected_blocks,description", MARKDOWN_TEST_CASES) def test_markdown_to_blocks(self, markdown, expected_blocks, description): """Test conversion of markdown to blocks.""" blocks = parse_markdown_to_blocks(markdown) assert len(blocks) == len(expected_blocks), f"Failed for: {description}" for actual, expected in zip(blocks, expected_blocks): assert actual["type"] == expected["type"] actual_content = actual[actual["type"]]["rich_text"][0]["text"]["content"] expected_content = expected[expected["type"]]["rich_text"][0]["text"]["content"] assert actual_content == expected_content def test_complex_markdown(self): """Test parsing of complex markdown with mixed elements.""" blocks = parse_markdown_to_blocks(COMPLEX_MARKDOWN) # Verify structure block_types = [block["type"] for block in blocks] assert "heading_1" in block_types assert "heading_2" in block_types assert "bulleted_list_item" in block_types assert "numbered_list_item" in block_types assert "code" in block_types assert "quote" in block_types assert "to_do" in block_types assert "paragraph" in block_types # Verify content preservation markdown_result = blocks_to_markdown(blocks) assert "Project Overview" in markdown_result assert "**Advanced**" in markdown_result assert "*Optional*" in markdown_result assert "```python" in markdown_result assert "Important note" in markdown_result def test_empty_lines(self): """Test handling of empty lines.""" markdown = "Line 1\n\nLine 2\n\n\nLine 3" blocks = parse_markdown_to_blocks(markdown) assert len(blocks) == 3 assert all(block["type"] == "paragraph" for block in blocks) def test_special_characters(self): """Test handling of special characters.""" markdown = "Special chars: & < > \" '\nEmojis: 🌟 ✨ 🚀" blocks = parse_markdown_to_blocks(markdown) assert len(blocks) == 2 assert "&" in blocks[0]["paragraph"]["rich_text"][0]["text"]["content"] assert "🌟" in blocks[1]["paragraph"]["rich_text"][0]["text"]["content"] class TestMarkdownRoundtrip: """Test markdown roundtrip conversion.""" @pytest.mark.parametrize("markdown,_,description", MARKDOWN_TEST_CASES) def test_markdown_roundtrip(self, markdown, _, description): """Test markdown -> blocks -> markdown conversion.""" blocks = parse_markdown_to_blocks(markdown) result = blocks_to_markdown(blocks) # Normalize whitespace for comparison normalized_original = "\n".join(line.strip() for line in markdown.split("\n") if line.strip()) normalized_result = "\n".join(line.strip() for line in result.split("\n") if line.strip()) assert normalized_original == normalized_result, f"Failed roundtrip for: {description}" def test_complex_markdown_roundtrip(self): """Test roundtrip of complex markdown.""" blocks = parse_markdown_to_blocks(COMPLEX_MARKDOWN) result = blocks_to_markdown(blocks) # Verify key elements preserved assert "# Project Overview" in result assert "## Features" in result assert "**Advanced**" in result assert "*Optional*" in result assert "```python" in result assert "def example():" in result assert "> Important note" in result assert "[ ]" in result assert "[x]" in result class TestBlockCreation: """Test block creation utilities.""" def test_create_rich_text_basic(self): """Test basic rich text creation.""" rich_text = create_rich_text("Test content") assert rich_text[0]["type"] == "text" assert rich_text[0]["text"]["content"] == "Test content" def test_create_rich_text_with_link(self): """Test rich text with link.""" rich_text = create_rich_text("Test link", link="https://example.com") assert rich_text[0]["text"]["link"]["url"] == "https://example.com" def test_create_rich_text_with_annotations(self): """Test rich text with annotations.""" annotations = {"bold": True, "italic": True} rich_text = create_rich_text("Test format", annotations=annotations) assert rich_text[0]["annotations"] == annotations def test_create_block_basic(self): """Test basic block creation.""" block = create_block("Test content", BlockType.PARAGRAPH) assert block["type"] == "paragraph" assert block["paragraph"]["rich_text"][0]["text"]["content"] == "Test content" def test_create_block_with_extra_props(self): """Test block creation with extra properties.""" block = create_block( "Test todo", BlockType.TO_DO, extra_props={"checked": True} ) assert block["to_do"]["checked"] is True def test_error_handling(): """Test error handling in markdown processing.""" # Test invalid block type with pytest.raises(ValueError): create_block("Test", "invalid_type") # Test empty content with pytest.raises(ValueError): create_rich_text("") # Test None content with pytest.raises(ValueError): create_rich_text(None) # Test invalid annotations with pytest.raises(ValueError): create_rich_text("Test", annotations={"invalid": True})

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/pbohannon/notion-api-mcp'

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