feat: harden migration flows and integration coverage
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user