from unittest import mock import pytest from dmp.ad_accounts.enums import AdAccountPlatform from dmp.ad_accounts.models import AdAccountDbt from dmp.adapters.fivetran.enums import ( AdsAccountsSyncMode, ConnectionSetupState, ConnectionSyncState, ) from dmp.adapters.fivetran.models import AdsConnection from dmp.app_connections.enums import AppConnectionStatus from dmp.tiktok.models import ( TikTokAdAccount, TikTokAdReportingConnection, TikTokUserConnection, ) from dmp.tiktok.repositories import TikTokAdReportingConnectionRepository from dmp.tiktok.services import ( TikTokAdReportingConnectionService, ) from tests.unit.faker import FakerTyped from tests.unit.types import BuildModel, CreateModel, CreateReportingModel class TestTikTokAdReportingConnectionService: @pytest.mark.db def test_delete_ad_account( self, service: TikTokAdReportingConnectionService, ad_reporting_connection_repository: TikTokAdReportingConnectionRepository, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, ) -> None: ad_account = create_model(TikTokAdAccount) ad_reporting_connection = create_model( TikTokAdReportingConnection, ad_accounts=[ad_account, create_model(TikTokAdAccount)], ) service.delete_ad_account(ad_reporting_connection, ad_account=ad_account) fivetran_client_mock.delete_connection.assert_not_called() connection = ad_reporting_connection_repository.first() assert connection assert connection.ad_accounts_count == 1 assert connection.ad_accounts[0].id != ad_account.id @pytest.mark.db def test_delete_last_ad_account( self, service: TikTokAdReportingConnectionService, ad_reporting_connection_repository: TikTokAdReportingConnectionRepository, create_model: CreateModel, ) -> None: ad_account = create_model(TikTokAdAccount) ad_reporting_connection = create_model( TikTokAdReportingConnection, ad_accounts=[ad_account] ) service.delete_ad_account(ad_reporting_connection, ad_account=ad_account) connections = ad_reporting_connection_repository.all() assert len(connections) == 0 @pytest.mark.db def test_delete_ad_account_not_linked_to_connection( self, service: TikTokAdReportingConnectionService, ad_reporting_connection_repository: TikTokAdReportingConnectionRepository, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, ) -> None: ad_account = create_model(TikTokAdAccount) ad_reporting_connection = create_model( TikTokAdReportingConnection, ad_accounts=[create_model(TikTokAdAccount)] ) service.delete_ad_account(ad_reporting_connection, ad_account=ad_account) fivetran_client_mock.delete_connection.assert_not_called() connection = ad_reporting_connection_repository.first() assert connection assert connection.ad_accounts_count == 1 @pytest.mark.db def test_delete_ad_account_should_delete_connection_if_ad_accounts_missing( self, service: TikTokAdReportingConnectionService, ad_reporting_connection_repository: TikTokAdReportingConnectionRepository, create_model: CreateModel, ) -> None: ad_account = create_model(TikTokAdAccount) ad_reporting_connection_without_ad_accounts = create_model( TikTokAdReportingConnection ) service.delete_ad_account( ad_reporting_connection_without_ad_accounts, ad_account=ad_account ) ad_reporting_connection = ad_reporting_connection_repository.first() assert not ad_reporting_connection @pytest.mark.db def test_refresh_connection_with_new_ad_account_for_existed_connection( self, service: TikTokAdReportingConnectionService, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, build_model: BuildModel, fake: FakerTyped, ) -> None: ad_account_1 = create_model(TikTokAdAccount) ad_account_2 = create_model(TikTokAdAccount) new_ad_account_external_id = fake.pystr() ad_reporting_connection = create_model( TikTokAdReportingConnection, ad_accounts=[ad_account_1, ad_account_2] ) fivetran_connection = build_model( AdsConnection, config={ "sync_mode": AdsAccountsSyncMode.SPECIFIC_ACCOUNTS, "accounts": [ ad_account_1.external_id, ad_account_2.external_id, new_ad_account_external_id, ], }, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SYNCING, "is_historical_sync": False, }, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection refreshed_ad_reporting_connection = service.refresh_connection( ad_reporting_connection ) assert refreshed_ad_reporting_connection assert refreshed_ad_reporting_connection.ad_accounts_count == 3 @pytest.mark.db def test_refresh_connection_with_deleted_ad_account_for_existed_connection( self, service: TikTokAdReportingConnectionService, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, build_model: BuildModel, ) -> None: ad_account_1 = create_model(TikTokAdAccount) ad_account_2 = create_model(TikTokAdAccount) ad_reporting_connection = create_model( TikTokAdReportingConnection, ad_accounts=[ad_account_1, ad_account_2] ) fivetran_connection = build_model( AdsConnection, config={ "sync_mode": AdsAccountsSyncMode.SPECIFIC_ACCOUNTS, "accounts": [ ad_account_1.external_id, ], }, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SYNCING, "is_historical_sync": False, }, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection refreshed_ad_reporting_connection = service.refresh_connection( ad_reporting_connection ) assert refreshed_ad_reporting_connection assert refreshed_ad_reporting_connection.ad_accounts_count == 1 @pytest.mark.db def test_refresh_connection_before_sync_details_for_specific_accounts( self, service: TikTokAdReportingConnectionService, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, build_model: BuildModel, fake: FakerTyped, ) -> None: ad_reporting_connection = create_model(TikTokAdReportingConnection) external_id = fake.pystr() external_id_2 = fake.pystr() fivetran_connection = build_model( AdsConnection, config={ "sync_mode": AdsAccountsSyncMode.SPECIFIC_ACCOUNTS, "accounts": [external_id, external_id_2], }, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SYNCING, "is_historical_sync": False, }, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection refreshed_ad_reporting_connection = service.refresh_connection( ad_reporting_connection ) assert refreshed_ad_reporting_connection assert refreshed_ad_reporting_connection.ad_accounts_count == 2 @pytest.mark.db def test_refresh_connection_for_incomplete_connection_not_create_ad_accounts( self, service: TikTokAdReportingConnectionService, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, build_model: BuildModel, fake: FakerTyped, ) -> None: ad_reporting_connection = create_model(TikTokAdReportingConnection) external_id = fake.pystr() external_id_2 = fake.pystr() fivetran_connection = build_model( AdsConnection, config={ "sync_mode": AdsAccountsSyncMode.SPECIFIC_ACCOUNTS, "accounts": [external_id, external_id_2], }, status={ "setup_state": ConnectionSetupState.INCOMPLETE, "sync_state": ConnectionSyncState.RESCHEDULED, "is_historical_sync": False, }, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection refreshed_ad_reporting_connection = service.refresh_connection( ad_reporting_connection ) assert refreshed_ad_reporting_connection assert refreshed_ad_reporting_connection.ad_accounts_count == 0 @pytest.mark.db def test_refresh_connection_for_connection_with_only_dbt_ad_accounts( self, service: TikTokAdReportingConnectionService, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, create_reporting_model: CreateReportingModel, build_model: BuildModel, ) -> None: ad_reporting_connection = create_model(TikTokAdReportingConnection) dbt_ad_account = create_reporting_model( AdAccountDbt, source_schema=ad_reporting_connection.fivetran_schema, platform=AdAccountPlatform.TIKTOK, ) fivetran_connection = build_model( AdsConnection, config={ "sync_mode": AdsAccountsSyncMode.SPECIFIC_ACCOUNTS, "accounts": [], }, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.RESCHEDULED, "is_historical_sync": False, }, schema=ad_reporting_connection.fivetran_schema, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection refreshed_ad_reporting_connection = service.refresh_connection( ad_reporting_connection ) assert refreshed_ad_reporting_connection assert refreshed_ad_reporting_connection.ad_accounts_count == 1 assert ( refreshed_ad_reporting_connection.ad_accounts[0].external_id == dbt_ad_account.id ) @pytest.mark.db def test_refresh_connection_for_connection_for_sync_all_empty_dbt( self, service: TikTokAdReportingConnectionService, fivetran_client_mock: mock.MagicMock, create_model: CreateModel, build_model: BuildModel, ) -> None: ad_account = create_model(TikTokAdAccount) user_connection = create_model( TikTokUserConnection, ad_accounts=[ad_account], ) ad_reporting_connection = create_model( TikTokAdReportingConnection, user_id=user_connection.user_id, identity_id=user_connection.identity_id, status=AppConnectionStatus.SYNCING_INITIAL, ) fivetran_connection = build_model( AdsConnection, config={ "sync_mode": AdsAccountsSyncMode.ALL_ACCOUNTS, }, status={ "setup_state": ConnectionSetupState.CONNECTED, "sync_state": ConnectionSyncState.SYNCING, "is_historical_sync": True, }, schema=ad_reporting_connection.fivetran_schema, ) fivetran_client_mock.get_ads_connection.return_value = fivetran_connection refreshed_ad_reporting_connection = service.refresh_connection( ad_reporting_connection ) assert refreshed_ad_reporting_connection assert refreshed_ad_reporting_connection.ad_accounts_count == 1 assert refreshed_ad_reporting_connection.ad_accounts[0].id == ad_account.id