"""Lambda test module.""" from unittest.mock import patch import botocore from src import app as index from src.common import lambda_exceptions import pytest processing_status = ( { 'Records': [ { 'Sns': { 'Subject': None, 'Message': '{"status": "processing", "errors": null, "input": {"key": ' '"81a0289f_d674_4aab_ab95_333dc61fb331.wav", ' '"bucket": "dev-orcd-asset-transcoder-input"}}', 'Timestamp': '2020-05-19T19:07:13.041Z' } } ] }, { 'status': 'encoding_processing', 'errors': None, 'filename': '81a0289f_d674_4aab_ab95_333dc61fb331.wav', 'timestamp': '2020-05-19T19:07:13.041Z', 'final_assets': [] } ) audio_waveform_success = ( { 'Records': [ { 'Sns': { 'Subject': None, 'Message': '{"status": "waveform_completed", "final_assets": [{"key": ' '"e2ce85c9_1bbc_4e47_9132_144e5671166d.dat", "asset_type": "DAT", ' '"asset_subtype": null}], ' '"input": {"key": "bb1ad880_e62f_47c7_87c3_fdb4e8a2566f.wav", "bucket": ' '"dev-orcd-asset-transcoder-input"}}', 'Timestamp': '2020-06-03T20:08:52.329Z' } } ] }, { 'status': 'encoding_waveform_completed', 'errors': None, 'filename': 'bb1ad880_e62f_47c7_87c3_fdb4e8a2566f.wav', 'timestamp': '2020-06-03T20:08:52.329Z', 'final_assets': [ { 'key': 'e2ce85c9_1bbc_4e47_9132_144e5671166d.dat', 'asset_type': 'DAT', 'asset_subtype': None } ] } ) audio_waveform_error = ( { 'Records': [ { 'Sns': { 'Subject': None, 'Message': '{"status": "error", "errors": {"audio_waveform_error": "An error occurred during the audio ' # noqa 'waveform generation. Error message: expected str, bytes or os.PathLike object, ' 'not NoneType"}, "input": {"key": "81a0289f_d674_4aab_ab95_333dc61fb331.wav", ' '"bucket": "dev-orcd-asset-transcoder-input"}}', 'Timestamp': '2020-05-19T19:16:25.578Z' } } ] }, { 'status': 'encoding_error', 'errors': { 'audio_waveform_error': 'An error occurred during the audio waveform generation. Error message: ' 'expected str, bytes or os.PathLike object, not NoneType' }, 'filename': '81a0289f_d674_4aab_ab95_333dc61fb331.wav', 'timestamp': '2020-05-19T19:16:25.578Z', 'final_assets': [] } ) image_encoding_error = ( { 'Records': [ { 'Sns': { 'Subject': None, 'Message': '{"status": "error", "errors": {"image_encoding_error": "An error occurred during the ' 'encoding. Error message: \'tuple\' object has no attribute \'ljust\'"}, ' '"input": {"key": "efb71882_b187_4d73_b14f_94753e263904.tif", ' '"bucket": "dev-orcd-asset-transcoder-input"}}', 'Timestamp': '2020-05-19T19:09:50.478Z' } } ] }, { 'status': 'encoding_error', 'errors': { 'image_encoding_error': "An error occurred during the encoding. Error message: '" "tuple' object has no attribute 'ljust'" }, 'filename': 'efb71882_b187_4d73_b14f_94753e263904.tif', 'timestamp': '2020-05-19T19:09:50.478Z', 'final_assets': [] } ) image_encoding_success = ( { 'Records': [ { 'Sns': { 'Subject': None, 'Message': '{"status": "completed", "final_assets": [{"key": "images/cover/' '5d8009dc_3d71_47d1_977d_d690e09b50e3_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpeg", ' '"asset_type": "JPG", "asset_subtype": "cover"}, {"key": "images/large_cover/' '1ec685a1_e05b_4b05_a3b6_8d91dd9b5fe6_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpeg", ' '"asset_type": "JPG", "asset_subtype": "large_cover"}, {"key": "images/xlarge_cover/' 'e0a0af1f_0bcb_41c6_b4b1_0e1656abb843_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpeg", ' '"asset_type": "JPG", "asset_subtype": "xlarge_cover"}, {"key": "images/tif/' 'b264b582_0039_4f8b_923b_377bd2cdeff1_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.tif", ' '"asset_type": "TIF", "asset_subtype": null}], "input": {"key": ' '"6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpg", ' '"bucket": "dev-orcd-asset-transcoder-input"}}', # noqa 'Timestamp': '2020-06-02T21:01:12.689Z' } } ] }, { 'status': 'encoding_completed', 'errors': None, 'filename': '6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpg', 'timestamp': '2020-06-02T21:01:12.689Z', 'final_assets': [ { 'key': 'images/cover/5d8009dc_3d71_47d1_977d_d690e09b50e3_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpeg', 'asset_type': 'JPG', 'asset_subtype': 'cover' }, { 'key': 'images/large_cover/' '1ec685a1_e05b_4b05_a3b6_8d91dd9b5fe6_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpeg', 'asset_type': 'JPG', 'asset_subtype': 'large_cover' }, { 'key': 'images/xlarge_cover/' 'e0a0af1f_0bcb_41c6_b4b1_0e1656abb843_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.jpeg', 'asset_type': 'JPG', 'asset_subtype': 'xlarge_cover' }, { 'key': 'images/tif/b264b582_0039_4f8b_923b_377bd2cdeff1_6b5ae514_d7a1_4e31_9402_1f2ce49929c5.tif', 'asset_type': 'TIF', 'asset_subtype': None } ] } ) audio_encoding_error = ( { 'Records': [ { 'Sns': { 'Subject': 'The Amazon Elastic Transcoder job 1591199434601-9by88e has failed.', 'Message': '{"state": "ERROR","errorCode": 4000,"messageDetails": ' '"4000 0e78cd2e-77b5-4702-9c55-72a45398e245: ' 'Amazon Elastic Transcoder could not interpret ' 'the media file.","version": "2012-09-25","jobId": "1591199434601-9by88e","pipelineId": ' '"1581101525157-xjcv85","input": {"key": "017a3220_1f5f_4364_a93c_b10dccda7054.tif"},' '"inputCount": 1,"outputs": [{"id": "1", "presetId": "1351620000001-300010", ' '"key": "encoding-image.mp3", "thumbnailPattern": "", "status": "Error", ' '"statusDetail": "4000 0e78cd2e-77b5-4702-9c55-72a45398e245: Amazon Elastic Transcoder ' 'could not interpret the media file.", "errorCode": 4000}]}', 'Timestamp': '2020-06-03T15:50:39.592Z' } } ] }, { 'status': 'encoding_error', 'errors': { 'elastic_transcoder_error': '4000 0e78cd2e-77b5-4702-9c55-72a45398e245: Amazon Elastic Transcoder ' 'could not interpret the media file.' }, 'filename': '017a3220_1f5f_4364_a93c_b10dccda7054.tif', 'timestamp': '2020-06-03T15:50:39.592Z', 'final_assets': [] } ) orchard_messages = [processing_status, audio_waveform_success, audio_waveform_error, image_encoding_success, image_encoding_error, audio_encoding_error] @patch('owsrequest.request.process') @pytest.mark.parametrize('sns_event,post_payload', orchard_messages) def test_handler_orchard_message(process, sns_event, post_payload): """Test handler function succeeds for messages from lambdas.""" process.return_value.status_code = 200 result = index.handler(sns_event, None) assert result == {'status': 'OK', 'message': 'final status message sent'} process.assert_called_with( application='lambda-asset-transcoder-final-status-notification', environment='test', method='POST', service_name='ows-asset-transcoder', path='/final', json=post_payload ) audio_encoding_success = ( { 'Records': [ { 'Sns': { 'Subject': 'Amazon Elastic Transcoder has finished transcoding job 1591131847133-vt306g.', 'Message': '{"state": "COMPLETED", "version": "2012-09-25", "jobId": "1591131847133-vt306g",' '"pipelineId" : "1581101525157-xjcv85","input" : {"key": ' '"fbaa96d4_5ea3_430a_b712_385c8a67f501.wav"}, "inputCount" : 1,"outputs": [{"id": "1",' '"presetId": "1351620000001-300110", "key": "1db43bfa_d8e8_4c1d_88cf_41fb21549434.flac",' '"status": "Complete", "duration": 36}, {"id": "2", "presetId": ' '"1351620000001-300010", "key": "17ad883b_4fff_45bb_a235_c25b88e5a016.mp3", "status":' '"Complete", "duration" : 36} ]}', 'Timestamp': '2020-06-02T21:04:12.896Z', } } ] }, { 'status': 'encoding_completed', 'errors': None, 'filename': 'fbaa96d4_5ea3_430a_b712_385c8a67f501.wav', 'timestamp': '2020-06-02T21:04:12.896Z', 'final_assets': [ { 'key': '1db43bfa_d8e8_4c1d_88cf_41fb21549434.flac', 'duration': 35150, 'container': 'flac' }, { 'key': '17ad883b_4fff_45bb_a235_c25b88e5a016.mp3', 'duration': 35186, 'container': 'mpeg audio' } ] } ) job_response = { 'ResponseMetadata': { 'RequestId': '2fe491b5-15fb-413b-9e4d-5b47f931f5d7', 'HTTPStatusCode': 200, 'HTTPHeaders': { 'x-amzn-requestid': '2fe491b5-15fb-413b-9e4d-5b47f931f5d7', 'content-type': 'application/json', 'content-length': '2318', 'date': 'Wed, 03 Jun 2020 21:02:44 GMT' }, 'RetryAttempts': 0 }, 'Job': { 'Id': '1591131847133-vt306g', 'Arn': 'arn:aws:elastictranscoder:us-east-1:103233932089:job/1591131847133-vt306g', 'PipelineId': '1581101525157-xjcv85', 'Input': { 'Key': 'fbaa96d4_5ea3_430a_b712_385c8a67f501.wav', 'DetectedProperties': {'FileSize': 6200532, 'DurationMillis': 35150} }, 'Inputs': [ { 'Key': 'fbaa96d4_5ea3_430a_b712_385c8a67f501.wav', 'DetectedProperties': {'FileSize': 6200532, 'DurationMillis': 35150} } ], 'Output': { 'Id': '1', 'Key': '1db43bfa_d8e8_4c1d_88cf_41fb21549434.flac', 'PresetId': '1351620000001-300110', 'Status': 'Complete', 'Duration': 36, 'FileSize': 2466094, 'DurationMillis': 35150, 'Watermarks': [] }, 'Outputs': [ { 'Id': '1', 'Key': '1db43bfa_d8e8_4c1d_88cf_41fb21549434.flac', 'PresetId': '1351620000001-300110', 'Status': 'Complete', 'Duration': 36, 'FileSize': 2466094, 'DurationMillis': 35150, 'Watermarks': [] }, { 'Id': '2', 'Key': '17ad883b_4fff_45bb_a235_c25b88e5a016.mp3', 'PresetId': '1351620000001-300010', 'Status': 'Complete', 'Duration': 36, 'FileSize': 1408566, 'DurationMillis': 35186, 'Watermarks': [] } ], 'Playlists': [], 'Status': 'Complete', 'Timing': { 'SubmitTimeMillis': 1591131847163, 'StartTimeMillis': 1591131848547, 'FinishTimeMillis': 1591131852598 } } } @patch('src.common.elastic_transcoder.client') @patch('owsrequest.request.process') def test_handler_elastic_transcoder_message(process, elastic_transcoder_client): """Test handler function succeeds for messages from elastic transcoder.""" process.return_value.status_code = 200 elastic_transcoder_client.read_job.return_value = job_response result = index.handler(audio_encoding_success[0], None) assert result == {'status': 'OK', 'message': 'final status message sent'} process.assert_called_with( application='lambda-asset-transcoder-final-status-notification', environment='test', method='POST', service_name='ows-asset-transcoder', path='/final', json=audio_encoding_success[1] ) @patch('src.common.elastic_transcoder.client') @patch('owsrequest.request.process') def test_handler_elastic_transcoder_message_error(process, elastic_transcoder_client): """Test handler function fails for an s3 error.""" process.return_value.status_code = 200 error = {'Error': {'Code': '403', 'Message': 'The security token included in the request is invalid.'}, 'ResponseMetadata': {'HTTPStatusCode': 403}} elastic_transcoder_client.read_job.side_effect = botocore.exceptions.ClientError(error, 'ReadObject') with pytest.raises(lambda_exceptions.S3Error): index.handler(audio_encoding_success[0], None) @pytest.fixture def sns_message(): """Fixture returns SNS event object.""" return { 'Records': [ { 'Sns': { 'Subject': 'Amazon Elastic Transcoder has scheduled job 1387847681009 - abcdef for transcoding.', 'Message': '', 'Timestamp': '2012-05-02T00:54:06.655Z', } } ] } def test_get_final_status_details_missing_message_failure(sns_message): """Test for fail extraction of status details with missing field.""" data = sns_message del data['Records'][0]['Sns']['Message'] error_message = 'Failed to parse SNS event body. Missing key: Message' with pytest.raises(Exception) as err: index.sns_event_get_final_status_details(data) assert err.value.message == error_message def test_get_final_status_details_not_json_message_failure(sns_message): """Test for fail extraction of status details with incorrect status.""" data = sns_message data['Records'][0]['Sns']['Message'] = '123' with pytest.raises(Exception): index.sns_event_get_final_status_details(data)