mirror of
https://github.com/apache/superset.git
synced 2026-09-03 05:51:53 +00:00
375 lines
12 KiB
Python
375 lines
12 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 dashboard permalink helpers."""
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
from flask import g
|
|
|
|
from superset.commands.dashboard.exceptions import DashboardAccessDeniedError
|
|
from superset.mcp_service.dashboard.permalink import (
|
|
DashboardLookupResult,
|
|
extract_dashboard_permalink_key,
|
|
get_dashboard_permalink,
|
|
get_matching_dashboard_permalink_state,
|
|
lookup_dashboard_reference,
|
|
refresh_request_user_for_permalink_access,
|
|
)
|
|
|
|
FOUND = "dashboard"
|
|
PERMALINK_URL = "https://example.test/dashboard/p/shared-key/"
|
|
|
|
|
|
def _lookup_finding(*found_identifiers: int | str):
|
|
"""Build a ``lookup`` callable that only resolves ``found_identifiers``."""
|
|
known = {str(identifier) for identifier in found_identifiers}
|
|
|
|
def lookup(identifier: int | str) -> str | None:
|
|
return FOUND if str(identifier) in known else None
|
|
|
|
return lookup
|
|
|
|
|
|
def _is_found(result: str | None) -> bool:
|
|
return result is not None
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("value", "expected"),
|
|
[
|
|
("bare-key", "bare-key"),
|
|
("/superset/dashboard/p/shared-key/", "shared-key"),
|
|
(
|
|
"https://example.test/prefix/dashboard/p/shared-key/?foo=bar#tab",
|
|
"shared-key",
|
|
),
|
|
(
|
|
"https://example.test/dashboard/not-p/shared-key/",
|
|
"https://example.test/dashboard/not-p/shared-key/",
|
|
),
|
|
],
|
|
)
|
|
def test_extract_dashboard_permalink_key(value: str, expected: str) -> None:
|
|
assert extract_dashboard_permalink_key(value) == expected
|
|
|
|
|
|
@patch(
|
|
"superset.commands.dashboard.permalink.get.GetDashboardPermalinkCommand.run",
|
|
side_effect=DashboardAccessDeniedError(),
|
|
)
|
|
@patch(
|
|
"superset.mcp_service.dashboard.permalink.refresh_request_user_for_permalink_access"
|
|
)
|
|
def test_get_dashboard_permalink_hides_access_denial(mock_refresh, mock_run) -> None:
|
|
assert get_dashboard_permalink("inaccessible-key") is None
|
|
mock_refresh.assert_called_once_with()
|
|
mock_run.assert_called_once_with()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("username", "email", "expected_kwargs"),
|
|
[
|
|
("admin", None, {"username": "admin"}),
|
|
(None, "admin@example.com", {"email": "admin@example.com"}),
|
|
],
|
|
)
|
|
def test_refresh_request_user_for_permalink_access(
|
|
app, username: str | None, email: str | None, expected_kwargs: dict[str, str]
|
|
) -> None:
|
|
current_user = type(
|
|
"CurrentUser",
|
|
(),
|
|
{"username": username, "email": email, "is_anonymous": False},
|
|
)()
|
|
refreshed_user = object()
|
|
with (
|
|
patch(
|
|
"superset.mcp_service.dashboard.permalink.load_user_with_relationships",
|
|
return_value=refreshed_user,
|
|
) as mock_load,
|
|
app.test_request_context("/mcp"),
|
|
):
|
|
g.user = current_user
|
|
refresh_request_user_for_permalink_access()
|
|
assert g.user is refreshed_user
|
|
|
|
mock_load.assert_called_once_with(**expected_kwargs)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("username", "email", "is_anonymous"),
|
|
[("anonymous", "anonymous@example.com", True), (None, None, False)],
|
|
)
|
|
def test_refresh_request_user_for_permalink_access_skips_unresolvable_user(
|
|
app, username: str | None, email: str | None, is_anonymous: bool
|
|
) -> None:
|
|
current_user = type(
|
|
"CurrentUser",
|
|
(),
|
|
{"username": username, "email": email, "is_anonymous": is_anonymous},
|
|
)()
|
|
with (
|
|
patch(
|
|
"superset.mcp_service.dashboard.permalink.load_user_with_relationships"
|
|
) as mock_load,
|
|
app.test_request_context("/mcp"),
|
|
):
|
|
g.user = current_user
|
|
refresh_request_user_for_permalink_access()
|
|
assert g.user is current_user
|
|
|
|
mock_load.assert_not_called()
|
|
|
|
|
|
def test_refresh_request_user_for_permalink_access_keeps_user_when_reload_fails(
|
|
app,
|
|
) -> None:
|
|
current_user = type(
|
|
"CurrentUser",
|
|
(),
|
|
{"username": "admin", "email": None, "is_anonymous": False},
|
|
)()
|
|
with (
|
|
patch(
|
|
"superset.mcp_service.dashboard.permalink.load_user_with_relationships",
|
|
return_value=None,
|
|
) as mock_load,
|
|
app.test_request_context("/mcp"),
|
|
):
|
|
g.user = current_user
|
|
refresh_request_user_for_permalink_access()
|
|
assert g.user is current_user
|
|
|
|
mock_load.assert_called_once_with(username="admin")
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("reference", "expected_match"),
|
|
[
|
|
# CreateDashboardPermalinkCommand stores str(dashboard.uuid).
|
|
("3f1a2b6c-9d4e-4f80-9c2a-7b1d5e6f8a90", True),
|
|
# Legacy permalinks may hold the numeric id or the slug.
|
|
("42", True),
|
|
("sales-dashboard", True),
|
|
("99", False),
|
|
("00000000-0000-0000-0000-000000000000", False),
|
|
],
|
|
)
|
|
def test_get_matching_dashboard_permalink_state_accepts_every_identifier(
|
|
app, reference: str, expected_match: bool
|
|
) -> None:
|
|
lookup_result = DashboardLookupResult(
|
|
result=object(),
|
|
permalink_key="key-1",
|
|
permalink_value={"dashboardId": reference, "state": {"activeTabs": ["TAB-A"]}},
|
|
)
|
|
with app.test_request_context("/mcp"):
|
|
state = get_matching_dashboard_permalink_state(
|
|
lookup_result,
|
|
42,
|
|
"3f1a2b6c-9d4e-4f80-9c2a-7b1d5e6f8a90",
|
|
"sales-dashboard",
|
|
)
|
|
|
|
assert (state is not None) is expected_match
|
|
|
|
|
|
def test_get_matching_dashboard_permalink_state_skips_check_when_permalink_resolved(
|
|
app,
|
|
) -> None:
|
|
"""The permalink-only path already selected the dashboard from the permalink,
|
|
so its state is never re-verified against the resolved identifiers.
|
|
"""
|
|
lookup_result = DashboardLookupResult(
|
|
result=object(),
|
|
permalink_key="key-1",
|
|
permalink_value={"dashboardId": "whatever", "state": {"activeTabs": ["TAB-A"]}},
|
|
resolved_from_permalink=True,
|
|
)
|
|
with app.test_request_context("/mcp"):
|
|
state = get_matching_dashboard_permalink_state(lookup_result, 42)
|
|
|
|
assert state is not None
|
|
assert state.key == "key-1"
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_identifier_only(mock_get_permalink) -> None:
|
|
result = lookup_dashboard_reference(
|
|
identifier=42,
|
|
permalink_key=None,
|
|
lookup=_lookup_finding(42),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result == FOUND
|
|
assert result.permalink_key is None
|
|
assert result.permalink_value is None
|
|
assert result.resolved_from_permalink is False
|
|
mock_get_permalink.assert_not_called()
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_identifier_wins_but_permalink_adds_state(
|
|
mock_get_permalink,
|
|
) -> None:
|
|
"""An explicit identifier selects the dashboard; the permalink only adds state."""
|
|
value = {"dashboardId": "42", "state": {"activeTabs": ["TAB-A"]}}
|
|
mock_get_permalink.return_value = ("key-1", value)
|
|
|
|
result = lookup_dashboard_reference(
|
|
identifier=42,
|
|
permalink_key="key-1",
|
|
lookup=_lookup_finding(42),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result == FOUND
|
|
assert result.permalink_key == "key-1"
|
|
assert result.permalink_value == value
|
|
assert result.resolved_from_permalink is False
|
|
|
|
|
|
@patch(
|
|
"superset.mcp_service.dashboard.permalink.get_dashboard_permalink",
|
|
return_value=None,
|
|
)
|
|
def test_lookup_dashboard_reference_keeps_dashboard_when_permalink_unresolvable(
|
|
mock_get_permalink,
|
|
) -> None:
|
|
result = lookup_dashboard_reference(
|
|
identifier=42,
|
|
permalink_key="expired-key",
|
|
lookup=_lookup_finding(42),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result == FOUND
|
|
assert result.permalink_key == "expired-key"
|
|
assert result.permalink_value is None
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_numeric_identifier_not_found(
|
|
mock_get_permalink,
|
|
) -> None:
|
|
"""A numeric identifier never falls back to permalink resolution."""
|
|
result = lookup_dashboard_reference(
|
|
identifier=99,
|
|
permalink_key=None,
|
|
lookup=_lookup_finding(),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result is None
|
|
assert result.permalink_key is None
|
|
mock_get_permalink.assert_not_called()
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_identifier_not_found_with_explicit_permalink(
|
|
mock_get_permalink,
|
|
) -> None:
|
|
"""An explicit permalink_key keeps the identifier's own not-found result."""
|
|
result = lookup_dashboard_reference(
|
|
identifier="missing-slug",
|
|
permalink_key="key-1",
|
|
lookup=_lookup_finding(),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result is None
|
|
assert result.permalink_key == "key-1"
|
|
mock_get_permalink.assert_not_called()
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_shared_url_identifier(mock_get_permalink) -> None:
|
|
value = {"dashboardId": "42", "state": {"activeTabs": ["TAB-A"]}}
|
|
mock_get_permalink.return_value = ("shared-key", value)
|
|
|
|
result = lookup_dashboard_reference(
|
|
identifier=PERMALINK_URL,
|
|
permalink_key=None,
|
|
lookup=_lookup_finding(42),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result == FOUND
|
|
assert result.permalink_key == "shared-key"
|
|
assert result.permalink_value == value
|
|
assert result.resolved_from_permalink is True
|
|
mock_get_permalink.assert_called_once_with("shared-key")
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_permalink_only(mock_get_permalink) -> None:
|
|
value = {"dashboardId": "42", "state": {"activeTabs": ["TAB-A"]}}
|
|
mock_get_permalink.return_value = ("key-1", value)
|
|
|
|
result = lookup_dashboard_reference(
|
|
identifier=None,
|
|
permalink_key="key-1",
|
|
lookup=_lookup_finding(42),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result == FOUND
|
|
assert result.permalink_key == "key-1"
|
|
assert result.resolved_from_permalink is True
|
|
|
|
|
|
@patch("superset.mcp_service.dashboard.permalink.get_dashboard_permalink")
|
|
def test_lookup_dashboard_reference_bare_string_falls_back_to_permalink(
|
|
mock_get_permalink,
|
|
) -> None:
|
|
"""An ambiguous bare string tries identifier lookup before permalink lookup."""
|
|
value = {"dashboardId": "42", "state": {"activeTabs": ["TAB-A"]}}
|
|
mock_get_permalink.return_value = ("maybe-key", value)
|
|
|
|
result = lookup_dashboard_reference(
|
|
identifier="maybe-key",
|
|
permalink_key=None,
|
|
lookup=_lookup_finding(42),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result == FOUND
|
|
assert result.permalink_key == "maybe-key"
|
|
assert result.resolved_from_permalink is True
|
|
mock_get_permalink.assert_called_once_with("maybe-key")
|
|
|
|
|
|
@patch(
|
|
"superset.mcp_service.dashboard.permalink.get_dashboard_permalink",
|
|
return_value=None,
|
|
)
|
|
def test_lookup_dashboard_reference_unresolvable_reference(mock_get_permalink) -> None:
|
|
result = lookup_dashboard_reference(
|
|
identifier="nonexistent",
|
|
permalink_key=None,
|
|
lookup=_lookup_finding(),
|
|
is_found=_is_found,
|
|
)
|
|
|
|
assert result.result is None
|
|
assert result.permalink_key == "nonexistent"
|
|
assert result.permalink_value is None
|
|
assert result.resolved_from_permalink is False
|