Subagents delivered tests across all uncovered backend modules: Schemas (100%): agent, auth, health, profile, settings, validation Services (98-100%): auth, profile, health, llm, mapping, resource, security, git, superset_lookup, sql_table_extractor, rbac API routes (new): auth, admin, health, environments, plugins, dashboards (helpers, projection, actions, listing), git (config, deps, env, helpers) Clean Release (100%): DTO, facade, policy_engine, stages, repos, preparation, source_isolation, compliance Git services: base, remote_providers Agent module: app, run, middleware, langgraph_setup Core: trace, cleanup, ws_log_handler, timezone, auth (config/oauth/security), matching Reports: normalizer, report_service, type_profiles Notifications: service, providers Also: - .gitignore: add .coverage, *.cover, coverage-* dirs - src/schemas/auth.py: fix AD group DN regex (comma in CN=...) - Remove co-located src/services/__tests__/ (caused pytest module collision)
194 lines
7.7 KiB
Python
194 lines
7.7 KiB
Python
# #region Test.Api.DashboardHelpers [C:3] [TYPE Module] [SEMANTICS test,dashboard,helpers]
|
|
# @BRIEF Unit tests for dashboard helper functions.
|
|
# @RELATION BINDS_TO -> [DashboardHelpers]
|
|
# @TEST_EDGE: empty_ref -> 404
|
|
# @TEST_EDGE: slug_resolution -> int | None
|
|
# @TEST_EDGE: numeric_ref_fallback -> int
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
_src = str(Path(__file__).resolve().parent.parent.parent / "src")
|
|
if _src not in sys.path:
|
|
sys.path.insert(0, _src)
|
|
|
|
|
|
# ── Deprecated sync functions ──
|
|
|
|
class TestDeprecatedSyncFunctions:
|
|
"""_find_dashboard_id_by_slug and _resolve_dashboard_id_from_ref (sync) raise RuntimeError."""
|
|
|
|
def test_find_dashboard_id_by_slug_deprecated(self):
|
|
from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug
|
|
with pytest.raises(RuntimeError, match="deprecated"):
|
|
_find_dashboard_id_by_slug(MagicMock(), "test-slug")
|
|
|
|
def test_resolve_dashboard_id_from_ref_multiple_defs(self):
|
|
"""The module has two defs of _resolve_dashboard_id_from_ref. The second wins.
|
|
It should raise HTTPException(404) for unknown refs, not RuntimeError."""
|
|
from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref
|
|
from fastapi import HTTPException as HE
|
|
|
|
client = MagicMock()
|
|
client.get_dashboards_page.return_value = (0, [])
|
|
|
|
with pytest.raises(HE) as exc:
|
|
_resolve_dashboard_id_from_ref("unknown-ref", client)
|
|
assert exc.value.status_code == 404
|
|
|
|
|
|
# ── _find_dashboard_id_by_slug_async ──
|
|
|
|
class TestFindDashboardIdBySlugAsync:
|
|
"""_find_dashboard_id_by_slug_async"""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_found_by_slug(self):
|
|
client = AsyncMock()
|
|
client.get_dashboards_page.return_value = (1, [{"id": 42}])
|
|
|
|
from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
|
|
result = await _find_dashboard_id_by_slug_async(client, "my-slug")
|
|
assert result == 42
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_not_found(self):
|
|
client = AsyncMock()
|
|
client.get_dashboards_page.return_value = (0, [])
|
|
|
|
from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
|
|
result = await _find_dashboard_id_by_slug_async(client, "ghost-slug")
|
|
assert result is None
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_exception_then_second_query(self):
|
|
client = AsyncMock()
|
|
# First query raises, second succeeds
|
|
client.get_dashboards_page.side_effect = [
|
|
Exception("First query failed"),
|
|
(1, [{"id": 99}]),
|
|
]
|
|
|
|
from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
|
|
result = await _find_dashboard_id_by_slug_async(client, "retry-slug")
|
|
assert result == 99
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_all_queries_fail(self):
|
|
client = AsyncMock()
|
|
client.get_dashboards_page.side_effect = Exception("All failed")
|
|
|
|
from src.api.routes.dashboards._helpers import _find_dashboard_id_by_slug_async
|
|
result = await _find_dashboard_id_by_slug_async(client, "fail-slug")
|
|
assert result is None
|
|
|
|
|
|
# ── _resolve_dashboard_id_from_ref_async ──
|
|
|
|
class TestResolveDashboardIdFromRefAsync:
|
|
"""_resolve_dashboard_id_from_ref_async"""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_numeric_ref(self):
|
|
from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
|
|
result = await _resolve_dashboard_id_from_ref_async("123", AsyncMock())
|
|
assert result == 123
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_slug_ref(self):
|
|
from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
|
|
client = AsyncMock()
|
|
client.get_dashboards_page.return_value = (1, [{"id": 42}])
|
|
result = await _resolve_dashboard_id_from_ref_async("my-dash", client)
|
|
assert result == 42
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_empty_ref(self):
|
|
from fastapi import HTTPException
|
|
from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
|
|
with pytest.raises(HTTPException) as exc:
|
|
await _resolve_dashboard_id_from_ref_async("", AsyncMock())
|
|
assert exc.value.status_code == 404
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_slug_not_found(self):
|
|
from fastapi import HTTPException
|
|
from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
|
|
client = AsyncMock()
|
|
client.get_dashboards_page.return_value = (0, [])
|
|
with pytest.raises(HTTPException) as exc:
|
|
await _resolve_dashboard_id_from_ref_async("ghost", client)
|
|
assert exc.value.status_code == 404
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_none_ref(self):
|
|
from fastapi import HTTPException
|
|
from src.api.routes.dashboards._helpers import _resolve_dashboard_id_from_ref_async
|
|
with pytest.raises(HTTPException) as exc:
|
|
await _resolve_dashboard_id_from_ref_async(None, AsyncMock()) # type: ignore
|
|
assert exc.value.status_code == 404
|
|
|
|
|
|
# ── _normalize_filter_values ──
|
|
|
|
class TestNormalizeFilterValues:
|
|
"""_normalize_filter_values"""
|
|
|
|
def test_normalize_returns_lowercase(self):
|
|
from src.api.routes.dashboards._helpers import _normalize_filter_values
|
|
assert _normalize_filter_values(["HELLO", "World"]) == ["hello", "world"]
|
|
|
|
def test_normalize_empty_list(self):
|
|
from src.api.routes.dashboards._helpers import _normalize_filter_values
|
|
assert _normalize_filter_values([]) == []
|
|
|
|
def test_normalize_none(self):
|
|
from src.api.routes.dashboards._helpers import _normalize_filter_values
|
|
assert _normalize_filter_values(None) == []
|
|
|
|
def test_normalize_removes_empty_strings(self):
|
|
from src.api.routes.dashboards._helpers import _normalize_filter_values
|
|
assert _normalize_filter_values(["a", "", " ", "b"]) == ["a", "b"]
|
|
|
|
def test_normalize_strips_whitespace(self):
|
|
from src.api.routes.dashboards._helpers import _normalize_filter_values
|
|
assert _normalize_filter_values([" Foo Bar "]) == ["foo bar"]
|
|
|
|
|
|
# ── _dashboard_git_filter_value ──
|
|
|
|
class TestDashboardGitFilterValue:
|
|
"""_dashboard_git_filter_value"""
|
|
|
|
def test_no_repo(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({"git_status": {"has_repo": False}}) == "no_repo"
|
|
|
|
def test_no_repo_status(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({"git_status": {"sync_status": "NO_REPO"}}) == "no_repo"
|
|
|
|
def test_diff(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({"git_status": {"sync_status": "DIFF", "has_repo": True}}) == "diff"
|
|
|
|
def test_ok(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({"git_status": {"sync_status": "OK", "has_repo": True}}) == "ok"
|
|
|
|
def test_error(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({"git_status": {"sync_status": "ERROR", "has_repo": True}}) == "error"
|
|
|
|
def test_missing_git_status(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({}) == "pending"
|
|
|
|
def test_none_git_status(self):
|
|
from src.api.routes.dashboards._helpers import _dashboard_git_filter_value
|
|
assert _dashboard_git_filter_value({"git_status": None}) == "pending"
|
|
# #endregion Test.Api.DashboardHelpers
|