"""Test handler.""" from unittest.mock import patch from constants.queries import ( SET_INSTANT_GRATS, UPDATE_PRODUCT) from ddex_ingester_common.release_correction.release_correction_constants import ( # noqa RELEASE_CORRECTION_SCHEDULING_FIELDS ) from ddex_ingester_common.release_correction.release_correction_diffs import ( ReleaseCorrectionDiffDetail) from ddex_ingester_common.schemas.s3_schema import S3Schema from ddex_ingester_common.schemas.state_machine_schema import ( StateMachineSchema ) import index import pytest @patch('index.diff_for_release_corrections') @patch('index.load_rc_json', return_value={'changes': []}) @patch('index.write_rc_json') @patch('index.load_ddex_json') @patch('index.build_instant_grats') @patch('index.is_previewable', return_value=True) @patch('index.retrieve_product_information') @patch('index.generate_special_instructions') @patch('index.set_instant_grats') @patch('index.collect_invalid_preview_messages', return_value=False) @patch('index.has_preorder_deals', return_value=True) @patch('index.update_preorder_data') @patch('index.graphql_gateway.execute') def test_handler( mock_graphql_execute, mock_update_preorder_data, mock_has_preorders, mock_gen_invalid_previews, mock_set_instant_grats, mock_gen_special_instructions, mock_retrieve_product_information, mock_is_previewable, mock_build_instant_grats, mock_ddex_json, mock_rc_json, mock_load_rc_json, mock_diff_for_rc, mock_event, mock_s3_data): """Test the lambda main handler.""" mock_ddex_json.return_value = mock_s3_data mock_event['product']['not_for_distribution'] = 'SWBDummy' mock_event['product']['status'] = 'in_content' index.handler(mock_event, None) mock_build_instant_grats.assert_called_once() mock_update_preorder_data.assert_called_once() mock_set_instant_grats.assert_called_once() mock_diff_for_rc.assert_called_once() mock_rc_json.assert_not_called() @patch('index.load_rc_json', return_value={'changes': []}) @patch('index.load_ddex_json') @patch('index.is_previewable', return_value=True) @patch('index.set_instant_grats') @patch('index.has_preorder_deals', return_value=False) @patch('index.update_preorder_data') @patch('index.graphql_gateway.execute') def test_handler_returns_with_no_preorders( mock_graphql_execute, mock_update_preorder_data, mock_has_preorders, mock_set_instant_grats, mock_is_previewable, mock_ddex_json, mock_load_rc_json, mock_event, mock_s3_data): """Test the lambda main handler returns when no preorders.""" mock_ddex_json.return_value = mock_s3_data index.handler(mock_event, None) mock_update_preorder_data.assert_not_called() @patch('index.load_rc_json', return_value={'changes': []}) @patch('index.load_ddex_json') @patch('index.is_previewable', return_value=True) @patch('index.generate_special_instructions') @patch('index.collect_invalid_preview_messages', return_value=['2020-01-01']) @patch('index.has_preorder_deals', return_value=True) @patch('index.update_preorder_data') @patch('index.graphql_gateway.execute') def test_handler_creates_special_instructions( mock_graphql_execute, mock_update_preorder_data, mock_has_preorders, mock_gen_invalid_previews, mock_gen_special_instructions, mock_is_previewable, mock_ddex_json, mock_load_rc_json, mock_event, mock_s3_data, mock_graphql_response): """Test the lambda main handler creates a special instruction.""" mock_ddex_json.return_value = mock_s3_data mock_graphql_execute.return_value = mock_graphql_response index.handler(mock_event, None) mock_gen_special_instructions.assert_called_with( ['2020-01-01'], 'OneTwoThree') @pytest.mark.parametrize( 'start_date, clip_date, expected_return', [ pytest.param('2020-04-02', '2020-04-01', True, id='Clip date earlier than preorder'), pytest.param('2020-04-02', '2020-04-02', True, id='Clip date same as preorder'), pytest.param('2020-04-02', '2020-04-03', False, id='Clip date later than preorder'), pytest.param('2020-04-02', None, False, id='No clip date available') ] ) def test_is_previewable( mock_s3_data_blank_dates, start_date, clip_date, expected_return): """Test that preview is set to Yes when ClipDate <= PreorderDate.""" s3_data = S3Schema().load(mock_s3_data_blank_dates) deals = s3_data.deals pre_order_term = deals[0].deal_terms[1] pre_order_term.start_date = start_date pre_order_term.clip_preview_date = clip_date is_preview = index.is_previewable(pre_order_term) assert is_preview is expected_return @pytest.mark.parametrize( 'test_input', [ pytest.param(True, id='Has preorder deal'), pytest.param(False, id='No preorder deal') ] ) def test_has_preorder_deals_returns_if_deal_found(mock_s3_data, test_input): """Test that has_preorder_deals finds pre_order flag if set.""" s3_data = S3Schema().load(mock_s3_data) s3_data.deals[0].deal_terms[1].pre_order = test_input s3_data.deals[0].deal_terms[2].pre_order = test_input has_deals = index.has_preorder_deals(s3_data.deals) assert has_deals is test_input @pytest.mark.parametrize( 'test_input, expected_return', [ pytest.param([{'pre_order': True, 'start_date': '2010-01-01', 'start_date_time ': '2010-01-03'}, {'pre_order': True, 'start_date': '2015-01-01', 'start_date_time': '2015-03-03'}], '2010-01-01', id='Earliest of two valid dates'), pytest.param([{'pre_order': False, 'start_date': '2010-01-01', 'start_date_time': '2010-01-03'}, {'pre_order': True, 'start_date': '2015-01-01', 'start_date_time': '2015-03-03'}], '2015-01-01', id='Only one valid date') ] ) def test_retrieve_earliest_preorder( mock_s3_data_blank_dates, test_input, expected_return): """Test retrieve_earliest_preorder returns earliest preorder object.""" s3_data = S3Schema().load(mock_s3_data_blank_dates) deals = s3_data.deals deals[0].deal_terms[0].pre_order = test_input[0]['pre_order'] deals[0].deal_terms[0].start_date = test_input[0]['start_date'] deals[0].deal_terms[1].pre_order = test_input[1]['pre_order'] deals[0].deal_terms[1].start_date = test_input[1]['start_date'] deals[0].deal_terms[1].start_date_time = test_input[1]['start_date_time'] earliest_deal = index.retrieve_earliest_preorder(deals) assert earliest_deal.start_date is expected_return @pytest.mark.parametrize( 'cover_art_date, track_listing_date, expected_return', [ pytest.param('2021-01-01:T01:02:03', '2010-01-01T00:00:00', ['NOTE: The SME Covert Art Preview Date 2021-01-01 is AFTER preorder date 2010-12-12'], # noqa id='Single value returned'), pytest.param('2021-01-01T01:01:01', '2021-01-01T:02:02:02Z', ['NOTE: The SME Track Listing Preview Date 2021-01-01 is AFTER preorder date 2010-12-12', # noqa 'NOTE: The SME Covert Art Preview Date 2021-01-01 is AFTER preorder date 2010-12-12'], # noqa id='Both values returned'), pytest.param('2010-01-01', None, [], id='No values returned') ] ) def test_collect_invalid_preview_messages( mock_s3_data_blank_dates, cover_art_date, track_listing_date, expected_return): """Test collect_invalid_preview_messages returns any invalid dates.""" s3_data = S3Schema().load(mock_s3_data_blank_dates) deals = s3_data.deals deals[0].deal_terms[0].cover_art_preview_date = cover_art_date deals[0].deal_terms[0].track_listing_preview_date = track_listing_date deals[0].deal_terms[0].start_date_time = '2010-12-12T00:00:00.00000' invalid_list = index.collect_invalid_preview_messages( deals[0].deal_terms[0]) assert invalid_list == expected_return @patch('index.write_rc_json') @patch('index.graphql_gateway.execute') def test_retrieve_product_information( mock_graphql_execute, mock_rc_json, mock_event, mock_graphql_response): """Test that retrieve_product_information returns correct data.""" upc = 123 mock_graphql_execute.return_value = mock_graphql_response expected =\ mock_graphql_response['data']['productByUpc'] graphql_response = index.retrieve_product_information(upc) assert graphql_response == expected @pytest.mark.parametrize( 'invalid_fields, existing_instructions, expected_return', [ pytest.param(['cover_art_preview_date', 'track_listing_preview_date'], None, 'cover_art_preview_date\n' # noqa + 'track_listing_preview_date\n', # noqa id='Two invalid fields'), pytest.param(['cover_art_preview_date'], 'Existing specialInstruction\n', 'cover_art_preview_date\n' # noqa + 'Existing specialInstruction\n', id='One invalid field') ] ) def test_generate_special_instructions( mock_event, invalid_fields, existing_instructions, expected_return): """Test generate_special_instructions generates correct text.""" response = index.generate_special_instructions( invalid_fields, existing_instructions) assert expected_return == response @pytest.mark.parametrize( 'date_one, date_two, expected', [('2020-01-01', '2020-02-02', '2020-01-01'), ('2020-02-02', '2020-01-01', '2020-01-01'), ('2020-01-01', None, '2020-01-01'), (None, '2020-01-01', '2020-01-01'), (None, None, None)] ) def test_get_earliest_date(date_one, date_two, expected): """Test get_earliest_date helper method.""" return_value = index.get_earliest_date(date_one, date_two) assert return_value == expected @pytest.mark.parametrize( 'previewable, special_instructions, payload_string', [(True, 'instruction one', 'yes'), (False, None, 'no')] ) @patch('index.graphql_gateway.execute') def test_update_product_payload( mock_graphql_execute, mock_event, previewable, special_instructions, payload_string ): """Test that update product calls GraphQL with correct payload.""" payload = { 'productId': 234324, 'preorderDate': '2020-04-02', 'previewable': payload_string } if special_instructions: payload['specialInstructions'] = special_instructions context = StateMachineSchema().load(mock_event) index.update_preorder_data( context, '2020-04-02', previewable, special_instructions) mock_graphql_execute.assert_called_with( UPDATE_PRODUCT, {'data': payload} ) def test_retrieve_instant_grats(mock_s3_data): """Test the retrieval of instant gratification content.""" s3_data = S3Schema().load(mock_s3_data) instant_grats = s3_data.deals[0].deal_terms[1].instant_gratifications assert instant_grats.start_date == '2020-04-18' def test_build_instant_grats(mock_event, mock_s3_data): """Test that build_instant_grats retrieves all instant grats.""" context = StateMachineSchema().load(mock_event) s3_data = S3Schema().load(mock_s3_data) expected_grats = { 'productId': 234324, 'trackGrats': [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421938, }, { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421900, } ] } instant_grats = index.build_instant_grats(context, s3_data.deals) assert instant_grats == expected_grats def test_build_instant_grats_no_grats(mock_event, mock_s3_data): """Test that build_instant_grats returns the same format with no grats.""" context = StateMachineSchema().load(mock_event) s3_data = S3Schema().load(mock_s3_data) s3_data.deals = [] instant_grats = index.build_instant_grats(context, s3_data.deals) assert 'productId' in instant_grats assert 'trackGrats' in instant_grats assert not instant_grats.get('trackGrats') def test_create_instant_grat(mock_event, mock_s3_data): """Test create_instant_grat method.""" context = StateMachineSchema().load(mock_event) s3_data = S3Schema().load(mock_s3_data) grat_term = s3_data.deals[0].deal_terms[1] date = grat_term.instant_gratifications.start_date reference = grat_term.instant_gratifications.references[0] expected_grats = { 'tuid': 33421938, 'grats': [ { 'date': '2020-04-18', 'storeId': 1 }, { 'date': '2020-04-18', 'storeId': 187 } ] } grats = index.create_instant_grat( context, reference, date) assert grats == expected_grats def test_create_instant_grat_throws_value_error(mock_event, mock_s3_data): """Test create_instant_grat method with value error.""" context = StateMachineSchema().load(mock_event) with pytest.raises(ValueError): index.create_instant_grat( context, [], '') @patch('index.graphql_gateway.execute') def test_set_instant_grats(mock_graphql_execute, mock_event): """Test that set_instant_grats calls GraphQL with correct payload.""" context = StateMachineSchema().load(mock_event) payload = { 'productId': context.product.product_id, 'trackGrats': [ { 'tuid': 12345, 'grats': [ { 'date': '2021-04-04', 'storeId': 187 }, { 'date': '2021-04-04', 'storeId': 1 } ] }, { 'tuid': 54321, 'grats': [ { 'date': '2021-05-05', 'storeId': 187 }, { 'date': '2021-05-05', 'storeId': 1 } ] } ] } index.set_instant_grats(payload) mock_graphql_execute.assert_called_with( SET_INSTANT_GRATS, {'data': payload} ) @pytest.mark.parametrize( 'test_input, expected', [('2020-01-01T00:12:34', '2020-01-01'), ('2020-01-02', '2020-01-02'), ('2020-01-03 02:04:05Z', '2020-01-03'), ('2020-04-02T01:01:01.000000', '2020-04-02'), (None, None)] ) def test_strip_time_component(test_input, expected): """Test strip_time_component method.""" result = index.strip_time_component(test_input) assert result == expected def test_collect_instant_grat_terms(mock_s3_data): """Test collect_instant_grat_terms returns instant grat terms.""" s3_data = S3Schema().load(mock_s3_data) terms = index.collect_instant_grat_terms(s3_data.deals) assert len(terms) == 2 def test_define_preorder_date(mock_s3_data): """Test define_preorder_date method.""" s3_data = S3Schema().load(mock_s3_data) # Use preorder deal term = s3_data.deals[0].deal_terms[1] response = index.define_preorder_date(term) assert response == term.start_date_time def test_create_release_correction_detail(): """Test that create_release_correction_detail returns DiffDetail.""" field_name = 'previewable' old_value = 'yes' new_value = 'no' rc_product_field = RELEASE_CORRECTION_SCHEDULING_FIELDS[field_name] expected_return = ReleaseCorrectionDiffDetail( field_name, None, old_value, new_value, rc_product_field.db_table_name, rc_product_field.db_field_name, rc_product_field.email_customer, rc_product_field.accept_update) response = index.create_release_correction_detail( field_name, old_value, new_value) assert response == expected_return @patch('index.write_rc_json') def test_unsupported_update_raises_exception( mock_write_rc_json, mock_event, mock_s3_data, mock_graphql_response): """Test check_for_unsupported_updates method.""" state_machine_context = StateMachineSchema().load(mock_event) s3_ddex_data = S3Schema().load(mock_s3_data) state_machine_context.product.not_for_distribution = 'N' s3_release_corrections = {'changes': []} instant_grats = { 'productId': 234324, 'trackGrats': [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421938, }, { 'grats': [ { 'date': '2020-04-20', 'storeId': 1, }, { 'date': '2020-04-20', 'storeId': 187, }, ], 'tuid': 33421900, } ] } with pytest.raises(index.ReleaseCorrectionUpdateException): index.check_for_unsupported_updates( state_machine_context, s3_ddex_data, mock_graphql_response['data']['productByUpc'], s3_release_corrections, False, '2020-04-01', instant_grats, {}) @pytest.mark.parametrize( 'input_bool, expected', [(True, 'yes'), (False, 'no')]) def test_format_previewable_field(input_bool, expected): """Test format_previewable_field method.""" formatted_value = index.format_previewable_field(input_bool) assert formatted_value == expected @patch('index.write_rc_json') def test_instant_grats_unsupported_update( mock_write_rc_json, mock_event, mock_s3_data, mock_graphql_response): """Test that an exception is thrown for updated trackGrats.""" state_machine_context = StateMachineSchema().load(mock_event) s3_ddex_data = S3Schema().load(mock_s3_data) state_machine_context.product.not_for_distribution = 'N' s3_release_corrections = {'changes': []} instant_grats = { 'productId': 234324, 'trackGrats': [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421938, }, { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421940, } ] } with pytest.raises(index.ReleaseCorrectionUpdateException) as e: index.check_for_unsupported_updates( state_machine_context, s3_ddex_data, mock_graphql_response['data']['productByUpc'], s3_release_corrections, True, '2020-01-01', instant_grats, {}) assert str(e.value) ==\ "Product TrackGrats update attempt. DDEX values {'33421938': '2020-04-18', '33421940': '2020-04-18'} does not match The Orchard's grats {'33421938': '2020-04-18', '33421900': '2020-04-20', '33421940': '2020-04-18'}" # noqa @patch('index.write_rc_json') def test_instant_grats_no_update( mock_write_rc_json, mock_event, mock_s3_data, mock_graphql_response): """Test that an exception is not thrown for same trackGrats.""" state_machine_context = StateMachineSchema().load(mock_event) s3_ddex_data = S3Schema().load(mock_s3_data) state_machine_context.product.not_for_distribution = 'N' s3_release_corrections = {'changes': []} instant_grats = { 'productId': 234324, 'trackGrats': [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421938, }, { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421940, }, { 'grats': [ { 'date': '2020-04-20', 'storeId': 1, }, { 'date': '2020-04-20', 'storeId': 187, }, ], 'tuid': 33421900, } ] } index.check_for_unsupported_updates( state_machine_context, s3_ddex_data, mock_graphql_response['data']['productByUpc'], s3_release_corrections, True, '2020-01-01', instant_grats, {}) def test_format_graphql_instant_grats(mock_graphql_response): """Test format_graphql_instant_grats method.""" raw_grats = mock_graphql_response['data']['productByUpc']['tracks'] expected_grats = {'33421900': '2020-04-20', '33421938': '2020-04-18', '33421940': '2020-04-18'} # noqa response = index.format_graphql_instant_grats(raw_grats) assert response == expected_grats def test_format_graphql_instant_grats_no_grats(mock_graphql_response): """Test format_graphql_instant_grats method.""" raw_grats = [{'instantGrats': None}] expected_grats = {} response = index.format_graphql_instant_grats(raw_grats) assert response == expected_grats def test_format_s3_instant_grats(): """Test format_s3_instant_grats method.""" raw_grats = [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, } ], 'tuid': 33421938, } ] expected_grats = {'33421938': '2020-04-18'} response = index.format_s3_instant_grats(raw_grats) assert response == expected_grats @patch('index.write_rc_json') @patch('index.load_rc_json') @patch('index.graphql_gateway.execute') @patch('index.load_ddex_json') def test_diff_for_release_corrections_no_diff( mock_load_ddex_json, mock_graphql_execute, mock_rc_json, mock_write_rc, mock_event, mock_s3_data, mock_graphql_response, s3_release_corrections): """Test that diff_for_release_corrections will write an RC detail.""" state_machine_context = StateMachineSchema().load(mock_event) s3_ddex_data = S3Schema().load(mock_s3_data) mock_graphql_response = mock_graphql_response['data']['productByUpc'] mock_rc_json.return_value = s3_release_corrections mock_load_ddex_json.return_value = mock_s3_data mock_graphql_execute.return_value = mock_graphql_response instant_grats = { 'productId': 234324, 'trackGrats': [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421938, }, { 'grats': [ { 'date': '2020-04-20', 'storeId': 1, }, { 'date': '2020-04-20', 'storeId': 187, }, ], 'tuid': 33421900, }, { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421940, } ] } index.diff_for_release_corrections( state_machine_context, s3_ddex_data, mock_graphql_response, s3_release_corrections, True, '2020-01-01', instant_grats) mock_write_rc.assert_not_called() @patch('index.write_rc_json') @patch('index.load_rc_json') @patch('index.graphql_gateway.execute') @patch('index.load_ddex_json') def test_diff_for_release_corrections_creates_diff( mock_load_ddex_json, mock_graphql_execute, mock_rc_json, mock_write_rc, mock_event, mock_s3_data, mock_graphql_response, s3_release_corrections): """Test that diff_for_release_corrections will write an RC detail.""" state_machine_context = StateMachineSchema().load(mock_event) s3_ddex_data = S3Schema().load(mock_s3_data) mock_graphql_response = mock_graphql_response['data']['productByUpc'] instant_grats = { 'productId': 234324, 'trackGrats': [ { 'grats': [ { 'date': '2020-04-18', 'storeId': 1, }, { 'date': '2020-04-18', 'storeId': 187, }, ], 'tuid': 33421938, }, { 'grats': [ { 'date': '2020-04-20', 'storeId': 1, }, { 'date': '2020-04-20', 'storeId': 187, }, ], 'tuid': 33421900, } ] } mock_rc_json.return_value = s3_release_corrections mock_load_ddex_json.return_value = mock_s3_data mock_graphql_execute.return_value = mock_graphql_response event_details = { 'bucket': state_machine_context.bucket, 'key': state_machine_context.key } expected_changes = {'changes': [ ReleaseCorrectionDiffDetail( field_name='previewable', old='yes', new='no', db_table_name='releases', db_field_name='previewable', email_customer=True, accept_update=False), ReleaseCorrectionDiffDetail( field_name='preorderDate', old='2020-01-01', new='2020-04-20', db_table_name='releases', db_field_name='preorder_date', email_customer=True, accept_update=False), ReleaseCorrectionDiffDetail( field_name='trackGrats', old={'33421938': '2020-04-18', '33421900': '2020-04-20', '33421940': '2020-04-18'}, # noqa new={'33421938': '2020-04-18', '33421900': '2020-04-20'}, db_table_name='releases', db_field_name='track_grats', email_customer=True, accept_update=False) ]} index.diff_for_release_corrections( state_machine_context, s3_ddex_data, mock_graphql_response, s3_release_corrections, False, '2020-04-20', instant_grats) mock_write_rc.assert_called_with(event_details, expected_changes)