"""Tests for jobs (encoding_queue_detail) logic.""" import copy import pytest from src.constants import fields from src.constants import maxwell from src.logic import jobs from src.models import dms_delivery_spec from src.models import orders from src.models import products def test_get_and_update_from_datasources(mocker): """Test updating records from various data sources.""" mocked_extra_data = mocker.patch('src.logic.jobs.extra_data') mocked_get_and_update = mocked_extra_data.get_and_update # Initial records value. records = mocker.Mock(0) # Each get_and_update() call returns a modified records list. records_versions = [ mocker.Mock(1), mocker.Mock(2), mocker.Mock(3), mocker.Mock(4)] mocked_get_and_update.side_effect = records_versions assert jobs.update_from_datasources(records) == ( records_versions[-1]) assert mocked_get_and_update.call_count == 4 assert mocked_get_and_update.call_args_list == [ [(records, [fields.ENCODING_QUEUE_ID], orders.get_encoding_queue_bulk), dict(remove_not_found=True)], [(records_versions[0], [ fields.ORDER_ID], orders.get_encoding_order_bulk), {}], [(records_versions[1], [ fields.ORDER_TYPE, fields.DMS_MASTER_MASTER_ID], dms_delivery_spec.get_dms_delivery_spec_bulk), {}], [(records_versions[2], [fields.UPC], products.get_release_bulk), {}], ] @pytest.mark.parametrize( 'records, expected_known_records', ( ( [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], ), ( [ {fields.ORDER_TYPE: 'ignored'}, {fields.ORDER_TYPE: 'foo'} ], [{fields.ORDER_TYPE: 'foo'}] ), ) ) def test_insert_details( records, expected_known_records, mocker): """Test inserting details records.""" mocked_config = mocker.patch('src.logic.jobs.config') mocked_config.IGNORE_ORDER_TYPES = ('ignored',) mocked_update_from_datasources = mocker.patch( 'src.logic.jobs.update_from_datasources') mocked_update_from_datasources.return_value = records mocked_jobs = mocker.patch('src.logic.jobs.jobs') mocked_create_bulk = mocked_jobs.create_bulk jobs.insert_details(records) assert mocked_update_from_datasources.call_count == 1 assert mocked_update_from_datasources.call_args == mocker.call(records) assert mocked_create_bulk.call_count == 1 assert mocked_create_bulk.call_args == mocker.call(expected_known_records) def test_insert_details_no_known_records(mocker): """Test inserting details records with empty known records.""" mocked_update_from_datasources = mocker.patch( 'src.logic.jobs.update_from_datasources') mocked_update_from_datasources.return_value = [] mocked_jobs = mocker.patch('src.logic.jobs.jobs') mocked_set_duplicate_bulk = mocked_jobs.set_duplicates_bulk mocked_create_bulk = mocked_jobs.create_bulk jobs.insert_details(mocker.Mock()) assert mocked_create_bulk.call_count == 0 assert mocked_set_duplicate_bulk.call_count == 0 @pytest.mark.parametrize( 'records, expected_known_records, not_found_records', ( ( [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], ), ( [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], [{fields.ORDER_TYPE: 'foo'}, {fields.ORDER_TYPE: 'bar'}], [{fields.ORDER_TYPE: 'foo'}] ), ( [ {fields.ORDER_TYPE: 'ignored'}, {fields.ORDER_TYPE: 'foo'} ], [{fields.ORDER_TYPE: 'foo'}], [{fields.ORDER_TYPE: 'foo'}], ), ([{fields.ORDER_TYPE: 'ignored'}], [], []), ) ) def test_update_details( records, expected_known_records, not_found_records, mocker): """Test update_details function.""" expected_insert_call_args_list = ( [((not_found_records,), {})] if not_found_records else []) mocked_config = mocker.patch('src.logic.jobs.config') mocked_config.IGNORE_ORDER_TYPES = ('ignore',) mocked_get_and_update = mocker.patch( 'src.logic.jobs.extra_data.get_and_update') mocked_get_and_update.return_value = records mocked_update_bulk = mocker.patch('src.logic.jobs.jobs.update_bulk') mocked_update_bulk.return_value = (42, not_found_records) mocked_insert = mocker.patch('src.logic.jobs.insert_details') jobs.update_details(records) assert mocked_get_and_update.call_count == 1 assert mocked_get_and_update.call_args == ( ( records, [fields.ENCODING_QUEUE_ID], orders.get_encoding_queue_bulk, ), {'remove_not_found': True}, ) assert mocked_update_bulk.call_count == 1 assert mocked_update_bulk.call_args(expected_known_records) assert mocked_insert.call_args_list == expected_insert_call_args_list def test_update_details_no_known_records(mocker): """Test update_details function with no known records.""" mocked_get_and_update = mocker.patch( 'src.logic.jobs.extra_data.get_and_update') mocked_get_and_update.return_value = [] mocked_update_bulk = mocker.patch('src.logic.jobs.jobs.update_bulk') mocked_insert = mocker.patch('src.logic.jobs.insert_details') jobs.update_details(mocker.Mock()) assert mocked_get_and_update.call_count == 1 assert mocked_update_bulk.call_count == 0 assert mocked_insert.call_count == 0 @pytest.mark.parametrize( 'records, fetch_fresh, fetch_eqd_args, ' 'expected_insert_calls, expected_update_calls', ( ( { maxwell.TYPE_INSERT: [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 1}], maxwell.TYPE_UPDATE: [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 2}], }, False, (), 1, 1), ( { 'some unknown operation': [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 1}], 'another unknown operation': [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 2}], }, False, (), 0, 0), ( { maxwell.TYPE_UPDATE: [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 3}, ], }, False, (), 0, 1), ( { maxwell.TYPE_INSERT: [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 4}, ], }, False, (), 1, 0), ({'unknown_operation': [{}]}, False, (), 0, 0), ( { maxwell.TYPE_INSERT: [ {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 5}, {'k1': 'v1', fields.ENCODING_QUEUE_DETAIL_ID: 6}, ], }, True, ([5, 6],), 1, 0), ) ) def test_process_details( records, fetch_fresh, fetch_eqd_args, expected_insert_calls, expected_update_calls, mocker): """Test process_details function.""" insert_mock = mocker.patch('src.logic.jobs.insert_details') update_mock = mocker.patch('src.logic.jobs.update_details') mock_manager = mocker.Mock() mock_manager.attach_mock(insert_mock, 'insert') mock_manager.attach_mock(update_mock, 'update') fresh_records = dict(a=1, b=2) expected_fresh_records_values = [1, 2] fetch_eqd_mock = mocker.patch( 'src.logic.jobs.jobs.get_encoding_queue_detail_bulk') fetch_eqd_mock.return_value = fresh_records config_mock = mocker.patch('src.logic.jobs.config') config_mock.FETCH_FRESH_DD_RECORDS = fetch_fresh expected_insert_call_args = (records.get(maxwell.TYPE_INSERT),) expected_update_call_args = (records.get(maxwell.TYPE_UPDATE),) jobs.process_details(records) if fetch_fresh: expected_insert_call_args = (expected_fresh_records_values,) expected_update_call_args = (expected_fresh_records_values,) assert fetch_eqd_mock.call_args[0] == fetch_eqd_args assert insert_mock.call_count == expected_insert_calls assert update_mock.call_count == expected_update_calls calls_list = [] if expected_insert_calls: calls_list.append(mocker.call.insert(expected_insert_call_args[0])) if expected_update_calls: calls_list.append(mocker.call.update(expected_update_call_args[0])) # Check the calls ensuring inserts happen first. mock_manager.assert_has_calls(calls_list) @pytest.fixture def records_with_duplicates(): """Return sample input records, duplicates and non duplicates.""" records = [ # Group #1 # expected duplicate #1 { fields.PRODUCT_ID: 1, fields.STORE_ID: 1, fields.ORDER_TYPE: 'track', fields.ENCODING_QUEUE_DETAIL_ID: 2, }, # expected duplicate #2 { fields.PRODUCT_ID: 1, fields.STORE_ID: 1, fields.ORDER_TYPE: 'track', fields.ENCODING_QUEUE_DETAIL_ID: 1, }, { fields.PRODUCT_ID: 2, # different product fields.STORE_ID: 1, fields.ORDER_TYPE: 'track', fields.ENCODING_QUEUE_DETAIL_ID: 10, }, { fields.PRODUCT_ID: 1, fields.STORE_ID: 2, # different store fields.ORDER_TYPE: 'track', fields.ENCODING_QUEUE_DETAIL_ID: 11, }, { fields.PRODUCT_ID: 1, fields.STORE_ID: 1, fields.ORDER_TYPE: 'release', # different type fields.ENCODING_QUEUE_DETAIL_ID: 12, }, # Not a duplicate because it has the largest # ENCODING_QUEUE_DETAIL_ID of all possible duplicates. { fields.PRODUCT_ID: 1, fields.STORE_ID: 1, fields.ORDER_TYPE: 'track', fields.ENCODING_QUEUE_DETAIL_ID: 3, }, # The same record as above to check how records for the # same DB row are handled: it should not be marked as a duplicate # too. { fields.PRODUCT_ID: 1, fields.STORE_ID: 1, fields.ORDER_TYPE: 'track', fields.ENCODING_QUEUE_DETAIL_ID: 3, }, # Group 2 # expected duplicate #1 { fields.PRODUCT_ID: 1, fields.STORE_ID: 2, fields.ORDER_TYPE: 'release', fields.ENCODING_QUEUE_DETAIL_ID: 2, }, # expected duplicate #2 { fields.PRODUCT_ID: 1, fields.STORE_ID: 2, fields.ORDER_TYPE: 'release', fields.ENCODING_QUEUE_DETAIL_ID: 1, }, { fields.PRODUCT_ID: 2, # different product fields.STORE_ID: 2, fields.ORDER_TYPE: 'release', fields.ENCODING_QUEUE_DETAIL_ID: 10, }, # largest ENCODING_QUEUE_DETAIL_ID. { fields.PRODUCT_ID: 1, fields.STORE_ID: 2, fields.ORDER_TYPE: 'release', fields.ENCODING_QUEUE_DETAIL_ID: 3, }, ] return ( # all records records, # duplicates [records[0], records[1], records[7], records[8]], # non duplicates [ records[2], records[3], records[4], records[5], records[9], records[10] ] ) def test_get_duplicates_in_current_batch(records_with_duplicates): """Test getting duplicates for a current batch of records.""" input_records, expected_duplicates, expected_non_duplicates = ( records_with_duplicates) records = copy.deepcopy(input_records) duplicates, non_duplicates = jobs.get_duplicates_in_current_batch( records) # Items come in undefined order, so we have to check each one. assert len(duplicates) == len(expected_duplicates) for r in duplicates: assert r in expected_duplicates assert len(non_duplicates) == len(expected_non_duplicates) for r in non_duplicates: assert r in expected_non_duplicates # Ensure input was not modified. assert records == input_records def test_get_duplicates_in_current_batch_empty(): """Test getting duplicates for a current empty batch of records.""" input_records = [] assert jobs.get_duplicates_in_current_batch(input_records) == ([], []) # Ensure input was not modified. assert input_records == []