281 lines
No EOL
10 KiB
Python
281 lines
No EOL
10 KiB
Python
"""End-to-end tests for Ghost settings functionality."""
|
|
|
|
import json
|
|
import pytest
|
|
|
|
from .conftest import BaseE2ETest
|
|
|
|
|
|
@pytest.mark.e2e
|
|
class TestSettingsContentAPIE2E(BaseE2ETest):
|
|
"""Test settings Content API functionality end-to-end."""
|
|
|
|
async def test_get_settings(self, mcp_server):
|
|
"""Test getting public settings."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Verify response structure
|
|
assert "settings" in response
|
|
assert isinstance(response["settings"], dict)
|
|
|
|
# Should have multiple settings
|
|
assert len(response["settings"]) > 0
|
|
|
|
# Verify settings structure - settings is a dict with direct key-value pairs
|
|
settings = response["settings"]
|
|
# Check for some expected settings keys
|
|
expected_keys = ["title", "description", "url"]
|
|
for key in expected_keys:
|
|
assert key in settings
|
|
|
|
async def test_get_settings_essential_keys(self, mcp_server):
|
|
"""Test that essential settings keys are present."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Extract all setting keys
|
|
setting_keys = list(response["settings"].keys())
|
|
|
|
# Essential settings that should be present
|
|
essential_keys = [
|
|
"title",
|
|
"description",
|
|
"url",
|
|
"timezone",
|
|
"locale"
|
|
]
|
|
|
|
# Verify essential keys are present
|
|
for key in essential_keys:
|
|
assert key in setting_keys, f"Essential setting '{key}' not found"
|
|
|
|
async def test_get_settings_data_types(self, mcp_server):
|
|
"""Test that settings have correct data types."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Check data types for each setting
|
|
for key, value in response["settings"].items():
|
|
assert isinstance(key, str), f"Setting key should be string: {key}"
|
|
# Value can be string, bool, int, list, or null
|
|
assert value is None or isinstance(value, (str, bool, int, list, dict))
|
|
|
|
async def test_get_settings_site_title(self, mcp_server):
|
|
"""Test that site title setting is accessible."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Check title setting
|
|
assert "title" in response["settings"], "Should have title setting"
|
|
|
|
title_value = response["settings"]["title"]
|
|
assert isinstance(title_value, str)
|
|
assert len(title_value) > 0, "Site title should not be empty"
|
|
|
|
async def test_get_settings_site_url(self, mcp_server):
|
|
"""Test that site URL setting is accessible and valid."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Check url setting
|
|
assert "url" in response["settings"], "Should have url setting"
|
|
|
|
url_value = response["settings"]["url"]
|
|
assert isinstance(url_value, str)
|
|
assert url_value.startswith("http"), "Site URL should start with http"
|
|
assert "localhost:2368" in url_value, "Should be localhost test instance"
|
|
|
|
async def test_get_site_info(self, mcp_server):
|
|
"""Test getting basic site information."""
|
|
from ghost_mcp.tools.content.settings import get_site_info
|
|
|
|
# Get site info
|
|
result = await get_site_info()
|
|
response = json.loads(result)
|
|
|
|
# Verify response structure
|
|
assert "site_info" in response
|
|
site = response["site_info"]
|
|
|
|
# Verify essential site info fields
|
|
essential_fields = ["title", "url", "version"]
|
|
for field in essential_fields:
|
|
assert field in site, f"Site info should include '{field}'"
|
|
|
|
async def test_get_site_info_title_matches_settings(self, mcp_server):
|
|
"""Test that site info title matches settings title."""
|
|
from ghost_mcp.tools.content.settings import get_site_info, get_settings
|
|
|
|
# Get both site info and settings
|
|
site_info_result = await get_site_info()
|
|
settings_result = await get_settings()
|
|
|
|
site_info_response = json.loads(site_info_result)
|
|
settings_response = json.loads(settings_result)
|
|
|
|
# Extract titles
|
|
site_title = site_info_response["site_info"]["title"]
|
|
settings_title = settings_response["settings"]["title"]
|
|
|
|
# Titles should match
|
|
assert site_title == settings_title, "Site info title should match settings title"
|
|
|
|
async def test_get_site_info_url_matches_settings(self, mcp_server):
|
|
"""Test that site info URL matches settings URL."""
|
|
from ghost_mcp.tools.content.settings import get_site_info, get_settings
|
|
|
|
# Get both site info and settings
|
|
site_info_result = await get_site_info()
|
|
settings_result = await get_settings()
|
|
|
|
site_info_response = json.loads(site_info_result)
|
|
settings_response = json.loads(settings_result)
|
|
|
|
# Extract URLs
|
|
site_url = site_info_response["site_info"]["url"]
|
|
settings_url = settings_response["settings"]["url"]
|
|
|
|
# URLs should match
|
|
assert site_url == settings_url, "Site info URL should match settings URL"
|
|
|
|
async def test_get_site_info_version_format(self, mcp_server):
|
|
"""Test that site info includes valid Ghost version."""
|
|
from ghost_mcp.tools.content.settings import get_site_info
|
|
|
|
# Get site info
|
|
result = await get_site_info()
|
|
response = json.loads(result)
|
|
|
|
site = response["site_info"]
|
|
version = site["version"]
|
|
|
|
# Version should be a non-empty string
|
|
assert isinstance(version, str)
|
|
assert len(version) > 0
|
|
|
|
# Should contain a dot (version format like 5.x.x)
|
|
assert "." in version, "Version should be in x.y.z format"
|
|
|
|
async def test_settings_no_sensitive_data(self, mcp_server):
|
|
"""Test that settings don't expose sensitive information."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Extract all setting keys
|
|
setting_keys = list(response["settings"].keys())
|
|
|
|
# Keys that should NOT be present in public settings
|
|
sensitive_keys = [
|
|
"db_password",
|
|
"mailgun_api_key",
|
|
"admin_api_key",
|
|
"content_api_key",
|
|
"smtp_password",
|
|
"oauth_client_secret"
|
|
]
|
|
|
|
# Verify sensitive keys are not exposed
|
|
for sensitive_key in sensitive_keys:
|
|
assert sensitive_key not in setting_keys, f"Sensitive key '{sensitive_key}' should not be exposed"
|
|
|
|
async def test_settings_readonly_access(self, mcp_server):
|
|
"""Test that Content API only provides read access to settings."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# This test verifies that we can read settings but not modify them
|
|
# through the Content API (which is read-only)
|
|
|
|
# Get settings should work
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Should return valid settings
|
|
assert "settings" in response
|
|
assert len(response["settings"]) > 0
|
|
|
|
# Note: Write operations would be through Admin API, which requires
|
|
# separate authentication and is not typically exposed through MCP tools
|
|
|
|
async def test_get_settings_pagination_metadata(self, mcp_server):
|
|
"""Test that settings include proper metadata structure."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Should have settings array
|
|
assert "settings" in response
|
|
|
|
# May or may not have meta depending on Ghost version,
|
|
# but if present should be properly structured
|
|
if "meta" in response:
|
|
meta = response["meta"]
|
|
assert isinstance(meta, dict)
|
|
|
|
async def test_settings_timezone_format(self, mcp_server):
|
|
"""Test that timezone setting is in valid format."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Check timezone setting
|
|
if "timezone" in response["settings"]: # timezone might not always be present
|
|
timezone_value = response["settings"]["timezone"]
|
|
|
|
# Should be a string
|
|
assert isinstance(timezone_value, str)
|
|
|
|
# Common timezone formats
|
|
valid_formats = [
|
|
timezone_value.startswith("Etc/"),
|
|
timezone_value.startswith("America/"),
|
|
timezone_value.startswith("Europe/"),
|
|
timezone_value.startswith("Asia/"),
|
|
timezone_value == "UTC",
|
|
"/" in timezone_value # General timezone format
|
|
]
|
|
|
|
assert any(valid_formats), f"Invalid timezone format: {timezone_value}"
|
|
|
|
async def test_settings_locale_format(self, mcp_server):
|
|
"""Test that locale setting is in valid format."""
|
|
from ghost_mcp.tools.content.settings import get_settings
|
|
|
|
# Get settings
|
|
result = await get_settings()
|
|
response = json.loads(result)
|
|
|
|
# Check locale setting
|
|
if "locale" in response["settings"]: # locale might not always be present
|
|
locale_value = response["settings"]["locale"]
|
|
|
|
# Should be a string
|
|
assert isinstance(locale_value, str)
|
|
assert len(locale_value) >= 2, "Locale should be at least 2 characters"
|
|
|
|
# Common locale formats (en, en-US, etc.)
|
|
# Should contain only letters, hyphens, and underscores
|
|
import re
|
|
assert re.match(r'^[a-zA-Z_-]+$', locale_value), f"Invalid locale format: {locale_value}" |