Skip to content

Commit 25f5928

Browse files
committed
fix: Repair e2e tests broken by multi-tenancy and add pytest-timeout
Fixed issues in e2e tests caused by multi-tenancy (team) implementation: - Added "teams": [] to JWT token payloads in test_admin_apis.py and test_main_apis.py to allow access to public resources and own private resources - Set "visibility": "public" for test servers, prompts, and resources to ensure proper access with public-only tokens - Simplified settings mock in test_admin_apis.py (replaced patch with MagicMock) - Fixed string escaping in resource data test (test_main_apis.py:979) - Cleaned up logging setup in test_admin_apis.py Added missing pytest-timeout>=2.4.0 package to dev dependencies to ensure timeout decorations are properly honored during test execution. Signed-off-by: Jonathan Springer <[email protected]>
1 parent 701aea0 commit 25f5928

File tree

4 files changed

+25
-24
lines changed

4 files changed

+25
-24
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ dev = [
129129
"pytest-httpx>=0.35.0",
130130
"pytest-md-report>=0.7.0",
131131
"pytest-rerunfailures>=16.0.1",
132+
"pytest-timeout>=2.4.0",
132133
"pytest-trio>=0.8.0",
133134
"pytest-xdist>=3.8.0",
134135
"pytype>=2024.10.11",

tests/e2e/test_admin_apis.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
# Standard
3838
import logging
39-
from unittest.mock import patch
39+
from unittest.mock import MagicMock
4040
from urllib.parse import quote
4141
import uuid
4242

@@ -45,16 +45,8 @@
4545
import pytest
4646
import pytest_asyncio
4747

48-
# from mcpgateway.db import Base
49-
# from mcpgateway.main import app, get_db
5048

51-
52-
# Configure logging for debugging
53-
def setup_logging():
54-
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
55-
56-
57-
setup_logging()
49+
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
5850

5951

6052
# pytest.skip("Temporarily disabling this suite", allow_module_level=True)
@@ -79,6 +71,7 @@ def create_test_jwt_token():
7971
"exp": int(expire.timestamp()),
8072
"iss": "mcpgateway",
8173
"aud": "mcpgateway-api",
74+
"teams": [], # Empty teams list allows access to public resources and own private resources
8275
}
8376

8477
# Use the test JWT secret key
@@ -161,19 +154,15 @@ async def mock_settings():
161154
# First-Party
162155
from mcpgateway.config import settings as real_settings
163156

164-
with patch("mcpgateway.config.settings") as mock_settings:
165-
# Copy all existing settings
166-
for attr in dir(real_settings):
167-
if not attr.startswith("_"):
168-
setattr(mock_settings, attr, getattr(real_settings, attr))
157+
MockSettings = MagicMock(wrap=real_settings)
169158

170-
# Override specific settings for testing
171-
mock_settings.cache_type = "database"
172-
mock_settings.mcpgateway_admin_api_enabled = True
173-
mock_settings.mcpgateway_ui_enabled = False
174-
mock_settings.auth_required = False
159+
# Override specific settings for testing
160+
mock_settings.cache_type = "database"
161+
mock_settings.mcpgateway_admin_api_enabled = True
162+
mock_settings.mcpgateway_ui_enabled = False
163+
mock_settings.auth_required = False
175164

176-
yield mock_settings
165+
yield mock_settings
177166

178167

179168
# -------------------------
@@ -227,6 +216,7 @@ async def test_admin_server_lifecycle(self, client: AsyncClient, mock_settings):
227216
"associatedTools": "", # Empty initially
228217
"associatedResources": "",
229218
"associatedPrompts": "",
219+
"visibility": "public", # Make public to allow access with public-only token
230220
}
231221

232222
# POST to /admin/servers should redirect
@@ -254,6 +244,7 @@ async def test_admin_server_lifecycle(self, client: AsyncClient, mock_settings):
254244
"associatedTools": "",
255245
"associatedResources": "",
256246
"associatedPrompts": "",
247+
"visibility": "public", # Keep public visibility
257248
}
258249
response = await client.post(f"/admin/servers/{server_id}/edit", data=edit_data, headers=TEST_AUTH_HEADER, follow_redirects=False)
259250
assert response.status_code == 200
@@ -497,6 +488,7 @@ async def test_admin_prompt_lifecycle(self, client: AsyncClient, mock_settings):
497488
"description": "Test prompt via admin",
498489
"template": "Hello {{name}}, this is a test prompt",
499490
"arguments": '[{"name": "name", "description": "User name", "required": true}]',
491+
"visibility": "public", # Make public to allow access with public-only token
500492
}
501493

502494
# POST to /admin/prompts should redirect
@@ -522,6 +514,7 @@ async def test_admin_prompt_lifecycle(self, client: AsyncClient, mock_settings):
522514
"description": "Updated description",
523515
"template": "Updated {{greeting}}",
524516
"arguments": '[{"name": "greeting", "description": "Greeting", "required": false}]',
517+
"visibility": "public", # Keep public visibility
525518
}
526519
response = await client.post(f"/admin/prompts/{prompt_id}/edit", data=edit_data, headers=TEST_AUTH_HEADER, follow_redirects=False)
527520
assert response.status_code == 200

tests/e2e/test_main_apis.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# -*- coding: utf-8 -*-
12
"""Location: ./tests/e2e/test_main_apis.py
23
Copyright 2025
34
SPDX-License-Identifier: Apache-2.0
@@ -107,7 +108,11 @@ def decorator(func):
107108

108109

109110
def generate_test_jwt():
110-
payload = {"sub": "test_user", "exp": int(time.time()) + 3600}
111+
payload = {
112+
"sub": "test_user",
113+
"exp": int(time.time()) + 3600,
114+
"teams": [], # Empty teams list allows access to public resources and own private resources
115+
}
111116
secret = settings.jwt_secret_key.get_secret_value()
112117
algorithm = settings.jwt_algorithm
113118
return jwt.encode(payload, secret, algorithm=algorithm)
@@ -972,7 +977,7 @@ async def test_create_resource_form_urlencoded(self, client: AsyncClient, mock_a
972977
import urllib.parse
973978

974979
resource_data = {
975-
"resource": urllib.parse.quote_plus('{"uri":"config/formtest","name":"form_test","description":"Form resource","mimeType":"application/json","content":"{"key":"value"}"}'),
980+
"resource": urllib.parse.quote_plus(r'{"uri":"config/formtest","name":"form_test","description":"Form resource","mimeType":"application/json","content":"{\"key\":\"value\"}"}'),
976981
"team_id": "",
977982
"visibility": "private",
978983
}
@@ -1068,7 +1073,7 @@ async def test_delete_resource(self, client: AsyncClient, mock_auth):
10681073
# API should probably return 409 instead of 400 for non-existent resource
10691074
async def test_resource_uri_conflict(self, client: AsyncClient, mock_auth):
10701075
"""Test creating resource with duplicate URI."""
1071-
resource_data = {"resource": {"uri": "duplicate/resource", "name": "duplicate", "content": "test", "team_id": None, "visibility": "private"}}
1076+
resource_data = {"resource": {"uri": "duplicate/resource", "name": "duplicate", "content": "test", "team_id": None, "visibility": "public"}}
10721077

10731078
# Create first resource
10741079
response = await client.post("/resources", json=resource_data, headers=TEST_AUTH_HEADER)

uv.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)