from unittest import mock import httpx import pytest from dmp.adapters.google import GoogleClientError from dmp.adapters.google.enums import ( GoogleAdAccountStatus, GoogleAdAccountUserAccessRole, ) from dmp.adapters.google.models import ( AccessToken, AdAccount, AdAccountApiResponse, AdAccountResourceResponse, CampaignApiResponse, CampaignsResponse, ClientAdAccount, Customer, CustomerUserAccess, NestedAdAccount, NestedAdAccountApiResponse, Summary, UserAdAccount, ) from dmp.google.models import ( GoogleAdAccount, GoogleUserConnection, GoogleUserConnectionAdAccount, ) from dmp.google.services import GoogleAdAccountService from tests.unit.faker import FakerTyped from tests.unit.types import BuildModel, CreateModel class TestGoogleAdAccountService: def test_get_api_ad_accounts_for_connection_single_is_valid( self, service: GoogleAdAccountService, google_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, ) -> None: ad_account_id = fake.integer() user_role = GoogleAdAccountUserAccessRole.STANDARD response = AdAccountResourceResponse( resourceNames=[f"{fake.pystr()}/{ad_account_id}"] ) ad_account = build_model( AdAccount, customer=build_model( Customer, id=str(ad_account_id), status=GoogleAdAccountStatus.ENABLED, testAccount=False, ), customerUserAccess=CustomerUserAccess(accessRole=user_role), ) campaign_response = build_model( CampaignApiResponse, return_results_count=fake.integer() ) google_client_mock.get_ad_account_resources.return_value = response google_client_mock.get_ad_account_details.return_value = AdAccountApiResponse( results=[ad_account] ) google_client_mock.get_campaign_ids.return_value = campaign_response result_ad_accounts = service.get_api_ad_accounts_for_connection( access_token=build_model(AccessToken).access_token ) assert result_ad_accounts assert len(result_ad_accounts) == 1 assert result_ad_accounts[0].id == ad_account.customer.id assert result_ad_accounts[0].name == ad_account.customer.descriptive_name assert result_ad_accounts[0].user_role == user_role assert ( result_ad_accounts[0].campaigns.summary.total_count == campaign_response.total_results_count ) def test_get_api_ad_accounts_for_connection_multiple_last_is_invalid( self, service: GoogleAdAccountService, google_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, ) -> None: ad_account_id = fake.integer() user_role = GoogleAdAccountUserAccessRole.STANDARD response = AdAccountResourceResponse( resourceNames=[f"{fake.pystr()}/{ad_account_id}"] ) ad_account_1 = build_model( AdAccount, customer=build_model( Customer, id=str(ad_account_id), status=GoogleAdAccountStatus.ENABLED, testAccount=False, ), customerUserAccess=CustomerUserAccess(accessRole=user_role), ) ad_account_2 = build_model( AdAccount, customer=build_model( Customer, id=str(ad_account_id), status=GoogleAdAccountStatus.ENABLED, testAccount=False, ), customerUserAccess=CustomerUserAccess( accessRole=GoogleAdAccountUserAccessRole.STANDARD ), ) campaign_response = build_model( CampaignApiResponse, return_results_count=fake.integer() ) google_client_mock.get_ad_account_resources.return_value = response google_client_mock.get_ad_account_details.return_value = AdAccountApiResponse( results=[ad_account_1, ad_account_2] ) google_client_mock.get_campaign_ids.return_value = campaign_response result_ad_accounts = service.get_api_ad_accounts_for_connection( access_token=build_model(AccessToken).access_token ) assert result_ad_accounts assert len(result_ad_accounts) == 1 assert result_ad_accounts[0].id == ad_account_1.customer.id assert result_ad_accounts[0].name == ad_account_1.customer.descriptive_name assert result_ad_accounts[0].user_role == user_role assert ( result_ad_accounts[0].campaigns.summary.total_count == campaign_response.total_results_count ) @pytest.mark.db def test_get_ad_accounts_for_connection( self, service: GoogleAdAccountService, build_model: BuildModel ) -> None: api_ad_account_1 = build_model(UserAdAccount) api_ad_account_2 = build_model(UserAdAccount) db_ad_account_1 = build_model(GoogleAdAccount, external_id=api_ad_account_1.id) db_ad_account_2 = build_model(GoogleAdAccount) user_connection = build_model( GoogleUserConnection, connection_ad_accounts=[ GoogleUserConnectionAdAccount(ad_account=db_ad_account_1), GoogleUserConnectionAdAccount(ad_account=db_ad_account_2), ], ) result_ad_accounts = service.get_ad_accounts_for_user_connection( connection=user_connection, external_ad_accounts=[api_ad_account_1, api_ad_account_2], ) assert result_ad_accounts assert len(result_ad_accounts) == 2 assert result_ad_accounts[0].id == db_ad_account_1.id assert result_ad_accounts[0].external_id == api_ad_account_1.id assert result_ad_accounts[0].name == api_ad_account_1.name assert ( result_ad_accounts[0].campaigns_count == api_ad_account_1.campaigns.summary.total_count ) assert result_ad_accounts[1].id != db_ad_account_2.id assert result_ad_accounts[1].external_id == api_ad_account_2.id assert result_ad_accounts[1].name == api_ad_account_2.name assert ( result_ad_accounts[1].campaigns_count == api_ad_account_2.campaigns.summary.total_count ) @pytest.mark.db def test_get_ad_accounts_for_connection_obtained_from_other_connections( self, service: GoogleAdAccountService, build_model: BuildModel, create_model: CreateModel, ) -> None: api_ad_account = build_model(UserAdAccount) db_ad_account_1 = create_model(GoogleAdAccount) db_ad_account_2 = create_model(GoogleAdAccount, external_id=api_ad_account.id) user_connection = build_model( GoogleUserConnection, connection_ad_accounts=[ GoogleUserConnectionAdAccount(ad_account=db_ad_account_1), ], ) result_ad_accounts = service.get_ad_accounts_for_user_connection( connection=user_connection, external_ad_accounts=[api_ad_account], ) assert result_ad_accounts assert len(result_ad_accounts) == 1 assert result_ad_accounts[0].id == db_ad_account_2.id assert result_ad_accounts[0].external_id == db_ad_account_2.external_id assert result_ad_accounts[0].name == api_ad_account.name assert ( result_ad_accounts[0].campaigns_count == api_ad_account.campaigns.summary.total_count ) def test_get_api_ad_accounts_for_connection_not_ad_user( self, service: GoogleAdAccountService, google_client_mock: mock.MagicMock, build_model: BuildModel, ) -> None: code = 401 error = { "error": { "code": code, "message": "", "status": "", "details": [ { "@type": "", "errors": [ { "errorCode": {"authenticationError": "NOT_ADS_USER"}, "message": "", } ], "requestId": "", } ], } } google_client_mock.get_ad_account_resources.side_effect = GoogleClientError( response=httpx.Response(status_code=code, content=str(error)) ) result_ad_accounts = service.get_api_ad_accounts_for_connection( access_token=build_model(AccessToken).access_token ) assert result_ad_accounts == [] @pytest.mark.db def test_get_nested_ad_accounts( self, service: GoogleAdAccountService, google_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, ) -> None: access_token = fake.plain_token() source_ad_account_id = fake.integer() access_role = fake.choice( [ GoogleAdAccountUserAccessRole.ADMIN, GoogleAdAccountUserAccessRole.STANDARD, ] ) campaigns_count = fake.integer(length=4) nested_ad_account_1 = build_model( NestedAdAccount, customerClient=build_model( ClientAdAccount, status=GoogleAdAccountStatus.ENABLED, id=str(fake.integer()), ), ) nested_ad_account_2 = build_model( NestedAdAccount, customerClient=build_model( ClientAdAccount, status=GoogleAdAccountStatus.CLOSED, id=str(fake.integer()), ), ) nested_ad_account_3 = build_model( NestedAdAccount, customerClient=build_model( ClientAdAccount, status=GoogleAdAccountStatus.ENABLED, id=str(fake.integer()), ), ) google_client_mock.get_nested_ad_account_details.return_value = ( NestedAdAccountApiResponse( results=[nested_ad_account_1, nested_ad_account_2, nested_ad_account_3] ) ) google_client_mock.get_campaign_ids.return_value = CampaignApiResponse( totalResultsCount=campaigns_count, # type: ignore ) ad_account_1 = build_model( UserAdAccount, id=str(nested_ad_account_1.customer_client.id), name=nested_ad_account_1.customer_client.descriptive_name or "", user_role=access_role, campaigns=CampaignsResponse( summary=Summary(total_count=campaigns_count), ), login_customer_id=str(source_ad_account_id), ) ad_account_2 = build_model( UserAdAccount, id=str(nested_ad_account_3.customer_client.id), name=nested_ad_account_3.customer_client.descriptive_name or "", user_role=access_role, campaigns=CampaignsResponse( summary=Summary(total_count=campaigns_count), ), login_customer_id=str(source_ad_account_id), ) results = service._get_nested_ad_accounts( access_token=access_token, source_ad_account_id=source_ad_account_id, access_role=access_role, ) assert results == {ad_account_1.id: ad_account_1, ad_account_2.id: ad_account_2}