"""Unit tests for TimedRelease logic.""" from unittest.mock import MagicMock import pytest from oto import response from oto import status as http_status from tests.unit.testutils import db from timed_release.constants import error from timed_release.logic import scheduled_update as scheduled_update_logic from timed_release.logic import timed_release from timed_release.models import scheduled_update as scheduled_update_model from timed_release.models import timed_release as timed_release_model @db.test_schema @pytest.mark.parametrize( 'mock_status, mock_message, expected_response', [ ( 200, { 'product_id': 12, 'schedule_datetime': '2023-01-27T18:03:22Z', 'schedule_time_offset': '01:00', 'process_at_datetime': '2023-01-27T17:03:22Z', 'delivery_store_ids': [581], 'scheduled_by': '1234', 'update': { 'sale_date_time': '2023-01-27' } }, [{ 'delivery_store': { 'id': 581 }, 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00' }] ), ( 404, None, [] ) ] ) def test_get_unsupported_timed_release_data( mock_status, mock_message, expected_response, monkeypatch): """Test for getting unsupported_timed data from a scheduled update.""" monkeypatch.setattr( scheduled_update_logic, 'get_scheduled_update', MagicMock(return_value=response.Response( status=mock_status, message=mock_message))) result = timed_release.get_unsupported_timed_release_data(12) assert result.status == http_status.OK assert result.message == expected_response @db.test_schema def test_get_timed_release_data(monkeypatch): """Test to get timed release details for given product id.""" db.insert_timed_release_data() expected_timed_release_data = { 'timed': [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'delivery_store': { 'id': 286 } }], 'staggered': [{ 'sale_date': '2023-01-25', 'delivery_store': { 'id': 1 } }] } expected_unsupported_timed_data = [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }] expected_response = { 'timed': [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'delivery_store': { 'id': 286 } }], 'staggered': [{ 'sale_date': '2023-01-25', 'delivery_store': { 'id': 1 } }], 'unsupported_timed': [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }] } monkeypatch.setattr( timed_release_model, 'get_product_timed_release_data', MagicMock(return_value=response.Response( message=expected_timed_release_data))) monkeypatch.setattr( timed_release, 'get_unsupported_timed_release_data', MagicMock(return_value=response.Response( message=expected_unsupported_timed_data))) result = timed_release.get_timed_release_data(12) assert result.status == http_status.OK assert result.message == expected_response @pytest.mark.parametrize( 'data, expected_status, expected_response', [ ( {}, 200, [] ), ( { 'unsupported_timed': [] }, 200, [] ), ( { 'unsupported_timed': [ { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }, { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 380 } } ] }, 200, [ { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }, { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 380 } } ] ), ] ) def test_update_unsupported_timed_release_data_success( data, expected_status, expected_response, monkeypatch): """Test to update unsupported timed release detail and scheduled update.""" orchard_identity_id = '03e83149-3c7f-4587-940b-ce83dec34bc8' monkeypatch.setattr( scheduled_update_logic, 'delete', MagicMock(return_value=response.Response( status=200))) monkeypatch.setattr( scheduled_update_logic, 'upsert', MagicMock(return_value=response.Response( status=200))) monkeypatch.setattr( timed_release, 'get_unsupported_timed_release_data', MagicMock(return_value=response.Response( status=200, message=expected_response))) result = timed_release.update_unsupported_timed_release_data( 12, data, orchard_identity_id) assert result.status == expected_status assert result.message == expected_response @pytest.mark.parametrize( 'data, expected_status, expected_response', [ ( { 'unsupported_timed': [ { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }, { 'sale_date_time': '2023-01-29T18:00:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 123 } } ] }, 400, error.ERROR_MESSAGE_NON_MATCHING_TIME_VALUES ), ( { 'unsupported_timed': [ { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }, { 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '00:30', 'delivery_store': { 'id': 123 } }, ] }, 400, error.ERROR_MESSAGE_NON_MATCHING_TIME_VALUES ) ] ) def test_update_unsupported_timed_release_data_failue( data, expected_status, expected_response, monkeypatch): """Test to update unsupported timed release detail and scheduled update.""" orchard_identity_id = '03e83149-3c7f-4587-940b-ce83dec34bc8' monkeypatch.setattr( scheduled_update_logic, 'delete', MagicMock(return_value=response.Response( status=200))) monkeypatch.setattr( scheduled_update_logic, 'upsert', MagicMock(return_value=response.Response( status=200))) monkeypatch.setattr( timed_release, 'get_unsupported_timed_release_data', MagicMock(return_value=response.Response( status=200, message=expected_response))) result = timed_release.update_unsupported_timed_release_data( 12, data, orchard_identity_id) assert result.status == expected_status assert result.errors['message'] == expected_response def test_update_unsupported_timed_release_upsert_args( monkeypatch, mocker): """Test for correct args passed into schedule update logic upsert.""" data = { 'unsupported_timed': [ { 'sale_date_time': '2023-01-27T10:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }, { 'sale_date_time': '2023-01-27T10:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 380 } } ] } expected_response = [ { 'sale_date_time': '2023-01-27T10:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }, { 'sale_date_time': '2023-01-27T10:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 380 } } ] expected_upsert_args = { 'delivery_store_ids': [581, 380], 'schedule_datetime': '2023-01-27T10:03:22Z', 'schedule_time_offset': '01:00', 'update': { 'sale_start_date': '2023-01-26' } } orchard_identity_id = '03e83149-3c7f-4587-940b-ce83dec34bc8' product_id = 12 monkeypatch.setattr( scheduled_update_logic, 'delete', MagicMock(return_value=response.Response( status=200))) monkeypatch.setattr( scheduled_update_logic, 'upsert', MagicMock(return_value=response.Response( status=200))) upsert_spy = mocker.spy(scheduled_update_logic, 'upsert') monkeypatch.setattr( timed_release, 'get_unsupported_timed_release_data', MagicMock(return_value=response.Response( status=200, message=expected_response))) result = timed_release.update_unsupported_timed_release_data( product_id, data, orchard_identity_id) assert result.status == 200 upsert_spy.assert_called_with( product_id, expected_upsert_args, orchard_identity_id, session=None) @db.test_schema def test_update_timed_release_data(monkeypatch): """Test to update timed release details for given product id.""" db.insert_timed_release_data() expected_response = { 'timed': [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'delivery_store': { 'id': 286 } }], 'staggered': [{ 'sale_date': '2023-01-25', 'delivery_store': { 'id': 1 } }], 'unsupported_timed': [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }] } expected_update_release_data = { 'timed': [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'delivery_store': { 'id': 286 } }], 'staggered': [{ 'sale_date': '2023-01-25', 'delivery_store': { 'id': 1 } }] } expected_scheduled_update_data = [{ 'sale_date_time': '2023-01-27T18:03:22Z', 'time_offset': '01:00', 'delivery_store': { 'id': 581 } }] orchard_identity_id = '03e83149-3c7f-4587-940b-ce83dec34bc8' data = expected_response data['timed'][0]['sale_date_time'] = '2023-01-27 18:03:22' monkeypatch.setattr( timed_release_model, 'update_product_timed_release_data', MagicMock(return_value=response.Response( status=200, message=expected_update_release_data))) monkeypatch.setattr( timed_release, 'update_unsupported_timed_release_data', MagicMock(return_value=response.Response( status=200, message=expected_scheduled_update_data))) monkeypatch.setattr( timed_release, 'get_timed_release_data', MagicMock(return_value=response.Response( status=200, message=expected_response))) result = timed_release.update_timed_release_data( 12, data, orchard_identity_id) assert result.status == http_status.OK assert result.message == expected_response @pytest.mark.parametrize( ( 'timed_release_side_effect, scheduled_update_resp,' 'expected_status, expected_message' ), [ # Case 1: Timed release found → return timed release ( response.Response( status=http_status.OK, message={ 'timed': {'sales_date_time': '2023-10-10T10:00:00Z'}, 'staggered': None } ), None, http_status.OK, { 'timed': {'sales_date_time': '2023-10-10T10:00:00Z'}, 'staggered': None } ), # Case 2: Staggered release found → return staggered release ( response.Response( status=http_status.OK, message={ 'timed': None, 'staggered': {'sales_date': '2023-10-10'} } ), None, http_status.OK, { 'timed': None, 'staggered': {'sales_date': '2023-10-10'} } ), # Case 3: Timed release NOT_FOUND, # scheduled update found → return scheduled update ( response.Response(status=http_status.NOT_FOUND, message=None), response.Response( status=http_status.OK, message={ 'timed': {'sales_date_time': '2023-11-11T11:00:00Z'}, 'staggered': None } ), http_status.OK, { 'timed': {'sales_date_time': '2023-11-11T11:00:00Z'}, 'staggered': None } ), # Case 4: Both NOT_FOUND → return 404 with error message ( response.Response(status=http_status.NOT_FOUND, message=None), response.Response(status=http_status.NOT_FOUND, message=None), http_status.NOT_FOUND, None ), # Case 5: Schedule fetched post sales_date_time → # return (sales_date_time - 12hours) adjusted datetime ( response.Response(status=http_status.NOT_FOUND, message=None), response.Response( status=http_status.OK, message={ 'timed': { 'sales_date_time': '2035-03-04T00:00:00Z' }, 'staggered': None } ), http_status.OK, { 'timed': { 'sales_date_time': '2035-03-04T00:00:00Z' }, 'staggered': None } ), # Case 6: Internal error → return 500 ( response.Response(status=http_status.INTERNAL_ERROR, message=None), None, http_status.INTERNAL_ERROR, None ) ] ) def test_get_product_timed_release_by_store_id( mocker, timed_release_side_effect, scheduled_update_resp, expected_status, expected_message ): """Test for get_product_timed_release_by_store_id.""" product_id = 12 store_id = 187 if isinstance(timed_release_side_effect, Exception): mocker.patch.object( timed_release_model, 'get_product_timed_release_by_store_id', side_effect=timed_release_side_effect, ) else: mocker.patch.object( timed_release_model, 'get_product_timed_release_by_store_id', return_value=timed_release_side_effect, ) if scheduled_update_resp is not None: mocker.patch.object( scheduled_update_model, 'get_product_scheduled_update_by_store_id', return_value=scheduled_update_resp, ) result = timed_release.get_product_timed_release_by_store_id( product_id, store_id) assert result.status == expected_status if expected_message: assert result.message == expected_message