sync worked

This commit is contained in:
2026-02-25 15:20:26 +03:00
parent 2a5b225800
commit 590ba49ddb
6 changed files with 319 additions and 61 deletions

View File

@@ -37,8 +37,11 @@ def db_session():
def _make_config_manager(cron="0 2 * * *"):
"""Creates a mock config manager with settable config."""
config = {"migration_sync_cron": cron}
"""Creates a mock config manager with a realistic AppConfig-like object."""
settings = MagicMock()
settings.migration_sync_cron = cron
config = MagicMock()
config.settings = settings
cm = MagicMock()
cm.get_config.return_value = config
cm.save_config = MagicMock()
@@ -63,11 +66,11 @@ async def test_get_migration_settings_returns_default_cron():
@pytest.mark.asyncio
async def test_get_migration_settings_returns_fallback_when_no_cron():
"""When migration_sync_cron is not in config, should return default '0 2 * * *'."""
"""When migration_sync_cron uses the default, should return '0 2 * * *'."""
from src.api.routes.migration import get_migration_settings
cm = MagicMock()
cm.get_config.return_value = {} # No cron key
# Use the default cron value (simulating a fresh config)
cm = _make_config_manager()
result = await get_migration_settings(config_manager=cm, _=None)
@@ -161,4 +164,123 @@ async def test_get_resource_mappings_respects_pagination(db_session):
assert len(result) == 2
# --- trigger_sync_now tests ---
@pytest.fixture
def _mock_env():
"""Creates a mock config environment object."""
env = MagicMock()
env.id = "test-env-1"
env.name = "Test Env"
env.url = "http://superset.test"
env.username = "admin"
env.password = "admin"
env.verify_ssl = False
env.timeout = 30
return env
def _make_sync_config_manager(environments):
"""Creates a mock config manager with environments list."""
settings = MagicMock()
settings.migration_sync_cron = "0 2 * * *"
config = MagicMock()
config.settings = settings
config.environments = environments
cm = MagicMock()
cm.get_config.return_value = config
return cm
@pytest.mark.asyncio
async def test_trigger_sync_now_creates_env_row_and_syncs(db_session, _mock_env):
"""Verify that trigger_sync_now creates an Environment row in DB before syncing,
preventing FK constraint violations on resource_mappings inserts."""
from src.api.routes.migration import trigger_sync_now
from src.models.mapping import Environment as EnvironmentModel
cm = _make_sync_config_manager([_mock_env])
with patch("src.api.routes.migration.SupersetClient") as MockClient, \
patch("src.api.routes.migration.IdMappingService") as MockService:
mock_client_instance = MagicMock()
MockClient.return_value = mock_client_instance
mock_service_instance = MagicMock()
MockService.return_value = mock_service_instance
result = await trigger_sync_now(config_manager=cm, db=db_session, _=None)
# Environment row must exist in DB
env_row = db_session.query(EnvironmentModel).filter_by(id="test-env-1").first()
assert env_row is not None
assert env_row.name == "Test Env"
assert env_row.url == "http://superset.test"
# Sync must have been called
mock_service_instance.sync_environment.assert_called_once_with("test-env-1", mock_client_instance)
assert result["synced_count"] == 1
assert result["failed_count"] == 0
@pytest.mark.asyncio
async def test_trigger_sync_now_rejects_empty_environments(db_session):
"""Verify 400 error when no environments are configured."""
from src.api.routes.migration import trigger_sync_now
cm = _make_sync_config_manager([])
with pytest.raises(HTTPException) as exc_info:
await trigger_sync_now(config_manager=cm, db=db_session, _=None)
assert exc_info.value.status_code == 400
assert "No environments" in exc_info.value.detail
@pytest.mark.asyncio
async def test_trigger_sync_now_handles_partial_failure(db_session, _mock_env):
"""Verify that if sync_environment raises for one env, it's captured in failed list."""
from src.api.routes.migration import trigger_sync_now
env2 = MagicMock()
env2.id = "test-env-2"
env2.name = "Failing Env"
env2.url = "http://fail.test"
env2.username = "admin"
env2.password = "admin"
env2.verify_ssl = False
env2.timeout = 30
cm = _make_sync_config_manager([_mock_env, env2])
with patch("src.api.routes.migration.SupersetClient") as MockClient, \
patch("src.api.routes.migration.IdMappingService") as MockService:
mock_service_instance = MagicMock()
mock_service_instance.sync_environment.side_effect = [None, RuntimeError("Connection refused")]
MockService.return_value = mock_service_instance
MockClient.return_value = MagicMock()
result = await trigger_sync_now(config_manager=cm, db=db_session, _=None)
assert result["synced_count"] == 1
assert result["failed_count"] == 1
assert result["details"]["failed"][0]["env_id"] == "test-env-2"
@pytest.mark.asyncio
async def test_trigger_sync_now_idempotent_env_upsert(db_session, _mock_env):
"""Verify that calling sync twice doesn't duplicate the Environment row."""
from src.api.routes.migration import trigger_sync_now
from src.models.mapping import Environment as EnvironmentModel
cm = _make_sync_config_manager([_mock_env])
with patch("src.api.routes.migration.SupersetClient"), \
patch("src.api.routes.migration.IdMappingService"):
await trigger_sync_now(config_manager=cm, db=db_session, _=None)
await trigger_sync_now(config_manager=cm, db=db_session, _=None)
env_count = db_session.query(EnvironmentModel).filter_by(id="test-env-1").count()
assert env_count == 1
# [/DEF:backend.src.api.routes.__tests__.test_migration_routes:Module]