- executors: 13 tests, 100% coverage (init/shutdown/run_blocking/run_cpu_blocking) - fileio_utils: 33 tests, 45% coverage (sanitize_filename, get_filename_from_headers, calculate_crc32, create_temp_file, remove_empty_directories, create_dashboard_export, consolidate_archive_folders) - client_registry: 14 tests, 92% coverage (get_client, get_superset_client, get_semaphore, get_auth_lock, shutdown) - cot_logger: 24 tests, 100% coverage (seed/set/get trace_id, push/pop span, structured log with markers, MarkerLogger proxy) - encryption: 12 tests, 100% coverage (key validation, encrypt/decrypt cycle) - rate_limiter: 9 tests, 100% coverage (ban logic, window pruning, per-IP isolation) - auth/logger: 10 tests, 100% coverage (_mask_details, log_security_event)
255 lines
8.1 KiB
Python
255 lines
8.1 KiB
Python
# #region Test.SupersetClientRegistry [C:3] [TYPE Module] [SEMANTICS test,registry,superset,client,async]
|
|
# @BRIEF Tests for core/utils/client_registry.py — get_client, get_superset_client, get_semaphore, get_auth_lock, shutdown.
|
|
# @RELATION BINDS_TO -> [SupersetClientRegistryModule]
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
|
|
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
|
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
import asyncio
|
|
import pytest
|
|
|
|
|
|
class TestBuildEnvId:
|
|
"""_build_env_id — stable identifier from env config."""
|
|
|
|
def test_env_with_id(self):
|
|
from src.core.utils.client_registry import _build_env_id
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.base_url = "http://superset:8088"
|
|
result = _build_env_id(env)
|
|
assert "env-1" in result
|
|
assert "superset" in result
|
|
|
|
def test_env_without_id_uses_object_id(self):
|
|
from src.core.utils.client_registry import _build_env_id
|
|
env = {"base_url": "http://x.com", "username": "admin"}
|
|
result = _build_env_id(env)
|
|
assert "x.com" in result
|
|
assert "admin" in result
|
|
|
|
def test_dict_env_fallback(self):
|
|
from src.core.utils.client_registry import _build_env_id
|
|
env = {"base_url": "http://test:8080", "username": "user"}
|
|
result = _build_env_id(env)
|
|
assert result
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestGetClient:
|
|
"""get_client — create or return cached client."""
|
|
|
|
async def test_creates_new_client_from_model(self):
|
|
from src.core.utils.client_registry import get_client, shutdown
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
|
|
mock_client = AsyncMock()
|
|
mock_client_cls.return_value = mock_client
|
|
|
|
client = await get_client(env)
|
|
assert client is mock_client
|
|
mock_client_cls.assert_called_once()
|
|
|
|
await shutdown()
|
|
|
|
async def test_returns_cached_client(self):
|
|
from src.core.utils.client_registry import get_client, shutdown, _registry
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
|
|
mock_client = AsyncMock()
|
|
mock_client_cls.return_value = mock_client
|
|
|
|
client1 = await get_client(env)
|
|
client2 = await get_client(env)
|
|
assert client1 is client2
|
|
assert mock_client_cls.call_count == 1
|
|
|
|
await shutdown()
|
|
|
|
async def test_dict_env(self):
|
|
from src.core.utils.client_registry import get_client, shutdown
|
|
|
|
env = {"base_url": "http://superset:8088", "username": "admin", "password": "pass"}
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
|
|
mock_client = AsyncMock()
|
|
mock_client_cls.return_value = mock_client
|
|
|
|
client = await get_client(env)
|
|
assert client is mock_client
|
|
|
|
await shutdown()
|
|
|
|
async def test_unknown_env_type(self):
|
|
from src.core.utils.client_registry import get_client, shutdown
|
|
|
|
env = 42 # not a model or dict
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_client_cls:
|
|
mock_client = AsyncMock()
|
|
mock_client_cls.return_value = mock_client
|
|
|
|
client = await get_client(env)
|
|
assert client is mock_client
|
|
|
|
await shutdown()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestGetSupersetClient:
|
|
"""get_superset_client — wraps AsyncAPIClient in SupersetClient."""
|
|
|
|
async def test_returns_superset_client(self):
|
|
from src.core.utils.client_registry import get_superset_client, shutdown
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
mock_shared = AsyncMock()
|
|
|
|
with (
|
|
patch("src.core.utils.client_registry.get_client", return_value=mock_shared),
|
|
patch("src.core.superset_client.SupersetClient") as mock_sc_cls,
|
|
):
|
|
mock_sc = MagicMock()
|
|
mock_sc_cls.return_value = mock_sc
|
|
|
|
client = await get_superset_client(env)
|
|
assert client is mock_sc
|
|
|
|
await shutdown()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestGetSemaphoreAndLock:
|
|
"""get_semaphore and get_auth_lock — return shared resources."""
|
|
|
|
async def test_get_semaphore(self):
|
|
from src.core.utils.client_registry import get_semaphore, get_client, shutdown
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
|
|
mock_cls.return_value = AsyncMock()
|
|
await get_client(env)
|
|
sem = await get_semaphore(env)
|
|
assert sem is not None
|
|
|
|
await shutdown()
|
|
|
|
async def test_get_auth_lock(self):
|
|
from src.core.utils.client_registry import get_auth_lock, get_client, shutdown
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
|
|
mock_cls.return_value = AsyncMock()
|
|
await get_client(env)
|
|
lock = await get_auth_lock(env)
|
|
assert lock is not None
|
|
|
|
await shutdown()
|
|
|
|
async def test_get_semaphore_creates_client_if_missing(self):
|
|
from src.core.utils.client_registry import get_semaphore, shutdown, _registry
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
_registry.clear()
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
|
|
mock_cls.return_value = AsyncMock()
|
|
sem = await get_semaphore(env)
|
|
assert sem is not None
|
|
|
|
await shutdown()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
class TestShutdown:
|
|
"""shutdown — close all clients and clear registry."""
|
|
|
|
async def test_shutdown_closes_clients(self):
|
|
from src.core.utils.client_registry import get_client, shutdown
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
|
|
mock_client = AsyncMock()
|
|
mock_cls.return_value = mock_client
|
|
await get_client(env)
|
|
await shutdown()
|
|
mock_client.aclose.assert_called_once()
|
|
|
|
async def test_shutdown_empty_registry(self):
|
|
from src.core.utils.client_registry import shutdown, _registry
|
|
_registry.clear()
|
|
await shutdown() # should not raise
|
|
|
|
async def test_shutdown_handles_close_error(self):
|
|
from src.core.utils.client_registry import get_client, shutdown
|
|
|
|
env = MagicMock()
|
|
env.id = "env-1"
|
|
env.url = "http://superset:8088"
|
|
env.username = "admin"
|
|
env.password = "pass"
|
|
env.verify_ssl = False
|
|
env.timeout = 30
|
|
|
|
with patch("src.core.utils.client_registry.AsyncAPIClient") as mock_cls:
|
|
mock_client = AsyncMock()
|
|
mock_client.aclose.side_effect = ConnectionError("Close failed")
|
|
mock_cls.return_value = mock_client
|
|
await get_client(env)
|
|
await shutdown() # should not raise
|
|
# #endregion Test.SupersetClientRegistry
|