# [DEF:backend.tests.core.test_mapping_service:Module] # # @TIER: STANDARD # @PURPOSE: Unit tests for the IdMappingService matching UUIDs to integer IDs. # @LAYER: Domain # @RELATION: VERIFIES -> backend.src.core.mapping_service # import pytest from datetime import datetime, timezone from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker import sys import os from pathlib import Path # Add backend directory to sys.path so 'src' can be resolved backend_dir = str(Path(__file__).parent.parent.parent.resolve()) if backend_dir not in sys.path: sys.path.insert(0, backend_dir) from src.models.mapping import Base, ResourceMapping, ResourceType from src.core.mapping_service import IdMappingService @pytest.fixture def db_session(): # In-memory SQLite for testing engine = create_engine('sqlite:///:memory:') Base.metadata.create_all(engine) Session = sessionmaker(bind=engine) session = Session() yield session session.close() class MockSupersetClient: def __init__(self, resources): self.resources = resources def get_all_resources(self, endpoint): return self.resources.get(endpoint, []) def test_sync_environment_upserts_correctly(db_session): service = IdMappingService(db_session) mock_client = MockSupersetClient({ "chart": [ {"id": 42, "uuid": "123e4567-e89b-12d3-a456-426614174000", "slice_name": "Test Chart"} ] }) service.sync_environment("test-env", mock_client) mapping = db_session.query(ResourceMapping).first() assert mapping is not None assert mapping.environment_id == "test-env" assert mapping.resource_type == ResourceType.CHART assert mapping.uuid == "123e4567-e89b-12d3-a456-426614174000" assert mapping.remote_integer_id == "42" assert mapping.resource_name == "Test Chart" def test_get_remote_id_returns_integer(db_session): service = IdMappingService(db_session) mapping = ResourceMapping( environment_id="test-env", resource_type=ResourceType.DATASET, uuid="uuid-1", remote_integer_id="99", resource_name="Test DS", last_synced_at=datetime.now(timezone.utc) ) db_session.add(mapping) db_session.commit() result = service.get_remote_id("test-env", ResourceType.DATASET, "uuid-1") assert result == 99 def test_get_remote_ids_batch_returns_dict(db_session): service = IdMappingService(db_session) m1 = ResourceMapping( environment_id="test-env", resource_type=ResourceType.DASHBOARD, uuid="uuid-1", remote_integer_id="11" ) m2 = ResourceMapping( environment_id="test-env", resource_type=ResourceType.DASHBOARD, uuid="uuid-2", remote_integer_id="22" ) db_session.add_all([m1, m2]) db_session.commit() result = service.get_remote_ids_batch("test-env", ResourceType.DASHBOARD, ["uuid-1", "uuid-2", "uuid-missing"]) assert len(result) == 2 assert result["uuid-1"] == 11 assert result["uuid-2"] == 22 assert "uuid-missing" not in result def test_sync_environment_updates_existing_mapping(db_session): """Verify that sync_environment updates an existing mapping (upsert UPDATE path).""" from src.models.mapping import ResourceMapping # Pre-populate a mapping existing = ResourceMapping( environment_id="test-env", resource_type=ResourceType.CHART, uuid="123e4567-e89b-12d3-a456-426614174000", remote_integer_id="10", resource_name="Old Name" ) db_session.add(existing) db_session.commit() service = IdMappingService(db_session) mock_client = MockSupersetClient({ "chart": [ {"id": 42, "uuid": "123e4567-e89b-12d3-a456-426614174000", "slice_name": "Updated Name"} ] }) service.sync_environment("test-env", mock_client) mapping = db_session.query(ResourceMapping).filter_by( uuid="123e4567-e89b-12d3-a456-426614174000" ).first() assert mapping.remote_integer_id == "42" assert mapping.resource_name == "Updated Name" # Should still be only one record (updated, not duplicated) count = db_session.query(ResourceMapping).count() assert count == 1 def test_sync_environment_skips_resources_without_uuid(db_session): """Resources missing uuid or having id=None should be silently skipped.""" service = IdMappingService(db_session) mock_client = MockSupersetClient({ "chart": [ {"id": 42, "slice_name": "No UUID"}, # Missing 'uuid' -> skipped {"id": None, "uuid": "valid-uuid", "slice_name": "ID is None"}, # id=None -> skipped {"id": None, "uuid": None, "slice_name": "Both None"}, # both None -> skipped ] }) service.sync_environment("test-env", mock_client) count = db_session.query(ResourceMapping).count() assert count == 0 def test_sync_environment_handles_api_error_gracefully(db_session): """If one resource type fails, others should still sync.""" class FailingClient: def get_all_resources(self, endpoint): if endpoint == "chart": raise ConnectionError("API timeout") if endpoint == "dataset": return [{"id": 99, "uuid": "ds-uuid-1", "table_name": "users"}] return [] service = IdMappingService(db_session) service.sync_environment("test-env", FailingClient()) count = db_session.query(ResourceMapping).count() assert count == 1 # Only dataset was synced; chart error was swallowed mapping = db_session.query(ResourceMapping).first() assert mapping.resource_type == ResourceType.DATASET def test_get_remote_id_returns_none_for_missing(db_session): """get_remote_id should return None when no mapping exists.""" service = IdMappingService(db_session) result = service.get_remote_id("test-env", ResourceType.CHART, "nonexistent-uuid") assert result is None def test_get_remote_ids_batch_returns_empty_for_empty_input(db_session): """get_remote_ids_batch should return {} for an empty list of UUIDs.""" service = IdMappingService(db_session) result = service.get_remote_ids_batch("test-env", ResourceType.CHART, []) assert result == {} def test_mapping_service_alignment_with_test_data(db_session): """**@TEST_DATA**: Verifies that the service aligns with the resource_mapping_record contract.""" # Contract: {'environment_id': 'prod-env-1', 'resource_type': 'chart', 'uuid': '123e4567-e89b-12d3-a456-426614174000', 'remote_integer_id': '42'} contract_data = { 'environment_id': 'prod-env-1', 'resource_type': ResourceType.CHART, 'uuid': '123e4567-e89b-12d3-a456-426614174000', 'remote_integer_id': '42' } mapping = ResourceMapping(**contract_data) db_session.add(mapping) db_session.commit() service = IdMappingService(db_session) result = service.get_remote_id( contract_data['environment_id'], contract_data['resource_type'], contract_data['uuid'] ) assert result == 42 def test_sync_environment_requires_existing_env(db_session): """**@PRE**: Verify behavior when environment_id is invalid/missing in DB. Note: The current implementation doesn't strictly check for environment existencia in the DB before polling, but it should handle it gracefully or follow the contract. """ service = IdMappingService(db_session) mock_client = MockSupersetClient({"chart": []}) # Even if environment doesn't exist in a hypothetical 'environments' table, # the service should still complete or fail according to defined error handling. # In GRACE-Poly, @PRE is a hard requirement. If we don't have an Env model check, # we simulate the intent. service.sync_environment("non-existent-env", mock_client) # If no error raised, at least verify no mappings were created for other envs assert db_session.query(ResourceMapping).count() == 0 # [/DEF:backend.tests.core.test_mapping_service:Module]