from datetime import datetime from unittest.mock import MagicMock import requests_mock from apollo_notifications.constants import SONY_CATEGORY_ID, SPOTIFY_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 ApplePlaylistClient 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, market_gb = "us", "gb" 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, market_to_playlists_to_updates_dates={ market_us: {1: before[:2], 2: before[-2:] + after[:1], 3: (before[1], after[0]), 4: before[:3]}, market_gb: { 1: before[:2], 3: before[-2:], 5: (before[1], after[0]), }, }, category_id_to_playlists_map={SONY_CATEGORY_ID: [1, 2], SPOTIFY_CATEGORY_ID: [5], APPLE_CATEGORY_ID: [3, 4]}, ) market_to_starred_playlists_to_users_map = { market_us: { 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)}, }, market_gb: {playlist_id(3): {user_id(i) for i in range(1, 5)}, playlist_id(5): {user_id(1)}}, } notifications_client = ApplePlaylistClient(config) market_to_items_map = notifications_client.get_market_to_updated_playlists_map( min_datetime=last_update_str, max_datetime=now_str, market_to_playlists_to_users_map=market_to_starred_playlists_to_users_map, ) assert len(market_to_items_map) == 1 assert market_us in market_to_items_map items = market_to_items_map[market_us] 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_us @requests_mock.Mocker(kw="requests_mocker") def test_process_starred_playlists_updates_by_market(db_session, mocked_sqs, **kwargs): mock_user_data_requests(**kwargs) market_us, market_gb = "us", "gb" 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, market_to_playlists_to_updates_dates={ market_us: {1: before[:2], 2: before[-2:] + after[:1], 3: (before[1], after[0]), 4: before[:3]}, market_gb: { 1: before[:2], 3: before[-2:], 5: (before[1], after[0]), }, }, category_id_to_playlists_map={SONY_CATEGORY_ID: [1, 2], SPOTIFY_CATEGORY_ID: [5], APPLE_CATEGORY_ID: [3, 4]}, ) market_to_starred_playlists_to_users_map = { market_us: { 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)}, }, market_gb: {playlist_id(3): {user_id(i) for i in range(1, 5)}, playlist_id(5): {user_id(1)}}, } notifications_client = ApplePlaylistClient(config) market_to_items_map = notifications_client.get_market_to_updated_playlists_map( min_datetime=last_update_str, max_datetime=now_str, market_to_playlists_to_users_map=market_to_starred_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_us, 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_us, playlists=market_to_items_map[market_us], 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_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_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 = "gb", "us" 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(3)}, market_gb: {user_id(1), user_id(2), 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 = ( { market_us: { 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)}, }, market_gb: {playlist_id(3): {user_id(i) for i in range(1, 5)}, 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, market_to_playlists_to_updates_dates={ market_us: {1: before[:2], 2: before[-2:] + after[:1], 3: (before[1], after[0]), 4: before[:3]}, market_gb: { 1: before[:2], 3: before[-2:], 5: (before[1], after[0]), }, }, category_id_to_playlists_map={SONY_CATEGORY_ID: [1, 2], SPOTIFY_CATEGORY_ID: [5], APPLE_CATEGORY_ID: [3, 4]}, 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.APPLE, 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.APPLE, 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 = ApplePlaylistClient(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_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_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