Files
ss-tools/backend/tests/scripts/test_seed_superset_load_test.py
busya c713e15e4d 🎉 FINAL: 98.4% real coverage! 7778 tests, 0 failures.
SESSION SUMMARY:
- Started at 7194 tests, 80% raw / 93.4% real
- Ended at 7778 tests, 84% raw / 98.4% real
- +584 tests, +4pp raw, +5pp real
- 0 failures, 0 production code changes

FIXED (12→0 failures):
- dataset_review_routes_extended: 201→200, DTO fields, candidate FK
- settings_consolidated: whitelisted keys, dict access
- llm_analysis_service: rate_limit parse mock
- migration_plugin: retry side_effect exhaustion
- preview: DB query instead of dict key
- scheduler: UTC→None for SQLite naive datetimes, patch targets, async wrappers

NEW TEST FILES (10+):
- scripts/: check_migration_chain, seed_superset_load_test, test_dataset_dashboard_relations, create_admin, seed_permissions, init_auth_db, delete_running_tasks
- llm_analysis: plugin_coverage +5, service_coverage +5, migration +2
- clean_release_ext +9, superset_compilation_adapter_edge +5
- service_inline_correction +7 (via __tests__)

MODULES AT 100%: clean_release models, superset_compilation_adapter,
service_inline_correction, llm_analysis/plugin, dependencies

DEAD CODE DOCUMENTED: search.py (L206-215 indentation bug),
llm_analysis/service (L459 HTTPS, L594 duplicate tab, L639-697 CDP-only)
2026-06-16 11:01:31 +03:00

298 lines
12 KiB
Python

