"""End-to-end tests for Ghost posts functionality.""" import json import pytest from .conftest import BaseE2ETest @pytest.mark.e2e class TestPostsContentAPIE2E(BaseE2ETest): """Test posts Content API functionality end-to-end.""" async def test_get_posts(self, mcp_server, sample_published_post): """Test getting published posts.""" # Get posts result = await self.call_mcp_tool(mcp_server, "get_posts") response = json.loads(result) # Verify response structure assert "posts" in response assert "meta" in response assert isinstance(response["posts"], list) # Verify our test post appears in the list post_titles = [post["title"] for post in response["posts"]] assert sample_published_post["title"] in post_titles async def test_get_posts_with_pagination(self, mcp_server): """Test getting posts with pagination parameters.""" # Get posts with limit result = await self.call_mcp_tool(mcp_server, "get_posts", limit=5) response = json.loads(result) assert "posts" in response assert len(response["posts"]) <= 5 # Test pagination metadata assert "meta" in response assert "pagination" in response["meta"] async def test_get_posts_with_include_fields(self, mcp_server): """Test getting posts with include fields.""" # Get posts with tags and authors included result = await self.call_mcp_tool(mcp_server, "get_posts", include="tags,authors") response = json.loads(result) # Verify posts include tags and authors if response["posts"]: post = response["posts"][0] assert "tags" in post assert "authors" in post async def test_get_post_by_id(self, mcp_server, sample_published_post): """Test getting a post by ID.""" # Get post by ID result = await self.call_mcp_tool(mcp_server, "get_post_by_id", post_id=sample_published_post["id"]) response = json.loads(result) # Verify response assert "posts" in response assert len(response["posts"]) == 1 post = response["posts"][0] assert post["id"] == sample_published_post["id"] assert post["title"] == sample_published_post["title"] async def test_get_post_by_slug(self, mcp_server, sample_published_post): """Test getting a post by slug.""" # Get post by slug result = await self.call_mcp_tool(mcp_server, "get_post_by_slug", slug=sample_published_post["slug"]) response = json.loads(result) # Verify response assert "posts" in response assert len(response["posts"]) == 1 post = response["posts"][0] assert post["slug"] == sample_published_post["slug"] assert post["title"] == sample_published_post["title"] async def test_search_posts(self, mcp_server, sample_published_post): """Test searching posts.""" # Extract a unique word from the test post title search_term = sample_published_post["title"].split()[0] # Search for posts result = await self.call_mcp_tool(mcp_server, "search_posts", query=search_term) response = json.loads(result) # Verify response assert "posts" in response assert isinstance(response["posts"], list) # Verify our test post appears in search results if response["posts"]: post_titles = [post["title"] for post in response["posts"]] matching_posts = [title for title in post_titles if search_term in title] assert len(matching_posts) > 0 async def test_get_post_by_nonexistent_id(self, mcp_server): """Test getting a post with non-existent ID returns proper error.""" result = await self.call_mcp_tool(mcp_server, "get_post_by_id", post_id="nonexistent-id") # MCP tools return JSON error responses instead of raising exceptions response = json.loads(result) assert "error" in response assert ("not found" in response["error"].lower() or "validation error" in response["error"].lower()) async def test_get_post_by_nonexistent_slug(self, mcp_server): """Test getting a post with non-existent slug returns proper error.""" result = await self.call_mcp_tool(mcp_server, "get_post_by_slug", slug="nonexistent-slug") # MCP tools return JSON error responses instead of raising exceptions response = json.loads(result) assert "error" in response assert ("not found" in response["error"].lower() or "validation error" in response["error"].lower()) @pytest.mark.e2e @pytest.mark.admin class TestPostsAdminAPIE2E(BaseE2ETest): """Test posts Admin API functionality end-to-end.""" async def test_create_post_draft(self, mcp_server, test_post_data, cleanup_test_content): """Test creating a draft post.""" # Create post result = await self.call_mcp_tool( mcp_server, "create_post", title=test_post_data["title"], content=test_post_data["content"], content_format=test_post_data["content_format"], status=test_post_data["status"] ) response = json.loads(result) # Verify response assert "posts" in response assert len(response["posts"]) == 1 post = response["posts"][0] assert post["title"] == test_post_data["title"] assert post["status"] == "draft" assert "id" in post # Track for cleanup cleanup_test_content["track_post"](post["id"]) async def test_create_post_published(self, mcp_server, test_post_data, cleanup_test_content): """Test creating a published post with complex content.""" # Complex post content based on the template in Lexical format complex_title = "Using Playwright MCP Server with Google Chrome Flatpak on Linux" complex_content = json.dumps({ "root": { "children": [ { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Using Playwright MCP Server with Google Chrome Flatpak on Linux", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "heading", "version": 1, "tag": "h1" }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "The Model Context Protocol (MCP) has revolutionized how AI assistants interact with external tools and services. One particularly powerful integration is the ", "type": "text", "version": 1 }, { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Playwright MCP server", "type": "link", "url": "https://github.com/microsoft/playwright-mcp", "version": 1 }, { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": ", which enables AI to control web browsers for automation tasks.", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "paragraph", "version": 1 }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "The Simple Solution", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "heading", "version": 1, "tag": "h2" }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Instead of complex configurations, we'll use a two-step approach:", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "paragraph", "version": 1 }, { "children": [ { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Install Google Chrome from Flathub", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "listitem", "version": 1, "value": 1 }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Create a symbolic link that Playwright expects", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "listitem", "version": 1, "value": 2 } ], "direction": "ltr", "format": "", "indent": 0, "type": "list", "version": 1, "listType": "number", "start": 1 }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Step 1: Install Google Chrome", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "heading", "version": 1, "tag": "h2" }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "First, install Google Chrome using Flatpak:", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "paragraph", "version": 1 }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "flatpak install flathub com.google.Chrome", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "code", "version": 1, "language": "bash" }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Conclusion", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "heading", "version": 1, "tag": "h2" }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "This simple solution eliminates complexity and combines Flatpak security with Playwright simplicity.", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "paragraph", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "root", "version": 1 } }) # Create published post with complex content result = await self.call_mcp_tool( mcp_server, "create_post", title=complex_title, content=complex_content, content_format="lexical", status="published", excerpt="Learn how to set up Playwright MCP server with Chrome Flatpak on Linux using a simple two-step approach.", featured=True, meta_title="Playwright MCP + Chrome Flatpak Setup Guide", meta_description="Simple guide to configure Playwright MCP server with Google Chrome Flatpak on Linux using symbolic links." ) response = json.loads(result) # Verify response assert "posts" in response post = response["posts"][0] assert post["status"] == "published" assert post["title"] == complex_title assert post["featured"] is True assert "published_at" in post assert post["published_at"] is not None assert post["excerpt"] == "Learn how to set up Playwright MCP server with Chrome Flatpak on Linux using a simple two-step approach." assert post["meta_title"] == "Playwright MCP + Chrome Flatpak Setup Guide" # Verify content contains key elements if "lexical" in post: lexical_content = post["lexical"] assert "Playwright MCP" in lexical_content assert "heading" in lexical_content # Has headings assert "code" in lexical_content # Has code blocks assert "list" in lexical_content # Has lists # Track for cleanup post_id = post["id"] cleanup_test_content["track_post"](post_id) # Retrieve the post using Admin API to verify all metadata including status retrieve_result = await self.call_mcp_tool( mcp_server, "get_admin_posts", filter=f"id:{post_id}" ) retrieve_response = json.loads(retrieve_result) assert "posts" in retrieve_response assert len(retrieve_response["posts"]) == 1 retrieved_post = retrieve_response["posts"][0] # Verify the post was stored correctly with all metadata assert retrieved_post["title"] == complex_title assert retrieved_post["status"] == "published" assert retrieved_post["featured"] is True assert retrieved_post["excerpt"] == "Learn how to set up Playwright MCP server with Chrome Flatpak on Linux using a simple two-step approach." # Verify content was stored and is accessible if "lexical" in retrieved_post and retrieved_post["lexical"]: retrieved_lexical_content = retrieved_post["lexical"] # Verify key content elements are present assert "Using Playwright MCP Server" in retrieved_lexical_content assert "Simple Solution" in retrieved_lexical_content assert "Install Google Chrome" in retrieved_lexical_content assert "flatpak install" in retrieved_lexical_content else: # If no lexical content, test should fail assert False, "Post content was not properly stored - no Lexical content found" async def test_create_and_verify_content_lexical(self, mcp_server, cleanup_test_content): """Test creating a post with Lexical content and verifying it was stored correctly.""" # Create a post with specific Lexical content test_title = "Content Verification Test - Lexical" test_content = json.dumps({ "root": { "children": [ { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "Test Heading for Verification", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "heading", "version": 1, "tag": "h2" }, { "children": [ { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "This is a test paragraph with ", "type": "text", "version": 1 }, { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": "a test link", "type": "link", "url": "https://example.com/test", "version": 1 }, { "detail": 0, "format": 0, "mode": "normal", "style": "", "text": " for content verification.", "type": "text", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "paragraph", "version": 1 } ], "direction": "ltr", "format": "", "indent": 0, "type": "root", "version": 1 } }) # Create the post create_result = await self.call_mcp_tool( mcp_server, "create_post", title=test_title, content=test_content, content_format="lexical", status="published" ) create_response = json.loads(create_result) assert "posts" in create_response created_post = create_response["posts"][0] post_id = created_post["id"] # Track for cleanup cleanup_test_content["track_post"](post_id) # Retrieve the post using Admin API to verify status and content retrieve_result = await self.call_mcp_tool( mcp_server, "get_admin_posts", filter=f"id:{post_id}" ) retrieve_response = json.loads(retrieve_result) assert "posts" in retrieve_response assert len(retrieve_response["posts"]) == 1 retrieved_post = retrieve_response["posts"][0] # Verify basic metadata assert retrieved_post["title"] == test_title assert retrieved_post["status"] == "published" # Verify Lexical content integrity assert "lexical" in retrieved_post retrieved_lexical = json.loads(retrieved_post["lexical"]) original_lexical = json.loads(test_content) # Verify structure assert "root" in retrieved_lexical assert retrieved_lexical["root"]["type"] == "root" assert len(retrieved_lexical["root"]["children"]) == 2 # Verify heading content heading = retrieved_lexical["root"]["children"][0] assert heading["type"] == "heading" assert heading["tag"] == "h2" assert heading["children"][0]["text"] == "Test Heading for Verification" # Verify paragraph with link paragraph = retrieved_lexical["root"]["children"][1] assert paragraph["type"] == "paragraph" assert len(paragraph["children"]) == 3 assert paragraph["children"][1]["type"] == "link" assert paragraph["children"][1]["url"] == "https://example.com/test" assert paragraph["children"][1]["text"] == "a test link" async def test_create_and_verify_content_html(self, mcp_server, cleanup_test_content): """Test creating a post with HTML content and verifying it was stored correctly.""" # Create a post with HTML content test_title = "Content Verification Test - HTML" test_content = """
This is a test paragraph with an HTML link for content verification.