Files
ss-tools/backend/src/api/routes/__tests__/test_dashboards.py

878 lines
34 KiB
Python

# [DEF:backend.src.api.routes.__tests__.test_dashboards:Module]
# @TIER: STANDARD
# @PURPOSE: Unit tests for Dashboards API endpoints
# @LAYER: API
# @RELATION: TESTS -> backend.src.api.routes.dashboards
import pytest
from unittest.mock import MagicMock, patch, AsyncMock
from datetime import datetime, timezone
from fastapi.testclient import TestClient
from src.app import app
from src.api.routes.dashboards import DashboardsResponse
from src.dependencies import get_current_user, has_permission, get_config_manager, get_task_manager, get_resource_service, get_mapping_service
from src.core.database import get_db
from src.services.profile_service import ProfileService as DomainProfileService
# Global mock user for get_current_user dependency overrides
mock_user = MagicMock()
mock_user.id = "u-1"
mock_user.username = "testuser"
mock_user.roles = []
admin_role = MagicMock()
admin_role.name = "Admin"
mock_user.roles.append(admin_role)
@pytest.fixture(autouse=True)
def mock_deps():
config_manager = MagicMock()
task_manager = MagicMock()
resource_service = MagicMock()
mapping_service = MagicMock()
db = MagicMock()
app.dependency_overrides[get_config_manager] = lambda: config_manager
app.dependency_overrides[get_task_manager] = lambda: task_manager
app.dependency_overrides[get_resource_service] = lambda: resource_service
app.dependency_overrides[get_mapping_service] = lambda: mapping_service
app.dependency_overrides[get_current_user] = lambda: mock_user
app.dependency_overrides[get_db] = lambda: db
app.dependency_overrides[has_permission("plugin:migration", "READ")] = lambda: mock_user
app.dependency_overrides[has_permission("plugin:migration", "EXECUTE")] = lambda: mock_user
app.dependency_overrides[has_permission("plugin:backup", "EXECUTE")] = lambda: mock_user
app.dependency_overrides[has_permission("tasks", "READ")] = lambda: mock_user
yield {
"config": config_manager,
"task": task_manager,
"resource": resource_service,
"mapping": mapping_service,
"db": db,
}
app.dependency_overrides.clear()
client = TestClient(app)
# [DEF:test_get_dashboards_success:Function]
# @TEST: GET /api/dashboards returns 200 and valid schema
# @PRE: env_id exists
# @POST: Response matches DashboardsResponse schema
def test_get_dashboards_success(mock_deps):
"""Uses @TEST_FIXTURE: dashboard_list_happy data."""
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
# @TEST_FIXTURE: dashboard_list_happy -> {"id": 1, "title": "Main Revenue"}
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{
"id": 1,
"title": "Main Revenue",
"slug": "main-revenue",
"git_status": {"branch": "main", "sync_status": "OK"},
"last_task": {"task_id": "task-1", "status": "SUCCESS"}
}
])
response = client.get("/api/dashboards?env_id=prod")
assert response.status_code == 200
data = response.json()
# exhaustive @POST assertions
assert "dashboards" in data
assert len(data["dashboards"]) == 1
assert data["dashboards"][0]["title"] == "Main Revenue"
assert data["total"] == 1
assert "page" in data
DashboardsResponse(**data)
# [/DEF:test_get_dashboards_success:Function]
# [DEF:test_get_dashboards_with_search:Function]
# @TEST: GET /api/dashboards filters by search term
# @PRE: search parameter provided
# @POST: Only matching dashboards returned
def test_get_dashboards_with_search(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
async def mock_get_dashboards(env, tasks, include_git_status=False):
return [
{"id": 1, "title": "Sales Report", "slug": "sales", "git_status": {"branch": "main", "sync_status": "OK"}, "last_task": None},
{"id": 2, "title": "Marketing Dashboard", "slug": "marketing", "git_status": {"branch": "main", "sync_status": "OK"}, "last_task": None}
]
mock_deps["resource"].get_dashboards_with_status = AsyncMock(
side_effect=mock_get_dashboards
)
response = client.get("/api/dashboards?env_id=prod&search=sales")
assert response.status_code == 200
data = response.json()
# @POST: Filtered result count must match search
assert len(data["dashboards"]) == 1
assert data["dashboards"][0]["title"] == "Sales Report"
# [/DEF:test_get_dashboards_with_search:Function]
# [DEF:test_get_dashboards_empty:Function]
# @TEST_EDGE: empty_dashboards -> {env_id: 'empty_env', expected_total: 0}
def test_get_dashboards_empty(mock_deps):
"""@TEST_EDGE: empty_dashboards -> {env_id: 'empty_env', expected_total: 0}"""
mock_env = MagicMock()
mock_env.id = "empty_env"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[])
response = client.get("/api/dashboards?env_id=empty_env")
assert response.status_code == 200
data = response.json()
assert data["total"] == 0
assert len(data["dashboards"]) == 0
assert data["total_pages"] == 1
DashboardsResponse(**data)
# [/DEF:test_get_dashboards_empty:Function]
# [DEF:test_get_dashboards_superset_failure:Function]
# @TEST_EDGE: external_superset_failure -> {env_id: 'bad_conn', status: 503}
def test_get_dashboards_superset_failure(mock_deps):
"""@TEST_EDGE: external_superset_failure -> {env_id: 'bad_conn', status: 503}"""
mock_env = MagicMock()
mock_env.id = "bad_conn"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(
side_effect=Exception("Connection refused")
)
response = client.get("/api/dashboards?env_id=bad_conn")
assert response.status_code == 503
assert "Failed to fetch dashboards" in response.json()["detail"]
# [/DEF:test_get_dashboards_superset_failure:Function]
# [DEF:test_get_dashboards_env_not_found:Function]
# @TEST: GET /api/dashboards returns 404 if env_id missing
# @PRE: env_id does not exist
# @POST: Returns 404 error
def test_get_dashboards_env_not_found(mock_deps):
mock_deps["config"].get_environments.return_value = []
response = client.get("/api/dashboards?env_id=nonexistent")
assert response.status_code == 404
assert "Environment not found" in response.json()["detail"]
# [/DEF:test_get_dashboards_env_not_found:Function]
# [DEF:test_get_dashboards_invalid_pagination:Function]
# @TEST: GET /api/dashboards returns 400 for invalid page/page_size
# @PRE: page < 1 or page_size > 100
# @POST: Returns 400 error
def test_get_dashboards_invalid_pagination(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
# Invalid page
response = client.get("/api/dashboards?env_id=prod&page=0")
assert response.status_code == 400
assert "Page must be >= 1" in response.json()["detail"]
# Invalid page_size
response = client.get("/api/dashboards?env_id=prod&page_size=101")
assert response.status_code == 400
assert "Page size must be between 1 and 100" in response.json()["detail"]
# [/DEF:test_get_dashboards_invalid_pagination:Function]
# [DEF:test_get_dashboard_detail_success:Function]
# @TEST: GET /api/dashboards/{id} returns dashboard detail with charts and datasets
def test_get_dashboard_detail_success(mock_deps):
with patch("src.api.routes.dashboards.SupersetClient") as mock_client_cls:
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_client = MagicMock()
mock_client.get_dashboard_detail.return_value = {
"id": 42,
"title": "Revenue Dashboard",
"slug": "revenue-dashboard",
"url": "/superset/dashboard/42/",
"description": "Overview",
"last_modified": "2026-02-20T10:00:00+00:00",
"published": True,
"charts": [
{
"id": 100,
"title": "Revenue by Month",
"viz_type": "line",
"dataset_id": 7,
"last_modified": "2026-02-19T10:00:00+00:00",
"overview": "line"
}
],
"datasets": [
{
"id": 7,
"table_name": "fact_revenue",
"schema": "mart",
"database": "Analytics",
"last_modified": "2026-02-18T10:00:00+00:00",
"overview": "mart.fact_revenue"
}
],
"chart_count": 1,
"dataset_count": 1
}
mock_client_cls.return_value = mock_client
response = client.get("/api/dashboards/42?env_id=prod")
assert response.status_code == 200
payload = response.json()
assert payload["id"] == 42
assert payload["chart_count"] == 1
assert payload["dataset_count"] == 1
# [/DEF:test_get_dashboard_detail_success:Function]
# [DEF:test_get_dashboard_detail_env_not_found:Function]
# @TEST: GET /api/dashboards/{id} returns 404 for missing environment
def test_get_dashboard_detail_env_not_found(mock_deps):
mock_deps["config"].get_environments.return_value = []
response = client.get("/api/dashboards/42?env_id=missing")
assert response.status_code == 404
assert "Environment not found" in response.json()["detail"]
# [/DEF:test_get_dashboard_detail_env_not_found:Function]
# [DEF:test_migrate_dashboards_success:Function]
# @TEST: POST /api/dashboards/migrate creates migration task
# @PRE: Valid source_env_id, target_env_id, dashboard_ids
# @POST: Returns task_id and create_task was called
def test_migrate_dashboards_success(mock_deps):
mock_source = MagicMock()
mock_source.id = "source"
mock_target = MagicMock()
mock_target.id = "target"
mock_deps["config"].get_environments.return_value = [mock_source, mock_target]
mock_task = MagicMock()
mock_task.id = "task-migrate-123"
mock_deps["task"].create_task = AsyncMock(return_value=mock_task)
response = client.post(
"/api/dashboards/migrate",
json={
"source_env_id": "source",
"target_env_id": "target",
"dashboard_ids": [1, 2, 3],
"db_mappings": {"old_db": "new_db"}
}
)
assert response.status_code == 200
data = response.json()
assert "task_id" in data
# @POST/@SIDE_EFFECT: create_task was called
mock_deps["task"].create_task.assert_called_once()
# [/DEF:test_migrate_dashboards_success:Function]
# [DEF:test_migrate_dashboards_no_ids:Function]
# @TEST: POST /api/dashboards/migrate returns 400 for empty dashboard_ids
# @PRE: dashboard_ids is empty
# @POST: Returns 400 error
def test_migrate_dashboards_no_ids(mock_deps):
response = client.post(
"/api/dashboards/migrate",
json={
"source_env_id": "source",
"target_env_id": "target",
"dashboard_ids": []
}
)
assert response.status_code == 400
assert "At least one dashboard ID must be provided" in response.json()["detail"]
# [/DEF:test_migrate_dashboards_no_ids:Function]
# [DEF:test_migrate_dashboards_env_not_found:Function]
# @PRE: source_env_id and target_env_id are valid environment IDs
def test_migrate_dashboards_env_not_found(mock_deps):
"""@PRE: source_env_id and target_env_id are valid environment IDs."""
mock_deps["config"].get_environments.return_value = []
response = client.post(
"/api/dashboards/migrate",
json={
"source_env_id": "ghost",
"target_env_id": "t",
"dashboard_ids": [1]
}
)
assert response.status_code == 404
assert "Source environment not found" in response.json()["detail"]
# [/DEF:test_migrate_dashboards_env_not_found:Function]
# [DEF:test_backup_dashboards_success:Function]
# @TEST: POST /api/dashboards/backup creates backup task
# @PRE: Valid env_id, dashboard_ids
# @POST: Returns task_id and create_task was called
def test_backup_dashboards_success(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_task = MagicMock()
mock_task.id = "task-backup-456"
mock_deps["task"].create_task = AsyncMock(return_value=mock_task)
response = client.post(
"/api/dashboards/backup",
json={
"env_id": "prod",
"dashboard_ids": [1, 2, 3],
"schedule": "0 0 * * *"
}
)
assert response.status_code == 200
data = response.json()
assert "task_id" in data
# @POST/@SIDE_EFFECT: create_task was called
mock_deps["task"].create_task.assert_called_once()
# [/DEF:test_backup_dashboards_success:Function]
# [DEF:test_backup_dashboards_env_not_found:Function]
# @PRE: env_id is a valid environment ID
def test_backup_dashboards_env_not_found(mock_deps):
"""@PRE: env_id is a valid environment ID."""
mock_deps["config"].get_environments.return_value = []
response = client.post(
"/api/dashboards/backup",
json={
"env_id": "ghost",
"dashboard_ids": [1]
}
)
assert response.status_code == 404
assert "Environment not found" in response.json()["detail"]
# [/DEF:test_backup_dashboards_env_not_found:Function]
# [DEF:test_get_database_mappings_success:Function]
# @TEST: GET /api/dashboards/db-mappings returns mapping suggestions
# @PRE: Valid source_env_id, target_env_id
# @POST: Returns list of database mappings
def test_get_database_mappings_success(mock_deps):
mock_source = MagicMock()
mock_source.id = "prod"
mock_target = MagicMock()
mock_target.id = "staging"
mock_deps["config"].get_environments.return_value = [mock_source, mock_target]
mock_deps["mapping"].get_suggestions = AsyncMock(return_value=[
{
"source_db": "old_sales",
"target_db": "new_sales",
"source_db_uuid": "uuid-1",
"target_db_uuid": "uuid-2",
"confidence": 0.95
}
])
response = client.get("/api/dashboards/db-mappings?source_env_id=prod&target_env_id=staging")
assert response.status_code == 200
data = response.json()
assert "mappings" in data
assert len(data["mappings"]) == 1
assert data["mappings"][0]["confidence"] == 0.95
# [/DEF:test_get_database_mappings_success:Function]
# [DEF:test_get_database_mappings_env_not_found:Function]
# @PRE: source_env_id and target_env_id are valid environment IDs
def test_get_database_mappings_env_not_found(mock_deps):
"""@PRE: source_env_id must be a valid environment."""
mock_deps["config"].get_environments.return_value = []
response = client.get("/api/dashboards/db-mappings?source_env_id=ghost&target_env_id=t")
assert response.status_code == 404
# [/DEF:test_get_database_mappings_env_not_found:Function]
# [DEF:test_get_dashboard_tasks_history_filters_success:Function]
# @TEST: GET /api/dashboards/{id}/tasks returns backup and llm tasks for dashboard
def test_get_dashboard_tasks_history_filters_success(mock_deps):
now = datetime.now(timezone.utc)
llm_task = MagicMock()
llm_task.id = "task-llm-1"
llm_task.plugin_id = "llm_dashboard_validation"
llm_task.status = "SUCCESS"
llm_task.started_at = now
llm_task.finished_at = now
llm_task.params = {"dashboard_id": "42", "environment_id": "prod"}
llm_task.result = {"summary": "LLM validation complete"}
backup_task = MagicMock()
backup_task.id = "task-backup-1"
backup_task.plugin_id = "superset-backup"
backup_task.status = "RUNNING"
backup_task.started_at = now
backup_task.finished_at = None
backup_task.params = {"env": "prod", "dashboards": [42]}
backup_task.result = {}
other_task = MagicMock()
other_task.id = "task-other"
other_task.plugin_id = "superset-backup"
other_task.status = "SUCCESS"
other_task.started_at = now
other_task.finished_at = now
other_task.params = {"env": "prod", "dashboards": [777]}
other_task.result = {}
mock_deps["task"].get_all_tasks.return_value = [other_task, llm_task, backup_task]
response = client.get("/api/dashboards/42/tasks?env_id=prod&limit=10")
assert response.status_code == 200
data = response.json()
assert data["dashboard_id"] == 42
assert len(data["items"]) == 2
assert {item["plugin_id"] for item in data["items"]} == {"llm_dashboard_validation", "superset-backup"}
# [/DEF:test_get_dashboard_tasks_history_filters_success:Function]
# [DEF:test_get_dashboard_thumbnail_success:Function]
# @TEST: GET /api/dashboards/{id}/thumbnail proxies image bytes from Superset
def test_get_dashboard_thumbnail_success(mock_deps):
with patch("src.api.routes.dashboards.SupersetClient") as mock_client_cls:
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.content = b"fake-image-bytes"
mock_response.headers = {"Content-Type": "image/png"}
def _network_request(method, endpoint, **kwargs):
if method == "POST":
return {"image_url": "/api/v1/dashboard/42/screenshot/abc123/"}
return mock_response
mock_client.network.request.side_effect = _network_request
mock_client_cls.return_value = mock_client
response = client.get("/api/dashboards/42/thumbnail?env_id=prod")
assert response.status_code == 200
assert response.content == b"fake-image-bytes"
assert response.headers["content-type"].startswith("image/png")
# [/DEF:test_get_dashboard_thumbnail_success:Function]
# [DEF:_build_profile_preference_stub:Function]
# @PURPOSE: Creates profile preference payload stub for dashboards filter contract tests.
# @PRE: username can be empty; enabled indicates profile-default toggle state.
# @POST: Returns object compatible with ProfileService.get_my_preference contract.
def _build_profile_preference_stub(username: str, enabled: bool):
preference = MagicMock()
preference.superset_username = username
preference.superset_username_normalized = str(username or "").strip().lower() or None
preference.show_only_my_dashboards = bool(enabled)
payload = MagicMock()
payload.preference = preference
return payload
# [/DEF:_build_profile_preference_stub:Function]
# [DEF:_matches_actor_case_insensitive:Function]
# @PURPOSE: Applies trim + case-insensitive owners OR modified_by matching used by route contract tests.
# @PRE: owners can be None or list-like values.
# @POST: Returns True when bound username matches any owner or modified_by.
def _matches_actor_case_insensitive(bound_username, owners, modified_by):
normalized_bound = str(bound_username or "").strip().lower()
if not normalized_bound:
return False
owner_tokens = []
for owner in owners or []:
token = str(owner or "").strip().lower()
if token:
owner_tokens.append(token)
modified_token = str(modified_by or "").strip().lower()
return normalized_bound in owner_tokens or bool(modified_token and modified_token == normalized_bound)
# [/DEF:_matches_actor_case_insensitive:Function]
# [DEF:test_get_dashboards_profile_filter_contract_owners_or_modified_by:Function]
# @TEST: GET /api/dashboards applies profile-default filter with owners OR modified_by trim+case-insensitive semantics.
# @PRE: Current user has enabled profile-default preference and bound username.
# @POST: Response includes only matching dashboards and effective_profile_filter metadata.
def test_get_dashboards_profile_filter_contract_owners_or_modified_by(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{
"id": 1,
"title": "Owner Match",
"slug": "owner-match",
"owners": [" John_Doe "],
"modified_by": "someone_else",
},
{
"id": 2,
"title": "Modifier Match",
"slug": "modifier-match",
"owners": ["analytics-team"],
"modified_by": " JOHN_DOE ",
},
{
"id": 3,
"title": "No Match",
"slug": "no-match",
"owners": ["another-user"],
"modified_by": "nobody",
},
])
with patch("src.api.routes.dashboards.ProfileService") as profile_service_cls:
profile_service = MagicMock()
profile_service.get_my_preference.return_value = _build_profile_preference_stub(
username=" JOHN_DOE ",
enabled=True,
)
profile_service.matches_dashboard_actor.side_effect = _matches_actor_case_insensitive
profile_service_cls.return_value = profile_service
response = client.get(
"/api/dashboards?env_id=prod&page_context=dashboards_main&apply_profile_default=true"
)
assert response.status_code == 200
payload = response.json()
assert payload["total"] == 2
assert {item["id"] for item in payload["dashboards"]} == {1, 2}
assert payload["effective_profile_filter"]["applied"] is True
assert payload["effective_profile_filter"]["source_page"] == "dashboards_main"
assert payload["effective_profile_filter"]["override_show_all"] is False
assert payload["effective_profile_filter"]["username"] == "john_doe"
assert payload["effective_profile_filter"]["match_logic"] == "owners_or_modified_by"
# [/DEF:test_get_dashboards_profile_filter_contract_owners_or_modified_by:Function]
# [DEF:test_get_dashboards_override_show_all_contract:Function]
# @TEST: GET /api/dashboards honors override_show_all and disables profile-default filter for current page.
# @PRE: Profile-default preference exists but override_show_all=true query is provided.
# @POST: Response remains unfiltered and effective_profile_filter.applied is false.
def test_get_dashboards_override_show_all_contract(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{"id": 1, "title": "Dash A", "slug": "dash-a", "owners": ["john_doe"], "modified_by": "john_doe"},
{"id": 2, "title": "Dash B", "slug": "dash-b", "owners": ["other"], "modified_by": "other"},
])
with patch("src.api.routes.dashboards.ProfileService") as profile_service_cls:
profile_service = MagicMock()
profile_service.get_my_preference.return_value = _build_profile_preference_stub(
username="john_doe",
enabled=True,
)
profile_service.matches_dashboard_actor.side_effect = _matches_actor_case_insensitive
profile_service_cls.return_value = profile_service
response = client.get(
"/api/dashboards?env_id=prod&page_context=dashboards_main&apply_profile_default=true&override_show_all=true"
)
assert response.status_code == 200
payload = response.json()
assert payload["total"] == 2
assert {item["id"] for item in payload["dashboards"]} == {1, 2}
assert payload["effective_profile_filter"]["applied"] is False
assert payload["effective_profile_filter"]["source_page"] == "dashboards_main"
assert payload["effective_profile_filter"]["override_show_all"] is True
assert payload["effective_profile_filter"]["username"] is None
assert payload["effective_profile_filter"]["match_logic"] is None
profile_service.matches_dashboard_actor.assert_not_called()
# [/DEF:test_get_dashboards_override_show_all_contract:Function]
# [DEF:test_get_dashboards_profile_filter_no_match_results_contract:Function]
# @TEST: GET /api/dashboards returns empty result set when profile-default filter is active and no dashboard actors match.
# @PRE: Profile-default preference is enabled with bound username and all dashboards are non-matching.
# @POST: Response total is 0 with deterministic pagination and active effective_profile_filter metadata.
def test_get_dashboards_profile_filter_no_match_results_contract(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{
"id": 101,
"title": "Team Dashboard",
"slug": "team-dashboard",
"owners": ["analytics-team"],
"modified_by": "someone_else",
},
{
"id": 102,
"title": "Ops Dashboard",
"slug": "ops-dashboard",
"owners": ["ops-user"],
"modified_by": "ops-user",
},
])
with patch("src.api.routes.dashboards.ProfileService") as profile_service_cls:
profile_service = MagicMock()
profile_service.get_my_preference.return_value = _build_profile_preference_stub(
username="john_doe",
enabled=True,
)
profile_service.matches_dashboard_actor.side_effect = _matches_actor_case_insensitive
profile_service_cls.return_value = profile_service
response = client.get(
"/api/dashboards?env_id=prod&page_context=dashboards_main&apply_profile_default=true"
)
assert response.status_code == 200
payload = response.json()
assert payload["total"] == 0
assert payload["dashboards"] == []
assert payload["page"] == 1
assert payload["page_size"] == 10
assert payload["total_pages"] == 1
assert payload["effective_profile_filter"]["applied"] is True
assert payload["effective_profile_filter"]["source_page"] == "dashboards_main"
assert payload["effective_profile_filter"]["override_show_all"] is False
assert payload["effective_profile_filter"]["username"] == "john_doe"
assert payload["effective_profile_filter"]["match_logic"] == "owners_or_modified_by"
# [/DEF:test_get_dashboards_profile_filter_no_match_results_contract:Function]
# [DEF:test_get_dashboards_page_context_other_disables_profile_default:Function]
# @TEST: GET /api/dashboards does not auto-apply profile-default filter outside dashboards_main page context.
# @PRE: Profile-default preference exists but page_context=other query is provided.
# @POST: Response remains unfiltered and metadata reflects source_page=other.
def test_get_dashboards_page_context_other_disables_profile_default(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{"id": 1, "title": "Dash A", "slug": "dash-a", "owners": ["john_doe"], "modified_by": "john_doe"},
{"id": 2, "title": "Dash B", "slug": "dash-b", "owners": ["other"], "modified_by": "other"},
])
with patch("src.api.routes.dashboards.ProfileService") as profile_service_cls:
profile_service = MagicMock()
profile_service.get_my_preference.return_value = _build_profile_preference_stub(
username="john_doe",
enabled=True,
)
profile_service.matches_dashboard_actor.side_effect = _matches_actor_case_insensitive
profile_service_cls.return_value = profile_service
response = client.get(
"/api/dashboards?env_id=prod&page_context=other&apply_profile_default=true"
)
assert response.status_code == 200
payload = response.json()
assert payload["total"] == 2
assert {item["id"] for item in payload["dashboards"]} == {1, 2}
assert payload["effective_profile_filter"]["applied"] is False
assert payload["effective_profile_filter"]["source_page"] == "other"
assert payload["effective_profile_filter"]["override_show_all"] is False
assert payload["effective_profile_filter"]["username"] is None
assert payload["effective_profile_filter"]["match_logic"] is None
profile_service.matches_dashboard_actor.assert_not_called()
# [/DEF:test_get_dashboards_page_context_other_disables_profile_default:Function]
# [DEF:test_get_dashboards_profile_filter_matches_display_alias_without_detail_fanout:Function]
# @TEST: GET /api/dashboards resolves Superset display-name alias once and filters without per-dashboard detail calls.
# @PRE: Profile-default filter is active, bound username is `admin`, dashboard actors contain display labels.
# @POST: Route matches by alias (`Superset Admin`) and does not call `SupersetClient.get_dashboard` in list filter path.
def test_get_dashboards_profile_filter_matches_display_alias_without_detail_fanout(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{
"id": 5,
"title": "Alias Match",
"slug": "alias-match",
"owners": [],
"created_by": None,
"modified_by": "Superset Admin",
},
{
"id": 6,
"title": "Alias No Match",
"slug": "alias-no-match",
"owners": [],
"created_by": None,
"modified_by": "Other User",
},
])
with patch("src.api.routes.dashboards.ProfileService") as profile_service_cls, patch(
"src.api.routes.dashboards.SupersetClient"
) as superset_client_cls, patch(
"src.api.routes.dashboards.SupersetAccountLookupAdapter"
) as lookup_adapter_cls:
profile_service = MagicMock()
profile_service.get_my_preference.return_value = _build_profile_preference_stub(
username="admin",
enabled=True,
)
profile_service.matches_dashboard_actor.side_effect = _matches_actor_case_insensitive
profile_service_cls.return_value = profile_service
superset_client = MagicMock()
superset_client_cls.return_value = superset_client
lookup_adapter = MagicMock()
lookup_adapter.get_users_page.return_value = {
"items": [
{
"environment_id": "prod",
"username": "admin",
"display_name": "Superset Admin",
"email": "admin@example.com",
"is_active": True,
}
],
"total": 1,
}
lookup_adapter_cls.return_value = lookup_adapter
response = client.get(
"/api/dashboards?env_id=prod&page_context=dashboards_main&apply_profile_default=true"
)
assert response.status_code == 200
payload = response.json()
assert payload["total"] == 1
assert {item["id"] for item in payload["dashboards"]} == {5}
assert payload["effective_profile_filter"]["applied"] is True
lookup_adapter.get_users_page.assert_called_once()
superset_client.get_dashboard.assert_not_called()
# [/DEF:test_get_dashboards_profile_filter_matches_display_alias_without_detail_fanout:Function]
# [DEF:test_get_dashboards_profile_filter_matches_owner_object_payload_contract:Function]
# @TEST: GET /api/dashboards profile-default filter matches Superset owner object payloads.
# @PRE: Profile-default preference is enabled and owners list contains dict payloads.
# @POST: Response keeps dashboards where owner object resolves to bound username alias.
def test_get_dashboards_profile_filter_matches_owner_object_payload_contract(mock_deps):
mock_env = MagicMock()
mock_env.id = "prod"
mock_deps["config"].get_environments.return_value = [mock_env]
mock_deps["task"].get_all_tasks.return_value = []
mock_deps["resource"].get_dashboards_with_status = AsyncMock(return_value=[
{
"id": 701,
"title": "Featured Charts",
"slug": "featured-charts",
"owners": [
{
"id": 11,
"first_name": "user",
"last_name": "1",
"username": None,
"email": "user_1@example.local",
}
],
"modified_by": "another_user",
},
{
"id": 702,
"title": "Other Dashboard",
"slug": "other-dashboard",
"owners": [
{
"id": 12,
"first_name": "other",
"last_name": "user",
"username": None,
"email": "other@example.local",
}
],
"modified_by": "other_user",
},
])
with patch("src.api.routes.dashboards.ProfileService") as profile_service_cls, patch(
"src.api.routes.dashboards._resolve_profile_actor_aliases",
return_value=["user_1"],
):
profile_service = DomainProfileService(db=MagicMock(), config_manager=MagicMock())
profile_service.get_my_preference = MagicMock(
return_value=_build_profile_preference_stub(
username="user_1",
enabled=True,
)
)
profile_service_cls.return_value = profile_service
response = client.get(
"/api/dashboards?env_id=prod&page_context=dashboards_main&apply_profile_default=true"
)
assert response.status_code == 200
payload = response.json()
assert payload["total"] == 1
assert {item["id"] for item in payload["dashboards"]} == {701}
assert payload["dashboards"][0]["title"] == "Featured Charts"
# [/DEF:test_get_dashboards_profile_filter_matches_owner_object_payload_contract:Function]
# [/DEF:backend.src.api.routes.__tests__.test_dashboards:Module]