style: format 9 files with Black
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -385,23 +385,25 @@ def test_dashboard_connect_page(monkeypatch):
|
||||
client = TestClient(app)
|
||||
|
||||
def mock_req(*args):
|
||||
return {'user_id': 'abc', 'type': 'user'}, None
|
||||
return {"user_id": "abc", "type": "user"}, None
|
||||
|
||||
monkeypatch.setattr(core.dashboard.routes, '_require_user_session', mock_req)
|
||||
monkeypatch.setattr(core.dashboard.routes, "_require_user_session", mock_req)
|
||||
|
||||
async def mock_sites(*args):
|
||||
return [{'alias': 'Test', 'plugin_type': 'dummy'}]
|
||||
return [{"alias": "Test", "plugin_type": "dummy"}]
|
||||
|
||||
monkeypatch.setattr(core.site_api, 'get_user_sites', mock_sites)
|
||||
monkeypatch.setattr(core.site_api, "get_user_sites", mock_sites)
|
||||
|
||||
class MockKeyMgr:
|
||||
async def list_keys(self, *a):
|
||||
return [{'id': '1', 'name': 'Key', 'key_prefix': 'prefix', 'scopes': 'all', 'use_count': 0}]
|
||||
return [
|
||||
{"id": "1", "name": "Key", "key_prefix": "prefix", "scopes": "all", "use_count": 0}
|
||||
]
|
||||
|
||||
monkeypatch.setattr(core.user_keys, "get_user_key_manager", lambda: MockKeyMgr())
|
||||
|
||||
resp = client.get("/dashboard/connect")
|
||||
|
||||
monkeypatch.setattr(core.user_keys, 'get_user_key_manager', lambda: MockKeyMgr())
|
||||
|
||||
resp = client.get('/dashboard/connect')
|
||||
|
||||
assert resp.status_code == 200
|
||||
assert "Test" in resp.text
|
||||
assert "Key" in resp.text
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
Integration tests for the per-user dynamic MCP endpoints.
|
||||
Uses Starlette TestClient to simulate real HTTP requests through the server.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from starlette.testclient import TestClient
|
||||
from unittest.mock import patch, MagicMock, AsyncMock
|
||||
@@ -12,22 +13,23 @@ from server import create_multi_endpoint_app
|
||||
app = create_multi_endpoint_app()
|
||||
client = TestClient(app)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_managers():
|
||||
"""Mock the core managers to avoid needing a real database or keys."""
|
||||
|
||||
|
||||
# Mock Site Manager
|
||||
mock_site_manager = MagicMock()
|
||||
mock_site_manager.list_all_sites.return_value = []
|
||||
|
||||
|
||||
# Mock Key Manager
|
||||
mock_key_manager = AsyncMock()
|
||||
mock_key_manager.validate_key.return_value = {
|
||||
"key_id": "test-key-123",
|
||||
"user_id": "user-123",
|
||||
"scopes": "read write"
|
||||
"scopes": "read write",
|
||||
}
|
||||
|
||||
|
||||
# Mock Database
|
||||
mock_db = AsyncMock()
|
||||
mock_db.get_site_by_alias.return_value = {
|
||||
@@ -37,39 +39,40 @@ def mock_managers():
|
||||
"alias": "myblog",
|
||||
"url": "https://example.com",
|
||||
"credentials": b"encrypted-blob",
|
||||
"status": "active"
|
||||
"status": "active",
|
||||
}
|
||||
|
||||
with patch("server.get_site_manager", return_value=mock_site_manager), \
|
||||
patch("core.user_keys.get_user_key_manager", return_value=mock_key_manager), \
|
||||
patch("core.database.get_database", return_value=mock_db):
|
||||
yield {
|
||||
"key_manager": mock_key_manager,
|
||||
"db": mock_db
|
||||
}
|
||||
|
||||
with (
|
||||
patch("server.get_site_manager", return_value=mock_site_manager),
|
||||
patch("core.user_keys.get_user_key_manager", return_value=mock_key_manager),
|
||||
patch("core.database.get_database", return_value=mock_db),
|
||||
):
|
||||
yield {"key_manager": mock_key_manager, "db": mock_db}
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_dynamic_endpoint_unauthorized():
|
||||
"""Test that requests without API key are rejected."""
|
||||
response = client.post(
|
||||
"/u/user-123/myblog/mcp",
|
||||
json={"jsonrpc": "2.0", "id": 1, "method": "initialize"}
|
||||
"/u/user-123/myblog/mcp", json={"jsonrpc": "2.0", "id": 1, "method": "initialize"}
|
||||
)
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_dynamic_endpoint_invalid_method(mock_managers):
|
||||
"""Test that unauthorized methods or valid requests with bad structure return JSON-RPC errors."""
|
||||
response = client.post(
|
||||
"/u/user-123/myblog/mcp",
|
||||
headers={"Authorization": "Bearer mhu_test-key"},
|
||||
json={"jsonrpc": "2.0", "id": 1, "method": "invalid_method"}
|
||||
json={"jsonrpc": "2.0", "id": 1, "method": "invalid_method"},
|
||||
)
|
||||
assert response.status_code == 200 # JSON-RPC errors are 200 OK
|
||||
assert response.status_code == 200 # JSON-RPC errors are 200 OK
|
||||
data = response.json()
|
||||
assert "error" in data
|
||||
assert "not supported" in data["error"]["message"].lower()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_dynamic_endpoint_initialize(mock_managers):
|
||||
"""Test standard MCP initialize on the dynamic endpoint."""
|
||||
@@ -77,15 +80,15 @@ def test_dynamic_endpoint_initialize(mock_managers):
|
||||
"/u/user-123/myblog/mcp",
|
||||
headers={"Authorization": "Bearer mhu_test-key"},
|
||||
json={
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "initialize",
|
||||
"params": {
|
||||
"protocolVersion": "2024-11-05",
|
||||
"capabilities": {},
|
||||
"clientInfo": {"name": "test-client", "version": "1.0.0"}
|
||||
}
|
||||
}
|
||||
"clientInfo": {"name": "test-client", "version": "1.0.0"},
|
||||
},
|
||||
},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
@@ -93,19 +96,20 @@ def test_dynamic_endpoint_initialize(mock_managers):
|
||||
assert "capabilities" in data["result"]
|
||||
assert "serverInfo" in data["result"]
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_dynamic_endpoint_mismatched_user(mock_managers):
|
||||
"""Test that if the key belongs to a different user, it's rejected."""
|
||||
mock_managers["key_manager"].validate_key.return_value = {
|
||||
"key_id": "test-key-123",
|
||||
"user_id": "different-user-456",
|
||||
"scopes": "read write"
|
||||
"scopes": "read write",
|
||||
}
|
||||
|
||||
|
||||
response = client.post(
|
||||
"/u/user-123/myblog/mcp",
|
||||
headers={"Authorization": "Bearer mhu_test-key"},
|
||||
json={"jsonrpc": "2.0", "id": 1, "method": "initialize"}
|
||||
json={"jsonrpc": "2.0", "id": 1, "method": "initialize"},
|
||||
)
|
||||
assert response.status_code == 403
|
||||
data = response.json()
|
||||
|
||||
@@ -4,39 +4,36 @@ import pytest
|
||||
from core.dashboard.routes import get_all_projects
|
||||
from core.site_manager import SiteManager, SiteConfig
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_all_projects_tenant_isolation(monkeypatch):
|
||||
"""Normal user should only see their own sites, ignoring global ones."""
|
||||
|
||||
|
||||
# Mock SiteManager with some global and user sites
|
||||
mgr = SiteManager()
|
||||
|
||||
|
||||
# Global site (no user_id)
|
||||
mgr.register_site(SiteConfig(site_id="global1", plugin_type="wordpress"))
|
||||
|
||||
|
||||
# User 123's site
|
||||
mgr.register_site(SiteConfig(
|
||||
site_id="user1site",
|
||||
plugin_type="wordpress",
|
||||
user_id="user-123"
|
||||
))
|
||||
|
||||
mgr.register_site(SiteConfig(site_id="user1site", plugin_type="wordpress", user_id="user-123"))
|
||||
|
||||
monkeypatch.setattr("core.site_manager.get_site_manager", lambda: mgr)
|
||||
|
||||
|
||||
# Normal user 456 (has no sites)
|
||||
session_456 = {"user_id": "user-456", "type": "user"}
|
||||
res = await get_all_projects(user_session=session_456)
|
||||
assert len(res["projects"]) == 0
|
||||
|
||||
|
||||
# Normal user 123 (has 1 site)
|
||||
session_123 = {"user_id": "user-123", "type": "user"}
|
||||
res = await get_all_projects(user_session=session_123)
|
||||
assert len(res["projects"]) == 1
|
||||
assert res["projects"][0]["site_id"] == "user1site"
|
||||
|
||||
|
||||
# Master user (sees all)
|
||||
class DummyMaster:
|
||||
user_type = "master"
|
||||
|
||||
|
||||
res = await get_all_projects(user_session=DummyMaster())
|
||||
assert len(res["projects"]) == 2
|
||||
|
||||
Reference in New Issue
Block a user