feat: harden migration flows and integration coverage

This commit is contained in:
2026-07-21 18:59:26 +03:00
parent d1d2a0f92e
commit 456d531a13
77 changed files with 7257 additions and 1481 deletions

View File

@@ -850,4 +850,71 @@ class TestMigrationPluginExecute:
call_args = mock_sync.sync_environment.call_args
assert call_args[1]["incremental"] is True
# ── Isolation: one import failure does not abort batch ──
@pytest.mark.asyncio
async def test_execute_import_failure_continues_batch(self):
"""One dashboard import raises SupersetAPIError; second still migrates."""
from src.core.utils.network import SupersetAPIError
plugin = MigrationPlugin()
src_env = _make_env("env-1", "Source")
tgt_env = _make_env("env-2", "Target")
dashboards = [
_make_dashboard(1, "Working"),
_make_dashboard(2, "Broken Import"),
]
mock_cm = MagicMock()
mock_cm.get_environments.return_value = [src_env, tgt_env]
mock_src_client = _make_mock_superset_client()
mock_src_client.get_dashboards = AsyncMock(return_value=(True, dashboards))
mock_src_client.export_dashboard = AsyncMock(return_value=(b"zip", "meta"))
mock_tgt_client = _make_mock_superset_client()
mock_tgt_client.import_dashboard = AsyncMock(
side_effect=[
None,
SupersetAPIError(
"API error during upload: Dataset boom",
status_code=422,
response_body='{"errors":[{"message":"Dataset boom","error_type":"GENERIC_COMMAND_ERROR","extra":{"issue_codes":[{"code":1010}]}}]}',
errors=[{
"message": "Dataset boom",
"error_type": "GENERIC_COMMAND_ERROR",
"extra": {"issue_codes": [{"code": 1010}]},
}],
),
]
)
mock_engine = MagicMock()
mock_engine.transform_zip.return_value = True
with patch('src.plugins.migration.get_config_manager', return_value=mock_cm), \
patch('src.plugins.migration.SupersetClient') as MockSC, \
patch('src.plugins.migration.MigrationEngine', return_value=mock_engine), \
patch('src.plugins.migration.create_temp_file') as mock_ctf, \
patch('src.plugins.migration.IdMappingService', return_value=_make_mock_mapping_service()), \
patch('src.plugins.migration.SessionLocal'):
MockSC.side_effect = [mock_src_client, mock_tgt_client]
mock_ctf.return_value.__enter__ = MagicMock(return_value="/tmp/test.zip")
result = await plugin.execute({
"source_env_id": "env-1",
"target_env_id": "env-2",
"selected_ids": [1, 2],
})
assert result["status"] == "PARTIAL_SUCCESS"
assert len(result["migrated_dashboards"]) == 1
assert result["migrated_dashboards"][0]["title"] == "Working"
assert len(result["failed_dashboards"]) == 1
failed = result["failed_dashboards"][0]
assert failed["title"] == "Broken Import"
assert "Dataset boom" in failed["error"]
assert failed.get("error_type") == "GENERIC_COMMAND_ERROR"
assert 1010 in (failed.get("issue_codes") or [])
# #endregion Test.MigrationPlugin