Skip to content

Commit fa7d046

Browse files
author
Suresh Kumar Moharajan
committed
move inline imports to top-level
Signed-off-by: Suresh Kumar Moharajan <suresh.kumar.m@ibm.com>
1 parent 551709e commit fa7d046

4 files changed

Lines changed: 12 additions & 49 deletions

File tree

tests/e2e/test_admin_apis.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
import pytest # noqa: E402
4747
import pytest_asyncio # noqa: E402
4848

49+
# First-Party
50+
from mcpgateway.config import settings # noqa: E402
51+
4952
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s")
5053

5154

@@ -673,9 +676,6 @@ async def test_admin_add_resource_accepts_parameterized_mime_types(self, client:
673676

674677
async def test_admin_add_resource_rejects_disallowed_mime_type(self, client: AsyncClient, mock_settings, monkeypatch):
675678
"""Test that resources with disallowed MIME types are rejected with 415 status."""
676-
# First-Party
677-
from mcpgateway.config import settings
678-
679679
# Configure a very restrictive MIME type list that excludes application/evil
680680
# We need to set both validation lists to ensure the error reaches ContentSecurityService
681681
allowed_types = [

tests/unit/mcpgateway/services/test_content_security.py

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
# -*- coding: utf-8 -*-
22
"""Unit tests for content security service."""
33

4+
import sys
5+
import threading
46
import pytest
57
import mcpgateway.services.content_security as cs_mod
68
from unittest.mock import patch, MagicMock
79

10+
from mcpgateway import config
811
from mcpgateway.services.content_security import (
912
ContentSecurityService,
1013
ContentSizeError,
@@ -213,8 +216,6 @@ def test_get_service_returns_singleton(self):
213216

214217
def test_get_service_thread_safe(self):
215218
"""Test that singleton is thread-safe."""
216-
import threading
217-
218219
results = []
219220

220221
def get_service():
@@ -327,7 +328,6 @@ def test_validate_empty_mime_type(self):
327328

328329
def test_validate_allowed_mime_type(self, monkeypatch):
329330
"""Test validation passes for allowed MIME types."""
330-
from mcpgateway import config
331331
# Ensure strict mode is off so this test is independent of .env settings
332332
monkeypatch.setattr(config.settings, "content_strict_mime_validation", False)
333333
service = ContentSecurityService()
@@ -339,7 +339,6 @@ def test_validate_allowed_mime_type(self, monkeypatch):
339339

340340
def test_validate_vendor_mime_type_log_only_mode(self, monkeypatch):
341341
"""Test that vendor types (x- prefix) are allowed in log-only mode."""
342-
from mcpgateway import config
343342
monkeypatch.setattr(config.settings, "content_strict_mime_validation", False)
344343

345344
service = ContentSecurityService()
@@ -349,7 +348,6 @@ def test_validate_vendor_mime_type_log_only_mode(self, monkeypatch):
349348

350349
def test_validate_vendor_mime_type_strict_mode(self, monkeypatch):
351350
"""Test that vendor types (x- prefix) are rejected in strict mode unless in allowlist."""
352-
from mcpgateway import config
353351
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
354352
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["text/plain"])
355353

@@ -365,7 +363,6 @@ def test_validate_vendor_mime_type_strict_mode(self, monkeypatch):
365363

366364
def test_validate_suffix_mime_type_log_only_mode(self, monkeypatch):
367365
"""Test that suffix types (with +) are allowed in log-only mode."""
368-
from mcpgateway import config
369366
monkeypatch.setattr(config.settings, "content_strict_mime_validation", False)
370367

371368
service = ContentSecurityService()
@@ -375,7 +372,6 @@ def test_validate_suffix_mime_type_log_only_mode(self, monkeypatch):
375372

376373
def test_validate_suffix_mime_type_strict_mode(self, monkeypatch):
377374
"""Test that suffix types (with +) are rejected in strict mode unless in allowlist."""
378-
from mcpgateway import config
379375
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
380376
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["text/plain"])
381377

@@ -392,7 +388,6 @@ def test_validate_suffix_mime_type_strict_mode(self, monkeypatch):
392388
def test_validate_disallowed_mime_type_strict_mode(self, monkeypatch):
393389
"""Test validation fails for disallowed MIME types in strict mode."""
394390
# Enable strict validation
395-
from mcpgateway import config
396391
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
397392

398393
service = ContentSecurityService()
@@ -406,7 +401,6 @@ def test_validate_disallowed_mime_type_strict_mode(self, monkeypatch):
406401
def test_validate_disallowed_mime_type_log_only_mode(self, monkeypatch):
407402
"""Test validation logs but doesn't raise in log-only mode."""
408403
# Disable strict validation (log-only mode)
409-
from mcpgateway import config
410404
monkeypatch.setattr(config.settings, "content_strict_mime_validation", False)
411405

412406
service = ContentSecurityService()
@@ -415,7 +409,6 @@ def test_validate_disallowed_mime_type_log_only_mode(self, monkeypatch):
415409

416410
def test_validate_with_logging_context(self, monkeypatch):
417411
"""Test validation with full logging context."""
418-
from mcpgateway import config
419412
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
420413

421414
service = ContentSecurityService()
@@ -429,7 +422,6 @@ def test_validate_with_logging_context(self, monkeypatch):
429422

430423
def test_validate_case_sensitive(self, monkeypatch):
431424
"""Test that MIME type validation is case-sensitive."""
432-
from mcpgateway import config
433425
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
434426

435427
service = ContentSecurityService()
@@ -447,7 +439,6 @@ class TestMimeTypeIntegration:
447439

448440
def test_size_and_mime_validation_order(self, monkeypatch):
449441
"""Test that size validation happens before MIME validation."""
450-
from mcpgateway import config
451442
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
452443

453444
service = ContentSecurityService()
@@ -473,7 +464,6 @@ class TestVendorSuffixMimeTypeInStrictMode:
473464

474465
def test_vendor_type_rejected_in_strict_mode_without_allowlist(self, monkeypatch):
475466
"""Test that application/x- vendor types are rejected in strict mode if not in allowlist."""
476-
from mcpgateway import config
477467
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
478468
# Use a custom allowlist that does NOT include application/x-custom
479469
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["text/plain"])
@@ -486,7 +476,6 @@ def test_vendor_type_rejected_in_strict_mode_without_allowlist(self, monkeypatch
486476

487477
def test_vendor_type_allowed_when_in_allowlist(self, monkeypatch):
488478
"""Test that vendor types pass when explicitly added to allowlist."""
489-
from mcpgateway import config
490479
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
491480
# Add vendor type to allowlist
492481
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["text/plain", "application/x-custom"])
@@ -497,7 +486,6 @@ def test_vendor_type_allowed_when_in_allowlist(self, monkeypatch):
497486

