"""Tests for track validators.""" import pytest from unittest.mock import MagicMock import json from backend import api from backend.constants import error from backend.constants import feature_flag from backend.constants import product as product_consts from backend.constants import track_field as tf from backend.utils import validation as validation_utils from backend.validation import track_validators from tests.testutils import mocks @pytest.fixture def expected_duplicate_name_error(): """Return duplicate name validation_error.""" return { 'track_name': { 'message': 'Track Name + Version already used in a previous track', 'validator': 'used', 'validator_value': True, 'error_code': 'track_name_version_already_in_use' }} @pytest.fixture def expected_duplicate_classical_name_error(): """Return classical duplicate name + composer validation_error.""" return { 'track_name': { 'message': 'Track Name + Version + Composer already used in a previous classical track', 'validator': 'used', 'validator_value': True, 'error_code': 'track_name_version_composer_classical_already_in_use' }} @pytest.fixture def expected_empty_name_error(): """Return empty name validation_error.""" return { 'track_name': { 'validator': 'required', 'validator_value': True, 'message': 'Field `track_name` is required', 'error_code': 'missing_track_name' }} @pytest.fixture def expected_missing_track_producer_error(): return { 'producer': { 'error_code': 'missing_producers', 'message': 'Field `producer` is required', 'validator': 'required', 'validator_value': True }} @pytest.fixture def expected_missing_writers_error(): """Return missing writers validation_error.""" return { 'writers': { 'error_code': 'missing_writers', 'message': 'Field `writers` is required', 'validator': 'required', 'validator_value': True }} @pytest.fixture def app_context(): """Create an api test app fixture.""" with api.app.app_context() as app_context: app_context.g.log = MagicMock() app_context.g.correlation_id = 'TEST_CORRELATION_ID' app_context.g.log_tags = {'TAG_1': 'TEST_1', 'TAG_2': 'TEST_2'} app_context.g.add_log_tags = MagicMock() yield def test_validate_track_complete_classical_called( mocker, mock_app, track_factory): """Verify validate track complete does classical check.""" track_dict = track_factory.create().to_dict() track_dict[tf.LOCALIZATIONS] = [] featured_artists = {} mocks.ows_product_get_product_by_id(mocker, 1, 123) mocks.ows_product_get_localization_languages(mocker) mock_validate_classical = mocker.patch.object( track_validators, 'validate_classical', side_effects={}) track_validators.validate_track_complete( featured_artists, track_dict, product_consts.CLASSICAL_GENRE, 'subgenre', None, [], set(), 'ENG', False, [], spatial_isrc_by_track_id={}) mock_validate_classical.assert_called_with(track_dict) def test_validate_track_complete_classical_not_called( mocker, mock_app, track_factory): """Verify validate track complete does not do classical check.""" track_dict = track_factory.create().to_dict() track_dict[tf.LOCALIZATIONS] = [] featured_artists = {} mocks.ows_product_get_product_by_id(mocker, 1, 123) mocks.ows_product_get_localization_languages(mocker) mock_validate_classical = mocker.patch.object( track_validators, 'validate_classical', side_effects={}) track_validators.validate_track_complete( featured_artists, track_dict, product_consts.OTHER_GENRE, 'subgenre', None, [], set(), 'ENG', False, [], spatial_isrc_by_track_id={}) mock_validate_classical.assert_not_called() def test_validate_track_complete_classical_skips_missing_writers( mocker, mock_app, track_factory): """Verify validate track complete does not require writers for classical tracks.""" track_dict = track_factory.create(writers__count=0).to_dict() track_dict[tf.LOCALIZATIONS] = [] featured_artists = {} mocks.ows_product_get_product_by_id(mocker, 1, 123) mocks.ows_product_get_localization_languages(mocker) error_obj = track_validators.validate_track_complete( featured_artists, track_dict, product_consts.CLASSICAL_GENRE, 'subgenre', None, [], set(), 'ENG', False, [], spatial_isrc_by_track_id={}) assert tf.WRITERS not in error_obj def test_validate_track_complete_non_classical_requires_writers( mocker, mock_app, track_factory, expected_missing_writers_error): """Verify validate track complete still requires writers for non-classical tracks.""" track_dict = track_factory.create(writers__count=0).to_dict() track_dict[tf.LOCALIZATIONS] = [] featured_artists = {} mocks.ows_product_get_product_by_id(mocker, 1, 123) mocks.ows_product_get_localization_languages(mocker) error_obj = track_validators.validate_track_complete( featured_artists, track_dict, product_consts.OTHER_GENRE, 'subgenre', None, [], set(), 'ENG', False, [], spatial_isrc_by_track_id={}) assert error_obj[tf.WRITERS] == expected_missing_writers_error['writers'] def test_validate_track_complete_passes_spatial_isrc_to_atmos_validator( mocker, mock_app, track_factory, feature_flags): """validate_atmos_audio_exists should receive spatial_isrc_by_track_id.""" feature_flags[feature_flag.CDAM_SPATIAL_ISRC_VALIDATION] = True track_dict = track_factory.create().to_dict() track_dict[tf.LOCALIZATIONS] = [] spatial_isrc_map = {track_dict[tf.TUID]: 'USRC17607839'} assets = [] mocks.ows_product_get_product_by_id(mocker, 1, 123) mocks.ows_product_get_localization_languages(mocker) mock_validate_atmos = mocker.patch.object( track_validators, 'validate_atmos_audio_exists', return_value={}) track_validators.validate_track_complete( {}, track_dict, product_consts.OTHER_GENRE, 'subgenre', None, assets, set(), 'ENG', False, [], spatial_isrc_map) mock_validate_atmos.assert_called_with(track_dict, assets, spatial_isrc_map, False) def test_validate_track_complete_skips_atmos_validator_when_flag_disabled( mocker, mock_app, track_factory, feature_flags): """validate_atmos_audio_exists should not be called when flag is off.""" feature_flags[feature_flag.CDAM_SPATIAL_ISRC_VALIDATION] = False track_dict = track_factory.create().to_dict() track_dict[tf.LOCALIZATIONS] = [] spatial_isrc_map = {track_dict[tf.TUID]: 'USRC17607839'} mocks.ows_product_get_product_by_id(mocker, 1, 123) mocks.ows_product_get_localization_languages(mocker) mock_validate_atmos = mocker.patch.object( track_validators, 'validate_atmos_audio_exists', return_value={}) track_validators.validate_track_complete( {}, track_dict, product_consts.OTHER_GENRE, 'subgenre', None, [], set(), 'ENG', False, [], spatial_isrc_map) mock_validate_atmos.assert_not_called() def test_validate_classical(track_factory): """Should return empty dict because track is valid.""" track_dict = track_factory.create( performer__count=1, composer__count=1).to_dict() validation_result = track_validators.validate_classical(track_dict) assert validation_result == {} def test_validate_classical_no_performer(track_factory): """Should return error because performer is missing.""" track_dict = track_factory.create(composer__count=1).to_dict() expected_error = { 'performer': validation_utils.format_missing_error( field_name='performer', error_code=error.MISSING_PERFORMER ) } validation_result = track_validators.validate_classical(track_dict) assert validation_result == expected_error def test_validate_classical_no_composer(track_factory): """Should return error because composer is missing.""" track_dict = track_factory.create(performer__count=1).to_dict() expected_error = { 'composer': validation_utils.format_error( validator=error.REQUIRED_CODE, message=json.dumps( { 'genre': 'classical', 'subgenre': 'Test' } ), error_code=error.MISSING_COMPOSER ) } validation_result = track_validators.validate_track_composer(track_dict, 'classical', 'Test') assert validation_result == expected_error def test_validate_classical_no_work(track_factory): """Should return error because work is missing.""" track_dict = track_factory.create( track_name=': movement', performer__count=1, composer__count=1).to_dict() expected_error = { 'work': validation_utils.format_missing_error( field_name='work', error_code=error.MISSING_WORK ) } validation_result = track_validators.validate_classical(track_dict) assert validation_result == expected_error def test_validate_classical_long_work(track_factory): """Should return error because work value is too long.""" long_work = 'a' * 201 movement = 'movement info' track_name = ': '.join([long_work, movement]) track = track_factory.create( performer__count=1, composer__count=1, track_name=track_name) error_dict = validation_utils.format_error( validator='max_length_exceeded', message=error.VALIDATION_ERROR_FIELD_TOO_LONG.format('work'), error_code=error.MAX_LENGTH_EXCEEDED.format(field_name='work') ) expected_error = {'work': error_dict} result = track_validators.validate_classical(track.to_dict()) assert result == expected_error def test_validate_classical_long_movement(track_factory): """Should return error because movement value is too long.""" work = 'work info' long_movement = 'a' * 201 track_name = ': '.join([work, long_movement]) track = track_factory.create( performer__count=1, composer__count=1, track_name=track_name) error_dict = validation_utils.format_error( validator='max_length_exceeded', message=error.VALIDATION_ERROR_FIELD_TOO_LONG.format('movement'), error_code=error.MAX_LENGTH_EXCEEDED.format(field_name='movement') ) expected_error = {'movement': error_dict} result = track_validators.validate_classical(track.to_dict()) assert result == expected_error def test_validate_classical_track_name_too_long(track_factory): """Should return error because work+movement is > 255 chars. Track model in DB allows track_name to be at most 255 chars. """ long_work = 'w' * 154 long_movement = 'l' * 100 track_name = ': '.join([long_work, long_movement]) track = track_factory.create( performer__count=1, composer__count=1, track_name=track_name) error_dict = validation_utils.format_error( validator='max_length_exceeded', message=error.VALIDATION_ERROR_COMBINED_FIELDS_TOO_LONG, error_code=error.MAX_LENGTH_EXCEEDED.format( field_name='combined_work_movement' ) ) expected_error = {'work': error_dict} result = track_validators.validate_classical(track.to_dict()) assert result == expected_error def test_validate_track_composer(track_factory): """Should return empty dict because track has composer.""" track_dict = track_factory.create(composer__count=1).to_dict() assert track_validators.validate_track_composer( track_dict, 'soundtrack', 'Film Soundtracks') == {} def test_validate_track_composer_error_case(track_factory): """Should return error because composer is missing.""" track_dict = track_factory.create().to_dict() expected_error = { 'composer': validation_utils.format_error( validator=error.REQUIRED_CODE, message=json.dumps( { 'genre': 'soundtrack', 'subgenre': 'Film Soundtracks' } ), error_code=error.MISSING_COMPOSER ) } assert track_validators.validate_track_composer( track_dict, 'soundtrack', 'Film Soundtracks') == expected_error def test_meta_language_code_is_required(): """Verify track.meta_language_code is required.""" track = {'meta_language_code': ''} validation_result = track_validators.validate_meta_language_code(track) expected = { 'meta_language_code': { 'validator': 'required', 'validator_value': True, 'message': 'Field `meta_language_code` is required', 'error_code': 'missing_meta_language_code' } } assert validation_result == expected def test_meta_language_code_is_required_happy_path(): """Should return empty dict because meta_language_code is present.""" track = {'meta_language_code': 'test'} validation_result = track_validators.validate_meta_language_code(track) assert validation_result == {} @pytest.mark.parametrize( 'ownership_rights', [ 'original_owner', 'acquired_rights', 'exclusive_licensee']) def test_validate_master_rights_has_rights(track_factory, ownership_rights): """Should return empty dict because all master rights info is specified.""" # noqa track = track_factory.create( ownership_rights=ownership_rights, recording_country_id=1, original_rights_holder_country_id=1 ).to_dict() validation_result = track_validators.validate_master_rights(track) assert validation_result == {} @pytest.mark.parametrize( 'ownership_rights', ['non_exclusive_licensee', 'no_rights']) def test_validate_master_rights_no_rights(track_factory, ownership_rights): """Should return empty dict since no ownership_rights.""" track = track_factory.create(ownership_rights=ownership_rights).to_dict() validation_result = track_validators.validate_master_rights(track) assert validation_result == {} def test_validate_master_rights_expanded_not_set(track_factory): """Should return error since ownership_rights is not specified.""" track = track_factory.create(ownership_rights=None).to_dict() validation_result = track_validators.validate_master_rights(track) assert 'ownership_rights' in validation_result assert validation_result['ownership_rights']['validator'] == 'required' def test_validate_master_rights_error_case(track_factory): """Should return error when several missing master rights fields. When Master Rights is chosen, Country of Recording and Nationality of First Producer fields are required. """ track = track_factory.create(ownership_rights='original_owner') expected_error = { 'recording_country_id': { 'message': 'Field `recording_country_id` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_recording_country_id' }, 'original_rights_holder_country_id': { 'message': 'Field `original_rights_holder_country_id` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_original_rights_holder_country_id' } } validation_result = track_validators.validate_master_rights( track.to_dict()) assert validation_result == expected_error def test_validate_master_rights_no_recording_country_id(track_factory): """Should return error because recording_country_id is missing.""" track = track_factory.create( ownership_rights='original_owner', original_rights_holder_country_id=1 ) expected_error = { 'recording_country_id': { 'message': 'Field `recording_country_id` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_recording_country_id' } } validation_result = track_validators.validate_master_rights( track.to_dict()) assert validation_result == expected_error def test_validate_master_rights_no_original_rights_holder_country_id( track_factory): """Return error because original_rights_holder_country_id is missing.""" track = track_factory.create( ownership_rights='original_owner', recording_country_id=1) expected_error = { 'original_rights_holder_country_id': { 'message': 'Field `original_rights_holder_country_id` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_original_rights_holder_country_id' } } validation_result = track_validators.validate_master_rights( track.to_dict()) assert validation_result == expected_error def test_validate_track_audio_exists_error_path(mocker): """Should return dict with error when there is no asset for given tuid.""" track = {'tuid': 1} assets = [] expected_error = { 'audio_file': { 'message': 'Field `audio_file` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_audio_file' } } mocks.ows_product_get_product_by_id(mocker, 1, 123) validation_result = track_validators.validate_track_audio_exists( track, assets, 1) assert validation_result == expected_error def test_validate_track_audio_exists_assets_v2_1(mocker, mock_app): """Should return empty dict because audio has status == `encoding_completed`.""" # noqa track = {'tuid': 1} mocks.ows_product_get_product_by_id(mocker, 1, 123) assets = [{'track_unique_id': 1, 'status': 'encoding_completed', 'asset_upload_type': 'stereo'}] validation_result = track_validators.validate_track_audio_exists( track, assets, 1) assert validation_result == {} def test_validate_track_audio_exists_error_path_assets_v2(mocker): """Should return dict with error when there is no asset for given tuid.""" track = {'tuid': 1} assets = [] expected_error = { 'audio_file': { 'message': 'Field `audio_file` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_audio_file' } } mocks.ows_product_get_product_by_id(mocker, 1, 123) validation_result = track_validators.validate_track_audio_exists( track, assets, 1) mocks.ows_product_get_product_by_id(mocker, 1, 123) assert validation_result == expected_error def test_validate_track_audio_without_track_unique_id(mocker): """Should not fail because track_unique_id absent in asset.""" track = {'tuid': 1} assets = [{'status': 'finished'}] expected_error = { 'audio_file': { 'message': 'Field `audio_file` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_audio_file' } } mocks.ows_product_get_product_by_id(mocker, 1, 123) validation_result = track_validators.validate_track_audio_exists( track, assets, 1) assert validation_result == expected_error def test_validate_atmos_audio_exists_assets_none(): """Should return status_unknown error when product_assets is None (assets service unavailable).""" track = {'tuid': 1} expected_error = { 'atmos_file': { 'message': error.ASSET_STATUS_MISSING, 'validator': 'status_unknown', 'validator_value': True, 'error_code': error.MISSING_ASSET_STATUS } } assert track_validators.validate_atmos_audio_exists(track, None, {}) == expected_error def test_validate_atmos_audio_exists_assets_empty(): """Should return empty dict when product_assets is an empty list.""" track = {'tuid': 1} assert track_validators.validate_atmos_audio_exists(track, [], {}) == {} def test_validate_atmos_audio_exists_no_atmos_asset(): """Should return empty dict when no ATMOS asset exists for the track.""" track = {'tuid': 1} assets = [{'track_unique_id': 1, 'status': 'encoding_completed', 'asset_upload_type': 'stereo'}] assert track_validators.validate_atmos_audio_exists(track, assets, {}) == {} def test_validate_atmos_audio_exists_no_atmos_asset_but_spatial_isrc_present(): """Should return error when spatial ISRC is set but no ATMOS asset exists.""" track = {'tuid': 1} assets = [{'track_unique_id': 1, 'status': 'encoding_completed', 'asset_upload_type': 'stereo'}] expected_error = { 'spatial_isrc': { 'message': error.UNEXPECTED_SPATIAL_ISRC_MSG, 'validator': 'unexpected_spatial_isrc', 'validator_value': True, 'error_code': error.UNEXPECTED_SPATIAL_ISRC } } assert track_validators.validate_atmos_audio_exists(track, assets, {1: 'USRC17607839'}) == expected_error def test_validate_atmos_audio_exists_atmos_not_encoding_completed(): """Should return error when ATMOS asset has not finished encoding.""" track = {'tuid': 1} assets = [{'track_unique_id': 1, 'status': 'uploading', 'asset_upload_type': 'atmos'}] expected_error = { 'atmos_file': { 'message': error.ATMOS_ENCODING_NOT_COMPLETED_MSG, 'validator': 'encoding_not_completed', 'validator_value': True, 'error_code': 'missing_atmos_audio_file' } } assert track_validators.validate_atmos_audio_exists(track, assets, {}) == expected_error def test_validate_atmos_audio_exists_missing_spatial_isrc(): """Should return error when ATMOS asset is complete but spatial ISRC is absent.""" track = {'tuid': 1} assets = [{'track_unique_id': 1, 'status': 'encoding_completed', 'asset_upload_type': 'atmos'}] expected_error = { 'spatial_isrc': { 'message': 'Field `spatial_isrc` is required', 'validator': 'required', 'validator_value': True, 'error_code': 'missing_spatial_isrc' } } assert track_validators.validate_atmos_audio_exists(track, assets, {}) == expected_error def test_validate_atmos_audio_exists_invalid_spatial_isrc(): """Should return error when ATMOS asset is complete but spatial ISRC has invalid format.""" track = {'tuid': 1} assets = [{'track_unique_id': 1, 'status': 'encoding_completed', 'asset_upload_type': 'atmos'}] expected_error = { 'spatial_isrc': { 'message': 'This spatial ISRC is invalid.', 'validator': 'isrc_pattern_mismatch', 'validator_value': True, 'error_code': 'invalid_isrc' } } assert track_validators.validate_atmos_audio_exists(track, assets, {1: 'INVALID'}) == expected_error def test_validate_atmos_audio_exists_valid(): """Should return empty dict when ATMOS asset is complete and spatial ISRC is valid.""" track = {'tuid': 1} assets = [{'track_unique_id': 1, 'status': 'encoding_completed', 'asset_upload_type': 'atmos'}] assert track_validators.validate_atmos_audio_exists(track, assets, {1: 'USRC17607839'}) == {} def test_validate_atmos_audio_exists_ignores_other_tracks(): """Should return empty dict when ATMOS asset belongs to a different track.""" track = {'tuid': 1} assets = [{'track_unique_id': 2, 'status': 'encoding_completed', 'asset_upload_type': 'atmos'}] assert track_validators.validate_atmos_audio_exists(track, assets, {}) == {} def test_validate_preview_start_point_longer_than_duration(track_factory, mocker): """Expect to return error because preview start point > track length.""" track = track_factory.create(preview_start_time=61).to_dict() assets = [ {'status': 'encoding_completed', 'track_unique_id': 2, 'duration': 61, 'asset_upload_type': 'stereo'}, {'status': 'encoding_completed', 'track_unique_id': 1, 'duration': 60, 'asset_upload_type': 'stereo'}, {'status': 'encoding_completed', 'track_unique_id': 3, 'duration': 60, 'asset_upload_type': 'stereo'}] expected_error = { 'preview_start_time': { 'message': error.INVALID_PREVIEW_START_TIME_MSG, 'validator': 'invalid_value', 'validator_value': True, 'error_code': 'invalid_preview_start_time' } } mocks.ows_product_get_product_by_id(mocker, 1, 123) validation_result = track_validators.validate_preview_start_time( track, assets, False) assert validation_result == expected_error @pytest.mark.parametrize( 'track_asset, expected_value', [({'status': 'encoding_completed', 'track_unique_id': 1, 'duration': 60}, True), ({'status': 'uploading', 'track_unique_id': 1, 'duration': 60}, False), ({}, False), (None, False)]) def test_is_asset_upload_finished_v2( track_asset, expected_value, mocker): """Expect result depending on parameters.""" mocks.ows_product_get_product_by_id(mocker, 1, 123) result = track_validators._is_asset_upload_finished(track_asset) assert result is expected_value def test_get_track_asset_success(track_factory, mocker): """Expect to get asset for given track.""" track = track_factory.create(tuid=2).to_dict() assets = [ {'status': 'finished', 'track_unique_id': 1, 'duration': 60}, {'status': 'uploading', 'track_unique_id': 2, 'duration': 60}, {'status': 'finished', 'track_unique_id': 3, 'duration': 60}] expected_asset = assets[1] mocks.ows_product_get_product_by_id(mocker, 1, 123) assert track_validators._get_track_asset(track, assets, 1) == expected_asset def test_get_track_asset_correction_mode_with_no_correction(track_factory, mocker): """Expect to fallback to orginial asset for given track.""" track = track_factory.create(tuid=2).to_dict() assets = [ {'status': 'finished', 'track_unique_id': 1, 'duration': 60}, {'status': 'uploading', 'track_unique_id': 2, 'duration': 60}, {'status': 'finished', 'track_unique_id': 3, 'duration': 60}] expected_asset = assets[1] mocks.ows_product_get_product_by_id(mocker, 1, 123) assert track_validators._get_track_asset( track, assets, is_correction_mode=True) == expected_asset @pytest.mark.parametrize( 'product_assets', [[{'status': 'finished', 'track_unique_id': 2, 'duration': 60}], None, []]) def test_get_track_asset_not_found(track_factory, product_assets, mocker): """Expect to get None when there is no asset for track.""" track = track_factory.create().to_dict() mocks.ows_product_get_product_by_id(mocker, 1, 123) assert track_validators._get_track_asset(track, product_assets, 1) is None def test_validate_preview_start_time_handles_none_duration_assets_v2( track_factory, mocker): """Expect to handle case when duration is None.""" track = track_factory.create(preview_start_time=10).to_dict() assets = [ {'status': 'encoding_completed', 'track_unique_id': 1, 'duration': None, 'asset_upload_type': 'stereo'}, {'status': 'uploading', 'track_unique_id': 2, 'duration': 60, 'asset_upload_type': 'stereo'}, {'status': 'encoding_completed', 'track_unique_id': 3, 'duration': 60, 'asset_upload_type': 'stereo'}] expected_error = { 'preview_start_time': { 'message': error.ASSET_STATUS_MISSING, 'validator': 'status_unknown', 'validator_value': True, 'error_code': 'missing_asset_status' } } mocks.ows_product_get_product_by_id(mocker, 1, 123) result = track_validators.validate_preview_start_time(track, assets, 1) assert result == expected_error def test_validate_track_name_success_when_no_name_match(mock_app, track_factory): """Expect successful result when no duplicate names.""" track = track_factory.create( track_name='Something', version='Some Version').to_dict() used_track_names = [('Something Else', 'Some Version', ())] result = track_validators.validate_track_name(track, used_track_names) assert result == {} def test_validate_track_name_success_when_no_version_match(mock_app, track_factory): """Expect successful result when no duplicate version.""" fake_name = 'Something' fake_version = 'Some Version' track = track_factory.create( track_name=fake_name, version=fake_version).to_dict() used_track_names = [(fake_name, 'Some Other Version', ())] result = track_validators.validate_track_name(track, used_track_names) assert result == {} def test_validate_track_name_success_when_no_artist_match(mock_app, track_factory): """Expect successful result when no duplicate artists.""" fake_name = 'Something' fake_version = 'Some Version' track = track_factory.create( track_name=fake_name, version=fake_version).to_dict() track['artists'] = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some Artist'}] used_track_names = [(fake_name, fake_version, (456,))] result = track_validators.validate_track_name(track, used_track_names) assert result == {} def test_validate_track_name_failure_when_name_version_match( mock_app, track_factory, expected_duplicate_name_error): """Expect error result when duplicate name and version.""" fake_name = 'Something' fake_version = 'Some Version' track = track_factory.create( track_name=fake_name, version=fake_version).to_dict() used_track_names = [(fake_name, fake_version, ())] result = track_validators.validate_track_name(track, used_track_names) assert result == expected_duplicate_name_error def test_validate_track_name_failure_when_same_name_version_artist( mock_app, track_factory, expected_duplicate_name_error): """Expect success result when duplicate name and different artist.""" track_name = 'Something' track_version = 'Some Version' track_artists = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some Artist'}, {'type': 'performer', 'track_artist_id': 456, 'name': 'Other Artist'}] track = track_factory.create( track_name=track_name, version=track_version).to_dict() track['artists'] = track_artists artists_tuple = ('Other Artist', 'Some Artist',) used_track_names = {(track_name, track_version, artists_tuple)} result = track_validators.validate_track_name(track, used_track_names) assert result == expected_duplicate_name_error def test_validate_track_name_ff_success_when_no_name_match(mock_app, track_factory): """Expect successful result when no duplicate names.""" track_version = 'Some Version' track = track_factory.create( track_name='Something', version=track_version).to_dict() product_tracks = [] product_tracks.append(track) product_tracks.append(track_factory.create( track_name='Something else', version=track_version).to_dict()) product_tracks.append(track_factory.create( track_name='Something more', version=track_version).to_dict()) result = track_validators.validate_track_name_ff('other', track, product_tracks) assert result == {} def test_validate_track_name_ff_success_when_no_version_match(mock_app, track_factory): """Expect successful result when no duplicate versions.""" track_name = 'Something' track_version = 'Some Version' track = track_factory.create( track_name=track_name, version=track_version).to_dict() product_tracks = [] product_tracks.append(track) product_tracks.append(track_factory.create( track_name=track_name, version='Some Other Version').to_dict()) product_tracks.append(track_factory.create( track_name=track_name, version='Some Another Version').to_dict()) result = track_validators.validate_track_name_ff('other', track, product_tracks) assert result == {} def test_validate_track_name_ff_success_when_no_artist_match(mock_app, track_factory): """Expect successful result when no duplicate artists.""" track_name = 'Something' track_version = 'Some Version' track_artists = [{'type': 'performer', 'track_artist_id': 123, 'name': 'Some Artist 1'}] track_artists2 = [{'type': 'performer', 'track_artist_id': 234, 'name': 'Some Artist 2'}] track_artists3 = [{'type': 'performer', 'track_artist_id': 345, 'name': 'Some Artist 3'}] track = track_factory.create(track_name=track_name, version=track_version).to_dict() track['artists'] = track_artists track2 = track_factory.create(track_name=track_name, version=track_version).to_dict() track2['artists'] = track_artists2 track3 = track_factory.create(track_name=track_name, version=track_version).to_dict() track3['artists'] = track_artists3 product_tracks = [track, track2, track3] result = track_validators.validate_track_name_ff('other', track, product_tracks) assert result == {} def test_validate_track_name_ff_failure_when_name_version_match( mock_app, track_factory, expected_duplicate_name_error): """Expect error result when duplicate name and version.""" track_name = 'Something' track_version = 'Some Version' track = track_factory.create( track_name=track_name, version=track_version).to_dict() product_tracks = [] product_tracks.append(track) product_tracks.append(track_factory.create( track_name=track_name, version=track_version).to_dict()) product_tracks.append(track_factory.create( track_name='Something not same name', version=track_version).to_dict()) result = track_validators.validate_track_name_ff('other', track, product_tracks) assert result == expected_duplicate_name_error def test_validate_track_name_ff_success_classical_when_name_version_match(mock_app, track_factory): """Expect success result when duplicate name and version but different composers when classical.""" track_name = 'Something' track_version = 'Some Version' track_artists = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some performer 1'}, {'type': 'composer', 'track_artist_id': 987, 'name': 'Some composer 1'}] track_artists2 = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some performer 1'}, {'type': 'composer', 'track_artist_id': 876, 'name': 'Some composer 2'}] track_artists3 = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some performer 1'}, {'type': 'composer', 'track_artist_id': 765, 'name': 'Some composer 3'}] track = track_factory.create(track_name=track_name, version=track_version).to_dict() track['artists'] = track_artists track2 = track_factory.create(track_name=track_name, version=track_version).to_dict() track2['artists'] = track_artists2 track3 = track_factory.create(track_name=track_name, version=track_version).to_dict() track3['artists'] = track_artists3 product_tracks = [track, track2, track3] result = track_validators.validate_track_name_ff('classical', track, product_tracks) assert result == {} def test_validate_track_name_ff_failure_classical_when_name_version_composer_match( mock_app, track_factory, expected_duplicate_classical_name_error): """Expect success result when duplicate name and version but different composers when classical.""" track_name = 'Something' track_version = 'Some Version' track_artists = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some performer 1'}, {'type': 'composer', 'track_artist_id': 987, 'name': 'Some composer 1'}] track_artists2 = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some performer 1'}, {'type': 'composer', 'track_artist_id': 987, 'name': 'Some composer 1'}] track_artists3 = [ {'type': 'performer', 'track_artist_id': 123, 'name': 'Some performer 1'}, {'type': 'composer', 'track_artist_id': 987, 'name': 'Some composer 1'}] track = track_factory.create(track_name=track_name, version=track_version).to_dict() track['artists'] = track_artists track2 = track_factory.create(track_name=track_name, version=track_version).to_dict() track2['artists'] = track_artists2 track3 = track_factory.create(track_name=track_name, version=track_version).to_dict() track3['artists'] = track_artists3 product_tracks = [track, track2, track3] result = track_validators.validate_track_name_ff('classical', track, product_tracks) assert result == expected_duplicate_classical_name_error def test_validate_track_name_is_empty(track_factory, expected_empty_name_error): """Expect failure result when track name is empty.""" track_name = '' track_version = 'Some Version' track = track_factory.create( track_name=track_name, version=track_version).to_dict() product_tracks = [track] result = track_validators.validate_track_name_ff('other', track, product_tracks) assert result == expected_empty_name_error def test_validate_track_producer_is_empty(track_factory, expected_missing_track_producer_error): """Expect failure result when track producer is empty.""" track = track_factory.create( track_name='test track', version='test version').to_dict() result = track_validators.validate_producers(track) assert result == expected_missing_track_producer_error def test_validate_track_producer_is_non_empty(track_factory): """Expect empty result when track producer is not empty.""" track = track_factory.create( track_name='test track', version='test version').to_dict() track_artists = [ {'type': 'producer', 'track_artist_id': 123, 'name': 'Some producer'} ] track['artists'] = track_artists result = track_validators.validate_producers(track) assert result == {} def test_validate_track_producer_is_empty_in_correction( track_factory, expected_missing_track_producer_error): """Expect failure result when track producer is empty for track in correction.""" track = track_factory.create( track_name='test track', version='test version').to_dict() track_artists = [ {'type': 'producer', 'track_artist_id': 123, 'name': 'Some producer'} ] track['artists'] = track_artists track_corrections = {track.get('tuid'): {'producer': []}} result = track_validators.validate_producers(track, True, track_corrections) assert result == expected_missing_track_producer_error def test_validate_track_producer_is_non_empty_in_correction(track_factory): """Expect empty result when track producer is not empty in correction.""" track = track_factory.create( track_name='test track', version='test version').to_dict() track_corrections = { track.get('tuid'): {'producer': [{'name': 'producer1', 'type': 'producer'}]} } result = track_validators.validate_producers(track, True, track_corrections) assert result == {}