"""Helper functions.""" from collections import defaultdict from copy import deepcopy from oto.response import Response from backend.constants import track_field as tf from tests.testutils.seed.languages_seed import language_code_to_id_map from tests.testutils.seed.languages_seed import language_id_to_code_map def wrap_api_results(data_list): """Wrap list of data into standard API message.""" return { 'items': data_list, 'pagination': { 'type': 'none', 'total_records': len(data_list) } } def create_track_update_request(track_data, update_data): """Make track update request data using track data for defaults.""" request_data = deepcopy(track_data) request_data.pop(tf.VOLUME_NUMBER) request_data.pop(tf.TRACK_NUMBER) request_data.pop(tf.PUBLISHERS) request_data.update(deepcopy(update_data)) if tf.ARTISTS not in update_data: # Remove ids for artist data for item in request_data[tf.ARTISTS]: item.pop(tf.TRACK_ARTIST_ID) if tf.WRITERS not in update_data: # Remove ids for writers data for item in request_data[tf.WRITERS]: item.pop(tf.TRACK_WRITER_ID) return request_data def make_localization_dict( tuid, language_id, track_name='', version='', artists=[]): """Create localization dict using data seed. The language_id field must be included in the data dict. """ return { tf.TUID: tuid, tf.LANGUAGE_ID: language_id, tf.TRACK_NAME: track_name, tf.VERSION: version, tf.ARTISTS: artists } def lookup_language_id(language_code): """Lookup language_id using a language_code.""" return language_code_to_id_map[language_code] def lookup_language_code(language_id): """Lookup language_code using a language_id.""" return language_id_to_code_map[language_id] def make_switch_tracks_arrgt(track1, track2): """Create switch track arrangement.""" return [ { tf.TUID: track1[tf.TUID], tf.VOLUME_NUMBER: track2[tf.VOLUME_NUMBER], tf.TRACK_NUMBER: track2[tf.TRACK_NUMBER] }, { tf.TUID: track2[tf.TUID], tf.VOLUME_NUMBER: track1[tf.VOLUME_NUMBER], tf.TRACK_NUMBER: track1[tf.TRACK_NUMBER] }] def copy_response(res): """Copy response and response message.""" message = res.message if message: message = deepcopy(res.message) return Response( message=message, errors=res.errors, status=res.status) def make_track_logic_response(data): """Make expected logic response.""" wrap_results = False if isinstance(data, dict) and 'items' in data: data = data['items'] wrap_results = True if isinstance(data, list): data = [make_track_logic_response(track) for track in data] else: data = deepcopy(data) if tf.LOCALIZATIONS not in data: data[tf.LOCALIZATIONS] = [] for field in ( tf.PUBLISHERS, tf.US_PUBLISHING_OBLIGATION, tf.THIRD_PARTY_PUBLISHER): if field in data: del data[field] if wrap_results: return wrap_api_results(data) else: return data def validate_track_response(track_res): """Validate track logic response contains standard fields. The logical response should have all the basic track metadata except for fields for publishing obligation. Artists and writers are also included. """ if 'items' in track_res: error_msg = '' for track in track_res['items']: error_msg = error_msg or validate_track_response(track) return error_msg track = track_res if tf.PUBLISHERS in track: return 'Publisher data cannot be included' if tf.ARTISTS not in track: return 'Artist data is missing' if tf.WRITERS not in track: return 'Writer data is missing' for field in tf.BASIC_MODEL_FIELDS: if field in (tf.US_PUBLISHING_OBLIGATION, tf.THIRD_PARTY_PUBLISHER): if field in track: return 'Publishing Obligation data cannot be in track data' elif field not in track: return 'Field {} is missing'.format(field) return '' def validate_track_is_copy(src_track, dest_track, exclude_fields=[]): """Validate destination track is a copy of source track.""" skip_fields = (tf.VOLUME_NUMBER, tf.TRACK_NUMBER,) + tuple(exclude_fields) for field in tf.BASIC_MODEL_FIELDS: if field in (tf.TUID, tf.PRODUCT_ID, tf.UPC,): if src_track[field] == dest_track[field]: return False, 'Field {} should not match'.format(field) elif field not in skip_fields: if src_track[field] != dest_track[field]: return False, 'Field {} should match'.format(field) for field in (tf.ARTISTS, tf.PUBLISHERS, tf.WRITERS,): if field in exclude_fields: continue src_set = { (field['type'], field['name'],) for field in src_track[field]} dest_set = { (field['type'], field['name'],) for field in dest_track[field]} if src_set != dest_set: return False, '{} field do not match'.format(field) return True, '' def validate_artists_are_same(response_artists, request_artists): """Validate response artists are the same as the request artists.""" assert len(response_artists) == len(request_artists) next_request_artist = iter(request_artists) for response_artist in response_artists: request_artist = next(next_request_artist) assert tf.TRACK_ARTIST_ID in response_artist assert response_artist[tf.TYPE] == request_artist[tf.TYPE] assert response_artist[tf.NAME] == request_artist[tf.NAME] def validate_writers_are_same(response_writers, request_writers): """Validate response writers are the same as the request writers.""" assert len(response_writers) == len(request_writers) next_request_writer = iter(request_writers) for response_writer in response_writers: request_writer = next(next_request_writer) assert tf.TRACK_WRITER_ID in response_writer assert response_writer[tf.TYPE] == request_writer[tf.TYPE] assert response_writer[tf.NAME] == request_writer[tf.NAME] def validate_localizations_copy_result(track_dict, new_localizations): """Validate localizations match. This accounts for differences in tuids and track_artist_ids. """ new_localizations_map = { loc[tf.LANGUAGE_ID]: loc for loc in new_localizations} founds_languages_set = { loc[tf.LANGUAGE_ID] for loc in track_dict[tf.LOCALIZATIONS]} assert founds_languages_set == set(new_localizations_map.keys()), \ 'Localized languages must match' for track_loc in track_dict[tf.LOCALIZATIONS]: new_loc = new_localizations_map[track_loc[tf.LANGUAGE_ID]] assert track_loc[tf.TUID] == track_dict[tf.TUID] assert track_loc[tf.TRACK_NAME] == new_loc[tf.TRACK_NAME] assert track_loc[tf.VERSION] == new_loc[tf.VERSION] assert len(track_loc[tf.ARTISTS]) == len(new_loc[tf.ARTISTS]) track_artist_types = defaultdict(int) for artist in track_loc[tf.ARTISTS]: track_artist_types[artist[tf.TYPE]] += 1 new_localizations_types = defaultdict(int) for artist in new_loc[tf.ARTISTS]: new_localizations_types[artist[tf.TYPE]] += 1 assert track_artist_types == new_localizations_types, \ 'Number of localizations with same artist type must match' def validate_artists_persister_response( before_apply_response, names_list, deltas_list, role_type): """Helper to validate role response. Since new ids are generated and the logic is a bit complex, use this function to evaluate track persister response is valid. This does not validate localizations. """ lookup = { track[tf.TUID]: track[tf.ARTISTS] for track in before_apply_response['items']} for delta in deltas_list: tuid = delta[tf.TUID] role_field_list = delta[tf.ARTISTS] other_previous = [ role for role in lookup[tuid] if role[tf.TYPE] != role_type] other_new = [ role for role in role_field_list if role[tf.TYPE] != role_type] assert other_previous == other_new, \ 'Other {} roles should be the same'.format(tf.ARTISTS) updated_names = [ role[tf.NAME] for role in role_field_list if role[tf.TYPE] == role_type] assert updated_names == names_list, 'Names for new roles must match'