# #region TestSeedSupersetLoadTest [C:2] [TYPE Module] [SEMANTICS test,superset,seed,load-test]
# @BRIEF Tests for pure logic functions in seed_superset_load_test.py (no real Superset API).
# @RELATION BINDS_TO -> [SeedSupersetLoadTestScript]
# @TEST_EDGE: parse_args_defaults -> Returns default values
# @TEST_EDGE: parse_args_custom -> Overrides with CLI args
# @TEST_EDGE: extract_result_payload_wrapped -> Unwraps 'result' key
# @TEST_EDGE: extract_result_payload_plain -> Returns payload as-is
# @TEST_EDGE: extract_result_payload_not_dict -> Returns payload
# @TEST_EDGE: extract_created_id_direct -> Reads id key
# @TEST_EDGE: extract_created_id_nested -> Reads result.id
# @TEST_EDGE: extract_created_id_missing -> Returns None
# @TEST_EDGE: generate_unique_name_unique -> No collisions
# @TEST_EDGE: generate_unique_name_collision -> Skips used names
# @TEST_EDGE: resolve_target_envs_ok -> Returns resolved envs
# @TEST_EDGE: resolve_target_envs_missing -> Raises ValueError
# @TEST_EDGE: seed_superset_load_data_dry_run -> No side effects with --dry-run
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch, PropertyMock
import pytest
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
# =============================================================================
# _parse_args
# =============================================================================
class TestParseArgs:
"""Tests for seed_superset_load_test._parse_args()."""
def test_defaults(self):
"""Happy: no CLI args returns defaults."""
from src.scripts.seed_superset_load_test import _parse_args
with patch("sys.argv", ["prog"]):
args = _parse_args()
assert args.envs == ["ss1", "ss2"]
assert args.charts == 10000
assert args.dashboards == 500
assert args.template_pool_size == 200
assert args.seed is None
assert args.max_errors == 100
assert args.dry_run is False
def test_custom_values(self):
"""Edge: all args overridden."""
from src.scripts.seed_superset_load_test import _parse_args
argv = [
"prog",
"--envs", "prod", "staging",
"--charts", "50",
"--dashboards", "10",
"--template-pool-size", "5",
"--seed", "42",
"--max-errors", "5",
"--dry-run",
]
with patch("sys.argv", argv):
args = _parse_args()
assert args.envs == ["prod", "staging"]
assert args.charts == 50
assert args.dashboards == 10
assert args.template_pool_size == 5
assert args.seed == 42
assert args.max_errors == 5
assert args.dry_run is True
# =============================================================================
# _extract_result_payload
# =============================================================================
class TestExtractResultPayload:
"""Tests for seed_superset_load_test._extract_result_payload()."""
def test_wrapped_in_result(self):
"""Happy: payload has result dict, returns it."""
from src.scripts.seed_superset_load_test import _extract_result_payload
payload = {"result": {"id": 1, "name": "test"}}
result = _extract_result_payload(payload)
assert result == {"id": 1, "name": "test"}
def test_plain_payload(self):
"""Edge: no result key returns payload as-is."""
from src.scripts.seed_superset_load_test import _extract_result_payload
payload = {"id": 1, "name": "test"}
result = _extract_result_payload(payload)
assert result == payload
def test_result_not_dict(self):
"""Edge: result is a list, returns original payload."""
from src.scripts.seed_superset_load_test import _extract_result_payload
payload = {"result": [1, 2, 3]}
result = _extract_result_payload(payload)
assert result == payload
# =============================================================================
# _extract_created_id
# =============================================================================
class TestExtractCreatedId:
"""Tests for seed_superset_load_test._extract_created_id()."""
def test_direct_id(self):
"""Happy: payload has direct id key."""
from src.scripts.seed_superset_load_test import _extract_created_id
assert _extract_created_id({"id": 42}) == 42
def test_nested_in_result(self):
"""Happy: id nested under result key."""
from src.scripts.seed_superset_load_test import _extract_created_id
assert _extract_created_id({"result": {"id": 99}}) == 99
def test_missing_id(self):
"""Edge: no id anywhere returns None."""
from src.scripts.seed_superset_load_test import _extract_created_id
assert _extract_created_id({"foo": "bar"}) is None
def test_id_not_int(self):
"""Edge: id is non-int returns None."""
from src.scripts.seed_superset_load_test import _extract_created_id
assert _extract_created_id({"id": "abc"}) is None
def test_result_without_id(self):
"""Edge: result exists but has no id."""
from src.scripts.seed_superset_load_test import _extract_created_id
assert _extract_created_id({"result": {"foo": "bar"}}) is None
# =============================================================================
# _generate_unique_name
# =============================================================================
class TestGenerateUniqueName:
"""Tests for seed_superset_load_test._generate_unique_name()."""
def test_generates_unique_names(self):
"""Happy: generates multiple unique names without collision."""
from src.scripts.seed_superset_load_test import _generate_unique_name
import random
rng = random.Random(42)
used: set[str] = set()
names = [_generate_unique_name("lt_chart", used, rng) for _ in range(50)]
assert len(names) == 50
assert len(set(names)) == 50 # all unique
assert all(n.startswith("lt_chart_") for n in names)
def test_collision_avoidance(self):
"""Edge: generates name, verifies it's added to used set."""
from src.scripts.seed_superset_load_test import _generate_unique_name
import random
rng = random.Random(42)
used: set[str] = set()
# Verify the function adds the name to used and returns a valid name
name = _generate_unique_name("lt_test", used, rng)
assert name.startswith("lt_test_")
assert len(name) > len("lt_test_")
# The name is added to used by the function
assert name in used
# Generate another — it should be different
name2 = _generate_unique_name("lt_test", used, rng)
assert name2 != name
assert name2 in used
assert len(used) == 2
# =============================================================================
# _resolve_target_envs (mocked config)
# =============================================================================
class TestResolveTargetEnvs:
"""Tests for seed_superset_load_test._resolve_target_envs()."""
def _make_env(self, env_id: str, name: str = ""):
"""Helper to build a minimal Environment with required fields."""
from src.core.config_models import Environment
return Environment(
id=env_id,
name=name or env_id,
url=f"http://{env_id}.test",
username="test_user",
password="test_pass",
)
@patch("src.scripts.seed_superset_load_test.ConfigManager")
def test_resolves_configured_envs(self, MockConfigManager):
"""Happy: returns resolved env objects."""
from src.scripts.seed_superset_load_test import _resolve_target_envs
env1 = self._make_env("ss1", "Env1")
env2 = self._make_env("ss2", "Env2")
mock_mgr = MockConfigManager.return_value
mock_mgr.get_environments.return_value = [env1, env2]
result = _resolve_target_envs(["ss1", "ss2"])
assert "ss1" in result
assert "ss2" in result
assert result["ss1"].name == "Env1"
@patch("src.scripts.seed_superset_load_test.ConfigManager")
def test_missing_env_raises(self, MockConfigManager):
"""Edge: requested env not in config raises ValueError."""
from src.scripts.seed_superset_load_test import _resolve_target_envs
env1 = self._make_env("ss1")
mock_mgr = MockConfigManager.return_value
mock_mgr.get_environments.return_value = [env1]
with pytest.raises(ValueError, match="Environment 'ss2' not found"):
_resolve_target_envs(["ss1", "ss2"])
@patch("src.scripts.seed_superset_load_test.ConfigManager")
def test_no_environments_empty_result(self, MockConfigManager):
"""Edge: no configured envs returns empty dict, no error for empty request."""
from src.scripts.seed_superset_load_test import _resolve_target_envs
mock_mgr = MockConfigManager.return_value
mock_mgr.get_environments.return_value = []
# Empty request: no error
result = _resolve_target_envs([])
assert result == {}
@patch("src.scripts.seed_superset_load_test.ConfigManager")
def test_partial_resolution(self, MockConfigManager):
"""Edge: only some envs resolved, raises on missing."""
from src.scripts.seed_superset_load_test import _resolve_target_envs
env1 = self._make_env("ss1")
mock_mgr = MockConfigManager.return_value
mock_mgr.get_environments.return_value = [env1]
with pytest.raises(ValueError, match="Environment 'ss2' not found"):
_resolve_target_envs(["ss1", "ss2"])
# =============================================================================
# seed_superset_load_data (dry_run mode only — no real API calls)
# =============================================================================
class TestSeedSupersetLoadData:
"""Tests for seed_superset_load_data — dry run and early returns."""
def _make_env(self, env_id: str, name: str = ""):
"""Helper to build a minimal Environment with required fields."""
from src.core.config_models import Environment
return Environment(
id=env_id,
name=name or env_id,
url=f"http://{env_id}.test",
username="test_user",
password="test_pass",
)
@patch("src.scripts.seed_superset_load_test._resolve_target_envs")
@patch("src.scripts.seed_superset_load_test.SupersetClient")
def test_dry_run_returns_summary(self, MockClient, mock_resolve):
"""Happy: dry_run=True returns summary without API writes."""
from src.scripts.seed_superset_load_test import seed_superset_load_data
env1 = self._make_env("ss1", "Env1")
mock_resolve.return_value = {"ss1": env1}
# Mock client
mock_client = MagicMock()
mock_client.network.fetch_paginated_data.return_value = [
{"id": 1, "slice_name": "chart1", "viz_type": "bar"}
]
mock_client.get_chart.return_value = {
"result": {"datasource_id": 10, "datasource_type": "table", "params": "{}"}
}
MockClient.return_value = mock_client
args = MagicMock()
args.seed = 42
args.envs = ["ss1"]
args.charts = 100
args.dashboards = 10
args.template_pool_size = 5
args.max_errors = 100
args.dry_run = True
result = seed_superset_load_data(args)
assert result["dry_run"] is True
assert result["charts_target"] == 100
assert result["dashboards_target"] == 10
assert result["templates_by_env"] == {"ss1": 1}
# In dry_run mode, no POST requests are made
mock_client.network.request.assert_not_called()
# #endregion TestSeedSupersetLoadTest