"""Test for ows_product model.""" from unittest.mock import MagicMock from owsrequest import request import pytest from product_digital import api from product_digital.connectors import sentry from product_digital.constants import error from product_digital.models import ows_product @pytest.fixture def test_upc(): """Return upc string for testing.""" return '012345678912' @pytest.fixture def test_product_id(): """Return product id for testing.""" return 12345 def test_check_upc_is_available(mocker, test_upc): """Test successful response when upc is available.""" mocker.patch.object( request, 'head', return_value=MagicMock(status_code=200)) upc_response = ows_product.check_upc_available(test_upc) assert upc_response.status == 200 def test_check_upc_is_available_when_unavailable(mocker, test_upc): """Test error resposne when upc is not available.""" mocker.patch.object( request, 'head', return_value=MagicMock(status_code=403)) upc_response = ows_product.check_upc_available(test_upc) assert upc_response.status == 400 assert upc_response.errors.get('message') == \ error.ERROR_MESSAGE_UPC_NOT_AVAILABLE def test_check_upc_is_available_when_upc_invalid(mocker, test_upc): """Test error resposne when upc is not available.""" mocker.patch.object( request, 'head', return_value=MagicMock(status_code=404)) upc_response = ows_product.check_upc_available(test_upc) assert upc_response.status == 400 assert upc_response.errors.get('message') == \ error.ERROR_MESSAGE_UPC_NOT_VALID def test_check_upc_is_available_error( test_request_context, mocker, test_upc, valid_get_header): """Test error response and logging when upc function returns unexpected.""" expected_error = 'ows-product returns 418 status' mocker.patch.object( request, 'head', return_value=MagicMock(status_code=418)) with api.app.test_request_context(headers=valid_get_header): mocker.spy(sentry.sentry_sdk, 'capture_message') mock_g = mocker.patch('product_digital.models.ows_product.g') upc_response = ows_product.check_upc_available(test_upc) assert upc_response.status == 500 mock_g.ows.log.error.assert_called_with(expected_error) sentry.sentry_sdk.capture_message.assert_called_with(message=expected_error) def test_check_ownership_vendor_is_owner(mocker, test_product_id): """Test that the call is successful when given vendor owns the product.""" vendor_id = 7123 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=200)) ownership_response = ows_product.check_ownership( test_product_id, 'vendor', vendor_id) request.head.assert_called_with( 'ows-product', '/vendor/{}/product/{}'.format( vendor_id, test_product_id)) assert ownership_response.status == 200 def test_check_ownership_vendor_is_not_owner(mocker, test_product_id): """Test that the call fails when given vendor does not own the product.""" vendor_id = 7123 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=403)) ownership_response = ows_product.check_ownership( test_product_id, 'vendor', vendor_id) assert ownership_response.status == 403 assert ownership_response.errors == { 'code': 'authorization_error', 'message': 'Product is not owned by account'} def test_check_ownership_subaccount_is_owner(mocker, test_product_id): """Test that the call is successful when given subaccount owns product.""" subaccount_id = 4128 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=200)) ownership_response = ows_product.check_ownership( test_product_id, 'subaccount', subaccount_id) request.head.assert_called_with( 'ows-product', '/subaccount/{}/product/{}'.format( subaccount_id, test_product_id)) assert ownership_response.status == 200 def test_check_ownership_subaccount_is_not_owner(mocker, test_product_id): """Test that the call fails when given subaccount does not own product.""" subaccount_id = 4128 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=403)) ownership_response = ows_product.check_ownership( test_product_id, 'subaccount', subaccount_id) assert ownership_response.status == 403 assert ownership_response.errors == { 'code': 'authorization_error', 'message': 'Product is not owned by account'} def test_check_ownership_invalid_account_type(mocker, test_product_id): """Test that the call fails when given an unrecognized account type.""" mocker.spy(request, 'head') ownership_response = ows_product.check_ownership( test_product_id, 'subcoconut', 13) request.head.assert_not_called assert ownership_response.status == 400 assert ownership_response.errors == { 'code': 'bad_request', 'message': 'Invalid account type'} def test_is_product_code_available_when_available(mocker): """Return 200 when product code available.""" product_code = 'some-prod-code' account_type = 'vendor' account_id = 12342 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=404)) product_code_response = ows_product.is_product_code_available( product_code, account_type, account_id) request.head.assert_called_with( 'ows-product', '/{}/{}/product_code/{}'.format( account_type, account_id, product_code)) assert product_code_response.status == 200 def test_is_product_code_available_when_not_available(mocker): """Return 403 when product code not available.""" product_code = 'some-prod-code' account_type = 'vendor' account_id = 12342 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=200)) product_code_response = ows_product.is_product_code_available( product_code, account_type, account_id) request.head.assert_called_with( 'ows-product', '/{}/{}/product_code/{}'.format( account_type, account_id, product_code)) assert product_code_response.status == 400 assert product_code_response.errors == { 'code': 'validation_error', 'message': error.ERROR_MESSAGE_PRODUCT_CODE_NOT_AVAILABLE} def test_is_product_code_available_when_unexpected_response( mocker, test_request_context, valid_get_header): """Return an error response when ows-product returns unexpected code.""" expected_error = 'ows-product returns 500 status' product_code = 'some-prod-code' account_type = 'vendor' account_id = 12342 mocker.patch.object( request, 'head', return_value=MagicMock(status_code=500)) with api.app.test_request_context(headers=valid_get_header): mocker.spy(sentry.sentry_sdk, 'capture_message') mock_g = mocker.patch('product_digital.models.ows_product.g') product_code_response = ows_product.is_product_code_available( product_code, account_type, account_id) assert product_code_response.status == 500 request.head.assert_called_with( 'ows-product', '/{}/{}/product_code/{}'.format( account_type, account_id, product_code)) mock_g.ows.log.error.assert_called_with(expected_error) sentry.sentry_sdk.capture_message.assert_called_with(message=expected_error) def test_get_product(mocker, test_request_context): """Test successful response when get product is called.""" mock_response_body = {'vendor_id': 1, 'subaccount_id': 0} mock_response = MagicMock() mock_response.status_code = 200 mock_response.json = MagicMock(return_value=mock_response_body) mocker.patch.object( request, 'get', return_value=mock_response) with api.app.test_request_context(): response = ows_product.get_product(1) assert response assert response.status == 200 assert response.message == mock_response_body def test_get_product_with_not_found_error(mocker, test_request_context): """Test not found error response when get product is called.""" mock_response_body = {'code': 'not_found_error', 'message': None} mock_response = MagicMock() mock_response.status_code = 404 mock_response.json = MagicMock(return_value=mock_response_body) mocker.patch.object( request, 'get', return_value=mock_response) with api.app.test_request_context(): # We should pass a response back up with the correct error fields response = ows_product.get_product(1) assert not response assert response.status == 404 assert response.errors == mock_response_body @pytest.mark.parametrize('mark_used', [True, False]) def test_get_provisioned_upc(mocker, mark_used): """Test successful response when get_provisioned_upc is called.""" mock_request_obj = _mock_post_request(mocker) mock_post_response = mock_request_obj.return_value mock_post_response.status_code = 200 mock_post_response.message = {'upc': '123456789101112'} mock_post_response.json.return_value = { 'upc': '123456789101112'} upc_provisioner_response = ows_product.get_provisioned_upc(mark_used) expected_post_payload = {'mark_used': mark_used} post_payload = mock_request_obj.call_args[1]['json'] assert post_payload == expected_post_payload assert upc_provisioner_response.status == 200 assert upc_provisioner_response.message == '123456789101112' ERROR_STATUSES = [ (404, 500), (500, 500) ] @pytest.mark.parametrize('mark_used', [True, False]) @pytest.mark.parametrize('endpoint_error_status, retrieval_error_status', ERROR_STATUSES) def test_get_provisioned_upc_with_error( mocker, endpoint_error_status, retrieval_error_status, mark_used): """Test get upc from upc provisioner endpoint with error.""" expected_response_error = { 'code': 'internal_error', 'message': 'Error fetching UPC' } mock_request_obj = _mock_post_request(mocker) mock_post_response = mock_request_obj.return_value mock_post_response.status_code = endpoint_error_status mock_post_response.json.return_value = {'upc': '123456789101112'} upc_provisioner_response = ows_product.get_provisioned_upc(mark_used) expected_post_payload = {'mark_used': mark_used} post_payload = mock_request_obj.call_args[1]['json'] assert post_payload == expected_post_payload assert upc_provisioner_response.status == retrieval_error_status assert upc_provisioner_response.errors == expected_response_error def _mock_post_request(mocker): mock_request_obj = mocker.patch.object( request, 'post' ) return mock_request_obj