288 lines
No EOL
11 KiB
Python
288 lines
No EOL
11 KiB
Python
"""End-to-end tests for Ghost search functionality."""
|
|
|
|
import json
|
|
import pytest
|
|
|
|
from .conftest import BaseE2ETest
|
|
|
|
|
|
@pytest.mark.e2e
|
|
class TestSearchE2E(BaseE2ETest):
|
|
"""Test search functionality end-to-end."""
|
|
|
|
async def test_search_posts_basic(self, mcp_server, sample_published_post):
|
|
"""Test basic post search functionality."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Extract a searchable term from the test post title
|
|
search_term = sample_published_post["title"].split()[0]
|
|
|
|
# Search for posts
|
|
result = await search_posts(search_term)
|
|
response = json.loads(result)
|
|
|
|
# Verify response structure
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
|
|
# Should find our test post
|
|
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, f"Should find posts matching '{search_term}'"
|
|
|
|
async def test_search_posts_with_limit(self, mcp_server, sample_published_post):
|
|
"""Test post search with result limit."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Use a broad search term
|
|
search_term = "test"
|
|
|
|
# Search with limit
|
|
result = await search_posts(search_term, limit=3)
|
|
response = json.loads(result)
|
|
|
|
# Verify limit is respected
|
|
assert "posts" in response
|
|
assert len(response["posts"]) <= 3
|
|
|
|
async def test_search_posts_case_insensitive(self, mcp_server, sample_published_post):
|
|
"""Test that post search is case insensitive."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Get a word from the title in different cases
|
|
original_word = sample_published_post["title"].split()[0]
|
|
lowercase_search = original_word.lower()
|
|
uppercase_search = original_word.upper()
|
|
|
|
# Search with lowercase
|
|
lowercase_result = await search_posts(lowercase_search)
|
|
lowercase_response = json.loads(lowercase_result)
|
|
|
|
# Search with uppercase
|
|
uppercase_result = await search_posts(uppercase_search)
|
|
uppercase_response = json.loads(uppercase_result)
|
|
|
|
# Both should return similar results
|
|
assert len(lowercase_response["posts"]) > 0
|
|
assert len(uppercase_response["posts"]) > 0
|
|
|
|
# Should find the same post regardless of case
|
|
lowercase_titles = [post["title"] for post in lowercase_response["posts"]]
|
|
uppercase_titles = [post["title"] for post in uppercase_response["posts"]]
|
|
|
|
test_post_title = sample_published_post["title"]
|
|
if test_post_title in lowercase_titles:
|
|
assert test_post_title in uppercase_titles
|
|
|
|
async def test_search_posts_partial_match(self, mcp_server, sample_published_post):
|
|
"""Test that post search supports partial word matching."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Get a partial word from the title
|
|
full_word = sample_published_post["title"].split()[0]
|
|
if len(full_word) > 3:
|
|
partial_word = full_word[:3] # First 3 characters
|
|
|
|
# Search with partial word
|
|
result = await search_posts(partial_word)
|
|
response = json.loads(result)
|
|
|
|
# Should find posts containing the partial match
|
|
assert "posts" in response
|
|
# Note: Not all Ghost installations support partial matching,
|
|
# so we just verify the search doesn't error
|
|
|
|
async def test_search_posts_special_characters(self, mcp_server):
|
|
"""Test post search with special characters."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Test search with special characters
|
|
special_searches = ["test!", "test-post", "test_post"]
|
|
|
|
for search_term in special_searches:
|
|
try:
|
|
result = await search_posts(search_term)
|
|
response = json.loads(result)
|
|
|
|
# Should return valid response structure
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
except Exception as e:
|
|
# Some special characters might not be supported,
|
|
# but should not cause server errors
|
|
assert "500" not in str(e), f"Server error with search term '{search_term}'"
|
|
|
|
async def test_search_posts_empty_query(self, mcp_server):
|
|
"""Test post search with empty query."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Search with empty string
|
|
try:
|
|
result = await search_posts("")
|
|
response = json.loads(result)
|
|
|
|
# Should return empty results or all posts
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
except Exception as e:
|
|
# Empty query might not be allowed, but should return proper error
|
|
assert "400" in str(e) or "validation" in str(e).lower()
|
|
|
|
async def test_search_posts_nonexistent_term(self, mcp_server):
|
|
"""Test post search with non-existent term."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Search for something that shouldn't exist
|
|
nonexistent_term = "xyzneverexistingtermabc123"
|
|
|
|
result = await search_posts(nonexistent_term)
|
|
response = json.loads(result)
|
|
|
|
# Should return empty results
|
|
assert "posts" in response
|
|
assert len(response["posts"]) == 0
|
|
|
|
async def test_search_posts_common_words(self, mcp_server):
|
|
"""Test post search with common words."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Test with common words
|
|
common_words = ["the", "and", "is", "test"]
|
|
|
|
for word in common_words:
|
|
result = await search_posts(word)
|
|
response = json.loads(result)
|
|
|
|
# Should return valid response
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
|
|
async def test_search_posts_unicode_characters(self, mcp_server):
|
|
"""Test post search with unicode characters."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Test with unicode characters
|
|
unicode_searches = ["café", "naïve", "résumé", "中文"]
|
|
|
|
for search_term in unicode_searches:
|
|
try:
|
|
result = await search_posts(search_term)
|
|
response = json.loads(result)
|
|
|
|
# Should return valid response structure
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
except Exception as e:
|
|
# Unicode might not be fully supported in all Ghost configurations
|
|
# but should not cause server errors
|
|
assert "500" not in str(e), f"Server error with unicode search '{search_term}'"
|
|
|
|
async def test_search_posts_long_query(self, mcp_server):
|
|
"""Test post search with very long query."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Very long search query
|
|
long_query = "this is a very long search query that tests the system's ability to handle long search terms without breaking or causing performance issues" * 3
|
|
|
|
try:
|
|
result = await search_posts(long_query)
|
|
response = json.loads(result)
|
|
|
|
# Should handle long queries gracefully
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
except Exception as e:
|
|
# Long queries might be rejected, but should return proper error
|
|
assert "400" in str(e) or "413" in str(e) or "length" in str(e).lower()
|
|
|
|
async def test_search_posts_multiple_words(self, mcp_server, sample_published_post):
|
|
"""Test post search with multiple words."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Get multiple words from the title
|
|
title_words = sample_published_post["title"].split()
|
|
if len(title_words) >= 2:
|
|
# Search with first two words
|
|
multi_word_search = f"{title_words[0]} {title_words[1]}"
|
|
|
|
result = await search_posts(multi_word_search)
|
|
response = json.loads(result)
|
|
|
|
# Should return valid results
|
|
assert "posts" in response
|
|
assert isinstance(response["posts"], list)
|
|
|
|
async def test_search_posts_pagination_metadata(self, mcp_server, sample_published_post):
|
|
"""Test that search results include proper pagination metadata."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Search for posts
|
|
search_term = "test"
|
|
result = await search_posts(search_term, limit=5)
|
|
response = json.loads(result)
|
|
|
|
# Should have posts array
|
|
assert "posts" in response
|
|
|
|
# May or may not have meta depending on implementation,
|
|
# but if present should be properly structured
|
|
if "meta" in response:
|
|
meta = response["meta"]
|
|
assert isinstance(meta, dict)
|
|
|
|
if "pagination" in meta:
|
|
pagination = meta["pagination"]
|
|
assert isinstance(pagination, dict)
|
|
|
|
async def test_search_posts_include_fields(self, mcp_server, sample_published_post):
|
|
"""Test search with include fields parameter."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Extract search term
|
|
search_term = sample_published_post["title"].split()[0]
|
|
|
|
# Search with include fields
|
|
result = await search_posts(search_term, include="tags,authors")
|
|
response = json.loads(result)
|
|
|
|
# Verify include fields work in search
|
|
if response["posts"]:
|
|
post = response["posts"][0]
|
|
assert "tags" in post
|
|
assert "authors" in post
|
|
|
|
async def test_search_posts_response_structure(self, mcp_server, sample_published_post):
|
|
"""Test that search results have proper post structure."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Search for posts
|
|
search_term = sample_published_post["title"].split()[0]
|
|
result = await search_posts(search_term)
|
|
response = json.loads(result)
|
|
|
|
# Verify each post has essential fields
|
|
if response["posts"]:
|
|
post = response["posts"][0]
|
|
|
|
essential_fields = ["id", "title", "slug", "status", "created_at", "updated_at", "url"]
|
|
for field in essential_fields:
|
|
assert field in post, f"Search result should include '{field}'"
|
|
|
|
async def test_search_only_published_posts(self, mcp_server, sample_post, sample_published_post):
|
|
"""Test that search only returns published posts, not drafts."""
|
|
from ghost_mcp.tools.content.posts import search_posts
|
|
|
|
# Search with a term that might match both posts
|
|
search_term = "test"
|
|
result = await search_posts(search_term)
|
|
response = json.loads(result)
|
|
|
|
# Verify all returned posts are published
|
|
for post in response["posts"]:
|
|
assert post["status"] == "published", "Search should only return published posts"
|
|
|
|
# Verify our published post might be in results
|
|
published_post_ids = [post["id"] for post in response["posts"]]
|
|
|
|
# Our draft post should NOT be in search results
|
|
assert sample_post["id"] not in published_post_ids, "Draft posts should not appear in search" |