from datetime import timedelta from unittest import mock import pytest from audience_common import datetimeutc from freezegun import freeze_time from campaigns.connectors.aws.kms import BaseKMS from campaigns.connectors.db import Database from campaigns.connectors.facebook import models as fb_models from campaigns.meta import constants from campaigns.meta.dtos import FacebookConnection from campaigns.meta.enums import FacebookConnectionStatus from campaigns.meta.handlers import ( GetFacebookConnectionStatusHandler, GetFacebookConnectionStatusRequest, ) from campaigns.meta.models import FacebookAssociation from campaigns.meta.repositories import FacebookAssociationRepository from tests.unit.types import BuildModel, CreateModel @pytest.fixture def handler( db: Database, dummy_kms: BaseKMS, facebook_client_mock: mock.MagicMock, facebook_association_repository: FacebookAssociationRepository, ) -> GetFacebookConnectionStatusHandler: return GetFacebookConnectionStatusHandler( db=db, kms=dummy_kms, facebook_client=facebook_client_mock, facebook_association_repository=facebook_association_repository, ) @freeze_time("2022-07-20") async def test_get_facebook_connection_status( handler: GetFacebookConnectionStatusHandler, facebook_client_mock: mock.MagicMock, build_model: BuildModel, create_model: CreateModel, ) -> None: granted_scopes = constants.REQUIRED_SCOPES api_user = build_model(fb_models.User) association = await create_model( FacebookAssociation, is_connected=True, user_id=api_user.id, user_name=api_user.name, ) facebook_client_mock.debug_token.return_value = fb_models.DebugToken( is_valid=True, scopes=granted_scopes ) facebook_client_mock.get_user.return_value = api_user connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=association.orchard_identity_id), ) assert connection.user assert connection.user.id == association.user_id assert connection.user.name == association.user_name assert connection.user.granted_scopes == granted_scopes assert connection.status == FacebookConnectionStatus.CONNECTED async def test_get_facebook_connection_status_no_association_found( handler: GetFacebookConnectionStatusHandler, identity_id: str ) -> None: connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=identity_id) ) assert connection == FacebookConnection.not_connected() async def test_get_facebook_connection_status_not_connected( handler: GetFacebookConnectionStatusHandler, create_model: CreateModel ) -> None: association = await create_model(FacebookAssociation, is_connected=False) connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=association.orchard_identity_id), ) assert connection.status == FacebookConnectionStatus.NOT_CONNECTED @freeze_time("2022-07-20") async def test_get_facebook_connection_status_association_expired( handler: GetFacebookConnectionStatusHandler, facebook_client_mock: mock.MagicMock, create_model: CreateModel, ) -> None: association = await create_model( FacebookAssociation, is_connected=True, expires_at=datetimeutc.now() - timedelta(days=10), ) connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=association.orchard_identity_id), ) assert connection.user assert connection.user.id == association.user_id assert connection.user.name == association.user_name assert connection.user.picture is None assert connection.user.granted_scopes == [] assert connection.status == FacebookConnectionStatus.INVALID async def test_get_facebook_connection_status_debug_token_invalid( handler: GetFacebookConnectionStatusHandler, facebook_client_mock: mock.MagicMock, create_model: CreateModel, ) -> None: association = await create_model(FacebookAssociation, is_connected=True) facebook_client_mock.debug_token.return_value = fb_models.DebugToken( is_valid=False, scopes=constants.REQUIRED_SCOPES ) connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=association.orchard_identity_id), ) assert connection.user assert connection.user.id == association.user_id assert connection.user.name == association.user_name assert connection.user.picture is None assert connection.user.granted_scopes == [] assert connection.status == FacebookConnectionStatus.INVALID async def test_get_facebook_connection_status_has_not_pages_scope( handler: GetFacebookConnectionStatusHandler, facebook_client_mock: mock.MagicMock, build_model: BuildModel, create_model: CreateModel, ) -> None: api_user = build_model(fb_models.User) granted_scopes = list( set(constants.REQUIRED_SCOPES) - {constants.SCOPE_PAGES_SHOW_LIST} ) association = await create_model( FacebookAssociation, is_connected=True, user_id=api_user.id, user_name=api_user.name, ) facebook_client_mock.debug_token.return_value = fb_models.DebugToken( is_valid=True, scopes=granted_scopes ) facebook_client_mock.get_user.return_value = api_user connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=association.orchard_identity_id), ) assert connection.user assert connection.user.id == association.user_id assert connection.user.name == association.user_name assert connection.user.picture == api_user.picture.data.url assert connection.user.granted_scopes == granted_scopes assert connection.status == FacebookConnectionStatus.NO_PAGES async def test_get_facebook_connection_status_scope_missing( handler: GetFacebookConnectionStatusHandler, facebook_client_mock: mock.MagicMock, build_model: BuildModel, create_model: CreateModel, ) -> None: api_user = build_model(fb_models.User) granted_scopes = list(set(constants.REQUIRED_SCOPES) - {constants.SCOPE_EMAIL}) association = await create_model( FacebookAssociation, is_connected=True, user_id=api_user.id, user_name=api_user.name, ) facebook_client_mock.debug_token.return_value = fb_models.DebugToken( is_valid=True, scopes=granted_scopes ) facebook_client_mock.get_user.return_value = api_user connection = await handler.handle( GetFacebookConnectionStatusRequest(identity_id=association.orchard_identity_id), ) assert connection.user assert connection.user.id == association.user_id assert connection.user.name == association.user_name assert connection.user.picture == api_user.picture.data.url assert connection.user.granted_scopes == granted_scopes assert connection.status == FacebookConnectionStatus.SCOPES_MISSING