from unittest import mock import httpx import pytest from dmp.adapters.meta.constants import ( ERROR_CODE_INVALID_ACCESS_TOKEN, ERROR_SUBCODE_APP_NOT_INSTALLED, ) from dmp.adapters.meta.exceptions import FacebookClientError from dmp.adapters.meta.models import ( DebugToken, Error, ErrorResponse, User, User as ApiUser, ) from dmp.audiences.models import Audience from dmp.config import Settings from dmp.meta.dtos import MetaUser from dmp.meta.enums import MetaUserConnectionStatus from dmp.meta.exceptions import MetaUserConnectionError from dmp.meta.models import MetaAdAccount, MetaUserAdAccount, MetaUserConnection from dmp.meta.repositories import MetaUserConnectionRepository from dmp.meta.services import MetaUserConnectionService from tests.unit.faker import FakerTyped from tests.unit.types import BuildModel, CreateModel class TestMetaUserConnectionService: def test_get_user_invalid_debug_token_response( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, fake: FakerTyped, ) -> None: user_id = fake.pystr() access_token = fake.plain_token() facebook_client_mock.debug_token.side_effect = FacebookClientError() with pytest.raises(MetaUserConnectionError) as exc: service.get_user(user_id, access_token=access_token) assert exc.value.user is None assert exc.value.status == MetaUserConnectionStatus.INVALID def test_get_user_invalid_debug_token( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, fake: FakerTyped, ) -> None: user_id = fake.pystr() access_token = fake.plain_token() facebook_client_mock.debug_token.return_value = DebugToken( is_valid=False, scopes=[] ) with pytest.raises(MetaUserConnectionError) as exc: service.get_user(user_id, access_token=access_token) assert exc.value.user is None assert exc.value.status == MetaUserConnectionStatus.INVALID def test_get_user_invalid_debug_token_by_user_connection( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, ) -> None: user_connection = build_model(MetaUserConnection) access_token = fake.plain_token() facebook_client_mock.debug_token.return_value = DebugToken( is_valid=False, scopes=[] ) with pytest.raises(MetaUserConnectionError) as exc: service.get_user(user_connection, access_token=access_token) assert exc.value.user == MetaUser( id=user_connection.user_id, name=user_connection.user_name, picture=user_connection.user_picture, granted_scopes=[], ) assert exc.value.status == MetaUserConnectionStatus.INVALID def test_get_user_insufficient_scopes( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, ) -> None: user_id = fake.pystr() access_token = fake.plain_token() granted_scopes: list[str] = [] api_user = build_model(ApiUser) facebook_client_mock.debug_token.return_value = DebugToken( is_valid=True, scopes=granted_scopes ) facebook_client_mock.get_user.return_value = api_user with pytest.raises(MetaUserConnectionError) as exc: service.get_user(user_id, access_token=access_token) assert exc.value.user == MetaUser( id=api_user.id, name=api_user.name, picture=api_user.picture.data.url, granted_scopes=granted_scopes, ) assert exc.value.status == MetaUserConnectionStatus.SCOPES_MISSING def test_get_user( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, settings: Settings, ) -> None: user_id = fake.pystr() access_token = fake.plain_token() api_user = build_model(ApiUser) facebook_client_mock.debug_token.return_value = DebugToken( is_valid=True, scopes=settings.meta_user_required_scopes ) facebook_client_mock.get_user.return_value = api_user user = service.get_user(user_id, access_token=access_token) assert user == MetaUser( id=api_user.id, name=api_user.name, picture=api_user.picture.data.url, granted_scopes=settings.meta_user_required_scopes, ) @pytest.mark.db def test_get_active_audience_sharing_connection( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, build_model: BuildModel, create_model: CreateModel, settings: Settings, ) -> None: audience = create_model(Audience) ad_account = create_model(MetaAdAccount) user_connection = create_model( MetaUserConnection, is_valid=True, ad_accounts=[ad_account] ) create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, ) facebook_client_mock.debug_token.return_value = DebugToken( is_valid=True, scopes=settings.meta_user_required_scopes ) facebook_client_mock.get_user.return_value = build_model(User) ad_account_connection = service.get_active_audience_sharing_connection( audience_id=audience.id, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, ) assert ad_account_connection == user_connection @pytest.mark.db def test_test_get_active_audience_sharing_connection_disconnect_user( self, user_connection_repository: MetaUserConnectionRepository, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, create_model: CreateModel, ) -> None: audience = create_model(Audience) ad_account = create_model(MetaAdAccount) user_connection = create_model( MetaUserConnection, is_valid=True, ad_accounts=[ad_account] ) create_model( MetaUserAdAccount, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, vendor_id=audience.vendor_id, subaccount_id=audience.subaccount_id, ) facebook_client_mock.debug_token.return_value = DebugToken( is_valid=False, scopes=[] ) ad_account_connection = service.get_active_audience_sharing_connection( audience_id=audience.id, ad_account_id=ad_account.id, identity_id=user_connection.identity_id, ) assert not ad_account_connection result = user_connection_repository.get(user_connection.id) assert result assert not result.is_valid def test_disconnect_user_token_validation_error( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, build_model: BuildModel, ) -> None: user_connection = build_model(MetaUserConnection) content = ErrorResponse( error=Error( code=ERROR_CODE_INVALID_ACCESS_TOKEN, error_subcode=ERROR_SUBCODE_APP_NOT_INSTALLED, ) ) facebook_client_mock.disconnect_user.side_effect = FacebookClientError( message="test_token_validation_error", response=httpx.Response(status_code=400, content=content.model_dump_json()), ) response = service.disconnect_user(user_connection) assert response def test_disconnect_user_not_token_validation_error( self, service: MetaUserConnectionService, facebook_client_mock: mock.MagicMock, build_model: BuildModel, fake: FakerTyped, ) -> None: user_connection = build_model(MetaUserConnection) content = ErrorResponse( error=Error(code=fake.integer(), error_subcode=fake.integer()) ) facebook_client_mock.disconnect_user.side_effect = FacebookClientError( message="test_token_validation_error", response=httpx.Response(status_code=400, content=content.model_dump_json()), ) with pytest.raises(FacebookClientError): service.disconnect_user(user_connection)