"""Lambda test module.""" import importlib from unittest import mock from unittest.mock import patch from unittest.mock import ANY from gql.transport.exceptions import TransportServerError from gql.transport.exceptions import TransportQueryError from src.common.exceptions import exceptions from src import app import pytest from datetime import datetime @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_products') @mock.patch('src.utils.graphql.get_tracks') @mock.patch('src.runner.s3_sound_recordings.write_sound_recording_version') @mock.patch('src.kafka_producer.produce_message') def test_handler(mock_kafka_producer, mock_s3, mock_get_tracks, mock_get_products, mock_ows, track_result, ows_result, products): """Test handler function normal execution.""" mock_ows.return_value = ows_result mock_get_tracks.return_value = track_result mock_get_products.return_value = products mock_s3.return_value = ('sr-version-old', 'new-version-id', True, datetime(2021, 1, 1)) app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) assert mock_kafka_producer.called assert mock_ows.called assert mock_ows.call_args_list == [ mock.call( ows_client=ANY, sound_recording_ids=['673bdd80-8704-46fb-8e3b-f3801a375295'], tuids=None, upcs=None, asset_ids=None, project_ids=None ) ] assert mock_get_products.called assert mock_get_tracks.called assert mock_get_tracks.call_args_list == [ mock.call([456, 123]) ] assert mock_s3.called assert mock_s3.call_args_list == [ mock.call( '673bdd80-8704-46fb-8e3b-f3801a375295', '{"track_connection": {"tracks": [{"product": {"id": "777", "display_upc": "upc-123", "status": "in_content", "release_date": "2019-06-15", "label": {"name": "Label Name", "uuid": "94177d6a-51d4-496f-aa1a-a52b75516814", "type": "Subaccount", "id": {"vendor": 999, "subaccount": 888}}, "subgenre": {"id": 327, "name": "Club/Dance", "genre": {"id": 11, "name": "World Music"}}}, "tuid": "456", "isrc": "isrc-123", "name": "Song Name", "explicit": "N", "version": "", "duration_minutes": 2, "duration_seconds": 30, "meta_language_code": "ENG", "language": {"iso_639_1_code": "EN"}, "p_info": "2020 Label Name", "participations": [{"participant": {"uuid": "3b57bccf-7377-4282-a5b0-4956aa831766", "name": "Music Person"}, "participated_as": "track_writer"}], "territories": ["US", "UK"], "recording_country": {"territory_code_a2": "FR"}, "original_rights_holder_country": {"territory_code_a2": "FR"}, "label_sound_recording": {"participations": [{"participant": {"uuid": "3b57bccf-7377-4282-a5b0-4956aa831766", "name": "Music Person"}, "role": {"name": "Vocal Engineer", "category": {"name": "Studio Personnel"}}}]}, "audio_attributes": [{"id": "1", "attribute": {"id": "1", "description": "Unique Audio"}}], "rights_attributes": [{"id": "1", "attribute": {"id": "1", "description": "Public Domain"}}], "offer_type": "ALL", "ownership_rights": "acquired_rights", "primary": true}]}, "assets": [{"uuid": "193396fb-b2cc-4d83-b4f4-991bc1226270", "filename": "59c22236-d35f-47d5-bb7e-489eac6e79af", "extension": "flac"}, {"uuid": "ecd5647a-0655-4740-9dbf-e3c464e63ed5", "filename": "390821c8-e8ba-4801-bd21-f61a46ece8f4", "extension": "wav"}], "isrc": "isrc-123"}' # noqa:E501 ) ] @mock.patch('src.app.logger') @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') @mock.patch('src.runner.s3_sound_recordings.write_sound_recording_version') def test_handler_without_expected_params( mock_s3, mock_get_tracks, mock_ows, mock_logger): """Test handler function without params.""" with pytest.raises(Exception): app.handler( {'something': ''}, None ) assert not mock_ows.called assert not mock_get_tracks.called assert not mock_s3.called assert mock_logger.exception.called @mock.patch('src.runner.logger') @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') @mock.patch('src.runner.s3_sound_recordings.write_sound_recording_version') def test_handler_missing_track_data( mock_s3, mock_get_tracks, mock_ows, mock_logger, ows_result): """Test that SRs with null GraphQL track data are skipped with a warning.""" mock_ows.return_value = ows_result mock_get_tracks.return_value = { 'tracks': [None] } app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) assert mock_ows.called assert mock_get_tracks.called assert not mock_s3.called mock_logger.warning.assert_called_once() @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') @mock.patch('src.runner.s3_sound_recordings.write_sound_recording_version') @mock.patch('src.kafka_producer.produce_message') def test_no_tracks(mock_kafka_producer, mock_s3, mock_gql, mock_ows): """Test sound recording with no assets.""" mock_ows.return_value = [{ 'id': '673bdd80-8704-46fb-8e3b-f3801a375295', 'isrc': 'isrc-123', 'primary_track_id': None, 'assets': [] }] mock_s3.return_value = ('sr-old-version', 'sr-version-123', True, datetime(2021, 1, 1)) app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) assert mock_kafka_producer.called assert mock_ows.called assert not mock_gql.called assert mock_s3.called assert mock_s3.call_args_list == [ mock.call( '673bdd80-8704-46fb-8e3b-f3801a375295', '{"track_connection": {"tracks": []}, "assets": [], "isrc": "isrc-123"}' # noqa:E501 ) ] @mock.patch('src.app.logger') @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') @mock.patch('src.common.connectors.s3_sound_recordings.write_sound_recording_version') # noqa:E501 def test_no_sound_recording(mock_s3, mock_gql, mock_ows, mock_logger): """Test sound recording not found.""" mock_ows.side_effect = Exception('sound recording not found') mock_s3.return_value = ('sr-version-123', True) with pytest.raises(Exception): app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) assert mock_ows.called assert not mock_gql.called assert not mock_s3.called assert mock_logger.exception.called @mock.patch('src.app.logger') @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') @mock.patch('src.common.connectors.s3_sound_recordings.write_sound_recording_version') # noqa:E501 def test_malformed_tracks(mock_s3, mock_gql, mock_ows, mock_logger): """Test response not match model.""" mock_ows.return_value = [{ 'id': '673bdd80-8704-46fb-8e3b-f3801a375295', 'isrc': 'isrc-123', 'assets': [ { 'asset_id': 'ecd5647a-0655-4740-9dbf-e3c464e63ed5', 'acr_id': 'acr-123', 'filename': '390821c8-e8ba-4801-bd21-f61a46ece8f4', 'extension': 'wav', 'tracks': [{'tuid': 456, 'upc': 'upc-456'}], 'source': 'ows-assets-db' } ] }] mock_gql.return_value = { 'tuid456': { 'isrc': 'isrc-123' } } with pytest.raises(KeyError): app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) assert mock_ows.called assert mock_gql.called assert not mock_s3.called assert mock_logger.exception.called @pytest.mark.parametrize( ('http_code', 'expected_exception'), [ (502, exceptions.RetryableException), (503, exceptions.RetryableException), (504, exceptions.RetryableException), (500, TransportServerError) ] ) @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') def test_graphql_http_exceptions( mock_get_tracks, mock_ows, http_code, expected_exception, ows_result): """Test handling HTTP codes from graphql.""" mock_ows.return_value = ows_result mock_get_tracks.side_effect = \ TransportServerError('error-msg', code=http_code) with pytest.raises(expected_exception): app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) @mock.patch('src.connectors.ows_sound_recordings.get_sound_recordings') @mock.patch('src.utils.graphql.get_tracks') def test_graphql_transport_exception(mock_get_tracks, mock_ows, ows_result): """Test handling exceptions from gql client.""" mock_ows.return_value = ows_result mock_get_tracks.side_effect = TransportQueryError('error-msg') with pytest.raises(exceptions.RetryableException) as exc: app.handler( {'sound_recording_ids': ['673bdd80-8704-46fb-8e3b-f3801a375295']}, None ) assert str(exc.value) == 'error-msg' @patch('config.secrets_manager_client') @patch('sentry_sdk.init') def test_handler_init_empty_sentry(mock_sentry, mock_secrets): """Test sentry init with dsn.""" mock_secret_values = { 'SENTRY_DSN': '' } mock_secrets.get_cred.side_effect = lambda x: mock_secret_values[x] assert not mock_sentry.called importlib.reload(app) assert not mock_sentry.called @patch('config.secrets_manager_client') @patch('sentry_sdk.init') def test_handler_init_none_sentry(mock_sentry, mock_secrets): """Test sentry init with dsn.""" mock_secret_values = { 'SENTRY_DSN': None } mock_secrets.get_cred.side_effect = lambda x: mock_secret_values[x] assert not mock_sentry.called importlib.reload(app) assert not mock_sentry.called