"""Tests for localizations utils.""" import collections from copy import deepcopy from oto import response import pytest from backend.constants import api as api_consts from backend.constants import track_field as tf from backend.models import ows_product from backend.utils import localization as loc_util from tests.testutils import mocks from tests.testutils.functions import make_localization_dict from tests.testutils.functions import wrap_api_results from tests.testutils.seed.languages_seed import languages_seed_data def test_add_track_localizations_to_response( mocker, test_track_response, test_track_model_response, track_localization_get_data): """Test add track localizations.""" mocks.ows_product_get_track_localizations( mocker, track_localization_get_data) result = loc_util.add_track_localizations_to_response( response.Response(test_track_model_response)) assert result.message == test_track_response def test_add_track_localizations_to_response_no_tracks( mocker, test_product_id): """Test track response is preserved if there are no tracks.""" empty_msg = { api_consts.ITEMS: []} result = loc_util.add_track_localizations_to_response( response.Response(message=empty_msg)) assert result.message == empty_msg def test_create_updates_for_track_localizations_version_field( mocker, track_factory): """Test track localizations are updated.""" lang_2_localization = make_localization_dict( tuid=1, language_id=2, track_name='name', version='red', artists=[{ tf.TRACK_ARTIST_ID: 100, tf.TYPE: 'performer', tf.NAME: 'artist name'}]) track = track_factory(version='remix') original_track = track.to_dict() original_track[tf.LOCALIZATIONS] = [ deepcopy(lang_2_localization), make_localization_dict(tuid=1, language_id=3, version='green'), make_localization_dict(tuid=1, language_id=4, version='blue')] expected_track = track.to_dict() expected_track[tf.LOCALIZATIONS] = [ deepcopy(lang_2_localization), make_localization_dict(tuid=1, language_id=3, version=''), make_localization_dict(tuid=1, language_id=4, version='blue2'), make_localization_dict(tuid=1, language_id=5, version='orange')] localizations_map = { 2: {'tuid': 1, 'language_id': 2, 'version': 'red'}, 4: {'tuid': 1, 'language_id': 4, 'version': 'blue2'}, 5: {'tuid': 1, 'language_id': 5, 'version': 'orange'} } mocks.ows_product_get_localization_languages(mocker) updated_track = loc_util.create_updates_for_track_localizations( original_track, localizations_map) assert updated_track == expected_track def test_create_updates_for_track_new_artist_localizations( mocker, track_factory): """Test track artist localizations are added. Track with no existing localizations has new artist localization added. """ LANG_ID = 2 NEW_ARTIST_NAME = 'Hoofy' # Generate track input data track = track_factory(performer__count=1) artist = track.artists[0] track_dict = track.to_dict() track_dict[tf.LOCALIZATIONS] = [] # Transformation Data localizations_delta_map = { LANG_ID: { tf.LANGUAGE_ID: LANG_ID, 'performer_names': [NEW_ARTIST_NAME]} } # Expected Output Data artist.name = NEW_ARTIST_NAME expected_localization = make_localization_dict( track.tuid, LANG_ID, artists=[artist.to_dict()]) expected_track = track_dict.copy() expected_track[tf.LOCALIZATIONS] = [expected_localization.copy()] mocks.ows_product_get_localization_languages(mocker) updated_track = loc_util.create_updates_for_track_localizations( track_dict, localizations_delta_map) assert updated_track == expected_track def test_create_updates_for_track_existing_artist_localizations( mocker, track_factory): """Test track localizations are updated. A localized artist name is updated with a new value. """ # Generate track input data LANG_ID = 2 NEW_ARTIST_NAME = 'Hoofy' # Generate track input data track = track_factory(performer__count=1) artist = track.artists[0] existing_localization = make_localization_dict( track.tuid, LANG_ID, artists=[artist.to_dict()]) track_dict = track.to_dict() track_dict[tf.LOCALIZATIONS] = [existing_localization] # Transformation Data localizations_delta_map = { LANG_ID: { tf.LANGUAGE_ID: LANG_ID, 'performer_names': [NEW_ARTIST_NAME]} } # Expected Output Data artist.name = NEW_ARTIST_NAME expected_localization = make_localization_dict( track.tuid, LANG_ID, artists=[artist.to_dict()]) expected_track = track_dict.copy() expected_track[tf.LOCALIZATIONS] = [expected_localization] mocks.ows_product_get_localization_languages(mocker) updated_track = loc_util.create_updates_for_track_localizations( track_dict, localizations_delta_map) assert updated_track == expected_track def test_create_updates_for_track_clear_artist_localizations( mocker, track_factory): """Test track localization is cleared. This happens if the field is updated, but the track localization isn't specified. Localizations for different artist types are also included to make sure they are not modified or omitted. """ # Generate track input data LANG_ID = 2 OTHER_LANG_ID = 3 NEW_ARTIST_NAME = 'Hoofy' # Generate track input data track = track_factory( performer__count=1, featuring__count=1, remixer__count=1) artist = track.artists[0] artist_dict = artist.to_dict() other_artist_dict = track.artists[1].to_dict() other_artist2_dict = track.artists[2].to_dict() existing_localizations = [ make_localization_dict( track.tuid, LANG_ID, artists=[ artist_dict.copy(), other_artist_dict.copy(), other_artist2_dict.copy()]), make_localization_dict( track.tuid, OTHER_LANG_ID, artists=[artist_dict.copy(), other_artist2_dict.copy()]) ] track_dict = track.to_dict() track_dict[tf.LOCALIZATIONS] = existing_localizations # Transformation Data localizations_delta_map = { OTHER_LANG_ID: { tf.LANGUAGE_ID: OTHER_LANG_ID, '{}_names'.format(artist.artist_type): [NEW_ARTIST_NAME] } } # Expected Output Data artist.name = NEW_ARTIST_NAME expected_localizations = [ make_localization_dict( track.tuid, LANG_ID, artists=[ other_artist_dict.copy(), other_artist2_dict.copy()]), make_localization_dict( track.tuid, OTHER_LANG_ID, artists=[artist.to_dict(), other_artist2_dict.copy()]) ] expected_track = track_dict.copy() expected_track[tf.LOCALIZATIONS] = deepcopy(expected_localizations.copy()) mocks.ows_product_get_localization_languages(mocker) updated_track = loc_util.create_updates_for_track_localizations( track_dict, localizations_delta_map) assert updated_track == expected_track def test_create_updates_for_track_with_duplicate_localizations( mocker, track_factory): """Test localization is ignored when same language as track metalanguage. Track with no existing localizations has new artist localization added. """ LANG_ID = 12 NEW_ARTIST_NAME = 'Hoofy' # Generate track input data track = track_factory(performer__count=1) track_dict = track.to_dict() track_dict[tf.LOCALIZATIONS] = [] mocks.ows_product_get_localization_languages(mocker) # Transformation Data localizations_delta_map = { LANG_ID: { tf.LANGUAGE_ID: LANG_ID, 'performer_names': [NEW_ARTIST_NAME]} } updated_track = loc_util.create_updates_for_track_localizations( track_dict, localizations_delta_map) assert updated_track[tf.LOCALIZATIONS] == [] def test_create_updates_for_track_localizations_bad_map(mocker): """Test track localizations raises exception on bad map.""" original_track = { tf.TUID: 1, tf.META_LANGUAGE_CODE: 'ENG', tf.VERSION: 'remix', tf.LOCALIZATIONS: [ make_localization_dict( tuid=1, language_id=3, version='green'), make_localization_dict( tuid=1, language_id=4, version='blue')] } localizations_map = { 2: {tf.LANGUAGE_ID: 2, tf.NAME: 'name'}, 4: {tf.LANGUAGE_ID: 4, tf.VERSION: 'version'} } mocks.ows_product_get_localization_languages(mocker) with pytest.raises(ValueError): loc_util.create_updates_for_track_localizations( original_track, localizations_map) def test_update_track_localization_response_artist_sorted( track_factory): """Test track artist localizations are sorted by track_artist_id.""" track = track_factory(performer__count=3).to_dict() track_localizations = [{ 'tuid': 100, 'language_id': 1, 'track_name': '', 'version': 'Radio edit PL', 'artists': {3: 'Three', 2: 'Two', 1: 'One'}}] updated_track = track.copy() updated_localization = deepcopy(track_localizations) updated_localization[0]['artists'] = [ {'track_artist_id': 1, 'type': 'performer', 'name': 'One'}, {'track_artist_id': 2, 'type': 'performer', 'name': 'Two'}, {'track_artist_id': 3, 'type': 'performer', 'name': 'Three'} ] updated_track['localizations'] = updated_localization loc_util._update_track_localization_response( track, track_localizations) assert track == updated_track def test_update_track_localization_response_empty_artists(): """Test track localizations are updated correctly.""" track = {'tuid': 100, 'version': 'super meow remix', 'artists': []} track_localizations = [{ 'tuid': 100, 'language_id': 1, 'track_name': '', 'version': 'Radio edit PL', 'artists': {}}] updated_track = track.copy() updated_localization = deepcopy(track_localizations) updated_localization[0]['artists'] = [] updated_track['localizations'] = updated_localization loc_util._update_track_localization_response( track, track_localizations) assert track == updated_track def test_update_track_localization_response_no_artists(): """Verify exception is raised if no track artists data is provided.""" track = {'tuid': 100, 'version': 'super meow remix'} track_localizations = [{ 'tuid': 100, 'language_id': 1, 'track_name': '', 'version': 'Radio edit PL', 'artists': {}}] with pytest.raises(ValueError): loc_util._update_track_localization_response( track, track_localizations) def test_copy_multiple_localizations( mocker, tds_copy_localization_single_track_no_artist): """Test localizations are copied and persisted to destination track.""" _test_copy_multiple_localizations( mocker, tds_copy_localization_single_track_no_artist) def test_copy_multiple_localizations_with_artists( mocker, tds_copy_localization_single_track_with_artists): """Test localizations are copied and persisted to destination track.""" _test_copy_multiple_localizations( mocker, tds_copy_localization_single_track_with_artists) def _test_copy_multiple_localizations(mocker, tds): """Generic test for track with multiple localizations.""" mocks.ows_product_get_track_localizations( mocker, wrap_api_results(tds['source_localizations'])) mocks.put_response(mocker) spy = mocker.spy(ows_product, 'update_localizations') res = loc_util.copy_tracks_localizations(tds['source_dest_track_list']) assert res.status == 200 updated_dest_track_dict = res.message[api_consts.ITEMS][0] tds['validate_copied_track'](updated_dest_track_dict) assert spy.call_args[0][0] == updated_dest_track_dict[tf.LOCALIZATIONS] def test_copy_localizations_no_artists( tds_copy_localization_single_track_no_artist): """Test localizations are copied to destination track.""" tds = tds_copy_localization_single_track_no_artist tds['source_track'][tf.LOCALIZATIONS] = tds['source_localizations'] updated_dest_track_dict = loc_util._copy_localizations( from_track=tds['source_track'], to_track=tds['destination_track']) tds['validate_copied_track'](updated_dest_track_dict) def test_copy_localizations_with_artists( tds_copy_localization_single_track_with_artists): """Test localizations are copied to destination track with artist.""" tds = tds_copy_localization_single_track_with_artists tds['source_track'][tf.LOCALIZATIONS] = tds['source_localizations'] updated_dest_track_dict = loc_util._copy_localizations( from_track=tds['source_track'], to_track=tds['destination_track']) tds['validate_copied_track'](updated_dest_track_dict) def test_copy_track_localizations_with_ows_product_get_error( mocker, tds_copy_localization_single_track_no_artist, mock_app): """Non-200 response from ows_product.get_track_localizations is handled.""" tds = tds_copy_localization_single_track_no_artist mocker.patch( 'backend.models.ows_product.get_track_localizations', return_value=response.create_not_found_response()) g_mock = mocker.patch('backend.utils.localization.g') with mock_app: copy_localizations_response = loc_util.copy_tracks_localizations( tds['source_dest_track_list']) assert not copy_localizations_response assert g_mock.log.error.called def test_copy_track_localizations_with_ows_product_update_error( mocker, tds_copy_localization_single_track_no_artist, mock_app): """Non-200 response from ows_product.update_localizations is handled.""" tds = tds_copy_localization_single_track_no_artist mocks.ows_product_get_track_localizations( mocker, wrap_api_results(tds['source_localizations'])) mocker.patch( 'backend.models.ows_product.update_localizations', return_value=response.create_not_found_response()) g_mock = mocker.patch('backend.utils.localization.g') with mock_app: copy_localizations_response = loc_util.copy_tracks_localizations( tds['source_dest_track_list']) assert not copy_localizations_response assert g_mock.log.error.called def test_group_localizations_by_tuid(): """Test localizations are grouped into {tuid: localizations_list}.""" localizations = [ {'tuid': 1, 'language_id': 1}, {'tuid': 1, 'language_id': 2}, {'tuid': 2, 'language_id': 1}, {'tuid': 2, 'language_id': 2}] expected = collections.defaultdict(list) for loc in localizations: expected[loc[tf.TUID]].append(loc) result = loc_util._group_localizations_by_tuid(localizations) assert result == expected def test_get_localization_languages(mocker): """Test for getting localization languages.""" mocks.ows_product_get_localization_languages(mocker) result = loc_util.get_localization_languages() assert len(result) == len(languages_seed_data) @pytest.fixture(params=[ (1, 'AFR'), (2, 'ARA'), (3, 'BUL') ]) def language_ids_codes(request): """Fixture for language ids and code.""" return request.param @pytest.fixture(params=[ (9999, None), (-2, None), (None, None) ]) def language_ids_codes_invalid(request): """Fixture for invalid language ids and code.""" return request.param @pytest.fixture(params=[ (None, 'afafa'), (None, '') ]) def language_codes_ids_invalid(request): """Fixture for invalid language ids and code.""" return request.param def test_language_id_to_code(mocker, language_ids_codes): """Test conversion of language id to language code.""" mocks.ows_product_get_localization_languages(mocker) lang_id, code = language_ids_codes assert loc_util.language_id_to_code(lang_id) == code def test_code_to_language_id(mocker, language_ids_codes): """Test conversion of language code to language id.""" mocks.ows_product_get_localization_languages(mocker) lang_id, code = language_ids_codes assert loc_util.code_to_language_id(code) == lang_id def test_invalid_language_id_to_code( mocker, language_ids_codes_invalid): """Test conversion of invalid language id to language code.""" mocks.ows_product_get_localization_languages(mocker) lang_id, code = language_ids_codes_invalid assert loc_util.language_id_to_code(lang_id) == code def test_invalid_code_to_language_id( mocker, language_codes_ids_invalid): """Test conversion of invalid language code to language id.""" mocks.ows_product_get_localization_languages(mocker) lang_id, code = language_codes_ids_invalid assert loc_util.code_to_language_id(code) == lang_id