"""Tests for user handlers.""" import json from unittest.mock import patch from owsrequest import flask_request from owsresponse import response import pytest from pythonfeatures import pythonfeatures from users import ( constants, # noqa ) from users.logic import notifications, user_info @pytest.mark.parametrize( 'users, user_type, expected_result, expected_status_code', ( ( ( {'a': 1}, {'b': 2}, ), constants.USER_INFO_USER_TYPE_OA, {'items': [{'a': 1}, {'b': 2}]}, 200, ), ( ( {'a': 1}, {'b': 2}, ), constants.USER_INFO_USER_TYPE_ALW, {'items': [{'a': 1}, {'b': 2}]}, 200, ), ( ( {'a': 1}, {'b': 2}, ), 'funky_user_type', { 'code': constants.UNSUPPORTED_USER_TYPE_CODE, 'message': constants.UNSUPPORTED_USER_TYPE, }, 400, ), ), ) def test_get_users(users, user_type, expected_result, expected_status_code, fixture_client, mocker): """Test 'GET /users' handler.""" get_users_mock = mocker.patch('users.logic.user_info.get_users') get_users_mock.return_value = response.Response(message=users) handler_response = fixture_client.get( '/users?{param}={value}'.format(param=constants.QUERY_PARAM_USER_TYPE, value=user_type) ) result = json.loads(handler_response.data.decode()) assert handler_response.status_code == expected_status_code assert result == expected_result def test_get_orchadmin_users_success(fixture_client, mocker): """Test that the OA user endpoint returns the expected user data for given user_ids.""" expected_result = { 'users': [{'user_id': '1', 'first_name': 'test', 'last_name': 'user', 'active': True}, None] } get_orchadmin_users_mock = mocker.patch('users.logic.user_info.get_orchadmin_users') get_orchadmin_users_mock.return_value = expected_result handler_response = fixture_client.post( '/users/orchadmin/dataloader', json={'user_ids': ['1', '2']}, ) result = json.loads(handler_response.data.decode()) assert handler_response.status_code == 200 assert result == expected_result def test_get_orchadmin_users_exception(fixture_client, mocker): """Test exception handling in /users/orchadmin/dataloader route.""" sentry_mock = mocker.patch('users.connectors.sentry.sentry_client.capture_exception') mocker.patch( 'users.logic.user_info.get_orchadmin_users', side_effect=Exception('Database failure') ) resp = fixture_client.post( '/users/orchadmin/dataloader', json={'user_ids': ['1', '2']}, ) assert resp.status_code == 500 result = json.loads(resp.data.decode()) assert result['message'] == ['Database failure'] assert sentry_mock.called @patch('users.handlers.user.Neo4jSession.__enter__') @patch('users.handlers.user.Neo4jSession.__exit__') def test_get_user_by_id(neo4j_exit, neo4j_enter, mocker, fixture_client): """Test /users/ handler.""" mocker.patch.object( user_info, 'get_user_raw', return_value=response.Response({'from': 'get_user_raw'}) ) handler_response = fixture_client.get('/users/alw:123') assert handler_response.status_code == 200 result = json.loads(handler_response.data.decode()) assert result == {'from': 'get_user_raw'} def test_get_minimum_user_details_by_id(mocker, fixture_client): """Test /users//minimum-details handler.""" expected_response = {'from': 'get_users_minimum_details'} mocker.patch.object( user_info, 'get_users_minimum_details', return_value=response.Response(expected_response) ) handler_response = fixture_client.get('/users/alw:123/minimum-details') assert handler_response.status_code == 200 result = json.loads(handler_response.data.decode()) assert result == expected_response user_info.get_users_minimum_details.assert_called_with('alw', '123', False) def test_get_minimum_user_details_by_id_include_roles(mocker, fixture_client): """Test /users//minimum-details handler.""" expected_response = {'from': 'get_users_minimum_details'} mocker.patch.object( user_info, 'get_users_minimum_details', return_value=response.Response(expected_response) ) handler_response = fixture_client.get('/users/alw:123/minimum-details?include_roles=Yes') assert handler_response.status_code == 200 result = json.loads(handler_response.data.decode()) assert result == expected_response user_info.get_users_minimum_details.assert_called_with('alw', '123', True) def test_get_user_session_metadata_for_app(mocker, fixture_client): """Test /users//session/ handler.""" mocker.patch.object( user_info, 'get_user_session_metadata_for_app', return_value=response.Response('whatever') ) assert fixture_client.get('/users/4444/session/alw').data.decode() == 'whatever' @pytest.mark.parametrize( 'users, param, expected_result, expected_status_code', ( ( 9, 'user_id', [ { 'contact_last_name': 'Langston', 'master': 'Y', 'active': 1, 'permissions': 'Administrator', 'vendor_id': 9, 'user_id': 9, 'contact_first_name': 'Grant', 'subaccount': 'El Subaccount', 'contact_email': 'QAORCHEmailContact@theorchard.com', 'subaccount_id': 3, } ], 200, ), ( 31045, 'contact_id', [ { 'contact_last_name': 'Langston', 'master': 'Y', 'active': 1, 'permissions': 'Administrator', 'vendor_id': 9, 'user_id': 9, 'contact_first_name': 'Grant', 'subaccount': 'El Subaccount', 'contact_email': 'QAORCHEmailContact@theorchard.com', 'subaccount_id': 3, } ], 200, ), ( 3, 'subaccount_id', [ { 'contact_last_name': 'Langston', 'master': 'Y', 'active': 1, 'permissions': 'Administrator', 'vendor_id': 9, 'user_id': 9, 'contact_first_name': 'Grant', 'subaccount': 'El Subaccount', 'contact_email': 'QAORCHEmailContact@theorchard.com', 'subaccount_id': 3, } ], 200, ), ( 456, 'funky_user_type', { 'code': constants.BAD_PARAMS_ERROR_CODE, 'message': constants.BAD_PARAMS_ERROR_message, }, 400, ), ), ) def test_get_user_document( users, param, expected_result, expected_status_code, fixture_client, mocker ): """Test 'GET /users/document' handler.""" get_users_mock = mocker.patch('users.logic.user_info.get_user_basic_info') get_users_mock.return_value = response.Response(message=expected_result) handler_response = fixture_client.get( '/users/document?{param}={value}'.format(param=param, value=users) ) result = json.loads(handler_response.data.decode()) assert handler_response.status_code == expected_status_code assert result == expected_result @pytest.mark.parametrize( ('data', 'headers', 'get_attributes_response', 'expected_response'), ( # ok ( {'active': 'Y'}, { 'Content-Type': 'application/json', 'Grass-Account-Id': 1234, 'Grass-Account-Type': 'vendor', }, response.Response( message={'user_id': 'alw:555', 'vendor_id': 1234, 'subaccount_id': 1} ), response.Response(), ), # alw user not found ( {'active': 'Y'}, { 'Content-Type': 'application/json', 'Grass-Account-Id': 1234, 'Grass-Account-Type': 'vendor', }, response.create_not_found_response(message={'pizza': 'party'}), response.create_not_found_response(message={'pizza': 'party'}), ), # missing `active` from json body ( {'test': 'Y'}, { 'Content-Type': 'application/json', 'Grass-Account-Id': '1234', 'Grass-Account-Type': 'vendor', }, response.Response( message={'user_id': 'alw:555', 'vendor_id': 1234, 'subaccount_id': 1} ), response.Response(status=400, message=constants.MISSING_STATUS_MESSAGE), ), ), ) @patch('users.handlers.user.Neo4jSession.__enter__') @patch('users.handlers.user.Neo4jSession.__exit__') def test_update_user_status( neo4j_exit, neo4j_enter, data, headers, get_attributes_response, expected_response, mocker, fixture_client, ): """Test PUT /users//status handler.""" mocker.patch.object(flask_request, 'verify_grass_headers', return_value=response.Response()) mocker.patch.object(notifications, 'subscribe_labelprofile') mocker.patch.object(notifications, 'unsubscribe_labelprofile') mocker.patch.object(user_info, 'update_user_status', return_value=expected_response) mocker.patch.object( user_info, 'get_attributes_for_vend_contact', return_value=get_attributes_response ) mocker.patch.object( pythonfeatures, 'get_single_feature_by_attributes', return_value=response.Response(message='control'), ) handler_response = fixture_client.put('/users/555/status', json=data, headers=headers) assert handler_response.status_code == expected_response.status @pytest.mark.parametrize( ('data', 'feature_response', 'deactivate_called'), ( # when Flag is OFF ({'active': 'Y'}, response.Response(message='control'), False), ({'active': 'N'}, response.Response(message='control'), False), # when Flag is ON ({'active': 'Y'}, response.Response(message='enabled'), False), ({'active': 'N'}, response.Response(message='enabled'), True), ), ) @patch('users.handlers.user.Neo4jSession.__enter__') @patch('users.handlers.user.Neo4jSession.__exit__') def test_update_user_status_with_auth0_deactivate( neo4j_exit, neo4j_enter, data, feature_response, deactivate_called, mocker, fixture_client ): """Test PUT /users//status handler.""" update_response = response.Response({'status': 'updated'}) deactivate_response = response.Response('response from deactivate_user') reactivate_response = response.Response('response from reactivate_user') headers = { 'Content-Type': 'application/json', 'Grass-Account-Id': '234234', 'Grass-Account-Type': 'vendor', } get_attributes = response.Response( message={'user_id': 'alw:1234', 'vendor_id': 234234, 'subaccount_id': 0} ) mocker.patch.object(flask_request, 'verify_grass_headers', return_value=response.Response()) mocker.patch.object(user_info, 'update_user_status', return_value=update_response) mocker.patch.object(user_info, 'get_attributes_for_vend_contact', return_value=get_attributes) mocker.patch.object( pythonfeatures, 'get_single_feature_by_attributes', return_value=feature_response ) mocker.patch.object(user_info, 'deactivate_user', return_value=deactivate_response) mocker.patch.object(user_info, 'reactivate_user', return_value=reactivate_response) mocker.patch.object(notifications, 'unsubscribe_labelprofile') mocker.patch.object(notifications, 'subscribe_labelprofile') handler_response = fixture_client.put('/users/1234/status', json=data, headers=headers) assert handler_response.status_code == 200 if deactivate_called: user_info.deactivate_user.assert_called_once_with('1234', 'alw') elif feature_response.message == 'enabled': user_info.reactivate_user.assert_called_once_with('1234') else: assert not user_info.deactivate_user.called assert not user_info.reactivate_user.called @pytest.mark.parametrize( 'url, headers, status_code, access_verification_result', ( ( '/vendor/25824/users', { 'Grass-Account-Id': 25824, 'Grass-Account-Type': 'vendor', 'Orchard-User-Id': 'alw:1800', }, 200, response.Response(status=200), ), ( '/subaccount/28518/users', { 'Grass-Account-Id': 25824, 'Grass-Account-Type': 'vendor', 'Orchard-User-Id': 'alw:1800', }, 403, response.Response(status=403), ), ), ) def test_get_contact_details( user_data, url, headers, status_code, access_verification_result, mocker, fixture_client ): """Test ///users.""" mocker.patch.object( flask_request, 'verify_grass_access', return_value=access_verification_result ) mocker.patch.object(user_info, 'get_contact_details', return_value=response.Response(user_data)) handler_response = fixture_client.get(url, headers=headers) assert handler_response.status_code == status_code if status_code == 200: result = json.loads(handler_response.data.decode()) assert result == user_data def test_get_alw_users_by_email_success(mocker, fixture_client): """Test /users/get-alw-users-by-email?email=email handler.""" expected_response = {'items': [{'email': 'email', 'user_id': 123, 'vendor_id': 456}]} mocker.patch.object( user_info, 'get_alw_users_by_email', return_value=response.Response(message=expected_response), ) handler_response = fixture_client.get('/users/get-alw-users-by-email?email=email') assert handler_response.status_code == 200 result = json.loads(handler_response.data.decode()) assert result == expected_response user_info.get_alw_users_by_email.assert_called_with('email') def test_get_alw_users_by_email_bad_param(mocker, fixture_client): """Test /users/get-alw-users-by-email handler.""" expected_response = { 'code': constants.BAD_PARAMS_ERROR_CODE, 'message': constants.BAD_PARAMS_ERROR_message, } mocker.patch.object( user_info, 'get_alw_users_by_email', return_value=response.Response(expected_response) ) handler_response = fixture_client.get('/users/get-alw-users-by-email') assert handler_response.status_code == 400 result = json.loads(handler_response.data.decode()) assert result == expected_response user_info.get_alw_users_by_email.assert_not_called() def test_get_user_roles(mocker, fixture_client): """Test /users/roles/ handler.""" headers = { 'Grass-Account-Id': '234234', 'Grass-Account-Type': 'vendor', 'Orchard-User-Id': 'alw:123', } mock_user_info = mocker.patch.object( user_info, 'get_roles_for_user', return_value=response.Response('x') ) assert fixture_client.get('/users/roles/alw', headers=headers).data.decode() == 'x' mock_user_info.assert_called_once_with('123', 'alw') def test_get_vend_contact_user_roles(mocker, fixture_client): """Test get_vend_contact_user_roles handler.""" mock_user_info = mocker.patch.object( user_info, 'get_roles_for_user', return_value=response.Response('x') ) result = fixture_client.get('/ws/users/roles/123') assert result.status == '200 OK' assert result.data.decode() == 'x' mock_user_info.assert_called_once_with(123, 'alw') @pytest.mark.parametrize( ('email', 'email_to_logic'), [ ['test@theorchard.com', 'test@theorchard.com'], ['Test@theorchard.com', 'test@theorchard.com'], ['fooBar@theorchard.com', 'foobar@theorchard.com'], ['FOO@theorchard.com', 'foo@theorchard.com'], ], ) def test_get_user_by_email(mocker, fixture_client, email, email_to_logic): """Test get_user_by_email handler.""" expected_response = {'user': 'details'} mocker.patch.object( user_info, 'get_alw_users_by_email', return_value=response.Response(expected_response) ) handler_response = fixture_client.get(f'/users/get-alw-users-by-email?email={email}') assert handler_response.status_code == 200 result = json.loads(handler_response.data.decode()) assert result == expected_response user_info.get_alw_users_by_email.assert_called_with(email_to_logic) @pytest.mark.parametrize( 'product_managers, headers, expected_result, expected_status_code', ( ( [{'a': 1}, {'b': 2}], {constants.GRASS_ACCOUNT_ID: 1234, constants.GRASS_ACCOUNT_TYPE: 'vendor'}, [{'a': 1}, {'b': 2}], 200, ), ( [], {constants.GRASS_ACCOUNT_ID: 1234, constants.GRASS_ACCOUNT_TYPE: 'vendor'}, [], 200, ), ), ) def test_get_product_managers( product_managers, headers, expected_result, expected_status_code, fixture_client, mocker ): """Test 'GET /users/product_managers' handler.""" get_product_managers_mock = mocker.patch('users.logic.user_info.get_product_managers') get_product_managers_mock.return_value = response.Response(message=product_managers) handler_response = fixture_client.get('/users/product_managers', headers=headers) result = json.loads(handler_response.data.decode()) assert handler_response.status_code == expected_status_code assert result == expected_result @pytest.mark.parametrize( 'mock_get_active_closers, expected_result, expected_status_code', ( ( response.Response( status=200, message={'items': [{'user_id': 12, 'first_name': 'test', 'last_name': 'user'}]}, ), {'items': [{'user_id': 12, 'first_name': 'test', 'last_name': 'user'}]}, 200, ), ( response.Response( status=200, message={'items': []}, ), {'items': []}, 200, ), ( response.create_fatal_response(), {'code': 'internal_error', 'message': None}, 500, ), ), ) def test_get_closers( mock_get_active_closers, expected_result, expected_status_code, fixture_client, mocker ): """Test 'GET /users/closers' handler.""" get_active_closers_mock = mocker.patch('users.logic.user_info.get_active_closers') get_active_closers_mock.return_value = mock_get_active_closers handler_response = fixture_client.get('/users/closers') result = json.loads(handler_response.data.decode()) assert handler_response.status_code == expected_status_code assert result == expected_result