498487
def test_text_vendor_type_rejected_in_strict_mode_without_allowlist(self, monkeypatch):
499488
"""Test that text/x- vendor types are rejected in strict mode if not in allowlist."""
500-
from mcpgateway import config
501489
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
502490
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["application/json"])
503491

@@ -509,7 +497,6 @@ def test_text_vendor_type_rejected_in_strict_mode_without_allowlist(self, monkey
509497

510498
def test_suffix_type_rejected_in_strict_mode_without_allowlist(self, monkeypatch):
511499
"""Test that suffix types (+json, +xml) are rejected in strict mode if not in allowlist."""
512-
from mcpgateway import config
513500
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
514501
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["text/plain"])
515502

@@ -521,7 +508,6 @@ def test_suffix_type_rejected_in_strict_mode_without_allowlist(self, monkeypatch
521508

522509
def test_suffix_type_allowed_when_in_allowlist(self, monkeypatch):
523510
"""Test that suffix types pass when explicitly added to allowlist."""
524-
from mcpgateway import config
525511
monkeypatch.setattr(config.settings, "content_strict_mime_validation", True)
526512
# Add suffix type to allowlist
527513
monkeypatch.setattr(config.settings, "content_allowed_resource_mimetypes", ["text/plain", "application/vnd.api+json"])
@@ -554,8 +540,6 @@ def inc(self, amount=1):
554540

555541
def test_noop_counter_import_fallback(self):
556542
"""Test that content_security module handles missing metrics gracefully (line 26)."""
557-
import sys
558-
559543
# Temporarily hide the metrics module to trigger the ImportError fallback
560544
original_metrics = sys.modules.get("mcpgateway.services.metrics")
561545
original_cs = sys.modules.get("mcpgateway.services.content_security")

tests/unit/mcpgateway/test_main.py

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
ToolMetrics,
4646
ToolRead,
4747
)
48+
from mcpgateway.services.content_security import ContentSizeError, ContentTypeError
4849

