Skip to main content
Glama

Notion API MCP Server

test_rich_text.py7.3 kB
""" Comprehensive validation tests for rich text description functionality. """ import pytest import pytest_asyncio from datetime import datetime from unittest.mock import AsyncMock, MagicMock from pydantic import ValidationError from notion_api_mcp.models.properties import ( RichTextContent, RichTextObject, RichTextProperty, TodoProperties, MAX_TEXT_LENGTH, MAX_TITLE_LENGTH ) # Test Data MARKDOWN_SAMPLES = [ ("Basic text", "Simple description"), ("Bold text", "This is **bold** text"), ("Italic text", "This is *italic* text"), ("Combined formatting", "**Bold** and *italic* text"), ("Links", "Check [this link](https://example.com)"), ("Lists", "- Item 1\n- Item 2\n- Item 3"), ("Code blocks", "```python\nprint('hello')\n```"), ("Special characters", "Special chars: &<>\"'"), ("Unicode", "Unicode: 你好,世界! 🌎"), ("Long text", "x" * 1000), # Reduced length for valid tests ] @pytest.fixture def mock_notion_api(): """Mock Notion API client""" mock = AsyncMock() mock.post = AsyncMock(return_value=AsyncMock( status_code=200, json=AsyncMock(return_value={"object": "page"}) )) return mock class TestRichTextModels: """Test rich text model validation and conversion.""" def test_rich_text_content_basic(self): """Test basic rich text content creation.""" content = RichTextContent(content="Test content") assert content.content == "Test content" assert content.link is None def test_rich_text_content_with_link(self): """Test rich text content with link.""" content = RichTextContent( content="Test with link", link={"url": "https://example.com"} ) assert content.link["url"] == "https://example.com" def test_rich_text_object_basic(self): """Test basic rich text object creation.""" text = RichTextObject( text=RichTextContent(content="Test text") ) assert text.type == "text" assert text.text.content == "Test text" assert text.annotations is None def test_rich_text_object_with_annotations(self): """Test rich text object with formatting annotations.""" text = RichTextObject( text=RichTextContent(content="Formatted text"), annotations={ "bold": True, "italic": False, "strikethrough": False, "underline": False, "code": False, "color": "default" } ) assert text.annotations["bold"] is True assert text.annotations["color"] == "default" @pytest.mark.parametrize("name,content", MARKDOWN_SAMPLES) def test_rich_text_content_variations(self, name, content): """Test various rich text content patterns.""" prop = RichTextProperty(rich_text=[ RichTextObject(text=RichTextContent(content=content)) ]) assert prop.type == "rich_text" assert prop.rich_text[0].text.content == content class TestTodoPropertiesRichText: """Test rich text handling in todo properties.""" def test_todo_description_basic(self): """Test basic description conversion.""" todo = TodoProperties( task="Test Task", description="Basic description" ) props = todo.to_notion_properties() assert props["Description"]["rich_text"][0]["text"]["content"] == "Basic description" def test_todo_description_max_length(self): """Test description length limits.""" long_text = "x" * (MAX_TEXT_LENGTH + 100) # Exceed limit with pytest.raises(ValidationError) as exc_info: TodoProperties( task="Test Task", description=long_text ) assert "String should have at most 2000 characters" in str(exc_info.value) @pytest.mark.parametrize("name,content", MARKDOWN_SAMPLES) def test_todo_description_formatting(self, name, content): """Test various description formatting patterns.""" todo = TodoProperties( task="Test Task", description=content ) props = todo.to_notion_properties() assert props["Description"]["rich_text"][0]["text"]["content"] == content @pytest.mark.asyncio class TestRichTextIntegration: """Integration tests for rich text functionality.""" async def test_create_todo_with_formatted_description(self, mock_notion_api): """Test creating a todo with formatted description.""" formatted_text = ( "# Important Task\n" "\n" "This task needs **immediate** attention:\n" "- First step\n" "- *Second* step\n" "- Final step\n" "\n" "[Reference link](https://example.com)" ) todo = TodoProperties( task="Integration Test", description=formatted_text ) props = todo.to_notion_properties() # Verify formatting structure rich_text = props["Description"]["rich_text"][0] assert rich_text["type"] == "text" assert rich_text["text"]["content"].strip() == formatted_text.strip() async def test_create_todo_with_special_characters(self, mock_notion_api): """Test creating a todo with special characters.""" special_chars = "Special chars: &<>\"' and emojis: 🌟✨🚀" todo = TodoProperties( task="Special Chars Test", description=special_chars ) props = todo.to_notion_properties() assert props["Description"]["rich_text"][0]["text"]["content"] == special_chars @pytest.mark.parametrize("name,content", MARKDOWN_SAMPLES) async def test_roundtrip_formatting(self, mock_notion_api, name, content): """Test roundtrip of formatted content through the API.""" # Create todo with formatted content todo = TodoProperties( task=f"Format Test: {name}", description=content ) props = todo.to_notion_properties() # Simulate API response api_response = { "properties": { "Task": {"title": [{"text": {"content": todo.task}}]}, "Description": {"rich_text": props["Description"]["rich_text"]} } } # Convert back from API format roundtrip_todo = TodoProperties.from_notion_properties(api_response["properties"]) assert roundtrip_todo.description == content def test_rich_text_error_handling(): """Test error handling for rich text operations.""" # Test empty content with pytest.raises(ValidationError): RichTextContent(content="") # Test None content with pytest.raises(ValidationError): RichTextContent(content=None) # Test invalid annotations with pytest.raises(ValidationError): RichTextObject( text=RichTextContent(content="Test"), annotations={"invalid": True} ) # Test exceeding max length with pytest.raises(ValidationError): RichTextContent(content="x" * (MAX_TEXT_LENGTH + 1))

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