"""Test handler."""
from unittest.mock import MagicMock, patch
from constants.status import EMAIL_FRIENDLY_STATUS
from ddex_ingester_common.release_correction.release_correction_diffs import (
ReleaseCorrectionDiffDetail)
import index
from index import (
build_general_fail_payload,
build_recipient_list,
build_submit_validation_errors_payload,
build_submit_video_validation_failure_payload,
extract_submit_validation_errors,
format_correction_field_name,
generate_known_error_template,
S3Schema,
StateMachineSchema
)
from mako.lookup import TemplateLookup
import pytest
def generate_standard_test_template(template_name, context, s3_data, **kwargs):
"""Generate test templates."""
mylookup = TemplateLookup(directories=['constants/templates'])
base_template = mylookup.get_template(template_name)
complete_template = base_template.render(
upc=context.product.upc,
sony_product_id=context.product.catalog_number,
title=context.product.product_name,
artist=context.product.display_artist_name,
format=s3_data.product.release_type,
owner=context.maintenance_owner,
label=context.orchard_label,
status=EMAIL_FRIENDLY_STATUS.get(context.product.status),
thread_id=context.message_thread_id,
grid=context.product.grid,
nfd=context.product.not_for_distribution,
# Fields specific to some templates
errors=kwargs.get('errors'),
vidops='vidops@theorchard.com',
orch_proj_code=kwargs.get('orch_proj_code'),
ddex_proj_code=kwargs.get('ddex_proj_code'),
old_start_date=kwargs.get('old_start_date'),
new_start_date=kwargs.get('new_start_date'),
start_date=kwargs.get('start_date'),
orig_release_date=kwargs.get('orig_release_date'),
release_date=kwargs.get('release_date'),
carveouts_updated=kwargs.get('carveouts_updated'),
carveouts_old=kwargs.get('carveouts_old'),
carveouts_new=kwargs.get('carveouts_new'),
carveouts_added=kwargs.get('carveouts_added'),
carveouts_removed=kwargs.get('carveouts_removed'),
rc_errors=kwargs.get('rc_errors'),
rc_audited_updates=kwargs.get('rc_audited_updates'),
product_artist_updates=kwargs.get('product_artist_updates'),
track_artist_updates=kwargs.get('track_artist_updates'),
rc_warnings=kwargs.get('rc_warnings'),
audio_asset_updates=kwargs.get('audio_asset_updates', []),
publisher_warnings=kwargs.get('publisher_warnings', []),
genre_warnings=kwargs.get('genre_warnings', []),
video_update_warnings=kwargs.get('video_update_warnings', []),
).replace('\n', '')
return complete_template
def test_extract_submit_validation_errors_returns_empty_on_general_fail(
context_parallel_fail):
"""Test errors are extracted."""
errors = extract_submit_validation_errors(context_parallel_fail['errors'])
assert errors == []
def test_extract_submit_validation_errors_returns_validation_errors(
context_submit_product_validation_results,
build_errors):
"""Test extraction of validation result errors."""
errors = extract_submit_validation_errors(
context_submit_product_validation_results['errors'])
expected = build_errors
assert errors == expected
def test_extract_submit_validation_errors_chinese_localization(
chinese_language_errors):
"""Test extraction of validation errors with chinese localization error."""
expected_result = ['ISRC: US3452345325: Simplified and Traditional variations are required when delivering Chinese content. One can be entered as the meta language while adding the other as a localization or both can be entered as localizations.'] # noqa
result = extract_submit_validation_errors(chinese_language_errors)
assert result == expected_result
@patch('index.load_ddex_json')
def test_build_template_with_validation_errors(
mock_load_ddex_json,
context_submit_product_validation_results,
build_errors):
"""Test the building of the HTML payload."""
context = StateMachineSchema().load(
context_submit_product_validation_results)
s3_data = S3Schema().load(context_submit_product_validation_results)
mock_load_ddex_json.return_value = s3_data
email_payload = build_submit_validation_errors_payload(
context, build_errors, s3_data)
complete_template = generate_standard_test_template(
'submit_validation_fail.mak', context, s3_data, errors=build_errors)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_template_with_general_errors(mock_load_ddex_json,
context_parallel_fail):
"""Test the building of the HTML payload."""
context = StateMachineSchema().load(context_parallel_fail)
s3_data = S3Schema().load(context_parallel_fail)
mock_load_ddex_json.return_value = s3_data
email_payload = build_general_fail_payload(
context,
s3_data
)
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_generate_known_error_template_process_participants(
mock_load_ddex_json,
context_parallel_fail):
"""Test generate_known_error_template with a generic exception."""
expected_subject = 'Failure to ingest SME Interop Product 886447094297 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa: E501
expected_error_message = 'Different spotify_uri for participant "AYA": "spotify:artist:6qI2JDpIy2zx81uUdGqx64" and "spotify:artist:1IPTC92TkaOIMj9Gohi8MF"' # noqa: E501
error = {
'Error': 'ProcessParticipantsException',
'Cause': '{\"errorMessage\": \"Different spotify_uri for participant \\\"AYA\\\": \\\"spotify:artist:6qI2JDpIy2zx81uUdGqx64\\\" and \\\"spotify:artist:1IPTC92TkaOIMj9Gohi8MF\\\"\", \"errorType\": \"ProcessParticipantsException\", \"requestId\": \"6cc9a4b3-9575-4004-ba98-7c886a1df328\", \"stackTrace\": [\" File \\\"/var/lang/lib/python3.11/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 169, in sentry_handler\\n reraise(*exc_info)\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/sentry_sdk/_compat.py\\\", line 127, in reraise\\n raise value\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 160, in sentry_handler\\n return handler(aws_event, aws_context, *args, **kwargs)\\n\", \" File \\\"/var/task/index.py\\\", line 45, in handler\\n participants = get_participants(parsed_ddex)\\n\", \" File \\\"/var/task/index.py\\\", line 119, in get_participants\\n check_participant_ids(\\n\", \" File \\\"/var/task/index.py\\\", line 190, in check_participant_ids\\n raise ProcessParticipantsException(message)\\n\"]}' # noqa: E501
}
context = StateMachineSchema().load(context_parallel_fail)
s3_data = S3Schema().load(context_parallel_fail)
mock_load_ddex_json.return_value = s3_data
email_payload, email_subject = generate_known_error_template(
context,
error,
s3_data,
{}
)
assert email_subject == expected_subject
assert expected_error_message in email_payload
@patch('index.load_ddex_json')
def test_generate_known_error_template_generic_exception(
mock_load_ddex_json,
context_parallel_fail):
"""Test generate_known_error_template with a generic exception."""
expected_subject = 'Failure to ingest SME Interop Product 886447094297 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa: E501
error = {
'Error': 'Exception',
'Cause': "{\"errorMessage\": \"Invalid language code LA\", \"errorType\": \"Exception\", \"requestId\": \"f0e0c976-bb3c-45b8-8330-b8a18e8c4d86\", \"stackTrace\": [\" File \\\"/var/lang/lib/python3.11/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 169, in sentry_handler\\n reraise(*exc_info)\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/sentry_sdk/_compat.py\\\", line 127, in reraise\\n raise value\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 160, in sentry_handler\\n return handler(aws_event, aws_context, *args, **kwargs)\\n\", \" File \\\"/var/task/index.py\\\", line 51, in handler\\n validation_results = validate(load_ddex_json(event), rules)\\n\", \" File \\\"/var/task/index.py\\\", line 79, in validate\\n validation_results.append(rule(data))\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/ddex_ingester_common/validation/rules.py\\\", line 273, in validate_product_localization_language_codes\\n return validate_language_codes(\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/ddex_ingester_common/validation/rules.py\\\", line 374, in validate_language_codes\\n language_id = metadata_helper.get_language_id(\\n\", \" File \\\"/var/lang/lib/python3.11/site-packages/ddex_ingester_common/helpers/metadata.py\\\", line 154, in get_language_id\\n raise Exception(f'Invalid language code {language_code}')\\n\"]}" # noqa: E501
}
context = StateMachineSchema().load(context_parallel_fail)
s3_data = S3Schema().load(context_parallel_fail)
mock_load_ddex_json.return_value = s3_data
email_payload, email_subject = generate_known_error_template(
context,
error,
s3_data,
{}
)
assert email_subject == expected_subject
assert 'Invalid language code LA' in email_payload
@patch('index.load_ddex_json')
def test_build_template_with_video_validation_error(
mock_load_ddex_json,
context_submit_video_validation_failure):
"""Test the building of the HTML payload."""
context = StateMachineSchema().load(
context_submit_video_validation_failure)
s3_data = S3Schema().load(context_submit_video_validation_failure)
mock_load_ddex_json.return_value = s3_data
email_payload = build_submit_video_validation_failure_payload(
context,
s3_data
)
complete_template = generate_standard_test_template(
'submit_video_validation_fail.mak', context, s3_data)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_template_with_general_errors_and_unknown_fields(
mock_load_ddex_json,
context_submit_product_validation_results):
"""Test the building of the HTML payload."""
context = StateMachineSchema().load(
context_submit_product_validation_results)
s3_data = S3Schema().load(context_submit_product_validation_results)
mock_load_ddex_json.return_value = s3_data
context.product.upc = None
context.product.product_name = None
context.product.display_artist_name = None
s3_data.product.release_type = None
context.maintenance_owner = None
email_payload = build_general_fail_payload(
context,
s3_data
)
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data)
assert complete_template == email_payload
@pytest.mark.parametrize('test_input,expected', [
(False, ['phanly@theorchard.com']),
(True, [])])
@patch('index.load_rc_json')
@patch('index.is_qa_env')
@patch('index.check_for_email_cc_list',
return_value='["qa-ddex-ingester@theorchard.com"]')
@patch('helpers.email.get_email_addresses_for_major_label',
return_value=None)
@patch('index.send_email')
@patch('index.retrieve_product_data')
@patch('index.load_ddex_json')
def test_qa_env_only_mails_support(
mock_load_ddex_json,
mock_product_data,
mock_send_email,
mock_get_email_addresses_for_major_label,
mock_check_cc_list,
mock_is_qa_env,
mock_rc_json,
context_in_content_update_success,
graphql_response,
test_input,
expected):
"""Test that emails will only be sent to support in the QA env."""
mock_load_ddex_json.return_value = context_in_content_update_success
mock_is_qa_env.return_value = test_input
mock_contact = graphql_response['label']
index.retrieve_contact_details = MagicMock(return_value=mock_contact)
index.handler(context_in_content_update_success, None)
mock_send_email.assert_called_once()
assert mock_send_email.mock_calls[0][1][3] == expected
def test_artwork_errors_extracted_successfully(artwork_error_data,
artwork_errors):
"""Test that artwork errors are extracted properly."""
errors = extract_submit_validation_errors(artwork_error_data['errors'])
expected = artwork_errors
assert errors == expected
@patch('index.load_rc_json')
@patch('index.retrieve_product_data')
@patch('index.load_ddex_json')
@patch('index.exit_without_email')
def test_context_success_exits_gracefully(
mock_exit_without_email,
mock_load_ddex_json,
mock_product_data,
mock_rc_json,
context_ingestion_success,
graphql_response
):
"""Test that the handler exits gracefully on successful ingestion."""
context = index.StateMachineSchema().load(context_ingestion_success)
expected_return = index.StateMachineSchema().dump(context)
mock_product_data.return_value = graphql_response
s3_data = context_ingestion_success
mock_load_ddex_json.return_value = s3_data
mock_exit_without_email.return_value = expected_return
result = index.handler(context_ingestion_success, None)
assert result == expected_return
mock_exit_without_email.assert_called_once()
@patch('index.retrieve_product_data')
@patch('index.retrieve_release_date', return_value='2016-01-02')
@patch('index.load_ddex_json')
@patch('index.load_rc_json', return_value={})
def test_in_content_path_returns_success(
mock_rc_json,
mock_load_ddex_json,
mock_retrieve_release_date,
mock_product_data,
context_in_content_update_success,
graphql_response):
"""Test the return values provided by successful in content ingestion."""
context_in_content_update_success['warnings'] = [
{
'field': 'carveouts',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': "Product Carveouts update attempt. DDEX values ['AD', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SV', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW'] does not match The Orchard's Carveouts ['TA']" # noqa
}
]
warnings = ['Carveouts']
context = StateMachineSchema().load(context_in_content_update_success)
s3_data = S3Schema().load(context_in_content_update_success)
mock_product_data.return_value = graphql_response
mock_load_ddex_json.return_value = s3_data
expected_subject = 'Update to In Content SME Interop Product 886448369561 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa
email_payload, subject = index.in_content_ddex_email_path(
context,
s3_data,
graphql_response
)
asset_updates = []
asset_updates.append('Audio asset updated. (USSM12001699)')
asset_updates.append('Audio asset updated. (USSM12001611)')
complete_template = generate_standard_test_template(
'update_success.mak', context, s3_data,
old_start_date='2020-12-21',
new_start_date='2010-12-21',
orig_release_date='2016-01-01',
release_date='2016-01-02',
carveouts_updated=True,
carveouts_old='AE, UE',
carveouts_new='DE, GB, SE, US',
carveouts_added='DE, GB, SE, US',
carveouts_removed='AE, UE',
rc_warnings=warnings,
new_artwork=True,
audio_asset_updates=asset_updates)
assert complete_template == email_payload
assert subject == expected_subject
@patch('index.load_ddex_json')
def test_in_processing_path_returns_val_errors(
mock_load_ddex_json,
context_submit_product_validation_results,
graphql_response,
build_errors):
"""Test in processing path returns validation result errors."""
context = StateMachineSchema().load(
context_submit_product_validation_results)
s3_data = S3Schema().load(context_submit_product_validation_results)
mock_load_ddex_json.return_value = s3_data
expected_subject = 'Failure to submit SME Interop Product 886447094297 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa
email_payload, subject = index.processing_ddex_email_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'submit_validation_fail.mak', context, s3_data, errors=build_errors)
assert complete_template == email_payload
assert subject == expected_subject
@patch('index.load_ddex_json')
def test_video_validation_error_path(
mock_load_ddex_json,
context_submit_video_validation_failure,
graphql_response
):
"""Test in processing path returns validation result errors."""
context = StateMachineSchema().load(
context_submit_video_validation_failure)
s3_data = S3Schema().load(context_submit_video_validation_failure)
mock_load_ddex_json.return_value = s3_data
expected_subject = 'Failure to submit SME Interop Product 886448369561 - Giveon - Seksikäs-Suklaa & Dosdela' # noqa
email_payload, subject = index.video_product_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'submit_video_validation_fail.mak', context, s3_data)
assert complete_template == email_payload
assert subject == expected_subject
@patch('index.load_ddex_json')
def test_in_processing_path_returns_gen_errors(mock_load_ddex_json,
context_parallel_fail,
graphql_response):
"""Test in processing path returns validation result errors."""
context = StateMachineSchema().load(context_parallel_fail)
s3_data = S3Schema().load(context_parallel_fail)
mock_load_ddex_json.return_value = s3_data
expected_subject = 'Failure to ingest SME Interop Product 886447094297 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa
email_payload, subject = index.processing_ddex_email_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data)
assert complete_template == email_payload
assert subject == expected_subject
@patch('index.load_ddex_json')
@patch('index.load_rc_json', return_value={})
def test_in_content_path_returns_fail(mock_rc_json,
mock_load_ddex_json,
context_in_content_update_fail,
graphql_response):
"""Test the return values provided by successful in content ingestion."""
context = StateMachineSchema().load(context_in_content_update_fail)
s3_data = S3Schema().load(context_in_content_update_fail)
mock_load_ddex_json.return_value = s3_data
expected_subject = 'Invalid Update to In Content SME Interop Product 886448369561 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa
email_payload, subject = index.in_content_ddex_email_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'update_failure.mak', context, s3_data)
assert complete_template == email_payload
assert subject == expected_subject
@patch('index.load_ddex_json')
def test_no_orchard_id_error_payload(mock_load_ddex_json,
context_product_not_found,
graphql_response):
"""Test that OrchardProductNotFound produces correct email template."""
context = StateMachineSchema().load(context_product_not_found)
s3_data = S3Schema().load(context_product_not_found)
mock_load_ddex_json.return_value = s3_data
expected_subject = 'Failure to find SME Interop Product 886447094297 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa
email_payload, subject = index.processing_ddex_email_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'no_product_found.mak', context, s3_data)
assert complete_template == email_payload
assert subject == expected_subject
@patch('index.load_ddex_json')
def test_multiple_generic_fails_returns_generic_error(
mock_load_ddex_json,
context_multiple_generic_errors,
graphql_response):
"""Test that multiple unknown errors result in a generic fail email."""
context = StateMachineSchema().load(context_multiple_generic_errors)
s3_data = S3Schema().load(context_multiple_generic_errors)
mock_load_ddex_json.return_value = s3_data
email_payload, email_subject = index.processing_ddex_email_path(
context,
s3_data,
graphql_response
)
expected_subject = 'Failure to ingest SME Interop Product 886447094297 - Test Artist - Seksikäs-Suklaa & Dosdela' # noqa
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data)
assert complete_template == email_payload
assert email_subject == expected_subject
@pytest.mark.parametrize(
'context_known_error',
['LookupSwitchboardDealException'],
indirect=True)
def test_no_swb_deal_returns_template(
context_known_error,
graphql_response):
"""Test that no SWB deal error returns appropriate template."""
context = StateMachineSchema().load(context_known_error)
s3_data = S3Schema().load(context_known_error)
expected_subject = "No Switchboard Deal for SME Interop Product 884977807875 - Michael Jackson - Michael Jackson's Vision" # noqa
email_payload, email_subject = index.processing_ddex_email_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'no_swb_deal_found.mak', context, s3_data)
assert complete_template == email_payload
assert email_subject == expected_subject
@pytest.mark.parametrize(
'context_known_error',
['LookupSwitchboardInactiveDealException'],
indirect=True)
def test_inactive_swb_deal_returns_template(
context_known_error,
graphql_response):
"""Test that an inactive SWB deal error returns appropriate template."""
context = StateMachineSchema().load(context_known_error)
s3_data = S3Schema().load(context_known_error)
expected_subject = "No Active Switchboard Deal for SME Interop Product 884977807875 - Michael Jackson - Michael Jackson's Vision" # noqa
email_payload, email_subject = index.processing_ddex_email_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'inactive_swb_deal_found.mak', context, s3_data)
assert complete_template == email_payload
assert email_subject == expected_subject
@patch('index.load_lambda_rc_json_files')
@patch('index.retrieve_vidops_email', return_value='vidops@theorchard.com')
@patch('index.load_ddex_json')
def test_video_update_in_content_update_fail(mock_load_ddex_json,
mock_retrieve_vidops_email,
mock_load_rc_files,
context_video_in_content,
graphql_response):
"""Test that correct payload is returned for in_content video updates."""
context = StateMachineSchema().load(context_video_in_content)
s3_data = S3Schema().load(context_video_in_content)
mock_load_ddex_json.return_value = s3_data
expected_subject = "Failure to update In Content SME Interop Product 886448369561 - Test Artist - Seksikäs-Suklaa & Dosdela" # noqa
graphql_response['status'] = 'in_content'
context.product.status = 'in_content'
rc = [
[
'videoAsset',
None,
'Old Version',
'New Version',
'releases',
'version',
True,
True
],
[
'releaseDate',
None,
'2001-01-01',
'2012-12-12',
'releases',
'label',
True,
True
]
]
email_payload, email_subject = index.build_video_update_payload(
context,
s3_data,
rc
)
warnings = [
{
'field': 'Video Asset',
'current_value': 'Old Version',
'new_value': 'New Version'
},
{
'field': 'Release Date',
'current_value': '2001-01-01',
'new_value': '2012-12-12'
}
]
complete_template = generate_standard_test_template(
'fail_update_video.mak', context,
s3_data, video_update_warnings=warnings)
assert complete_template == email_payload
assert email_subject == expected_subject
@patch('index.build_general_fail_payload')
@patch('index.load_ddex_json')
def test_video_general_fail_scenario(
mock_load_ddex_json,
mock_build_general_fail_payload,
context_video_in_content,
graphql_response
):
"""Test that generic Video fails are reported correctly."""
context = StateMachineSchema().load(context_video_in_content)
s3_data = S3Schema().load(context_video_in_content)
s3_data = context_video_in_content
mock_load_ddex_json.return_value = s3_data
mock_build_general_fail_payload.return_value = ''
context.errors = [
{
'Error': 'UnknownFailReason'
},
{
'Error': 'AnotherReason'
}
]
index.video_product_path(
context,
s3_data,
graphql_response
)
mock_build_general_fail_payload.assert_called_once()
@patch('index.load_ddex_json')
def test_project_code_mismatch_fail(
mock_load_ddex_json,
context_video_in_content,
graphql_response
):
"""Test that project code mismatches are reported correctly."""
context = StateMachineSchema().load(context_video_in_content)
s3_data = S3Schema().load(context_video_in_content)
mock_load_ddex_json.return_value = s3_data
context.errors = [
{
'Error': 'ProjectCodeMismatchException'
},
{
'Error': 'AnotherReason'
}
]
expected_subject = "Failure to ingest SME Interop Product 886448369561 - Test Artist - Seksikäs-Suklaa & Dosdela" # noqa
email_payload, email_subject = index.video_product_path(
context,
s3_data,
graphql_response
)
complete_template = generate_standard_test_template(
'fail_project_code_mismatch.mak', context, s3_data,
orch_proj_code=graphql_response['project']['projectCode'],
ddex_proj_code=s3_data.project.project_code)
assert complete_template == email_payload
assert email_subject == expected_subject
@pytest.mark.parametrize('new, expected', [
(
['AE', 'DE'],
{
'carveouts_updated': True,
'carveouts_old': 'AE, UE',
'carveouts_new': 'AE, DE',
'carveouts_added': 'DE',
'carveouts_removed': 'UE'
}
),
(
['AE', 'DE', 'UE'],
{
'carveouts_updated': True,
'carveouts_old': 'AE, UE',
'carveouts_new': 'AE, DE, UE',
'carveouts_added': 'DE',
'carveouts_removed': ''
}
),
(
[],
{
'carveouts_updated': True,
'carveouts_old': 'AE, UE',
'carveouts_new': '',
'carveouts_added': '',
'carveouts_removed': 'AE, UE'
}
),
(
['AE', 'UE'],
{
'carveouts_updated': None,
'carveouts_old': '',
'carveouts_new': '',
'carveouts_added': '',
'carveouts_removed': ''
}
)])
def test_format_carveout_changes(new, expected):
"""Test the format_carveout_changes method."""
orig = ['AE', 'UE']
response = index.format_carveout_changes(orig, new)
assert response == expected
assert response['carveouts_updated'] is expected['carveouts_updated']
@pytest.mark.parametrize('orig, new, expected', [
(['AE', 'DE'], ['AE', 'DE'], False),
(['AE', 'DE'], ['AE', 'UE'], True),
([], [], False)])
def test_carveouts_changed(orig, new, expected):
"""Test carveouts_changed method."""
response = index.carveouts_changed(orig, new)
assert response is expected
@patch('index.load_rc_json')
@patch('index.load_ddex_json')
def test_build_release_correction_fail_payload(
mock_load_ddex_json,
mock_rc_json,
context_video_in_content,
graphql_response):
"""Test build_release_correction_fail_payload method."""
context_video_in_content['warnings'] = [
{
'field': 'carveouts',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': "Product Carveouts update attempt. DDEX values ['AD', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SV', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW'] does not match The Orchard's Carveouts ['TA']" # noqa
},
{
'field': 'publisherNames',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': {'isrc': 'QZCDB2000001', 'current_values': ['Publisher 1', 'Publisher 2', 'Publisher 3'], 'new_values': ['Publisher 1', 'Publisher 2']} # noqa
},
{
'field': 'publisherNames',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': {'isrc': 'QZCDB2000002', 'current_values': ['Publisher 3', 'Publisher 4', 'Publisher 3'], 'new_values': ['Publisher 2', 'Publisher 3']} # noqa
},
]
context = StateMachineSchema().load(context_video_in_content)
s3_data = S3Schema().load(context_video_in_content)
rc_json = {
'changes': [
[
'format',
'Single',
'SomeDustyVinyl',
'releases',
'format',
True,
False
],
[
'other_field',
'OldValue',
'NewValue',
'releases',
'other_field',
True,
False
],
]
}
mock_rc_json.return_value = rc_json
context.errors['Error'] = 'ReleaseCorrectionUpdateException'
mock_load_ddex_json.return_value = s3_data
email_payload = index.build_release_correction_fail_payload(
context,
s3_data,
graphql_response
)
formatted_errors = set(
format_correction_field_name(ReleaseCorrectionDiffDetail(*change))
for change in rc_json.get('changes')
)
formatted_errors.add('Carveouts')
publisher_warnings = [
{
'isrc': 'Publishers (QZCDB2000001)',
'current_values': 'Publisher 1
Publisher 2
Publisher 3
', # noqa
'new_values': 'Publisher 1
Publisher 2
'
},
{
'isrc': 'Publishers (QZCDB2000002)',
'current_values': 'Publisher 3
Publisher 4
Publisher 3
', # noqa
'new_values': 'Publisher 2
Publisher 3
'
}
]
carveout_data = {
'carveouts_updated': True,
'carveouts_old': 'AE, CA',
'carveouts_new': 'AD, AF, AG, AI, AL, AM, AN, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BM, BN, BO, BR, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CS, CU, CV, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, ST, SV, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, YE, YT, ZA, ZM, ZW', # noqa
'carveouts_added': 'AD, AF, AG, AI, AL, AM, AN, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI, BJ, BM, BN, BO, BR, BS, BT, BV, BW, BY, BZ, CC, CD, CF, CG, CH, CI, CK, CL, CM, CN, CO, CR, CS, CU, CV, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH, ER, ES, ET, FI, FJ, FK, FM, FO, FR, GA, GB, GD, GE, GF, GG, GH, GI, GL, GM, GN, GP, GQ, GR, GS, GT, GU, GW, GY, HK, HM, HN, HR, HT, HU, ID, IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, MG, MH, MK, ML, MM, MN, MO, MP, MQ, MR, MS, MT, MU, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF, PG, PH, PK, PL, PM, PN, PR, PS, PT, PW, PY, QA, RE, RO, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SJ, SK, SL, SM, SN, SO, SR, ST, SV, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT, TV, TW, TZ, UA, UG, UM, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WF, WS, YE, YT, ZA, ZM, ZW', # noqa
'carveouts_removed': 'AE'
}
complete_template = generate_standard_test_template(
'release_correction_failure.mak', context, s3_data,
rc_errors=formatted_errors,
publisher_warnings=publisher_warnings,
carveouts_updated=carveout_data.get('carveouts_updated'),
carveouts_old=carveout_data.get('carveouts_old'),
carveouts_new=carveout_data.get('carveouts_new'),
carveouts_added=carveout_data.get('carveouts_added'),
carveouts_removed=carveout_data.get('carveouts_removed'),
)
assert complete_template == email_payload
@patch('index.retrieve_release_date', return_value='2016-01-02')
@patch('index.load_ddex_json')
def test_build_release_correction_update_payload(
mock_rc_json,
mock_check_release_date,
context_in_content_update_success,
graphql_response):
"""Test build_in_content_success_payload method with RC."""
context_in_content_update_success['warnings'] = [
{
'field': 'carveouts',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': "Product Carveouts update attempt. DDEX values ['AD', 'AF', 'AG', 'AI', 'AL', 'AM', 'AO', 'AQ', 'AR', 'AS', 'AT', 'AU', 'AW', 'AX', 'AZ', 'BA', 'BB', 'BD', 'BE', 'BF', 'BG', 'BH', 'BI', 'BJ', 'BM', 'BN', 'BO', 'BR', 'BS', 'BT', 'BV', 'BW', 'BY', 'BZ', 'CA', 'CC', 'CD', 'CF', 'CG', 'CH', 'CI', 'CK', 'CL', 'CM', 'CN', 'CO', 'CR', 'CU', 'CV', 'CX', 'CY', 'CZ', 'DE', 'DJ', 'DK', 'DM', 'DO', 'DZ', 'EC', 'EE', 'EG', 'EH', 'ER', 'ES', 'ET', 'FI', 'FJ', 'FK', 'FM', 'FO', 'FR', 'GA', 'GD', 'GE', 'GF', 'GG', 'GH', 'GI', 'GL', 'GM', 'GN', 'GP', 'GQ', 'GR', 'GS', 'GT', 'GU', 'GW', 'GY', 'HK', 'HM', 'HN', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IM', 'IN', 'IO', 'IQ', 'IR', 'IS', 'IT', 'JE', 'JM', 'JO', 'JP', 'KE', 'KG', 'KH', 'KI', 'KM', 'KN', 'KP', 'KR', 'KW', 'KY', 'KZ', 'LA', 'LB', 'LC', 'LI', 'LK', 'LR', 'LS', 'LT', 'LU', 'LV', 'LY', 'MA', 'MC', 'MD', 'MG', 'MH', 'MK', 'ML', 'MM', 'MN', 'MO', 'MP', 'MQ', 'MR', 'MS', 'MT', 'MU', 'MV', 'MW', 'MX', 'MY', 'MZ', 'NA', 'NC', 'NE', 'NF', 'NG', 'NI', 'NL', 'NO', 'NP', 'NR', 'NU', 'NZ', 'OM', 'PA', 'PE', 'PF', 'PG', 'PH', 'PK', 'PL', 'PM', 'PN', 'PR', 'PS', 'PT', 'PW', 'PY', 'QA', 'RE', 'RO', 'RU', 'RW', 'SA', 'SB', 'SC', 'SD', 'SE', 'SG', 'SH', 'SI', 'SJ', 'SK', 'SL', 'SM', 'SN', 'SO', 'SR', 'ST', 'SV', 'SY', 'SZ', 'TC', 'TD', 'TF', 'TG', 'TH', 'TJ', 'TK', 'TL', 'TM', 'TN', 'TO', 'TR', 'TT', 'TV', 'TW', 'TZ', 'UA', 'UG', 'UM', 'UY', 'UZ', 'VA', 'VC', 'VE', 'VG', 'VI', 'VN', 'VU', 'WF', 'WS', 'YE', 'YT', 'ZA', 'ZM', 'ZW'] does not match The Orchard's Carveouts ['TA']" # noqa
},
{
'field': 'publisherNames',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': {'isrc': 'QZCDB2000001', 'current_values': ['Publisher 1', 'Publisher 2', 'Publisher 3'], 'new_values': ['Publisher 1', 'Publisher 2']} # noqa
},
{
'field': 'publisherNames',
'warning_type': 'ReleaseCorrectionUpdateException',
'exception': {'isrc': 'QZCDB2000002', 'current_values': ['Publisher 3', 'Publisher 4', 'Publisher 3'], 'new_values': ['Publisher 2', 'Publisher 3']} # noqa
},
]
context = StateMachineSchema().load(context_in_content_update_success)
s3_data = S3Schema().load(context_in_content_update_success)
rc_json = [
[
'productVersion',
None,
'old version',
'New Version',
'releases',
'version',
True,
True
],
[
'imprint',
None,
'old imprint',
'New Imprint Music Co.',
'releases',
'label',
True,
True
],
[
'performer',
None,
None,
'new performer field',
'releases',
'performer',
True,
True
],
[
'featuring',
None,
None,
'new featuring field',
'releases',
'featuring',
True,
True
],
[
'featuring',
None,
None,
'new track featuring field',
'track',
'featuring',
True,
True
],
[
'genreId',
None,
12,
20,
'releases',
'genre_id',
True,
True
],
[
'subgenreId',
None,
982,
[223],
'releases',
'release_subgenre',
True,
True
],
[
'trackName',
'USSM12001698',
'old name',
'new name',
'track',
'track_name',
True,
True
],
[
'performer',
'USSM12001698',
None,
[{'type': 'performer', 'name': 'Giveon'}],
'track',
'track_artist',
True,
True
],
[
'writer',
'USSM12001698',
None,
['Composer Name'],
'track',
'track_writer',
True,
True
],
[
'trackGrats',
None,
{'33421938': '2020-01-01', '33421930': '2020-01-02'},
{'33421938': '2020-03-01', '33421930': '2020-03-02',
'33421933': '2020-05-02'},
'releases',
'instant_grats',
True,
False
]
]
email_payload = index.build_in_content_success_payload(
context,
s3_data,
graphql_response,
rc_json
)
rc_audited_updates = [
{
'field_name': 'Genre',
'old_value': 'Classical',
'new_value': 'Blues'
},
{
'field_name': 'Imprint',
'old_value': 'old imprint',
'new_value': 'New Imprint Music Co.'
},
{
'field_name': 'Instant grat (TUID: 33421930)',
'old_value': '2020-01-02',
'new_value': '2020-03-02'
},
{
'field_name': 'Instant grat (TUID: 33421933)',
'old_value': 'No Value Found',
'new_value': '2020-05-02'
},
{
'field_name': 'Instant grat (TUID: 33421938)',
'old_value': '2020-01-01',
'new_value': '2020-03-01'
},
{
'field_name': 'Product Version',
'old_value': 'old version',
'new_value': 'New Version'
},
{
'field_name': 'Subgenre',
'old_value': 'Classic Blues',
'new_value': 'Boogie Woogie'
},
{
'field_name': 'Track Name (USSM12001698)',
'old_value': 'old name',
'new_value': 'new name'
},
]
product_artist_updates = ['Product Artists have been updated.']
track_artist_updates = ['Track Artists have been updated. (USSM12001698)']
asset_updates = []
asset_updates.append('Audio asset updated. (USSM12001699)')
asset_updates.append('Audio asset updated. (USSM12001611)')
publisher_warnings = [
{
'isrc': 'Publishers (QZCDB2000001)',
'current_values': 'Publisher 1
Publisher 2
Publisher 3
', # noqa
'new_values': 'Publisher 1
Publisher 2
'
},
{
'isrc': 'Publishers (QZCDB2000002)',
'current_values': 'Publisher 3
Publisher 4
Publisher 3
', # noqa
'new_values': 'Publisher 2
Publisher 3
'
}
]
complete_template = generate_standard_test_template(
'update_success.mak', context, s3_data,
rc_audited_updates=rc_audited_updates,
product_artist_updates=product_artist_updates,
track_artist_updates=track_artist_updates,
old_start_date='2020-12-21',
new_start_date='2010-12-21',
orig_release_date='2016-01-01',
release_date='2016-01-02',
carveouts_updated=True,
carveouts_old='AE, UE',
carveouts_new='DE, GB, SE, US',
carveouts_added='DE, GB, SE, US',
carveouts_removed='AE, UE',
rc_warnings=['Carveouts'],
new_artwork=True,
audio_asset_updates=asset_updates,
publisher_warnings=publisher_warnings,)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_validation_rule_rejection_payload(
mock_load_ddex_json,
context_validation_rule_rejection,
graphql_response):
"""Test build_release_correction_fail_payload method."""
context = StateMachineSchema().load(context_validation_rule_rejection)
s3_data = S3Schema().load(context_validation_rule_rejection)
mock_load_ddex_json.return_value = s3_data
email_payload = index.build_validation_rule_rejection_payload(
context,
s3_data,
graphql_response
)
errors = [
'Invalid genre',
'Orchard Label ID is missing',
'Project artist is missing',
]
complete_template = generate_standard_test_template(
'validation_rule_rejection.mak', context, s3_data,
errors=errors)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_set_track_metadata_fail_payload(
mock_load_ddex_json,
context_validation_rule_rejection,
graphql_response):
"""Test build_set_track_metadata_fail_payload method."""
context = StateMachineSchema().load(context_validation_rule_rejection)
s3_data = S3Schema().load(context_validation_rule_rejection)
mock_load_ddex_json.return_value = s3_data
error = {
'Error': 'SetTrackMetadataException',
'Cause': "{\"errorMessage\": \"{'url': 'https://qa-ows-track.theorchard.io/track/33466504?get_artist_info_ids_from_existing_rows=1', 'status': 400, 'statusText': 'BAD REQUEST', 'body': {'code': 'validation_error', 'message': {'explicit': ['Swear word found']}}, 'ISRC': 'FIMRR1800137'}\", \"errorType\": \"SetTrackMetadataException\", \"stackTrace\": [\" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 161, in sentry_handler\\n reraise(*exc_info)\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/_compat.py\\\", line 54, in reraise\\n raise value\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 152, in sentry_handler\\n return handler(aws_event, aws_context, *args, **kwargs)\\n\", \" File \\\"/var/task/index.py\\\", line 151, in handler\\n raise SetTrackMetadataException(graphql_response) from err\\n\"]}" # noqa
}
email_payload = index.build_set_track_metadata_fail_payload(
context,
s3_data,
graphql_response,
error
)
errors = [
'Swear word found on the lyrics of non explicit track with ISRC: FIMRR1800137.', # noqa
]
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data,
errors=errors)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_poll_video_fatal_exception_payload(
mock_load_ddex_json,
context_validation_rule_rejection,
graphql_response):
"""Test build_poll_video_fatal_exception_payload method."""
context = StateMachineSchema().load(context_validation_rule_rejection)
s3_data = S3Schema().load(context_validation_rule_rejection)
mock_load_ddex_json.return_value = s3_data
error = {
'Error': 'PollVideoFatalException',
'Cause': '{\"errorMessage\": \"validate_analysis failed. Errors: Cannot convert video to a standard resolution.\", \"errorType\": \"RuntimeError\", \"stackTrace\": [\" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 161, in sentry_handler\\n reraise(*exc_info)\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/_compat.py\\\", line 54, in reraise\\n raise value\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 152, in sentry_handler\\n return handler(aws_event, aws_context, *args, **kwargs)\\n\", \" File \\\"/var/task/index.py\\\", line 47, in handler\\n workflow_result = get_workflow_status(context.video.workflow_id)\\n\", \" File \\\"/var/task/index.py\\\", line 77, in get_workflow_status\\n raise RuntimeError(\\n\"]}' # noqa
}
email_payload = index.build_poll_video_fatal_exception_payload(
context,
s3_data,
graphql_response,
error
)
errors = [
'Cannot convert video to a standard resolution.'
]
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data,
errors=errors)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_poll_video_fatal_exception_payload_multiple_errors(
mock_load_ddex_json,
context_validation_rule_rejection,
graphql_response):
"""Test build_poll_video_fatal_exception_payload method."""
context = StateMachineSchema().load(context_validation_rule_rejection)
s3_data = S3Schema().load(context_validation_rule_rejection)
mock_load_ddex_json.return_value = s3_data
error = {
'Error': 'PollVideoFatalException',
'Cause': '{\"errorMessage\": \"validate_analysis failed. Errors: Cannot convert video to a standard resolution. | Another error\", \"errorType\": \"RuntimeError\", \"stackTrace\": [\" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 161, in sentry_handler\\n reraise(*exc_info)\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/_compat.py\\\", line 54, in reraise\\n raise value\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 152, in sentry_handler\\n return handler(aws_event, aws_context, *args, **kwargs)\\n\", \" File \\\"/var/task/index.py\\\", line 47, in handler\\n workflow_result = get_workflow_status(context.video.workflow_id)\\n\", \" File \\\"/var/task/index.py\\\", line 77, in get_workflow_status\\n raise RuntimeError(\\n\"]}' # noqa
}
email_payload = index.build_poll_video_fatal_exception_payload(
context,
s3_data,
graphql_response,
error
)
errors = [
'Cannot convert video to a standard resolution.',
'Another error'
]
complete_template = generate_standard_test_template(
'generic_failure.mak', context, s3_data,
errors=errors)
assert complete_template == email_payload
@patch('index.load_ddex_json')
def test_build_poll_video_fatal_exception_payload_malformed_error(
mock_load_ddex_json,
context_validation_rule_rejection,
graphql_response):
"""Test build_poll_video_fatal_exception_payload method."""
context = StateMachineSchema().load(context_validation_rule_rejection)
s3_data = S3Schema().load(context_validation_rule_rejection)
mock_load_ddex_json.return_value = s3_data
error = {
'Error': 'PollVideoFatalException',
'Cause': '{\"errorMessage\": \"This: is: madness.::| \", \"errorType\": \"RuntimeError\", \"stackTrace\": [\" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 161, in sentry_handler\\n reraise(*exc_info)\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/_compat.py\\\", line 54, in reraise\\n raise value\\n\", \" File \\\"/var/lang/lib/python3.8/site-packages/sentry_sdk/integrations/aws_lambda.py\\\", line 152, in sentry_handler\\n return handler(aws_event, aws_context, *args, **kwargs)\\n\", \" File \\\"/var/task/index.py\\\", line 47, in handler\\n workflow_result = get_workflow_status(context.video.workflow_id)\\n\", \" File \\\"/var/task/index.py\\\", line 77, in get_workflow_status\\n raise RuntimeError(\\n\"]}' # noqa
}
email_payload = index.build_poll_video_fatal_exception_payload(
context,
s3_data,
graphql_response,
error
)
assert not email_payload
@pytest.mark.parametrize('value,expected', [
(['length one list'], 'length one list'),
([2], 2),
(None, None)])
def test_format_detail_values_for_email(value, expected):
"""Test format_detail_values_for_email method."""
result = index.format_detail_values_for_email(value)
assert result == expected
@pytest.mark.parametrize(('context_fixture, graphql_fixture,'
'expected_recipients'),
[
('context', 'graphql_response', [
'qa-ddex-ingester@theorchard.com']),
('context_with_deal_coordinators',
'graphql_response', [
'qa-ddex-ingester@theorchard.com',
'awesome-coordinator@theorchard.com'
])
])
@patch('index.check_for_email_cc_list',
return_value=['qa-ddex-ingester@theorchard.com'])
@patch('helpers.email.get_email_addresses_for_major_label',
return_value=None)
def test_build_recipient_list(mock_get_email_addresses_for_major_label,
mock_email_cc_list,
context_fixture, graphql_fixture,
expected_recipients, request):
"""Test build_recipient_list."""
context = request.getfixturevalue(context_fixture)
graphql = request.getfixturevalue(graphql_fixture)
output = build_recipient_list(
StateMachineSchema().load(context),
graphql
)
assert output[0] == expected_recipients
@patch('index.load_rc_json')
@patch('index.is_qa_env')
@patch('index.check_for_email_cc_list',
return_value='["qa-ddex-ingester@theorchard.com"]')
@patch('index.send_email')
@patch('index.retrieve_product_data')
@patch('index.load_ddex_json')
@patch('index.build_in_content_success_payload',
wraps=index.build_in_content_success_payload)
@patch('index.exit_without_email')
@patch('index.compare_release_dates', return_value=None)
@patch('index.format_carveout_changes', return_value={})
def test_in_content_update_no_changes_triggers_no_email(
mock_format_carveout_changes,
mock_compare_release_dates,
mock_exit_without_email,
mock_in_content_success,
mock_load_ddex_json,
mock_product_data,
mock_send_email,
mock_check_cc_list,
mock_is_qa_env,
mock_rc_json,
context_in_content_update_success,
graphql_response,):
"""Test build_in_content_success_payload can trigger no email."""
context_in_content_update_success['tracks'] = []
context_in_content_update_success['product']['artwork'] = None
context = index.StateMachineSchema().load(
context_in_content_update_success)
expected_return = index.StateMachineSchema().dump(context)
mock_product_data.return_value = graphql_response
s3_data = context_in_content_update_success
mock_load_ddex_json.return_value = s3_data
mock_exit_without_email.return_value = expected_return
result = index.handler(context_in_content_update_success, None)
mock_in_content_success.assert_called_once()
mock_exit_without_email.assert_called_once()
assert result == expected_return
def test_create_email_template_for_known_error_double_nested_list(
context_in_content_update_success,
graphql_response):
"""Test create_email_template_for_known_error with a double nested list."""
context = index.StateMachineSchema().load(
context_in_content_update_success)
s3_data = context_in_content_update_success
errors = [
[
{
'code': 'MISSING_COMPOSER',
'reason': 'Field `composer` is required'
}
]
]
result = index.create_email_template_for_known_error(
context,
errors,
s3_data,
graphql_response
)
assert result
@patch('index.check_for_email_cc_list',
return_value=['qa-ddex-ingester@theorchard.com'])
@patch('index.boto3.client')
@patch('index.send_email')
def test_no_context_failure(
mock_send_email,
mock_boto3_client,
mock_check_cc_list,
ddex_xml,
fail_before_parsing_ddex_event):
"""Test the handling of errors without enough data in the event context."""
mock_s3_client = MagicMock(name='get_object')
mock_fileobject = MagicMock(name='read')
mock_fileobject.read.return_value = ddex_xml
mock_s3_client.get_object.return_value = {
'Body': mock_fileobject
}
mock_boto3_client.return_value = mock_s3_client
expected_email_body = '
Dear users,
We have failed to read the DDEX file for the product with GRID: A10301A00035480592 and UPC: 195497918690.
| UPC | Grid | Sony Product ID | Title | Artist | Orchard Label | Sony Maint. Owner | Format | Status | Not For Distribution |
|---|---|---|---|---|---|---|---|---|---|
| 195497918690 | A10301A00035480592 | G0100035480592 | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown |
The failure was caused by the error:
"Invalid language code MUL"
Please update the Product\'s Metadata Language in the source system and try the ingestion again.
' # noqa: E501 expected_email_subject =\ 'Failure to ingest SME Interop Product 195497918690' index.handler(fail_before_parsing_ddex_event, {}) mock_send_email.assert_called_with( ['qa-ddex-ingester@theorchard.com'], expected_email_body, expected_email_subject, [] ) @patch('index.check_for_email_cc_list', return_value=['qa-ddex-ingester@theorchard.com']) @patch('index.boto3.client') @patch('index.send_email') def test_no_context_failure_missing_error_message( mock_send_email, mock_boto3_client, mock_check_cc_list, ddex_xml, fail_before_parsing_ddex_event): """Test handling errors without context and missing the error message.""" mock_s3_client = MagicMock(name='get_object') mock_fileobject = MagicMock(name='read') mock_fileobject.read.return_value = ddex_xml mock_s3_client.get_object.return_value = {'Body': mock_fileobject} mock_boto3_client.return_value = mock_s3_client expected_email_body = 'Dear users,
We have failed to read the DDEX file for the product with GRID: A10301A00035480592 and UPC: 195497918690.
| UPC | Grid | Sony Product ID | Title | Artist | Orchard Label | Sony Maint. Owner | Format | Status | Not For Distribution |
|---|---|---|---|---|---|---|---|---|---|
| 195497918690 | A10301A00035480592 | G0100035480592 | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown | Unknown |
Due to this we have no more information about the product or what caused the failure.
A support engineer will look into this and report back, there\'s no action needed on your part.
' # noqa: E501 expected_email_subject =\ 'Failure to ingest SME Interop Product 195497918690' fail_before_parsing_ddex_event['errors']['Cause'] = None index.handler(fail_before_parsing_ddex_event, {}) mock_send_email.assert_called_with( ['qa-ddex-ingester@theorchard.com'], expected_email_body, expected_email_subject, [] ) @patch('index.exit_without_email') def test_purged_release_success( mock_exit_without_email, context_ingestion_success): """Test handling a successful ingest of a purged release DDEX.""" context = context_ingestion_success.copy() context['is_purged_release'] = True index.handler(context, {}) mock_exit_without_email.assert_called() @patch('index.handle_no_context_failure') def test_purged_release_failure( mock_handle_no_context_failure, context_ingestion_success): """Test handling a failed ingest of a purged release DDEX.""" context = context_ingestion_success.copy() context['is_purged_release'] = True context['errors'] = {'error': 'message'} index.handler(context, {}) mock_handle_no_context_failure.assert_called() @patch('index.config') def test_check_for_email_cc_list_without_ddex_provider(mock_config, context): """Test check_for_email_cc_list without ddex_provider.""" mock_config.REPORT_ERRORS_CC = '["qa-ddex-ingester@theorchard.com"]' mock_config.REPORT_ERRORS_SME_ANALYTICS_DUMMY_EMAIL = ( '["qa-ddex-ingest-analytics-dummy-events@sonymusic-pde.com"]') result = index.check_for_email_cc_list('') assert result == ['qa-ddex-ingester@theorchard.com'] @patch('index.config') def test_check_for_email_cc_list_with_ddex_provider(mock_config, context): """Test check_for_email_cc_list with ddex_provider.""" mock_config.REPORT_ERRORS_CC = '["qa-ddex-ingester@theorchard.com"]' mock_config.REPORT_ERRORS_SME_ANALYTICS_DUMMY_EMAIL = ( '["qa-ddex-ingest-analytics-dummy-events@sonymusic-pde.com"]') result = index.check_for_email_cc_list('SME') assert result == ['qa-ddex-ingester@theorchard.com'] @patch('index.config') def test_check_for_email_cc_list_with_ddex_provider_sme_analytics( mock_config, context): """Test check_for_email_cc_list with ddex_provider SME_ANALYTICS.""" mock_config.REPORT_ERRORS_CC = '["qa-ddex-ingester@theorchard.com"]' mock_config.REPORT_ERRORS_SME_ANALYTICS_DUMMY_EMAIL = ( '["qa-ddex-ingest-analytics-dummy-events@sonymusic-pde.com"]') result = index.check_for_email_cc_list('SME_ANALYTICS_PROVIDER') assert result == [ 'qa-ddex-ingest-analytics-dummy-events@sonymusic-pde.com']