Files
superset2/tests/unit_tests/mcp_service/test_mcp_caching.py

144 lines
5.0 KiB
Python

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Tests for MCP response caching middleware."""
from unittest.mock import MagicMock, patch
from superset.mcp_service.caching import _build_caching_settings
def test_build_caching_settings_empty_config():
"""Empty config returns empty settings."""
result = _build_caching_settings({})
assert result == {}
def test_build_caching_settings_list_ttls():
"""List operation TTLs are mapped to settings."""
config = {
"list_tools_ttl": 300,
"list_resources_ttl": 300,
"list_prompts_ttl": 300,
}
result = _build_caching_settings(config)
assert result["list_tools_settings"] == {"ttl": 300}
assert result["list_resources_settings"] == {"ttl": 300}
assert result["list_prompts_settings"] == {"ttl": 300}
def test_build_caching_settings_item_ttls():
"""Individual item TTLs are mapped to settings."""
config = {
"read_resource_ttl": 3600,
"get_prompt_ttl": 3600,
}
result = _build_caching_settings(config)
assert result["read_resource_settings"] == {"ttl": 3600}
assert result["get_prompt_settings"] == {"ttl": 3600}
def test_build_caching_settings_call_tool_with_exclusions():
"""Call tool settings include TTL and exclusions."""
config = {
"call_tool_ttl": 3600,
"excluded_tools": ["execute_sql", "generate_chart"],
}
result = _build_caching_settings(config)
assert result["call_tool_settings"] == {
"ttl": 3600,
"excluded_tools": ["execute_sql", "generate_chart"],
}
def test_create_response_caching_middleware_returns_none_when_disabled():
"""Caching middleware returns None when disabled in config."""
mock_flask_app = MagicMock()
mock_flask_app.config.get.return_value = {"enabled": False}
with patch(
"superset.mcp_service.flask_singleton.get_flask_app",
return_value=mock_flask_app,
):
with patch("flask.has_app_context", return_value=True):
from superset.mcp_service.caching import create_response_caching_middleware
result = create_response_caching_middleware()
assert result is None
def test_create_response_caching_middleware_returns_none_when_no_prefix():
"""Caching middleware returns None when CACHE_KEY_PREFIX is not set."""
mock_flask_app = MagicMock()
mock_flask_app.config.get.return_value = {
"enabled": True,
"CACHE_KEY_PREFIX": None,
}
with patch(
"superset.mcp_service.flask_singleton.get_flask_app",
return_value=mock_flask_app,
):
with patch("flask.has_app_context", return_value=True):
from superset.mcp_service.caching import create_response_caching_middleware
result = create_response_caching_middleware()
assert result is None
def test_create_response_caching_middleware_creates_middleware():
"""Caching middleware is created when properly configured."""
mock_flask_app = MagicMock()
mock_flask_app.config.get.return_value = {
"enabled": True,
"CACHE_KEY_PREFIX": "mcp_cache_v1_",
"list_tools_ttl": 300,
}
mock_store = MagicMock()
mock_middleware = MagicMock()
with patch(
"superset.mcp_service.flask_singleton.get_flask_app",
return_value=mock_flask_app,
):
with patch("flask.has_app_context", return_value=True):
with patch(
"superset.mcp_service.caching.get_mcp_store", return_value=mock_store
):
with patch(
"fastmcp.server.middleware.caching.ResponseCachingMiddleware",
return_value=mock_middleware,
) as mock_middleware_class:
from superset.mcp_service.caching import (
create_response_caching_middleware,
)
result = create_response_caching_middleware()
assert result is mock_middleware
# Verify middleware was created with store and settings
mock_middleware_class.assert_called_once()
call_kwargs = mock_middleware_class.call_args[1]
assert call_kwargs["cache_storage"] is mock_store
assert call_kwargs["list_tools_settings"] == {"ttl": 300}