"""Tests for common handlers (common.py).""" import json from unittest.mock import MagicMock, NonCallableMagicMock, call, patch import pytest import werkzeug from owsrequest import flask_request from account import api, config from account.constants import header from account.handlers import common as handlers_common from account.logic import identity, vendor from tests.unit.helpers import ( grass as grass_test_helper, subaccount as subaccount_test_helper, vendor as vendor_test_helper, ) from .conftest import fast_patch fixture_orchard_user_id = 'oa:100' def test_hello(fixture_client): """Test route for health check returns 200 status.""" result = fixture_client.get(config.HEALTH_CHECK) assert result.status_code == 200 assert result.headers.get(header.CORRELATION_ID) def test_get_all_vendor_currency_codes( monkeypatch, fixture_client, fixture_vendor_currency_codes_response ): """Test route that gets all vendor currency codes.""" from owsresponse import response fast_patch( monkeypatch, { vendor: dict( get_all_vendor_currency_codes=response.Response( fixture_vendor_currency_codes_response ) ) }, ) result = fixture_client.get('/vendor-currency-codes') vendor.get_all_vendor_currency_codes.assert_called() assert result.status_code == 200 assert json.loads(result.data.decode('utf-8')) == fixture_vendor_currency_codes_response def test_get_all_vendor_currency_codes_for_rejected_grass_access( monkeypatch, fixture_client, fixture_vendor_currency_codes_response ): """Test rejected GRASS access when fetching vendor currency codes.""" error_response = ( 'Direct access through ows-grass is blocked. ' 'Only non-ows-grass microservice-to-microservice' ' requests are allowed.' ) result = fixture_client.get( '/vendor-currency-codes', headers={header.GRASS_ACCOUNT_TYPE: 'vendor', header.GRASS_ACCOUNT_ID: 123}, ) assert result.status_code == 400 assert result.data.decode('utf-8') == error_response # Exception handler tests test_exception_handler_500 = werkzeug.exceptions.InternalServerError('this is a 500') test_exception_handler_400 = werkzeug.exceptions.BadRequest('this is a 400') test_exception_handler_non_json_exception = Exception('oops') test_exception_handler_json_exception = Exception( json.dumps( { 'status': 404, 'code': 'applesauce_not_found', 'message': 'Applesauce Not Found', } ) ) test_exception_handler_no_status_json_exception = Exception( json.dumps( { 'code': 'applesauce_not_found', 'message': 'Applesauce Not Found', } ) ) test_exception_handler_none_status_json_exception = Exception( json.dumps( { 'status': None, 'code': 'applesauce_not_found', 'message': 'Applesauce Not Found', } ) ) @patch('account.handlers.common.sentry_client') @patch('account.handlers.common.g') @pytest.mark.parametrize( ( 'test_description', 'exception', 'code_key', 'expected_code', 'message_key', 'expected_message', 'expected_sentry_client_capture_exception_calls', 'expected_g_log_exception_calls', ), [ ( '500 error', test_exception_handler_500, 'code', 500, 'description', 'this is a 500', [call()], [call(test_exception_handler_500)], ), ( '400 error', test_exception_handler_400, 'code', 400, 'description', 'this is a 400', [call()], [call(test_exception_handler_400)], ), ( 'Non JSON Exception', test_exception_handler_non_json_exception, 'status_code', 500, 'json', {'code': 'bad_request', 'message': 'oops'}, [call()], [call(test_exception_handler_non_json_exception)], ), ( 'JSON Exception', test_exception_handler_json_exception, 'status_code', 404, 'json', {'code': 'applesauce_not_found', 'message': 'Applesauce Not Found'}, [], [call(test_exception_handler_json_exception)], ), ( 'JSON Exception with No Status Specified', test_exception_handler_no_status_json_exception, 'status_code', 500, 'json', {'code': 'applesauce_not_found', 'message': 'Applesauce Not Found'}, [call()], [call(test_exception_handler_no_status_json_exception)], ), ( 'JSON Exception with None Status Specified', test_exception_handler_none_status_json_exception, 'status_code', 500, 'json', {'code': 'applesauce_not_found', 'message': 'Applesauce Not Found'}, [call()], [call(test_exception_handler_none_status_json_exception)], ), ], ) def test_exception_handler( mock_g, mock_sentry_client, test_description, exception, code_key, expected_code, message_key, expected_message, expected_sentry_client_capture_exception_calls, expected_g_log_exception_calls, app_context, ): mock_logger = NonCallableMagicMock( error=MagicMock(), ) with api.app.test_request_context(): mock_g.log = mock_logger result = handlers_common.exception_handler(exception) assert mock_logger.exception.mock_calls == expected_g_log_exception_calls, test_description assert mock_sentry_client.capture_exception.mock_calls == ( expected_sentry_client_capture_exception_calls ), test_description assert getattr(result, code_key) == expected_code, test_description assert getattr(result, message_key) == expected_message, test_description @pytest.mark.parametrize( [ 'account_from_headers', 'expected', 'header_tuple', 'account_identity', 'orchard_user_id', ], [ ( subaccount_test_helper.grass_subaccount_account( subaccount_test_helper.subaccount_response() ), 200, ['subaccount', 1], subaccount_test_helper.fixture_identity_subaccount(), None, ), ( vendor_test_helper.fixture_grass_vendor_account( vendor_test_helper.fixture_full_vendor() ), 200, ['vendor', 2], vendor_test_helper.fixture_identity_vendor(), None, ), (grass_test_helper.fixture_grass_account_none(), 401, None, None, None), ( grass_test_helper.fixture_grass_account_none(), 200, None, None, fixture_orchard_user_id, ), ], ) def test_identity( monkeypatch, fixture_client, account_from_headers, expected, account_identity, header_tuple, fixture_user_details, orchard_user_id, ): """Test route that returns account identity.""" headers = {'Content-Type': 'application/json'} if header_tuple: headers['Grass-Account-Type'] = header_tuple[0] headers['Grass-Account-Id'] = header_tuple[1] if orchard_user_id: headers['Orchard-user-id'] = orchard_user_id fast_patch(monkeypatch, {flask_request: dict(get_grass_headers=account_from_headers)}) fast_patch(monkeypatch, {identity: dict(get_identity=account_identity)}) fast_patch(monkeypatch, {identity: dict(get_user_details=fixture_user_details)}) result = fixture_client.head('/identity', headers=headers) assert result.status_code == expected assert result.headers.get('Correlation-Id')