"""End-to-end tests for Ghost MCP validation functionality.""" import json import pytest from .conftest import BaseE2ETest @pytest.mark.e2e @pytest.mark.admin class TestPostValidationE2E(BaseE2ETest): """Test post validation functionality end-to-end.""" async def test_validate_title_required(self, mcp_server): """Test that title is required for post creation.""" result = await self.call_mcp_tool( mcp_server, "create_post", title="" ) response = json.loads(result) assert "error" in response assert "title" in response["error"].lower() assert "required" in response["error"].lower() async def test_validate_title_too_long(self, mcp_server): """Test that overly long titles are rejected.""" long_title = "A" * 300 # Exceeds 255 character limit result = await self.call_mcp_tool( mcp_server, "create_post", title=long_title ) response = json.loads(result) assert "error" in response assert "too long" in response["error"].lower() assert "255" in response["error"] async def test_validate_invalid_status(self, mcp_server): """Test that invalid status values are rejected.""" result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", status="invalid_status" ) response = json.loads(result) assert "error" in response assert "invalid" in response["error"].lower() or "status" in response["error"].lower() assert "context" in response async def test_validate_scheduled_without_date(self, mcp_server): """Test that scheduled posts require published_at.""" result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Scheduled Post", status="scheduled" ) response = json.loads(result) assert "error" in response assert "scheduled" in response["error"].lower() assert "published_at" in response["error"].lower() assert "context" in response async def test_validate_invalid_published_at(self, mcp_server): """Test that invalid datetime formats are rejected.""" result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", status="scheduled", published_at="invalid-date-format" ) response = json.loads(result) assert "error" in response assert "invalid" in response["error"].lower() assert "datetime" in response["error"].lower() or "format" in response["error"].lower() assert "ISO" in response.get("context", "") async def test_validate_invalid_content_format(self, mcp_server): """Test that invalid content formats are rejected.""" result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content="Some content", content_format="invalid_format" ) response = json.loads(result) assert "error" in response assert "content format" in response["error"].lower() or "format" in response["error"].lower() assert "context" in response async def test_validate_invalid_lexical_json(self, mcp_server): """Test that malformed Lexical JSON is rejected.""" invalid_lexical = '{"root": {"invalid": json}}' # Invalid JSON result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content=invalid_lexical, content_format="lexical" ) response = json.loads(result) assert "error" in response assert "json" in response["error"].lower() assert "context" in response async def test_validate_lexical_missing_root(self, mcp_server): """Test that Lexical JSON without root is rejected.""" invalid_lexical = json.dumps({"notroot": {"children": []}}) result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content=invalid_lexical, content_format="lexical" ) response = json.loads(result) assert "error" in response assert "root" in response["error"].lower() assert "context" in response async def test_validate_lexical_missing_required_props(self, mcp_server): """Test that Lexical JSON with missing required properties is rejected.""" invalid_lexical = json.dumps({ "root": { "children": [], # Missing required properties like direction, format, indent, type, version } }) result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content=invalid_lexical, content_format="lexical" ) response = json.loads(result) assert "error" in response assert "missing" in response["error"].lower() or "property" in response["error"].lower() assert "context" in response async def test_validate_lexical_invalid_node_type(self, mcp_server): """Test that Lexical nodes with invalid types are rejected.""" invalid_lexical = json.dumps({ "root": { "children": [ { "type": "invalid_node_type", "version": 1, "children": [] } ], "direction": "ltr", "format": "", "indent": 0, "type": "root", "version": 1 } }) result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content=invalid_lexical, content_format="lexical" ) response = json.loads(result) assert "error" in response assert "invalid" in response["error"].lower() assert "type" in response["error"].lower() async def test_validate_lexical_heading_without_tag(self, mcp_server): """Test that heading nodes without tag property are rejected.""" invalid_lexical = json.dumps({ "root": { "children": [ { "type": "heading", "version": 1, "children": [ { "type": "text", "text": "Heading text", "version": 1 } ] # Missing "tag" property for heading } ], "direction": "ltr", "format": "", "indent": 0, "type": "root", "version": 1 } }) result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content=invalid_lexical, content_format="lexical" ) response = json.loads(result) assert "error" in response assert "heading" in response["error"].lower() assert "tag" in response["error"].lower() async def test_validate_lexical_link_without_url(self, mcp_server): """Test that link nodes without URL property are rejected.""" invalid_lexical = json.dumps({ "root": { "children": [ { "type": "paragraph", "version": 1, "direction": "ltr", "format": "", "indent": 0, "children": [ { "type": "link", "text": "Link text", "version": 1 # Missing "url" property for link } ] } ], "direction": "ltr", "format": "", "indent": 0, "type": "root", "version": 1 } }) result = await self.call_mcp_tool( mcp_server, "create_post", title="Test Post", content=invalid_lexical, content_format="lexical" ) response = json.loads(result) assert "error" in response assert "link" in response["error"].lower() assert "url" in response["error"].lower() async def test_validate_html_malformed(self, mcp_server): """Test that malformed HTML is rejected.""" invalid_html = "
Unclosed paragraph