Files
ss-tools/backend/tests/scripts/test_check_migration_chain.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

227 lines
9.0 KiB
Python

# #region TestCheckMigrationChain [C:2] [TYPE Module] [SEMANTICS test,alembic,migration,chain]
# @BRIEF Tests for migration chain validation logic (mock alembic, no real DB).
# @RELATION BINDS_TO -> [check_migration_chain]
# @TEST_EDGE: check_migration_heads_single -> OK prints single head
# @TEST_EDGE: check_migration_heads_multiple -> Exits 1 with error
# @TEST_EDGE: check_migration_heads_empty -> Exits 1 with error
# @TEST_EDGE: chain_integrity_ok -> Traverses clean chain
# @TEST_EDGE: chain_integrity_broken_link -> Exits 1
# @TEST_EDGE: chain_integrity_merge_migration -> Handles tuple down_revision
# @TEST_EDGE: chain_integrity_broken_merge -> Exits 1
# @TEST_EDGE: main_runs_all_checks -> Calls both functions
# @TEST_EDGE: main_with_multiple_heads -> Exits early
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch, call
import pytest
# Ensure src is importable
sys.path.insert(0, str(Path(__file__).parent.parent.parent / "src"))
# =============================================================================
# check_migration_heads
# =============================================================================
class TestCheckMigrationHeads:
"""Tests for check_migration_chain.check_migration_heads()."""
# Alembic imports are inside function body, so patch the real full paths
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_single_head(self, MockAlembicConfig, MockScriptDir, capsys):
"""Happy: single head prints OK."""
mock_script = MagicMock()
mock_script.get_heads.return_value = ["abc123"]
rev = MagicMock()
rev.doc = "Initial migration"
mock_script.get_revision.return_value = rev
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_heads
check_migration_heads()
captured = capsys.readouterr()
assert "OK: Single migration head" in captured.out
assert "abc123" in captured.out
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_multiple_heads_exits(self, MockAlembicConfig, MockScriptDir):
"""Edge: multiple heads prints error and exits 1."""
mock_script = MagicMock()
mock_script.get_heads.return_value = ["head1", "head2"]
rev1 = MagicMock()
rev1.doc = "Migration A"
rev2 = MagicMock()
rev2.doc = "Migration B"
def get_revision_side_effect(rev_id):
return {"head1": rev1, "head2": rev2}[rev_id]
mock_script.get_revision.side_effect = get_revision_side_effect
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_heads
with pytest.raises(SystemExit) as exc:
check_migration_heads()
assert exc.value.code == 1
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_no_heads_exits(self, MockAlembicConfig, MockScriptDir):
"""Edge: no heads prints error and exits 1."""
mock_script = MagicMock()
mock_script.get_heads.return_value = []
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_heads
with pytest.raises(SystemExit) as exc:
check_migration_heads()
assert exc.value.code == 1
# =============================================================================
# check_migration_chain_integrity
# =============================================================================
class TestCheckMigrationChainIntegrity:
"""Tests for check_migration_chain.check_migration_chain_integrity()."""
def _make_revision(self, rev_id, down_revision, doc=""):
rev = MagicMock()
rev.revision = rev_id
rev.down_revision = down_revision
rev.doc = doc
return rev
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_chain_ok(self, MockAlembicConfig, MockScriptDir, capsys):
"""Happy: linear chain traversed without error."""
head = self._make_revision("c", "b", "Third")
middle = self._make_revision("b", "a", "Second")
root = self._make_revision("a", None, "First")
mock_script = MagicMock()
mock_script.get_heads.return_value = ["c"]
def get_revision(rev_id):
mapping = {"c": head, "b": middle, "a": root}
return mapping[rev_id]
mock_script.get_revision.side_effect = get_revision
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_chain_integrity
check_migration_chain_integrity()
captured = capsys.readouterr()
assert "Chain integrity verified" in captured.out
assert "3 migrations traversed" in captured.out
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_broken_link_exits(self, MockAlembicConfig, MockScriptDir):
"""Edge: missing down_revision exits 1."""
head = self._make_revision("c", "bogus", "Broken")
mock_script = MagicMock()
mock_script.get_heads.return_value = ["c"]
def get_revision(rev_id):
if rev_id == "c":
return head
raise Exception(f"Revision {rev_id} not found")
mock_script.get_revision.side_effect = get_revision
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_chain_integrity
with pytest.raises(SystemExit) as exc:
check_migration_chain_integrity()
assert exc.value.code == 1
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_merge_migration_handled(self, MockAlembicConfig, MockScriptDir, capsys):
"""Edge: merge migration (tuple down_revision) traversed correctly."""
merge = self._make_revision("merge", ("a", "b"), "Merge")
parent_a = self._make_revision("a", None, "Root A")
parent_b = self._make_revision("b", None, "Root B")
mock_script = MagicMock()
mock_script.get_heads.return_value = ["merge"]
def get_revision(rev_id):
mapping = {"merge": merge, "a": parent_a, "b": parent_b}
return mapping[rev_id]
mock_script.get_revision.side_effect = get_revision
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_chain_integrity
check_migration_chain_integrity()
captured = capsys.readouterr()
assert "Chain integrity verified" in captured.out
assert "3 migrations traversed" in captured.out
@patch("alembic.script.ScriptDirectory")
@patch("alembic.config.Config")
def test_broken_merge_link_exits(self, MockAlembicConfig, MockScriptDir):
"""Edge: merge migration with missing parent exits 1."""
merge = self._make_revision("merge", ("a", "ghost"), "Merge")
mock_script = MagicMock()
mock_script.get_heads.return_value = ["merge"]
def get_revision(rev_id):
if rev_id == "merge":
return merge
if rev_id == "a":
return self._make_revision("a", None, "Root A")
raise Exception(f"Revision {rev_id} not found")
mock_script.get_revision.side_effect = get_revision
MockScriptDir.from_config.return_value = mock_script
from src.scripts.check_migration_chain import check_migration_chain_integrity
with pytest.raises(SystemExit) as exc:
check_migration_chain_integrity()
assert exc.value.code == 1
# =============================================================================
# main
# =============================================================================
class TestMain:
"""Tests for check_migration_chain.main()."""
@patch("src.scripts.check_migration_chain.check_migration_chain_integrity")
@patch("src.scripts.check_migration_chain.check_migration_heads")
def test_main_calls_both(self, mock_heads, mock_integrity, capsys):
"""Happy: main() calls both checks and prints pass."""
from src.scripts.check_migration_chain import main
main()
mock_heads.assert_called_once()
mock_integrity.assert_called_once()
captured = capsys.readouterr()
assert "All checks passed" in captured.out
@patch("src.scripts.check_migration_chain.check_migration_chain_integrity")
@patch("src.scripts.check_migration_chain.check_migration_heads")
def test_main_stops_on_heads_failure(self, mock_heads, mock_integrity):
"""Edge: main exits early if check_migration_heads fails."""
mock_heads.side_effect = SystemExit(1)
from src.scripts.check_migration_chain import main
with pytest.raises(SystemExit) as exc:
main()
assert exc.value.code == 1
mock_integrity.assert_not_called()
# #endregion TestCheckMigrationChain