import copy from dataclasses import asdict from datetime import datetime, timedelta import pytest from apollo_utils.job.clients.base.client import BaseApiClient import charts_messages.schemas as schemas import charts_messages.utils as utils from charts_messages.client.utils import get_event_by_id from charts_messages.handler import handler from charts_messages.processings.get_accounts_to_notify import get_users_to_notify, filter_users_to_notify_by_settings from tests.helpers import create_incoming_event, get_full_event_with_data, get_accounts_nested_data_by_isrc, \ AccountSettingsNestedData, AccountSettingsDataNestedVendors, get_fake_notification_settings_v2, \ get_fake_user_accounts_with_devices from charts_messages.utils import deserialize_event, check_received_event, get_tracks_to_process @pytest.mark.parametrize( "event,expected_result", ( ({"created_at": datetime.utcnow() - timedelta(seconds=5), "ttl": 500}, True), ({"created_at": datetime.utcnow() - timedelta(seconds=10), "ttl": 1}, False), ), ) def test_check_received_event_ttl(event, expected_result): result = check_received_event(event) assert result == expected_result @pytest.mark.parametrize( "ttl,created_at,expected_result", ( (10, datetime.utcnow().isoformat(), True), (1, (datetime.utcnow() - timedelta(seconds=10)).isoformat(), False), ), ) def test_check_received_event(test_logger, ttl, created_at, expected_result): incoming_event = create_incoming_event(event_ttl=ttl, event_created_at=created_at) # check if the schema for incoming event works correct: event = deserialize_event( logger=test_logger, schema=schemas.IncomingEvent(), event=incoming_event ) result = check_received_event(event) assert result == expected_result def get_url(request): url = request.url index = url.find("api/") return url[index:] @pytest.mark.parametrize( "event_id", ( 123, ), ) def test_get_event_by_id(mocker, event_id): event = get_full_event_with_data(event_id=str(event_id)) def client_mock(*args, **kwargs): url = get_url(args[0]) if url == "api/service/events/": return event mocker_call = mocker.patch.object(BaseApiClient, "_send_request", side_effect=client_mock) result = get_event_by_id(event_id=event_id) assert mocker_call.call_count == 1 assert result["id"] == str(event_id) @pytest.mark.parametrize( "event_type", ( "additions", "removals", "moves", ), ) def test_get_tracks_to_process(mocker, event_type): def fake_key(): return event_type mocker.patch.object(utils, "get_changelog_key", fake_key) event = get_full_event_with_data() result_to_compare = event["data"]["changelog"][event_type] result = get_tracks_to_process(event) assert result == result_to_compare @pytest.mark.parametrize( "changelog,keys_length", ( ("additions", 5), ("removals", 5), ("moves", 10) ) ) def test_get_users_to_notify(mocker, test_logger, changelog, keys_length): def fake_key(): return changelog mocker.patch.object(utils, "get_changelog_key", fake_key) event = get_full_event_with_data() changelog_tracks = get_tracks_to_process(event) isrc_track_mapping = utils.get_tracks_asdict(changelog_tracks) assert len(isrc_track_mapping.keys()) == keys_length chart = utils.get_event_chart_raw_info(event) accounts = get_accounts_nested_data_by_isrc(isrc_track_mapping.keys()) def client_mock(*args, **kwargs): url = get_url(args[0]) if url == "api/service/users/favorites/list/": return {"data": accounts} accounts_for_expected_result = copy.deepcopy(accounts) expected_user_accounts_to_notify = filter_users_to_notify_by_settings( test_logger, chart, accounts_for_expected_result ) mocker.patch.object(BaseApiClient, "_send_request", side_effect=client_mock) user_accounts_to_notify = get_users_to_notify(test_logger, chart, list(isrc_track_mapping.keys())) assert user_accounts_to_notify == expected_user_accounts_to_notify @pytest.mark.parametrize( "settings_version,expected_result", ( ("1", utils.handle_user_notification_settings_v1), ("2", utils.handle_user_notification_settings_v2), ) ) def test_get_user_settings_handler(settings_version, expected_result): handler = utils.get_user_settings_handler(settings_version) assert handler == expected_result @pytest.mark.parametrize( "vendors,markets,notifications,chart_meta_dsp,chart_meta_country_code,expected_result", ( ({"apple": True, "spotify": True}, ["global"], True, "spotify", "global", True), ({"apple": True, "spotify": True}, ["global"], True, "apple", "us", False), ({"apple": True, "spotify": False}, ["global"], True, "spotify", "us", False), ({"apple": False, "spotify": True}, ["global"], True, "apple", "us", False), ({"apple": True, "spotify": True}, ["global"], False, "spotify", "us", False), ({"apple": True, "spotify": True}, ["global"], False, "apple", "us", False), ({"apple": True, "spotify": True}, ["uk"], True, "spotify", "us", False), ({"apple": True, "spotify": True}, ["uk"], True, "apple", "us", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "spotify", "us", True), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "apple", "de", True), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "spotify", "nz", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "apple", "nz", False), ) ) def test_handle_user_notification_settings_v1( vendors, markets, notifications, chart_meta_dsp, chart_meta_country_code, expected_result ): settings = asdict( AccountSettingsNestedData( vendors=AccountSettingsDataNestedVendors(**vendors), notifications=notifications, markets=markets ) ) event = create_incoming_event(meta_dsp=chart_meta_dsp, meta_country_code=chart_meta_country_code) chart = utils.get_event_chart_raw_info(event) result = utils.handle_user_notification_settings_v1(chart=chart, user_settings_data=settings) assert result == expected_result @pytest.mark.parametrize( "categories,markets,notifications,chart_meta_dsp,chart_meta_country_code,expected_result", ( ({"apple": True, "spotify": True}, ["global"], True, "spotify", "global", True), ({"apple": True, "spotify": True}, ["global"], True, "apple", "us", False), ({"apple": True, "spotify": False}, ["global"], True, "spotify", "us", False), ({"apple": False, "spotify": True}, ["global"], True, "apple", "us", False), ({"apple": True, "spotify": True}, ["global"], False, "spotify", "us", False), ({"apple": True, "spotify": True}, ["global"], False, "apple", "us", False), ({"apple": True, "spotify": True}, ["uk"], True, "spotify", "us", False), ({"apple": True, "spotify": True}, ["uk"], True, "apple", "us", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "spotify", "us", True), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "apple", "de", True), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "spotify", "nz", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], True, "apple", "nz", False), ({"apple": False, "spotify": False}, ["uk", "fr", "us", "de"], True, "spotify", "us", False), ({"apple": False, "spotify": False}, ["uk", "fr", "us", "de"], True, "apple", "de", False), ({"apple": False, "spotify": False}, ["uk", "fr", "us", "de"], True, "spotify", "nz", False), ({"apple": False, "spotify": False}, ["uk", "fr", "us", "de"], True, "apple", "nz", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], False, "spotify", "us", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], False, "apple", "de", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], False, "spotify", "nz", False), ({"apple": True, "spotify": True}, ["uk", "fr", "us", "de"], False, "apple", "nz", False), ) ) def test_handle_user_notification_settings_v2( categories, markets, notifications, chart_meta_dsp, chart_meta_country_code, expected_result ): settings = get_fake_notification_settings_v2(categories, markets, notifications) event = create_incoming_event(meta_dsp=chart_meta_dsp, meta_country_code=chart_meta_country_code) chart = utils.get_event_chart_raw_info(event) result = utils.handle_user_notification_settings_v2(chart=chart, user_settings_data=settings) assert result == expected_result @pytest.mark.parametrize( "messages,expected_ok,expected_failed", ( ({"ok": [{}, {}], "failed": [{}]}, 2, 1), ({"ok": [{}, {}, {}]}, 3, 0), ) ) def test_get_messages_status_length(messages, expected_ok, expected_failed): ok, failed = utils.get_messages_status_length(messages) assert ok == expected_ok assert failed == expected_failed @pytest.mark.parametrize( "event_type,ttl,created_at", ( ("additions", 10000, datetime.utcnow().isoformat()), ("removals", 10000, datetime.utcnow().isoformat()), ("moves", 10000, datetime.utcnow().isoformat()) ), ) def test_handler(mocker, test_logger, event_type, ttl, created_at): def fake_key(): return event_type def fake_message_meta(): return "message_meta" mocker.patch.object(utils, "get_changelog_key", fake_key) mocker.patch.object(utils, "get_message_meta_type", fake_message_meta) event = create_incoming_event(event_ttl=ttl, event_created_at=created_at) mocker.patch.object(utils, "handle_diff_source_events", event) api_service_events = get_full_event_with_data(event=event) # we use copy of full event with data to create further dependant responses api_service_events_deepcopy = copy.deepcopy(api_service_events) tracks = get_tracks_to_process(api_service_events_deepcopy) isrc_track_mapping = {track["track"]["isrc"]: track for track in tracks} accounts = get_accounts_nested_data_by_isrc(isrc_track_mapping.keys()) def client_mock(*args, **kwargs): url = get_url(args[0]) if url == "api/service/events/": return api_service_events if url == "api/service/users/favorites/list/": return {"data": accounts} if url == "api/service/messages/": return {"ok": [{}], "failed": [{}]} mocker.patch.object(BaseApiClient, "_send_request", side_effect=client_mock) handler(logger=test_logger, event=[event]) @pytest.mark.parametrize( "event_type,ttl,created_at", ( ("update", 10000, datetime.utcnow().isoformat()), ), ) def test_update_handler(mocker, test_logger, event_type, ttl, created_at): from charts_messages.handlers.update import update_handler event = create_incoming_event(event_ttl=ttl, event_created_at=created_at) api_service_events = get_full_event_with_data(event=event) accounts = get_fake_user_accounts_with_devices() def client_mock(*args, **kwargs): url = get_url(args[0]) if url == "api/service/events/": return api_service_events if url == "api/service/users/settings/list/": return {"data": accounts} if url == "api/service/messages/": return {"ok": [{}], "failed": [{}]} mocker.patch.object(BaseApiClient, "_send_request", side_effect=client_mock) update_handler(logger=test_logger, events=[event])