4950
# --------------------------------------------------------------------------- #
5051
# Constants #
@@ -1143,9 +1144,6 @@ def test_create_resource_endpoint(self, mock_create, test_client, auth_headers):
11431144
@patch("mcpgateway.main.resource_service.register_resource")
11441145
def test_create_resource_content_size_error(self, mock_create, test_client, auth_headers):
11451146
"""Test create_resource returns 413 for content size limit exceeded."""
1146-
# First-Party
1147-
from mcpgateway.services.content_security import ContentSizeError
1148-
11491147
mock_create.side_effect = ContentSizeError("Resource", 150000, 102400)
11501148
req = {"resource": {"uri": "test/resource", "name": "Test Resource", "content": "x" * 150000}, "team_id": None, "visibility": "private"}
11511149
response = test_client.post("/resources/", json=req, headers=auth_headers)
@@ -1158,8 +1156,6 @@ def test_create_resource_content_size_error(self, mock_create, test_client, auth
11581156
@patch("mcpgateway.main.resource_service.register_resource")
11591157
def test_create_resource_content_type_error(self, mock_create, test_client, auth_headers):
11601158
"""Test create_resource returns 415 for unsupported MIME type."""
1161-
from mcpgateway.services.content_security import ContentTypeError
1162-
11631159
mock_create.side_effect = ContentTypeError("application/x-malicious", ["text/plain", "application/json"])
11641160
req = {"resource": {"uri": "test/resource", "name": "Test Resource", "mime_type": "text/plain", "content": "hello"}, "team_id": None, "visibility": "private"}
11651161
response = test_client.post("/resources/", json=req, headers=auth_headers)
@@ -1174,9 +1170,6 @@ def test_create_resource_content_type_error(self, mock_create, test_client, auth
11741170
@patch("mcpgateway.main.resource_service.register_resource")
11751171
def test_create_resource_content_size_error(self, mock_create, test_client, auth_headers):
11761172
"""Test create_resource returns 413 for content size limit exceeded."""
1177-
# First-Party
1178-
from mcpgateway.services.content_security import ContentSizeError
1179-
11801173
mock_create.side_effect = ContentSizeError("Resource", 150000, 102400)
11811174
req = {"resource": {"uri": "test/resource", "name": "Test Resource", "content": "x" * 150000}, "team_id": None, "visibility": "private"}
11821175
response = test_client.post("/resources/", json=req, headers=auth_headers)
@@ -1368,9 +1361,6 @@ def test_update_prompt_validation_and_integrity_errors(self, mock_update, exc, s
13681361
@patch("mcpgateway.main.prompt_service.register_prompt")
13691362
def test_create_prompt_content_size_error(self, mock_create, test_client, auth_headers):
13701363
"""Test create_prompt returns 413 for content size limit exceeded."""
1371-
# First-Party
1372-
from mcpgateway.services.content_security import ContentSizeError
1373-
13741364
mock_create.side_effect = ContentSizeError("Prompt", 15000, 10240)
13751365
req = {"prompt": {"name": "test_prompt", "template": "x" * 15000}, "team_id": None, "visibility": "private"}
13761366
response = test_client.post("/prompts/", json=req, headers=auth_headers)
@@ -1383,9 +1373,6 @@ def test_create_prompt_content_size_error(self, mock_create, test_client, auth_h
13831373
@patch("mcpgateway.main.prompt_service.update_prompt")
13841374
def test_update_prompt_content_size_error(self, mock_update, test_client, auth_headers):
13851375
"""Test update_prompt returns 413 for content size limit exceeded."""
1386-
# First-Party
1387-
from mcpgateway.services.content_security import ContentSizeError
1388-
13891376
mock_update.side_effect = ContentSizeError("Prompt", 15000, 10240)
13901377
req = {"template": "x" * 15000}
13911378
response = test_client.put("/prompts/test_prompt", json=req, headers=auth_headers)
@@ -1398,9 +1385,6 @@ def test_update_prompt_content_size_error(self, mock_update, test_client, auth_h
13981385
@patch("mcpgateway.main.prompt_service.register_prompt")
13991386
def test_create_prompt_content_size_error(self, mock_create, test_client, auth_headers):
14001387
"""Test create_prompt returns 413 for content size limit exceeded."""
1401-
# First-Party
1402-
from mcpgateway.services.content_security import ContentSizeError
1403-
14041388
mock_create.side_effect = ContentSizeError("Prompt", 15000, 10240)
14051389
req = {"prompt": {"name": "test_prompt", "template": "x" * 15000}, "team_id": None, "visibility": "private"}
14061390
response = test_client.post("/prompts/", json=req, headers=auth_headers)
@@ -1413,9 +1397,6 @@ def test_create_prompt_content_size_error(self, mock_create, test_client, auth_h
14131397
@patch("mcpgateway.main.prompt_service.update_prompt")
14141398
def test_update_prompt_content_size_error(self, mock_update, test_client, auth_headers):
14151399
"""Test update_prompt returns 413 for content size limit exceeded."""
1416-
# First-Party
1417-
from mcpgateway.services.content_security import ContentSizeError
1418-
14191400
mock_update.side_effect = ContentSizeError("Prompt", 15000, 10240)
14201401
req = {"template": "x" * 15000}
14211402
response = test_client.put("/prompts/test_prompt", json=req, headers=auth_headers)

tests/unit/mcpgateway/test_main_error_handlers.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,22 @@
99
"""
1010

1111
# Standard
12+
import asyncio
13+
import json
1214
from unittest.mock import AsyncMock, MagicMock, patch
1315

1416
# Third-Party
1517
from fastapi.testclient import TestClient
1618
from pydantic import SecretStr, ValidationError
1719
from sqlalchemy.exc import IntegrityError
20+
from starlette.requests import Request
1821
import jwt
1922
import pytest
2023

2124
# First-Party
2225
from mcpgateway.config import settings
26+
from mcpgateway.main import content_type_exception_handler
27+
from mcpgateway.services.content_security import ContentTypeError
2328
from mcpgateway.services.gateway_service import (
2429
GatewayConnectionError,
2530
GatewayDuplicateConflictError,
@@ -590,11 +595,6 @@ def test_update_server_permission_error(self, test_client, auth_headers):
590595

591596
def test_content_type_exception_handler():
592597
"""Test ContentTypeError exception handler returns 415 with proper format."""
593-
# First-Party
594-
from mcpgateway.main import content_type_exception_handler
595-
from mcpgateway.services.content_security import ContentTypeError
596-
from starlette.requests import Request
597-
598598
# Create a mock request
599599
mock_request = MagicMock(spec=Request)
600600

@@ -605,13 +605,11 @@ def test_content_type_exception_handler():
605605
)
606606

607607
# Call the exception handler
608-
import asyncio
609608
response = asyncio.run(content_type_exception_handler(mock_request, exc))
610609

611610
# Verify response
612611
assert response.status_code == 415
613612
content = response.body.decode()
614-
import json
615613
result = json.loads(content)
616614
assert "detail" in result
617615
assert result["detail"]["error"] == "Unsupported MIME type"

0 commit comments

Comments
 (0)