import datetime from unittest import mock import pytest from dmp.ad_accounts.models import AdAccountDbt from dmp.ad_reporting.enums import AdReportingPlatform from dmp.adapters.fivetran.enums import ConnectionSetupState, ConnectionSyncState from dmp.adapters.fivetran.models import AdsConnection, AdsConnectionConfig from dmp.app_connections.enums import AppConnectionStatus from dmp.meta.handlers import ( RefreshMetaAdReportingConnectionHandler, RefreshMetaAdReportingConnectionRequest, ) from dmp.meta.models import MetaAdReportingConnection from dmp.meta.repositories import MetaAdReportingConnectionRepository from tests.unit.types import BuildModel, CreateModel, CreateReportingModel class TestRefreshMetaAdReportingConnectionHandler: @pytest.mark.db def test_refresh_ad_reporting_connection_connection_pending( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.INCOMPLETE, "sync_state": ConnectionSyncState.SCHEDULED, }, ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.PENDING, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.PENDING @pytest.mark.db def test_refresh_ad_reporting_connection_connection_broken( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={"setup_state": ConnectionSetupState.BROKEN} ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.PENDING, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.ERROR @pytest.mark.db def test_refresh_ad_reporting_connection_connected_not_synced_and_not_processed_store( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SCHEDULED, }, ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.PENDING, synced_at=None, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() fivetran_client_mock.sync_connection_data.assert_called() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.PENDING @pytest.mark.db def test_refresh_ad_reporting_connection_connection_syncing( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SYNCING, "is_historical_sync": False, }, ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.PENDING, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.SYNCING @pytest.mark.db def test_refresh_ad_reporting_connection_synced_but_not_processed( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SCHEDULED, }, ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.PENDING, synced_at=datetime.datetime(2021, 1, 1, tzinfo=datetime.UTC), ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.status == AppConnectionStatus.WAITING_FOR_PROCESSING @pytest.mark.db def test_refresh_ad_reporting_connection_connection_connected_after_being_broken( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, create_reporting_model: CreateReportingModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SCHEDULED, }, ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.ERROR, synced_at=datetime.datetime(2021, 1, 1, tzinfo=datetime.UTC), ) create_reporting_model( AdAccountDbt, source_schema=connection.fivetran_schema, platform=AdReportingPlatform.META, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.CONNECTED @pytest.mark.db def test_refresh_ad_reporting_connection_connection_connected_synced_and_processed_store( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, create_reporting_model: CreateReportingModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SCHEDULED, }, ) fivetran_schema = "schema" connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, fivetran_schema=fivetran_schema, status=AppConnectionStatus.WAITING_FOR_PROCESSING, synced_at=datetime.datetime(2021, 1, 1, tzinfo=datetime.UTC), ) create_reporting_model( AdAccountDbt, source_schema=connection.fivetran_schema, platform=AdReportingPlatform.META, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.CONNECTED @pytest.mark.db def test_refresh_ad_reporting_connection_connection_connected_rescheduled_not_synced_and_not_processed_store( self, handler: RefreshMetaAdReportingConnectionHandler, ad_reporting_connection_repository: MetaAdReportingConnectionRepository, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.RESCHEDULED, "is_historical_sync": False, }, ) connection = create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.SYNCING_INITIAL, synced_at=datetime.datetime(2021, 1, 1, tzinfo=datetime.UTC), ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) refreshed_connection = ad_reporting_connection_repository.first() assert refreshed_connection assert refreshed_connection.id == connection.id assert refreshed_connection.synced_at == fivetran_connection.succeeded_at assert refreshed_connection.status == AppConnectionStatus.SYNCING @pytest.mark.db def test_refresh_ad_reporting_connection_warns_on_missing_sync_mode( self, handler: RefreshMetaAdReportingConnectionHandler, build_model: BuildModel, create_model: CreateModel, fivetran_client_mock: mock.MagicMock, caplog: pytest.LogCaptureFixture, ) -> None: fivetran_connection = build_model( AdsConnection, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SCHEDULED, }, config=build_model(AdsConnectionConfig, sync_mode=None), ) create_model( MetaAdReportingConnection, fivetran_connector_id=fivetran_connection.id, status=AppConnectionStatus.PENDING, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection with caplog.at_level("WARNING"): handler.handle( RefreshMetaAdReportingConnectionRequest( fivetran_connection_id=fivetran_connection.id ) ) assert ( f"Meta connection {fivetran_connection.id} is missing sync_mode in config" in caplog.text )