from datetime import datetime from unittest.mock import MagicMock import pytest import requests_mock from apollo_notifications.constants import APPLE_CATEGORY_ID, SONY_CATEGORY_ID from apollo_notifications.push_client.client import PushClient from apollo_notifications.utils import dump_datetime from tests.helpers import * from apollo_main_db.apollo.models import ApolloKeyValueStorage from apollo_main_db.push_notifications.models import PushMessage, PushMessageReduced from client import SpotifyPlaylistClient from config import config from index import _job, process_starred_playlists_updates_by_market from logger import logger def mock_user_data_requests(requests_mocker): mocked_feed = requests_mocker.register_uri( "POST", "https://test/api/service/messages/feed/", json={"status": "OK"} ) mocked_push = requests_mocker.register_uri( "POST", "https://test/api/service/messages/push/", json={"status": "OK"} ) return mocked_feed, mocked_push def test_get_market_to_playlists_map(db_session): market = 'us' now = datetime.utcnow() now_str = dump_datetime(now) last_update_str = dump_datetime(now - timedelta(hours=2)) before = [dump_datetime(now - timedelta(minutes=(30 * i))) for i in range(1, 6)] # 0.5 1 1.5 2 2.5 after = [dump_datetime(now + timedelta(seconds=(2 * i))) for i in range(1, 4)] create_basic_structures( playlists_n=5, users_n=4, playlist_to_updates_dates={ 1: before[:2], 2: before[-2:] + after[:1], 3: (before[1], after[0]), 4: before[:3], 5: before[:2] }, market=market, category_id_to_playlists_map={ SONY_CATEGORY_ID: {1, 2}, SPOTIFY_CATEGORY_ID: {3, 4}, APPLE_CATEGORY_ID: {5} } ) playlists_to_users_map = { playlist_id(1): {user_id(1), user_id(2)}, playlist_id(2): {user_id(i) for i in range(1, 5)}, playlist_id(3): {user_id(1), user_id(3)}, playlist_id(5): {user_id(1), user_id(2)} } notifications_client = SpotifyPlaylistClient(config) market_to_items_map = notifications_client.get_market_to_updated_playlists_map( min_datetime=last_update_str, max_datetime=now_str, starred_playlists_to_users_map=playlists_to_users_map ) assert len(market_to_items_map) == 1 assert market in market_to_items_map items = market_to_items_map[market] items.sort(key=lambda x: (x.playlist_id, x.user_id)) expected_items = [ (playlist_id(1), user_id(1), before[0]), (playlist_id(1), user_id(2), before[0]), (playlist_id(3), user_id(1), before[1]), (playlist_id(3), user_id(3), before[1]), ] assert len(items) == len(expected_items) for i, item in enumerate(items): expected_item = expected_items[i] _playlist_id, _user_id, _date = expected_item assert item.playlist_id == _playlist_id assert item.user_id == _user_id assert item.date == load_datetime(_date) assert item.country_code == market @pytest.mark.parametrize( "market", ("us", None) ) @requests_mock.Mocker(kw="requests_mocker") def test_process_starred_playlists_updates_by_market(db_session, mocked_sqs, market, **kwargs): mock_user_data_requests(**kwargs) now = datetime.utcnow() now_str = dump_datetime(now) last_update_str = dump_datetime(now - timedelta(hours=2)) before = [dump_datetime(now - timedelta(minutes=(30 * i))) for i in range(1, 6)] # 0.5 1 1.5 2 2.5 after = [dump_datetime(now + timedelta(seconds=(2 * i))) for i in range(1, 4)] create_basic_structures( playlists_n=4, users_n=4, playlist_to_updates_dates={ 1: before[:2], 2: before[-2:] + after[:1], 3: (before[1], after[0]), 4: before[:3] }, market=market ) playlists_to_users_map = { playlist_id(1): {user_id(1), user_id(2)}, playlist_id(2): {user_id(i) for i in range(1, 5)}, playlist_id(3): {user_id(1), user_id(3)}, } notifications_client = SpotifyPlaylistClient(config) market_to_items_map = notifications_client.get_market_to_updated_playlists_map( min_datetime=last_update_str, max_datetime=now_str, starred_playlists_to_users_map=playlists_to_users_map ) # create existing messages existing_messages = {str(get_playlist_update_push_key( before[0], playlist_id(1), user_id(1), config.TOPIC, market, config.VENDOR))} # run it push_client = PushClient(logger=logger, sqs_client=mocked_sqs, config=config, db_session=db_session) process_starred_playlists_updates_by_market( count=1, market=market, playlists=market_to_items_map[market], existing_messages=existing_messages, notifications_client=notifications_client, push_client=push_client ) # test it expected_push_list = [ create_push_message_data( playlist_idx=playlist_user_date[0], user_idx=playlist_user_date[1], topic=config.TOPIC, country_code=market, title=config.TITLE, date=playlist_user_date[2], add_playlist_image=True ) for playlist_user_date in [ (1, 2, before[0]), (3, 1, before[1]), (3, 3, before[1]), ] ] expected_push_id_to_data_map = {p.pop('id'): p for p in expected_push_list} push_messages = db_session.query(PushMessage).all() push_messages_reduced = db_session.query(PushMessageReduced).all() verify_push_messages(expected_push_id_to_data_map, push_messages) verify_push_messages(expected_push_id_to_data_map, push_messages_reduced) @requests_mock.Mocker(kw="requests_mocker") def test_job(db_session, mocked_sqs, **kwargs): mock_user_data_requests(**kwargs) market_us, market_gb, null_market = 'gb', 'us', None now = datetime.utcnow() last_update_str = dump_datetime(now - timedelta(hours=2)) before = [dump_datetime(now - timedelta(minutes=(30 * i))) for i in range(1, 6)] # 0.5 1 1.5 2 2.5 after = [dump_datetime(now + timedelta(minutes=(2 * i))) for i in range(1, 4)] # mock user_data client user_data_mock = MagicMock() user_data_mock.parse_v1_mobile_settings.return_value = ( { market_us: {user_id(1), user_id(2)}, market_gb: {user_id(3)} }, {user_id(1): account_id(1), user_id(2): account_id(2), user_id(3): account_id(3)}, ) user_data_mock.parse_starred_playlists.return_value = ({ playlist_id(1): {user_id(1), user_id(2), user_id(3)}, playlist_id(2): {user_id(2), user_id(3), user_id(4)}, playlist_id(3): {user_id(1), user_id(3)}, playlist_id(4): {user_id(1), user_id(2), user_id(3)}, playlist_id(5): {user_id(1)} }, {playlist_id(1), playlist_id(2), playlist_id(3), playlist_id(4), playlist_id(5)}) # create db data create_basic_structures( playlists_n=5, users_n=4, playlist_to_updates_dates={ 1: before[:2], 2: before[-2:] + after[:1], 3: (before[1], after[0]), 4: before[:2], 5: before[:2] }, market_to_playlists_map={ market_us: range(1, 5), null_market: (5,) }, blacklisted_playlists=(4,), last_update_str=last_update_str ) # create existing push messages existing = [ factories.PushMessageReducedFactory.create( id=get_playlist_update_push_key( before[0], playlist_id(1), user_id(1), config.TOPIC, market_us, config.VENDOR), inner_id=1000, topic=TopicEnum.PLAYLIST_UPDATE, vendor=VendorEnum.SPOTIFY, date=before[0] ), factories.PushMessageReducedFactory.create( id=get_playlist_update_push_key( before[0], playlist_id(1), user_id(3), config.TOPIC, market_us, config.VENDOR), inner_id=1001, topic=TopicEnum.PLAYLIST_UPDATE, vendor=VendorEnum.SPOTIFY, date=before[0] ), ] db_session.commit() # run it push_client = PushClient(logger=logger, sqs_client=mocked_sqs, config=config, db_session=db_session) notifications_client = SpotifyPlaylistClient(config) _job( notifications_client=notifications_client, push_client=push_client, user_data_client=user_data_mock, blacklist_cache=lambda f: f ) # test it expected_us_push_list = [ create_push_message_data( playlist_idx=playlist_user_date[0], user_idx=playlist_user_date[1], topic=config.TOPIC, country_code=market_us, title=config.TITLE, date=playlist_user_date[2], add_playlist_image=True ) for playlist_user_date in [ (1, 2, before[0]), (3, 1, before[1]), (3, 3, before[1]), ] ] expected_null_push_list = [ create_push_message_data( playlist_idx=playlist_user_date[0], user_idx=playlist_user_date[1], topic=config.TOPIC, country_code=null_market, title=config.TITLE, date=playlist_user_date[2], add_playlist_image=True ) for playlist_user_date in [ (5, 1, before[0]), ] ] expected_push_list = expected_us_push_list + expected_null_push_list expected_push_id_to_data_map = {p.pop('id'): p for p in expected_push_list} push_messages = db_session.query(PushMessage).all() created_push_messages_reduced = db_session.query(PushMessageReduced).filter(PushMessageReduced.inner_id.notin_( [p.inner_id for p in existing] )).all() verify_push_messages(expected_push_id_to_data_map, push_messages) verify_push_messages(expected_push_id_to_data_map, created_push_messages_reduced) current_update = db_session.query(ApolloKeyValueStorage) \ .filter(ApolloKeyValueStorage.key == config.LAST_RUN_DATE_KEY).first() assert current_update is not None assert current_update.value > last_update_str @requests_mock.Mocker(kw="requests_mocker") def test_job_first_run(db_session, mocked_sqs, **kwargs): mock_user_data_requests(**kwargs) market, market_gb = 'us', 'gb' now = datetime.utcnow() before = [dump_datetime(now - timedelta(minutes=(30 * i))) for i in range(1, 6)] # 0.5 1 1.5 2 2.5 after = [dump_datetime(now + timedelta(minutes=(2 * i))) for i in range(1, 4)] # mock user_data client user_data_mock = MagicMock() user_data_mock.parse_v1_mobile_settings.return_value = ( { market: {user_id(1), user_id(2)}, market_gb: {user_id(3)} }, {user_id(1): account_id(1), user_id(2): account_id(2), user_id(3): account_id(3)}, ) user_data_mock.parse_starred_playlists.return_value = ({ playlist_id(1): {user_id(1), user_id(2), user_id(3)}, playlist_id(2): {user_id(2), user_id(3), user_id(4)}, playlist_id(3): {user_id(1), user_id(3)}, playlist_id(4): {user_id(1), user_id(2), user_id(3)} }, {playlist_id(1), playlist_id(2), playlist_id(3), playlist_id(4)}) # create db data create_basic_structures( playlists_n=4, users_n=4, playlist_to_updates_dates={ 1: before[:2], 2: after[:1], 3: (before[1], after[0]), 4: before[:2] }, market=market, blacklisted_playlists=(4,), ) # create existing push messages existing = [ factories.PushMessageReducedFactory.create( id=get_playlist_update_push_key(before[0], playlist_id(1), user_id(1), config.TOPIC, market, config.VENDOR), inner_id=1000, topic=TopicEnum.PLAYLIST_UPDATE, vendor=VendorEnum.SPOTIFY, date=before[0] ), factories.PushMessageReducedFactory.create( id=get_playlist_update_push_key(before[0], playlist_id(1), user_id(3), config.TOPIC, market, config.VENDOR), inner_id=1001, topic=TopicEnum.PLAYLIST_UPDATE, vendor=VendorEnum.SPOTIFY, date=before[0] ), ] db_session.commit() # run it push_client = PushClient(logger=logger, sqs_client=mocked_sqs, config=config, db_session=db_session) notifications_client = SpotifyPlaylistClient(config) _job( notifications_client=notifications_client, push_client=push_client, user_data_client=user_data_mock, blacklist_cache=lambda f: f ) # test it expected_push_list = [ create_push_message_data( playlist_idx=playlist_user_date[0], user_idx=playlist_user_date[1], topic=config.TOPIC, country_code=market, title=config.TITLE, date=playlist_user_date[2], add_playlist_image=True ) for playlist_user_date in [ (1, 2, before[0]), (3, 1, before[1]), (3, 3, before[1]), ] ] expected_push_id_to_data_map = {p.pop('id'): p for p in expected_push_list} push_messages = db_session.query(PushMessage).all() created_push_messages_reduced = db_session.query(PushMessageReduced).filter(PushMessageReduced.inner_id.notin_( [p.inner_id for p in existing] )).all() verify_push_messages(expected_push_id_to_data_map, push_messages) verify_push_messages(expected_push_id_to_data_map, created_push_messages_reduced) current_update = db_session.query(ApolloKeyValueStorage) \ .filter(ApolloKeyValueStorage.key == config.LAST_RUN_DATE_KEY).first() assert current_update is not None assert current_update.value >= dump_datetime(now)