def test_list_devices_with_search_constraints(self): expressions = shared_messages.SearchExpression( expression='serial_number') expected_response = device_message.ListDevicesResponse( devices=[device_message.Device(serial_number='6789')], additional_results=False) request = device_message.Device(query=shared_messages.SearchRequest( query_string='sn:6789', expressions=[expressions], returned_fields=['serial_number'])) response = self.service.list_devices(request) self.assertEqual(response.devices[0].serial_number, expected_response.devices[0].serial_number) self.assertFalse(response.additional_results)
def test_list_devices_with_search_constraints(self): expressions = shared_messages.SearchExpression( expression='serial_number') expected_response = device_messages.ListDevicesResponse( devices=[ device_messages.Device(serial_number='6789', guest_permitted=True) ], total_results=1, total_pages=1) request = device_messages.Device(query=shared_messages.SearchRequest( query_string='sn:6789', expressions=[expressions], returned_fields=['serial_number'])) response = self.service.list_devices(request) self.assertEqual(response, expected_response)
def test_list_shelves_with_search_constraints(self): expressions = shared_messages.SearchExpression(expression='location') expected_response = shelf_messages.ListShelfResponse(shelves=[ shelf_messages.Shelf(location=self.shelf.location, shelf_request=shelf_messages.ShelfRequest( location=self.shelf.location, urlsafe_key=self.shelf.key.urlsafe())) ], total_results=1, total_pages=1) request = shelf_messages.Shelf( query=shared_messages.SearchRequest(query_string='location:NYC', expressions=[expressions], returned_fields=['location'])) response = self.service.list_shelves(request) self.assertEqual(response, expected_response)
def testSearchRequest(self): search_exp = shared_messages.SearchExpression( expression='FAKE-EXPRESSION', direction=shared_messages.SortDirection(0)) search_request = shared_messages.SearchRequest( query_string='FAKE-QUERY-STRING', expressions=[search_exp], returned_fields=['FAKE-RETURN']) self.assertEqual(search_request.query_string, 'FAKE-QUERY-STRING') self.assertListEqual(search_request.returned_fields, ['FAKE-RETURN']) self.assertEqual(search_request.expressions[0].expression, 'FAKE-EXPRESSION') self.assertEqual(search_request.expressions[0].direction.name, 'ASCENDING')
class DeviceApiTest(parameterized.TestCase, loanertest.EndpointsTestCase): """Tests for the Device API.""" def setUp(self): super(DeviceApiTest, self).setUp() self.service = device_api.DeviceApi() self.login_admin_endpoints_user() self.shelf = shelf_model.Shelf.enroll(user_email=loanertest.USER_EMAIL, location='NYC', capacity=10, friendly_name='GnG', latitude=40.6892534, longitude=-74.0466891, altitude=1.0) self.device = device_model.Device() self.device.serial_number = '123ABC' self.device.asset_tag = '12345' self.device.enrolled = True self.device.device_model = 'Google Pixelbook' self.device.due_date = datetime.datetime(2017, 11, 15) self.device.last_known_healthy = datetime.datetime(2017, 11, 1) self.device.shelf = self.shelf.key self.device.assigned_user = loanertest.USER_EMAIL self.device.assignment_date = datetime.datetime(2017, 11, 1) self.device.current_ou = '/' self.device.ou_changed_date = datetime.datetime(2017, 11, 1) self.device.locked = False self.device.lost = False self.device.chrome_device_id = 'unique_id_1' self.device.last_heartbeat = datetime.datetime(2017, 11, 1) self.device.damaged = False self.device.damaged_reason = None self.device.put() self.device.set_last_reminder(0) self.device.set_next_reminder(1, datetime.timedelta(hours=2)) device_model.Device( serial_number='6789', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_2', shelf=self.shelf.key, damaged=False, ).put() self.unenrolled_device = device_model.Device( serial_number='4567', enrolled=False, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_3', damaged=False, ) self.unenrolled_device.put() self.unenrolled_device_directory = { 'deviceId': 'unique_id', 'serialNumber': '4567', 'status': 'ACTIVE', 'lastSync': datetime.datetime.utcnow(), 'model': 'HP Chromebook 13 G1', 'orgUnitPath': constants.ORG_UNIT_DICT['DEFAULT'], } def tearDown(self): super(DeviceApiTest, self).tearDown() self.service = None @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_enroll(self, mock_directoryclass): """Tests Enroll with mock methods.""" mock_directoryclient = mock_directoryclass.return_value mock_directoryclient.get_chrome_device.return_value = ( self.unenrolled_device) mock_directoryclient.get_chrome_device_by_serial.return_value = ( self.unenrolled_device_directory) retrieved_device = device_model.Device.get( serial_number=self.unenrolled_device.serial_number) self.assertFalse(retrieved_device.enrolled) request = device_messages.DeviceRequest( serial_number=self.unenrolled_device.serial_number) with mock.patch.object(self.service, 'check_xsrf_token', autospec=True) as mock_xsrf_token: response = self.service.enroll(request) self.assertIsInstance(response, message_types.VoidMessage) retrieved_device = device_model.Device.get( serial_number=self.unenrolled_device.serial_number) self.assertTrue(retrieved_device.enrolled) self.assertEqual(mock_xsrf_token.call_count, 1) @parameterized.parameters((datastore_errors.BadValueError, ), (device_model.DeviceCreationError, )) @mock.patch.object(device_model, 'Device', autospec=True) def test_enroll_error(self, test_error, mock_device_cls): mock_device_cls.enroll.side_effect = test_error request = device_messages.DeviceRequest( serial_number=self.unenrolled_device.serial_number) with self.assertRaises(endpoints.BadRequestException): self.service.enroll(request) @mock.patch.object(device_model, 'Device', autospec=True) def test_unenroll_error(self, mock_device_cls): mock_device_cls.get.return_value.unenroll.side_effect = ( device_model.FailedToUnenrollError()) request = device_messages.DeviceRequest( serial_number=self.unenrolled_device.serial_number) with self.assertRaises(endpoints.BadRequestException): self.service.unenroll(request) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_unenroll(self, mock_xsrf_token, mock_directoryclass): mock_directoryclient = mock_directoryclass.return_value mock_directoryclient.move_chrome_device_org_unit.return_value = ( loanertest.TEST_DIR_DEVICE_DEFAULT) request = device_messages.DeviceRequest( serial_number=self.device.serial_number) self.assertTrue(self.device.enrolled) response = self.service.unenroll(request) self.assertFalse(self.device.enrolled) self.assertIsNone(self.device.assigned_user) self.assertIsNone(self.device.due_date) self.assertIsInstance(response, message_types.VoidMessage) assert mock_xsrf_token.call_count == 1 @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_unlock(self, mock_xsrf_token, mock_directoryclass): self.device.lost = True self.device.locked = True self.device.put() mock_directoryclient = mock_directoryclass.return_value mock_directoryclient.move_chrome_device_org_unit.return_value = ( loanertest.TEST_DIR_DEVICE_DEFAULT) request = device_messages.DeviceRequest( serial_number=self.device.serial_number) response = self.service.unlock(request) self.assertFalse(self.device.locked) self.assertFalse(self.device.lost) self.assertIsInstance(response, message_types.VoidMessage) assert mock_xsrf_token.call_count == 1 @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_unlock_directory_error(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.reenable_chrome_device.side_effect = ( directory.DirectoryRPCError) with self.assertRaises(endpoints.BadRequestException): self.service.unlock( device_messages.DeviceRequest(asset_tag='12345')) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_unlock_move_ou_error(self, mock_directory_class): del mock_directory_class # Unused. with mock.patch.object( self.device, 'move_to_default_ou', side_effect=device_model.UnableToMoveToDefaultOUError): with self.assertRaises(endpoints.BadRequestException): self.service.unlock( device_messages.DeviceRequest(asset_tag='12345')) @mock.patch('__main__.device_model.Device.device_audit_check') def test_device_audit_check(self, mock_device_audit_check): request = device_messages.DeviceRequest(unknown_identifier='6765') self.assertRaisesRegexp(device_api.endpoints.NotFoundException, device_api._NO_DEVICE_MSG % '6765', self.service.device_audit_check, request) device_model.Device(serial_number='12345', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_1', damaged=False).put() request = device_messages.DeviceRequest(unknown_identifier='12345') response = self.service.device_audit_check(request) assert mock_device_audit_check.call_count == 1 self.assertIsInstance(response, message_types.VoidMessage) def test_device_audit_check_device_not_enrolled(self): request = device_messages.DeviceRequest( unknown_identifier=self.device.serial_number) self.device.enrolled = False with self.assertRaises(device_api.endpoints.BadRequestException): self.service.device_audit_check(request) def test_device_audit_check_device_damaged(self): request = device_messages.DeviceRequest( unknown_identifier=self.device.serial_number) self.device.damaged = True with self.assertRaises(device_api.endpoints.BadRequestException): self.service.device_audit_check(request) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_not_found(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' request = device_messages.DeviceRequest(unknown_identifier='not-found') with self.assertRaises(device_api.endpoints.NotFoundException): self.service.get_device(request) def test_get_device_unenrolled(self): request = device_messages.DeviceRequest( unknown_identifier=self.device.serial_number) self.device.enrolled = False with self.assertRaises(device_api.endpoints.BadRequestException): self.service.get_device(request) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' asset_tag_response = self.service.get_device( device_messages.DeviceRequest(asset_tag='12345')) chrome_device_id_response = self.service.get_device( device_messages.DeviceRequest(chrome_device_id='unique_id_1')) serial_number_response = self.service.get_device( device_messages.DeviceRequest(serial_number='123ABC')) urlkey_response = self.service.get_device( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) unknown_identifier_response = self.service.get_device( device_messages.DeviceRequest(unknown_identifier='123ABC')) self.assertIsInstance(asset_tag_response, device_messages.Device) self.assertIsInstance(chrome_device_id_response, device_messages.Device) self.assertIsInstance(serial_number_response, device_messages.Device) self.assertIsInstance(urlkey_response, device_messages.Device) self.assertIsInstance(unknown_identifier_response, device_messages.Device) self.assertEqual(self.device.serial_number, asset_tag_response.serial_number) self.assertEqual(self.device.device_model, urlkey_response.device_model) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_no_permission(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' email = 'random@{}'.format(loanertest.USER_DOMAIN) self.login_endpoints_user(email=email) with self.assertRaises(endpoints.UnauthorizedException): self.service.get_device( device_messages.DeviceRequest( serial_number=self.device.serial_number)) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_has_permission(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' device = self.service.get_device( device_messages.DeviceRequest( serial_number=self.device.serial_number)) self.assertIsInstance(device, device_messages.Device) self.assertEqual(device.serial_number, self.device.serial_number) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_assigned_user(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' email = 'random@{}'.format(loanertest.USER_DOMAIN) self.login_endpoints_user(email=email) self.device.assigned_user = email self.device.put() device = self.service.get_device( device_messages.DeviceRequest( serial_number=self.device.serial_number)) self.assertIsInstance(device, device_messages.Device) self.assertEqual(device.serial_number, self.device.serial_number) @parameterized.parameters(directory.DirectoryRPCError, directory.GivenNameDoesNotExistError) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_directory_errors(self, test_error, mock_directory_class): request = device_messages.DeviceRequest(asset_tag='12345') mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.side_effect = test_error self.assertIsNone(self.service.get_device(request).given_name) @parameterized.parameters(( device_messages.Device(enrolled=True), 2, ), ( device_messages.Device(current_ou='/'), 2, ), ( device_messages.Device(enrolled=False), 1, ), ( device_messages.Device(query=shared_messages.SearchRequest( query_string='sn:6789')), 1, ), ( device_messages.Device(query=shared_messages.SearchRequest( query_string='at:12345')), 1, )) def test_list_devices(self, request, response_length): response = self.service.list_devices(request) self.assertEqual(response_length, len(response.devices)) def test_list_devices_invalid_page_size(self): with self.assertRaises(endpoints.BadRequestException): request = device_messages.Device(page_size=0) self.service.list_devices(request) def test_list_devices_with_search_constraints(self): expressions = shared_messages.SearchExpression( expression='serial_number') expected_response = device_messages.ListDevicesResponse( devices=[ device_messages.Device(serial_number='6789', guest_permitted=True) ], total_results=1, total_pages=1) request = device_messages.Device(query=shared_messages.SearchRequest( query_string='sn:6789', expressions=[expressions], returned_fields=['serial_number'])) response = self.service.list_devices(request) self.assertEqual(response, expected_response) def test_list_devices_with_filter_message(self): message = device_messages.Device(enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/') filters = api_utils.to_dict(message, device_model.Device) request = device_messages.Device(**filters) response = self.service.list_devices(request) expected_response = device_messages.ListDevicesResponse( devices=[ device_messages.Device(serial_number='6789', identifier='6789', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', locked=False, lost=False, chrome_device_id='unique_id_2', damaged=False, guest_permitted=True) ], total_results=1, total_pages=1) self.assertEqual(response, expected_response) @mock.patch('__main__.device_api.shelf_api.get_shelf') def test_list_devices_with_shelf_filter(self, mock_get_shelf): # Test for shelf location as filter. mock_get_shelf.return_value = self.shelf shelf_request_message = shelf_messages.ShelfRequest( location=self.shelf.location) message = shelf_messages.Shelf(shelf_request=shelf_request_message) request = device_messages.Device(shelf=message) response = self.service.list_devices(request) mock_get_shelf.assert_called_once_with(shelf_request_message) self.assertEqual(len(response.devices), 2) def test_list_devices_with_offset(self): request = device_messages.Device(page_size=1, page_number=1) response = self.service.list_devices(request) self.assertEqual(1, len(response.devices)) previouse_response = response # Get next page results and make sure it's not the same as last. request = device_messages.Device(page_size=1, page_number=2) response = self.service.list_devices(request) self.assertEqual(1, len(response.devices)) self.assertNotEqual(response, previouse_response) def test_list_devices_inactive_no_shelf(self): request = device_messages.Device(enrolled=False, page_size=1) response = self.service.list_devices(request) expected_response = device_messages.ListDevicesResponse( devices=[ device_messages.Device( serial_number=self.unenrolled_device.serial_number, identifier=self.unenrolled_device.serial_number, enrolled=self.unenrolled_device.enrolled, device_model=self.unenrolled_device.device_model, current_ou=self.unenrolled_device.current_ou, locked=self.unenrolled_device.locked, lost=self.unenrolled_device.lost, chrome_device_id=self.unenrolled_device.chrome_device_id, damaged=self.unenrolled_device.damaged, guest_permitted=True) ], total_results=1, total_pages=1) self.assertEqual(expected_response, response) @mock.patch('__main__.device_model.Device.list_by_user') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_list_user_devices(self, mock_xsrf_token, mock_list_by_user): self.login_endpoints_user() device2 = device_model.Device() device2.serial_number = '123ABC' device2.assigned_user = loanertest.USER_EMAIL device2.assignment_date = datetime.datetime(2017, 11, 1) device2.due_date = datetime.datetime(2017, 11, 4) device2.put() request = message_types.VoidMessage() mock_list_by_user.return_value = [self.device, device2] response = self.service.list_user_devices(request) self.assertEqual(response.devices[0].serial_number, self.device.serial_number) self.assertEqual(len(response.devices), 2) assert mock_xsrf_token.call_count == 1 mock_list_by_user.assert_called_once_with(loanertest.USER_EMAIL) @mock.patch('__main__.device_model.Device.enable_guest_mode') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_enable_guest_mode(self, mock_xsrf_token, mock_enableguest): config_model.Config.set('allow_guest_mode', True) self.login_endpoints_user() self.service.enable_guest_mode( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) assert mock_enableguest.called assert mock_xsrf_token.call_count == 1 mock_xsrf_token.reset_mock() mock_enableguest.reset_mock() self.service.enable_guest_mode( device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id)) assert mock_enableguest.called assert mock_xsrf_token.call_count == 1 def guest_disabled_error(*args, **kwargs): del args, kwargs # Unused. raise device_model.GuestNotAllowedError( device_model._GUEST_MODE_DISABLED_MSG) def directory_error(*args, **kwargs): del args, kwargs # Unused. raise device_model.EnableGuestError( 'Directory broke, all your fault.') mock_enableguest.side_effect = guest_disabled_error with self.assertRaises(endpoints.UnauthorizedException): self.service.enable_guest_mode( device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id)) mock_enableguest.side_effect = directory_error with self.assertRaises(endpoints.InternalServerErrorException): self.service.enable_guest_mode( device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id)) def test_enable_guest_unassigned(self): config_model.Config.set('allow_guest_mode', True) self.device.assigned_user = None self.device.put() with self.assertRaisesRegexp(endpoints.UnauthorizedException, device_model._UNASSIGNED_DEVICE): self.service.enable_guest_mode( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch('__main__.device_model.Device.loan_extend') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_extend_loan(self, mock_xsrf_token, mock_loanextend): tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1) self.login_endpoints_user() self.service.extend_loan( device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()), extend_date=tomorrow)) mock_loanextend.assert_called_once_with( user_email=loanertest.USER_EMAIL, extend_date_time=tomorrow) assert mock_xsrf_token.call_count == 1 mock_xsrf_token.reset_mock() mock_loanextend.reset_mock() self.service.extend_loan( device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id), extend_date=tomorrow)) mock_loanextend.assert_called_once_with( user_email=loanertest.USER_EMAIL, extend_date_time=tomorrow) assert mock_xsrf_token.call_count == 1 mock_loanextend.side_effect = device_model.ExtendError self.assertRaises( device_api.endpoints.BadRequestException, self.service.extend_loan, device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id), extend_date=tomorrow)) def test_extend_loan_unassigned(self): self.device.assigned_user = None self.device.put() with self.assertRaisesRegexp(endpoints.UnauthorizedException, device_model._UNASSIGNED_DEVICE): self.service.extend_loan( device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id))) @mock.patch('__main__.device_model.Device.mark_damaged') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_damaged(self, mock_xsrf_token, mock_markdamaged): self.login_endpoints_user() self.service.mark_damaged( device_messages.DamagedRequest( device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()), damaged_reason='Foo')) mock_markdamaged.assert_called_once_with( user_email=loanertest.USER_EMAIL, damaged_reason='Foo') assert mock_xsrf_token.call_count == 1 mock_xsrf_token.reset_mock() mock_markdamaged.reset_mock() self.service.mark_damaged( device_messages.DamagedRequest( # No reason given. device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()))) mock_markdamaged.assert_called_once_with( user_email=loanertest.USER_EMAIL, damaged_reason=None) assert mock_xsrf_token.call_count == 1 @mock.patch.object(device_model.Device, 'mark_damaged') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_damaged__unauthorized(self, mock_xsrf, mock_markdamaged): del mock_xsrf # unusued. self.login_endpoints_user() mock_markdamaged.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.mark_damaged( device_messages.DamagedRequest( device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()), damaged_reason='Foo')) def test_mark_undamaged(self): with mock.patch.object(self.device, 'mark_undamaged') as mock_markundamaged: self.service.mark_undamaged( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) mock_markundamaged.assert_called_once_with( user_email=loanertest.SUPER_ADMIN_EMAIL) @mock.patch.object(device_model.Device, 'mark_undamaged') def test_mark_undamaged__unauthorized(self, mock_markundamaged): self.login_endpoints_user() with mock.patch.object(self.service, 'check_xsrf_token') as mock_xsrf: mock_markundamaged.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.mark_undamaged( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) assert mock_xsrf.call_count == 1 @mock.patch('__main__.device_model.Device.mark_lost') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_lost(self, mock_xsrf_token, mock_marklost): self.service.mark_lost( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) mock_marklost.assert_called_once_with( user_email=loanertest.SUPER_ADMIN_EMAIL) assert mock_xsrf_token.call_count == 1 @mock.patch.object(device_model.Device, 'mark_lost') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_lost__unauthorized(self, mock_xsrf_token, mock_marklost): del mock_xsrf_token # unused. mock_marklost.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.mark_lost( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch('__main__.device_model.Device.mark_pending_return') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_pending_return(self, mock_xsrf_token, mock_markreturned): self.login_endpoints_user() self.service.mark_pending_return( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) mock_markreturned.assert_called_once_with( user_email=loanertest.USER_EMAIL) assert mock_xsrf_token.call_count == 1 def test_mark_pending_return_unassigned(self): self.device.assigned_user = None self.device.put() with self.assertRaisesRegexp(endpoints.UnauthorizedException, device_model._UNASSIGNED_DEVICE): self.service.mark_pending_return( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch('__main__.device_model.Device.resume_loan') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_resume_loan(self, mock_xsrf_token, mock_resume_loan): self.login_endpoints_user() self.service.resume_loan( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) assert mock_resume_loan.call_count == 1 assert mock_xsrf_token.call_count == 1 @mock.patch.object(device_model.Device, 'resume_loan') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_resume_loan__unauthorized(self, mock_xsrf_token, mock_resume_loan): del mock_xsrf_token # unused. self.login_endpoints_user() mock_resume_loan.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.resume_loan( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) def test_get_device_errors(self): # No identifiers. with self.assertRaises(endpoints.BadRequestException): device_api._get_device(device_messages.DeviceRequest()) # URL-safe key that is still URL-safe, but technically not a key. with self.assertRaises(device_api.endpoints.BadRequestException): device_api._get_device( device_messages.DeviceRequest(urlkey='bad-key'))
class SearchTest(loanertest.EndpointsTestCase, parameterized.TestCase): _ASSIGNED_DATE = datetime.datetime(year=2017, month=1, day=1) @parameterized.parameters(( shelf_messages.Shelf(location='NY', capacity=50), 'location:NY capacity:50 enabled:True', ), ( shelf_messages.Shelf(location='NY', capacity=50, enabled=False), 'location:NY capacity:50 enabled:False', )) def test_to_query(self, message, expected_query): """Tests the creation of a valid search query from ndb properties.""" query = search_utils.to_query(message, shelf_model.Shelf) # The query is split because ndb properties are unordered when called by # model_class._properties. This test would be flaky otherwise. self.assertCountEqual(query.split(' '), expected_query.split(' ')) @parameterized.named_parameters( ('Shelf Message', shelf_messages.Shelf(), search.ScoredDocument( doc_id='test_doc_id', fields=[ search.NumberField(name='capacity', value=20.0), search.TextField(name='location', value='US MTV'), search.AtomField(name='location', value='US-MTV'), search.AtomField(name='enabled', value='True'), search.GeoField(name='lat_long', value=search.GeoPoint(52.37, 4.88)), search.TextField(name='not_present', value='MTV') ]), shelf_messages.Shelf(enabled=True, location='US-MTV', capacity=20, latitude=52.37, longitude=4.88), 1), ('Device Message', device_messages.Device(), search.ScoredDocument( doc_id='test_doc_id', fields=[ search.DateField(name='assignment_date', value=_ASSIGNED_DATE), search.TextField(name='serial_number', value='1234'), search.AtomField(name='enrolled', value='True'), search.TextField(name='assigned_user', value='user') ]), device_messages.Device( enrolled=True, serial_number='1234', assigned_user='******', max_extend_date=_ASSIGNED_DATE + datetime.timedelta(days=14), assignment_date=_ASSIGNED_DATE), 0)) def test_document_to_message(self, message, test_search_document, expected_message, log_call_count): """Tests the creation of a protorpc message from a search document.""" with mock.patch.object(search_utils, 'logging', autospec=True) as mock_logging: response_message = search_utils.document_to_message( test_search_document, message) self.assertEqual(response_message, expected_message) self.assertEqual(mock_logging.error.call_count, log_call_count) def test_calculate_page_offset(self): """Tests the calculation of page offset.""" page_size = 10 page_number = 5 offset = search_utils.calculate_page_offset(page_size, page_number) self.assertEqual(40, offset) def test_calculate_total_pages(self): """Tests the calculation of total pages.""" page_size = 6 total_results = 11 total_pages = search_utils.calculate_total_pages( page_size, total_results) self.assertEqual(2, total_pages) @parameterized.named_parameters( { 'testcase_name': 'QueryStringOnly', 'request': shared_messages.SearchRequest(query_string='enrolled:True'), 'expected_values': ('enrolled:True', None, []) }, { 'testcase_name': 'QueryStringWithReturnedFields', 'request': shared_messages.SearchRequest(query_string='location:US-NYC', returned_fields=['location']), 'expected_values': ('location:US-NYC', None, ['location']) }, ) def test_set_search_query_options(self, request, expected_values): """Tests setting the query options without sort options from message.""" returned_query, returned_sort_options, returned_returned_fields = ( search_utils.set_search_query_options(request)) expected_query, expected_sort_options, expcted_returned_fields = ( expected_values) self.assertEqual(expected_sort_options, returned_sort_options) self.assertEqual(expected_query, returned_query) self.assertEqual(expcted_returned_fields, returned_returned_fields) @parameterized.named_parameters( { 'testcase_name': 'ExpressionWithDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression( expression='enrolled', direction=shared_messages.SortDirection.ASCENDING) ]), 'expected_sort_options_expressions': [ search.SortExpression( expression='enrolled', direction=search.SortExpression.ASCENDING) ] }, { 'testcase_name': 'MultipleExpressionsWithDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression( expression='enrolled', direction=shared_messages.SortDirection.ASCENDING), shared_messages.SearchExpression( expression='serial_number', direction=shared_messages.SortDirection.DESCENDING) ]), 'expected_sort_options_expressions': [ search.SortExpression( expression='enrolled', direction=search.SortExpression.ASCENDING), search.SortExpression( expression='serial_number', direction=search.SortExpression.DESCENDING) ] }, { 'testcase_name': 'ExpressionWithoutDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression(expression='enrolled') ]), 'expected_sort_options_expressions': [search.SortExpression(expression='enrolled')] }, { 'testcase_name': 'MultipleExpressionsWithoutDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression(expression='enrolled'), shared_messages.SearchExpression( expression='serial_number') ]), 'expected_sort_options_expressions': [ search.SortExpression( expression='enrolled', direction=search.SortExpression.DESCENDING), search.SortExpression( expression='serial_number', direction=search.SortExpression.DESCENDING) ] }, ) def test_set_search_query_options_with_sort_options( self, request, expected_sort_options_expressions): """Tests setting query options with sort options from message.""" returned_query, returned_sort_options, returned_returned_fields = ( search_utils.set_search_query_options(request)) del returned_query # Unused. del returned_returned_fields # Unused. for i in range(len(returned_sort_options.expressions)): self.assertEqual(returned_sort_options.expressions[i].expression, expected_sort_options_expressions[i].expression) self.assertEqual(returned_sort_options.expressions[i].direction, expected_sort_options_expressions[i].direction)
class ShelfApiTest(parameterized.TestCase, loanertest.EndpointsTestCase): """Test for the Shelf API.""" def setUp(self): super(ShelfApiTest, self).setUp() self.patcher_directory = mock.patch( '__main__.device_model.directory.DirectoryApiClient') self.mock_directoryclass = self.patcher_directory.start() self.addCleanup(self.patcher_directory.stop) self.service = shelf_api.ShelfApi() self.login_admin_endpoints_user() self.patcher_xsrf = mock.patch( '__main__.shelf_api.root_api.Service.check_xsrf_token') self.shelf = shelf_model.Shelf.enroll(user_email=loanertest.USER_EMAIL, location='NYC', capacity=10, friendly_name='GnG', latitude=40.6892534, longitude=-74.0466891, altitude=1.0) shelf1 = shelf_model.Shelf.enroll(user_email=loanertest.USER_EMAIL, location='MTV', capacity=20) shelf2 = shelf_model.Shelf.enroll(user_email=loanertest.USER_EMAIL, location='SAO', capacity=10) self.disabled_shelf = shelf_model.Shelf.enroll( user_email=loanertest.USER_EMAIL, location='SVL', capacity=10, friendly_name='Bay') self.disabled_shelf.disable(loanertest.USER_EMAIL) self.shelf_locations = [ self.shelf.location, shelf1.location, shelf2.location, self.disabled_shelf.location ] self.device1_key = device_model.Device( serial_number='12345', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_1', damaged=False, ).put() self.device2_key = device_model.Device( serial_number='54321', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_2', damaged=False, ).put() self.device3_key = device_model.Device( serial_number='67890', enrolled=True, shelf=self.shelf.key, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_3', damaged=False, ).put() self.device4_key = device_model.Device( serial_number='ABC123', enrolled=True, shelf=self.shelf.key, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_4', damaged=False, ).put() self.device_identifiers = [ self.device1_key.get().serial_number, self.device2_key.get().serial_number, self.device3_key.get().serial_number ] def tearDown(self): super(ShelfApiTest, self).tearDown() self.service = None @mock.patch('__main__.root_api.Service.check_xsrf_token') @mock.patch('__main__.shelf_model.Shelf.enroll') def test_enroll(self, mock_enroll, mock_xsrf_token): """Test Enroll with mock methods.""" request = shelf_messages.EnrollShelfRequest( location='nyc', capacity=100, friendly_name='test', latitude=12.5, longitude=12.5, altitude=2.0, responsible_for_audit='precise', audit_interval_override=33, audit_notification_enabled=True) response = self.service.enroll(request) assert mock_xsrf_token.call_count == 1 self.assertIsInstance(response, message_types.VoidMessage) def test_enroll_bad_request(self): request = shelf_messages.EnrollShelfRequest(capacity=10) with self.assertRaisesRegexp(shelf_api.endpoints.BadRequestException, 'Entity has uninitialized properties'): self.service.enroll(request) request = shelf_messages.EnrollShelfRequest(location='nyc', capacity=10, latitude=12.5) with self.assertRaisesRegexp(shelf_api.endpoints.BadRequestException, shelf_model._LAT_LONG_MSG): self.service.enroll(request) @mock.patch('__main__.root_api.Service.check_xsrf_token') def test_get_by_location(self, mock_xsrf_token): request = shelf_messages.ShelfRequest(location='NYC') response = self.service.get(request) assert mock_xsrf_token.call_count == 1 self.assertEqual(self.shelf.location, response.location) self.assertEqual(self.shelf.friendly_name, response.friendly_name) def test_disable_by_location(self): request = shelf_messages.ShelfRequest(location='NYC') self.assertTrue(self.shelf.enabled) response = self.service.disable(request) self.assertFalse(self.shelf.enabled) self.assertIsInstance(response, message_types.VoidMessage) @mock.patch('__main__.root_api.Service.check_xsrf_token') def test_update_using_location(self, mock_xsrf_token): request = shelf_messages.UpdateShelfRequest( shelf_request=shelf_messages.ShelfRequest(location='NYC'), location='NYC-9th') response = self.service.update(request) assert mock_xsrf_token.call_count == 1 self.assertEqual(self.shelf.location, 'NYC-9th') shelf = shelf_model.Shelf.get(friendly_name='GnG') self.assertEqual(shelf.location, 'NYC-9th') self.assertIsInstance(response, message_types.VoidMessage) @parameterized.parameters(( shelf_messages.Shelf(capacity=10), 2, ), ( shelf_messages.Shelf(enabled=False), 1, ), ( shelf_messages.Shelf(query=shared_messages.SearchRequest( query_string='enabled:True capacity:10')), 2, ), ( shelf_messages.Shelf(query=shared_messages.SearchRequest( query_string='enabled:False')), 1, )) @mock.patch('__main__.root_api.Service.check_xsrf_token') def test_list_shelves(self, request, response_length, mock_xsrf_token): response = self.service.list_shelves(request) assert mock_xsrf_token.call_count == 1 self.assertEqual(response_length, len(response.shelves)) def test_list_shelves_invalid_page_size(self): with self.assertRaises(endpoints.BadRequestException): request = shelf_messages.Shelf(page_size=0) self.service.list_shelves(request) def test_list_shelves_with_search_constraints(self): expressions = shared_messages.SearchExpression(expression='location') expected_response = shelf_messages.ListShelfResponse(shelves=[ shelf_messages.Shelf(location=self.shelf.location, shelf_request=shelf_messages.ShelfRequest( location=self.shelf.location, urlsafe_key=self.shelf.key.urlsafe())) ], total_results=1, total_pages=1) request = shelf_messages.Shelf( query=shared_messages.SearchRequest(query_string='location:NYC', expressions=[expressions], returned_fields=['location'])) response = self.service.list_shelves(request) self.assertEqual(response, expected_response) def test_list_shelves_with_offset(self): previouse_shelf_locations = [] request = shelf_messages.Shelf(enabled=True, page_size=1, page_number=1) response = self.service.list_shelves(request) self.assertEqual(len(response.shelves), 1) previouse_shelf_locations.append(response.shelves[0].location) # Get next page results and make sure it's not the same as last. request = shelf_messages.Shelf(enabled=True, page_size=1, page_number=2) response = self.service.list_shelves(request) self.assertEqual(len(response.shelves), 1) self.assertNotIn(response.shelves[0], previouse_shelf_locations) previouse_shelf_locations.append(response.shelves[0].location) # Get next page results and make sure it's not the same as last 2. request = shelf_messages.Shelf(enabled=True, page_size=1, page_number=3) response = self.service.list_shelves(request) self.assertEqual(len(response.shelves), 1) self.assertNotIn(response.shelves[0], previouse_shelf_locations) previouse_shelf_locations.append(response.shelves[0].location) @mock.patch('__main__.root_api.Service.check_xsrf_token') @mock.patch('__main__.shelf_api.logging.info') def test_audit_using_shelf_location(self, mock_logging, mock_xsrf_token): request = shelf_messages.ShelfAuditRequest( shelf_request=shelf_messages.ShelfRequest(location='NYC'), device_identifiers=self.device_identifiers) response = self.service.audit(request) assert mock_xsrf_token.call_count == 1 mock_logging.assert_called() for identifier in self.device_identifiers: datastore_device = device_model.Device.get( serial_number=identifier) self.assertEqual(datastore_device.shelf.get().location, 'NYC') self.assertFalse(self.shelf.audit_requested) self.assertEqual(self.shelf.last_audit_by, loanertest.SUPER_ADMIN_EMAIL) self.assertIsInstance(response, message_types.VoidMessage) def test_audit_invalid_device(self): request = shelf_messages.ShelfAuditRequest( shelf_request=shelf_messages.ShelfRequest(location='NYC'), device_identifiers=['Invalid']) with self.assertRaisesRegexp( endpoints.NotFoundException, shelf_api._DEVICE_DOES_NOT_EXIST_MSG % 'Invalid'): self.service.audit(request) @mock.patch.object(device_model.Device, 'search') @mock.patch.object(shelf_api, 'get_shelf', autospec=True) def test_audit_remove_devices(self, mock_get_shelf, mock_model_device_search): shelf = self.device2_key.get() shelf.shelf = self.shelf.key shelf.put() mock_model_device_search.return_value = (search.SearchResults( results=[ search.ScoredDocument(doc_id=self.device2_key.urlsafe()), search.ScoredDocument(doc_id=self.device3_key.urlsafe()), search.ScoredDocument(doc_id=self.device4_key.urlsafe()) ], number_found=3)) mock_get_shelf.return_value = self.shelf request = shelf_messages.ShelfAuditRequest( shelf_request=shelf_messages.ShelfRequest( location=self.shelf.location), device_identifiers=[self.device3_key.get().serial_number]) self.service.audit(request) self.assertEqual(self.device3_key.get().shelf, self.shelf.key) self.assertEqual(self.device2_key.get().shelf, None) self.assertEqual(self.device4_key.get().shelf, None) def test_get_shelf_urlsafe_key(self): """Test getting a shelf using the urlsafe key.""" request = shelf_messages.ShelfRequest( urlsafe_key=self.shelf.key.urlsafe()) shelf = shelf_api.get_shelf(request) self.assertEqual(shelf, self.shelf) def test_get_shelf_using_location(self): """Test getting a shelf using the location.""" request = shelf_messages.ShelfRequest(location=self.shelf.location) shelf = shelf_api.get_shelf(request) self.assertEqual(shelf, self.shelf) def test_get_shelf_using_location_error(self): """Test getting a shelf with an invalid location.""" request = shelf_messages.ShelfRequest(location='Not_Valid') with self.assertRaisesRegexp( endpoints.NotFoundException, shelf_api._SHELF_DOES_NOT_EXIST_MSG % request.location): shelf_api.get_shelf(request)
class SearchTest(loanertest.EndpointsTestCase, parameterized.TestCase): @parameterized.parameters(( shelf_messages.Shelf(location='NY', capacity=50), 'location:NY capacity:50 enabled:True', ), ( shelf_messages.Shelf(location='NY', capacity=50, enabled=False), 'location:NY capacity:50 enabled:False', )) def test_to_query(self, message, expected_query): """Tests the creation of a valid search query from ndb properties.""" query = search_utils.to_query(message, shelf_model.Shelf) # The query is split because ndb properties are unordered when called by # model_class._properties. This test would be flaky otherwise. self.assertCountEqual(query.split(' '), expected_query.split(' ')) @mock.patch.object(search_utils, 'logging', autospec=True) def test_document_to_message(self, mock_logging): """Tests the creation of a protorpc message from a search document.""" test_search_document = search.ScoredDocument( doc_id='test_doc_id', fields=[ search.NumberField(name='capacity', value=20.0), search.TextField(name='location', value='US MTV'), search.AtomField(name='location', value='US-MTV'), search.AtomField(name='enabled', value='True'), search.GeoField(name='lat_long', value=search.GeoPoint(52.37, 4.88)), search.TextField(name='not_present', value='MTV') ]) expected_message = shelf_messages.Shelf(enabled=True, location='US-MTV', capacity=20, latitude=52.37, longitude=4.88) response_message = search_utils.document_to_message( test_search_document, shelf_messages.Shelf()) self.assertEqual(response_message, expected_message) self.assertTrue(response_message.enabled) assert mock_logging.error.call_count == 1 def test_get_search_cursor(self): """Tests the creation of a search cursor with a web_safe_string.""" expected_cursor_web_safe_string = 'False:ODUxODBhNTgyYTQ2ZmI0MDU' returned_cursor = ( search_utils.get_search_cursor(expected_cursor_web_safe_string)) self.assertEqual(expected_cursor_web_safe_string, returned_cursor.web_safe_string) @mock.patch.object(search, 'Cursor', autospec=True) def test_get_search_cursor_error(self, mock_cursor): """Tests the creation of a search cursor when an error occurs.""" mock_cursor.side_effect = ValueError with self.assertRaisesWithLiteralMatch(endpoints.BadRequestException, search_utils._CORRUPT_KEY_MSG): search_utils.get_search_cursor(None) @parameterized.named_parameters( { 'testcase_name': 'QueryStringOnly', 'request': shared_messages.SearchRequest(query_string='enrolled:True'), 'expected_values': ('enrolled:True', None, []) }, { 'testcase_name': 'QueryStringWithReturnedFields', 'request': shared_messages.SearchRequest(query_string='location:US-NYC', returned_fields=['location']), 'expected_values': ('location:US-NYC', None, ['location']) }, ) def test_set_search_query_options(self, request, expected_values): """Tests setting the query options without sort options from message.""" returned_query, returned_sort_options, returned_returned_fields = ( search_utils.set_search_query_options(request)) expected_query, expected_sort_options, expcted_returned_fields = ( expected_values) self.assertEqual(expected_sort_options, returned_sort_options) self.assertEqual(expected_query, returned_query) self.assertEqual(expcted_returned_fields, returned_returned_fields) @parameterized.named_parameters( { 'testcase_name': 'ExpressionWithDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression( expression='enrolled', direction=shared_messages.SortDirection.ASCENDING) ]), 'expected_sort_options_expressions': [ search.SortExpression( expression='enrolled', direction=search.SortExpression.ASCENDING) ] }, { 'testcase_name': 'MultipleExpressionsWithDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression( expression='enrolled', direction=shared_messages.SortDirection.ASCENDING), shared_messages.SearchExpression( expression='serial_number', direction=shared_messages.SortDirection.DESCENDING) ]), 'expected_sort_options_expressions': [ search.SortExpression( expression='enrolled', direction=search.SortExpression.ASCENDING), search.SortExpression( expression='serial_number', direction=search.SortExpression.DESCENDING) ] }, { 'testcase_name': 'ExpressionWithoutDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression(expression='enrolled') ]), 'expected_sort_options_expressions': [search.SortExpression(expression='enrolled')] }, { 'testcase_name': 'MultipleExpressionsWithoutDirection', 'request': shared_messages.SearchRequest( query_string='enrolled:True', expressions=[ shared_messages.SearchExpression(expression='enrolled'), shared_messages.SearchExpression( expression='serial_number') ]), 'expected_sort_options_expressions': [ search.SortExpression( expression='enrolled', direction=search.SortExpression.DESCENDING), search.SortExpression( expression='serial_number', direction=search.SortExpression.DESCENDING) ] }, ) def test_set_search_query_options_with_sort_options( self, request, expected_sort_options_expressions): """Tests setting query options with sort options from message.""" returned_query, returned_sort_options, returned_returned_fields = ( search_utils.set_search_query_options(request)) del returned_query # Unused. del returned_returned_fields # Unused. for i in range(len(returned_sort_options.expressions)): self.assertEqual(returned_sort_options.expressions[i].expression, expected_sort_options_expressions[i].expression) self.assertEqual(returned_sort_options.expressions[i].direction, expected_sort_options_expressions[i].direction)
class DeviceApiTest(parameterized.TestCase, loanertest.EndpointsTestCase): """Tests for the Device API.""" def setUp(self): super(DeviceApiTest, self).setUp() self.service = device_api.DeviceApi() self.login_admin_endpoints_user() # Set bootstrap to completed so that maintenance mode will not be invoked. config_model.Config.set('bootstrap_completed', True) self.shelf = shelf_model.Shelf.enroll(user_email=loanertest.USER_EMAIL, location='NYC', capacity=10, friendly_name='GnG', latitude=40.6892534, longitude=-74.0466891, altitude=1.0) self.device = device_model.Device() self.device.serial_number = '123ABC' self.device.asset_tag = '12345' self.device.enrolled = True self.device.device_model = 'Google Pixelbook' self.device.due_date = datetime.datetime(2017, 11, 15) self.device.last_known_healthy = datetime.datetime(2017, 11, 1) self.device.shelf = self.shelf.key self.device.assigned_user = loanertest.USER_EMAIL self.device.assignment_date = datetime.datetime(2017, 11, 1) self.device.current_ou = '/' self.device.ou_changed_date = datetime.datetime(2017, 11, 1) self.device.locked = False self.device.lost = False self.device.chrome_device_id = 'unique_id_1' self.device.last_heartbeat = datetime.datetime(2017, 11, 1) self.device.damaged = False self.device.damaged_reason = None self.device.put() self.device.set_last_reminder(0) self.device.set_next_reminder(1, datetime.timedelta(hours=2)) device_model.Device( serial_number='6789', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_2', shelf=self.shelf.key, damaged=False, ).put() self.unenrolled_device = device_model.Device( serial_number='4567', enrolled=False, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_3', damaged=False, ) self.unenrolled_device.put() self.unenrolled_device_directory = { 'deviceId': 'unique_id', 'serialNumber': '4567', 'status': 'ACTIVE', 'lastSync': datetime.datetime.utcnow(), 'model': 'HP Chromebook 13 G1', 'orgUnitPath': constants.ORG_UNIT_DICT['DEFAULT'], } def tearDown(self): super(DeviceApiTest, self).tearDown() self.service = None @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_enroll(self, mock_directoryclass): """Tests Enroll with mock methods.""" mock_directoryclient = mock_directoryclass.return_value mock_directoryclient.get_chrome_device.return_value = ( self.unenrolled_device) mock_directoryclient.get_chrome_device_by_serial.return_value = ( self.unenrolled_device_directory) retrieved_device = device_model.Device.get( serial_number=self.unenrolled_device.serial_number) self.assertFalse(retrieved_device.enrolled) request = device_messages.DeviceRequest( serial_number=self.unenrolled_device.serial_number) with mock.patch.object(self.service, 'check_xsrf_token', autospec=True) as mock_xsrf_token: response = self.service.enroll(request) self.assertIsInstance(response, message_types.VoidMessage) retrieved_device = device_model.Device.get( serial_number=self.unenrolled_device.serial_number) self.assertTrue(retrieved_device.enrolled) self.assertEqual(mock_xsrf_token.call_count, 1) @parameterized.parameters((datastore_errors.BadValueError, ), (device_model.DeviceCreationError, )) @mock.patch.object(device_model, 'Device', autospec=True) def test_enroll_error(self, test_error, mock_device_cls): mock_device_cls.enroll.side_effect = test_error request = device_messages.DeviceRequest( serial_number=self.unenrolled_device.serial_number) with self.assertRaises(endpoints.BadRequestException): self.service.enroll(request) @mock.patch.object(device_model, 'Device', autospec=True) def test_unenroll_error(self, mock_device_cls): mock_device_cls.get.return_value.unenroll.side_effect = ( device_model.FailedToUnenrollError()) request = device_messages.DeviceRequest( serial_number=self.unenrolled_device.serial_number) with self.assertRaises(endpoints.BadRequestException): self.service.unenroll(request) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_unenroll(self, mock_xsrf_token, mock_directoryclass): mock_directoryclient = mock_directoryclass.return_value mock_directoryclient.move_chrome_device_org_unit.return_value = ( loanertest.TEST_DIR_DEVICE_DEFAULT) request = device_messages.DeviceRequest( serial_number=self.device.serial_number) self.assertTrue(self.device.enrolled) response = self.service.unenroll(request) self.assertFalse(self.device.enrolled) self.assertIsNone(self.device.assigned_user) self.assertIsNone(self.device.due_date) self.assertIsInstance(response, message_types.VoidMessage) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_unlock(self, mock_xsrf_token, mock_directoryclass): self.device.lost = True self.device.locked = True self.device.put() mock_directoryclient = mock_directoryclass.return_value mock_directoryclient.move_chrome_device_org_unit.return_value = ( loanertest.TEST_DIR_DEVICE_DEFAULT) request = device_messages.DeviceRequest( serial_number=self.device.serial_number) response = self.service.unlock(request) self.assertFalse(self.device.locked) self.assertFalse(self.device.lost) self.assertIsInstance(response, message_types.VoidMessage) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_unlock_directory_error(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.reenable_chrome_device.side_effect = ( directory.DirectoryRPCError) with self.assertRaises(endpoints.BadRequestException): self.service.unlock( device_messages.DeviceRequest(asset_tag='12345')) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_unlock_move_ou_error(self, mock_directory_class): del mock_directory_class # Unused. with mock.patch.object( self.device, 'move_to_default_ou', side_effect=device_model.UnableToMoveToDefaultOUError): with self.assertRaises(endpoints.BadRequestException): self.service.unlock( device_messages.DeviceRequest(asset_tag='12345')) @mock.patch('__main__.device_model.Device.device_audit_check') def test_device_audit_check(self, mock_device_audit_check): request = device_messages.DeviceRequest(identifier='6765') self.assertRaisesRegexp(device_api.endpoints.NotFoundException, device_api._NO_DEVICE_MSG % '6765', self.service.device_audit_check, request) device_model.Device(serial_number='12345', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', chrome_device_id='unique_id_1', damaged=False).put() request = device_messages.DeviceRequest(identifier='12345') response = self.service.device_audit_check(request) self.assertEqual(mock_device_audit_check.call_count, 1) self.assertIsInstance(response, message_types.VoidMessage) def test_device_audit_check_device_not_enrolled(self): request = device_messages.DeviceRequest( identifier=self.device.serial_number) self.device.enrolled = False with self.assertRaises(device_api.endpoints.BadRequestException): self.service.device_audit_check(request) def test_device_audit_check_device_damaged(self): request = device_messages.DeviceRequest( identifier=self.device.serial_number) self.device.damaged = True with self.assertRaises(device_api.endpoints.BadRequestException): self.service.device_audit_check(request) def test_device_audit_check_audit_error(self): request = device_messages.DeviceRequest( identifier=self.device.serial_number) self.testbed.mock_raiseevent.side_effect = device_model.DeviceAuditEventError with self.assertRaises(device_api.endpoints.BadRequestException): self.service.device_audit_check(request) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_not_found(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' request = device_messages.DeviceRequest(identifier='not-found') with self.assertRaises(device_api.endpoints.NotFoundException): self.service.get_device(request) def test_get_device_unenrolled(self): request = device_messages.DeviceRequest( identifier=self.device.serial_number) self.device.enrolled = False with self.assertRaises(device_api.endpoints.BadRequestException): self.service.get_device(request) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' asset_tag_response = self.service.get_device( device_messages.DeviceRequest(asset_tag='12345')) chrome_device_id_response = self.service.get_device( device_messages.DeviceRequest(chrome_device_id='unique_id_1')) serial_number_response = self.service.get_device( device_messages.DeviceRequest(serial_number='123ABC')) urlkey_response = self.service.get_device( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) identifier_response = self.service.get_device( device_messages.DeviceRequest(identifier='123ABC')) self.assertIsInstance(asset_tag_response, device_messages.Device) self.assertIsInstance(chrome_device_id_response, device_messages.Device) self.assertIsInstance(serial_number_response, device_messages.Device) self.assertIsInstance(urlkey_response, device_messages.Device) self.assertIsInstance(identifier_response, device_messages.Device) self.assertEqual(self.device.serial_number, asset_tag_response.serial_number) self.assertEqual(self.device.device_model, urlkey_response.device_model) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_no_permission(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' email = 'random@{}'.format(loanertest.USER_DOMAIN) self.login_endpoints_user(email=email) with self.assertRaises(endpoints.UnauthorizedException): self.service.get_device( device_messages.DeviceRequest( serial_number=self.device.serial_number)) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_has_permission(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' device = self.service.get_device( device_messages.DeviceRequest( serial_number=self.device.serial_number)) self.assertIsInstance(device, device_messages.Device) self.assertEqual(device.serial_number, self.device.serial_number) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_assigned_user(self, mock_directory_class): mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.return_value = 'given name value' email = 'random@{}'.format(loanertest.USER_DOMAIN) self.login_endpoints_user(email=email) self.device.assigned_user = email self.device.put() device = self.service.get_device( device_messages.DeviceRequest( serial_number=self.device.serial_number)) self.assertIsInstance(device, device_messages.Device) self.assertEqual(device.serial_number, self.device.serial_number) @parameterized.parameters(directory.DirectoryRPCError, directory.GivenNameDoesNotExistError) @mock.patch.object(directory, 'DirectoryApiClient', autospec=True) def test_get_device_directory_errors(self, test_error, mock_directory_class): request = device_messages.DeviceRequest(asset_tag='12345') mock_directory_client = mock_directory_class.return_value mock_directory_client.given_name.side_effect = test_error self.assertIsNone(self.service.get_device(request).given_name) @parameterized.parameters(( device_messages.Device(enrolled=True), 2, ), ( device_messages.Device(current_ou='/'), 2, ), ( device_messages.Device(enrolled=False), 1, ), ( device_messages.Device(query=shared_messages.SearchRequest( query_string='sn:6789')), 1, ), ( device_messages.Device(query=shared_messages.SearchRequest( query_string='at:12345')), 1, )) def test_list_devices(self, request, response_length): response = self.service.list_devices(request) self.assertLen(response.devices, response_length) def test_list_devices_with_search_constraints(self): expressions = shared_messages.SearchExpression( expression='serial_number') expected_response = device_messages.ListDevicesResponse( devices=[device_messages.Device(serial_number='6789')], has_additional_results=False) request = device_messages.Device(query=shared_messages.SearchRequest( query_string='sn:6789', expressions=[expressions], returned_fields=['serial_number'])) response = self.service.list_devices(request) self.assertEqual(response.devices[0].serial_number, expected_response.devices[0].serial_number) self.assertFalse(response.has_additional_results) def test_list_devices_with_filter_message(self): message = device_messages.Device(enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/') filters = api_utils.to_dict(message, device_model.Device) request = device_messages.Device(**filters) response = self.service.list_devices(request) expected_response = device_messages.ListDevicesResponse( devices=[ device_messages.Device(serial_number='6789', identifier='6789', enrolled=True, device_model='HP Chromebook 13 G1', current_ou='/', locked=False, lost=False, chrome_device_id='unique_id_2', damaged=False, guest_permitted=True, onboarded=False) ], has_additional_results=False) self.assertEqual(response, expected_response) @mock.patch('__main__.device_api.shelf_api.get_shelf') def test_list_devices_with_shelf_filter(self, mock_get_shelf): # Test for shelf location as filter. mock_get_shelf.return_value = self.shelf shelf_request_message = shelf_messages.ShelfRequest( location=self.shelf.location) message = shelf_messages.Shelf(shelf_request=shelf_request_message) request = device_messages.Device(shelf=message) response = self.service.list_devices(request) mock_get_shelf.assert_called_once_with(shelf_request_message) self.assertLen(response.devices, 2) def test_list_devices_with_page_token(self): request = device_messages.Device(enrolled=True, page_size=1) response_devices = [] while True: response = self.service.list_devices(request) for device in response.devices: response_devices.append(device) request = device_messages.Device(enrolled=True, page_size=1, page_token=response.page_token) if not response.has_additional_results: break self.assertLen(response_devices, 2) @mock.patch.object(search_utils, 'to_query', return_value='enrolled:enrolled', autospec=True) def test_list_devices_with_malformed_page_token(self, mock_to_query): """Test list devices with a fake token, raises BadRequestException.""" request = device_messages.Device(page_token='malformedtoken') with self.assertRaises(endpoints.BadRequestException): self.service.list_devices(request) def test_list_devices_inactive_no_shelf(self): request = device_messages.Device(enrolled=False, page_size=1) response = self.service.list_devices(request) expected_response = device_messages.ListDevicesResponse( devices=[ device_messages.Device( serial_number=self.unenrolled_device.serial_number, identifier=self.unenrolled_device.serial_number, enrolled=self.unenrolled_device.enrolled, device_model=self.unenrolled_device.device_model, current_ou=self.unenrolled_device.current_ou, locked=self.unenrolled_device.locked, lost=self.unenrolled_device.lost, chrome_device_id=self.unenrolled_device.chrome_device_id, damaged=self.unenrolled_device.damaged, guest_permitted=True, onboarded=False) ], has_additional_results=False) self.assertEqual(expected_response, response) @mock.patch('__main__.device_model.Device.list_by_user') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_list_user_devices(self, mock_xsrf_token, mock_list_by_user): self.login_endpoints_user() device2 = device_model.Device() device2.serial_number = '123ABC' device2.assigned_user = loanertest.USER_EMAIL device2.assignment_date = datetime.datetime(2017, 11, 1) device2.due_date = datetime.datetime(2017, 11, 4) device2.put() request = message_types.VoidMessage() mock_list_by_user.return_value = [self.device, device2] response = self.service.list_user_devices(request) self.assertEqual(response.devices[0].serial_number, self.device.serial_number) self.assertLen(response.devices, 2) self.assertEqual(mock_xsrf_token.call_count, 1) mock_list_by_user.assert_called_once_with(loanertest.USER_EMAIL) @mock.patch('__main__.device_model.Device.enable_guest_mode') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_enable_guest_mode(self, mock_xsrf_token, mock_enableguest): config_model.Config.set('allow_guest_mode', True) self.login_endpoints_user() self.service.enable_guest_mode( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) self.assertTrue(mock_enableguest.called) self.assertEqual(mock_xsrf_token.call_count, 1) mock_xsrf_token.reset_mock() mock_enableguest.reset_mock() self.service.enable_guest_mode( device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id)) self.assertTrue(mock_enableguest.called) self.assertEqual(mock_xsrf_token.call_count, 1) def guest_disabled_error(*args, **kwargs): del args, kwargs # Unused. raise device_model.GuestNotAllowedError( device_model._GUEST_MODE_DISABLED_MSG) def directory_error(*args, **kwargs): del args, kwargs # Unused. raise device_model.EnableGuestError( 'Directory broke, all your fault.') mock_enableguest.side_effect = guest_disabled_error with self.assertRaises(endpoints.UnauthorizedException): self.service.enable_guest_mode( device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id)) mock_enableguest.side_effect = directory_error with self.assertRaises(endpoints.InternalServerErrorException): self.service.enable_guest_mode( device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id)) def test_enable_guest_unassigned(self): config_model.Config.set('allow_guest_mode', True) self.device.assigned_user = None self.device.put() with self.assertRaisesRegexp( endpoints.UnauthorizedException, device_model._UNASSIGNED_DEVICE % self.device.identifier): self.service.enable_guest_mode( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch('__main__.device_model.Device.loan_extend') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_extend_loan(self, mock_xsrf_token, mock_loanextend): tomorrow = datetime.datetime.utcnow() + datetime.timedelta(days=1) self.login_endpoints_user() self.service.extend_loan( device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()), extend_date=tomorrow)) mock_loanextend.assert_called_once_with( user_email=loanertest.USER_EMAIL, extend_date_time=tomorrow) self.assertEqual(mock_xsrf_token.call_count, 1) mock_xsrf_token.reset_mock() mock_loanextend.reset_mock() self.service.extend_loan( device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id), extend_date=tomorrow)) mock_loanextend.assert_called_once_with( user_email=loanertest.USER_EMAIL, extend_date_time=tomorrow) self.assertEqual(mock_xsrf_token.call_count, 1) mock_loanextend.side_effect = device_model.ExtendError self.assertRaises( device_api.endpoints.BadRequestException, self.service.extend_loan, device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id), extend_date=tomorrow)) def test_extend_loan_unassigned(self): self.device.assigned_user = None self.device.put() with self.assertRaisesRegexp( endpoints.UnauthorizedException, device_model._UNASSIGNED_DEVICE % self.device.identifier): self.service.extend_loan( device_messages.ExtendLoanRequest( device=device_messages.DeviceRequest( chrome_device_id=self.device.chrome_device_id))) @mock.patch('__main__.device_model.Device.mark_damaged') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_damaged(self, mock_xsrf_token, mock_markdamaged): self.login_endpoints_user() self.service.mark_damaged( device_messages.DamagedRequest( device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()), damaged_reason='Foo')) mock_markdamaged.assert_called_once_with( user_email=loanertest.USER_EMAIL, damaged_reason='Foo') self.assertEqual(mock_xsrf_token.call_count, 1) mock_xsrf_token.reset_mock() mock_markdamaged.reset_mock() self.service.mark_damaged( device_messages.DamagedRequest( # No reason given. device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()))) mock_markdamaged.assert_called_once_with( user_email=loanertest.USER_EMAIL, damaged_reason=None) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch.object(device_model.Device, 'mark_damaged') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_damaged__unauthorized(self, mock_xsrf, mock_markdamaged): del mock_xsrf # Unused. self.login_endpoints_user() mock_markdamaged.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.mark_damaged( device_messages.DamagedRequest( device=device_messages.DeviceRequest( urlkey=self.device.key.urlsafe()), damaged_reason='Foo')) def test_mark_undamaged(self): with mock.patch.object(self.device, 'mark_undamaged') as mock_markundamaged: self.service.mark_undamaged( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) mock_markundamaged.assert_called_once_with( user_email=loanertest.SUPER_ADMIN_EMAIL) @mock.patch.object(device_model.Device, 'mark_undamaged') def test_mark_undamaged__unauthorized(self, mock_markundamaged): self.login_endpoints_user() with mock.patch.object(self.service, 'check_xsrf_token') as mock_xsrf_token: mock_markundamaged.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.mark_undamaged( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch('__main__.device_model.Device.mark_lost') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_lost(self, mock_xsrf_token, mock_marklost): self.service.mark_lost( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) mock_marklost.assert_called_once_with( user_email=loanertest.SUPER_ADMIN_EMAIL) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch.object(device_model.Device, 'mark_lost') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_lost__unauthorized(self, mock_xsrf_token, mock_marklost): del mock_xsrf_token # Unused. mock_marklost.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.mark_lost( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch('__main__.device_model.Device.mark_pending_return') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_mark_pending_return(self, mock_xsrf_token, mock_markreturned): self.login_endpoints_user() self.service.mark_pending_return( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) mock_markreturned.assert_called_once_with( user_email=loanertest.USER_EMAIL) self.assertEqual(mock_xsrf_token.call_count, 1) def test_mark_pending_return_unassigned(self): self.device.assigned_user = None self.device.put() with self.assertRaisesRegexp( endpoints.UnauthorizedException, device_model._UNASSIGNED_DEVICE % self.device.identifier): self.service.mark_pending_return( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch.object(device_model.Device, 'complete_onboard') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_complete_onboard(self, mock_xsrf_token, mock_completeonboard): self.login_endpoints_user() self.service.complete_onboard( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) mock_completeonboard.assert_called_once_with( user_email=loanertest.USER_EMAIL) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch('__main__.device_model.Device.resume_loan') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_resume_loan(self, mock_xsrf_token, mock_resume_loan): self.login_endpoints_user() self.service.resume_loan( device_messages.DeviceRequest(urlkey=self.device.key.urlsafe())) self.assertEqual(mock_resume_loan.call_count, 1) self.assertEqual(mock_xsrf_token.call_count, 1) @mock.patch.object(device_model.Device, 'resume_loan') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_resume_loan__unauthorized(self, mock_xsrf_token, mock_resume_loan): del mock_xsrf_token # Unused. self.login_endpoints_user() mock_resume_loan.side_effect = device_model.UnauthorizedError() with self.assertRaises(endpoints.UnauthorizedException): self.service.resume_loan( device_messages.DeviceRequest( urlkey=self.device.key.urlsafe())) @mock.patch.object(bigquery, 'BigQueryClient') @mock.patch.object(root_api.Service, 'check_xsrf_token', autospec=True) def test_get_history(self, mock_xsrf_token, mock_bigquery): device_request = device_messages.DeviceRequest() device_request.asset_tag = '12345' request = device_messages.HistoryRequest(device=device_request) device_response = device_messages.Device() device_response.asset_tag = '12345' expected_response = device_messages.HistoryResponse() for _ in range(2): expected_response.devices.append(device_response) expected_response.timestamp.append( datetime.datetime(2019, 10, 22, 20, 43, 37)) expected_response.actor.append('*****@*****.**') expected_response.summary.append( 'Beginning new loan for user [email protected] with device 12345.' ) bigquery_response = [ (u"Key('Device', 5158133238333440)", datetime.datetime(2019, 10, 22, 20, 43, 37), u'*****@*****.**', u'enable_guest_mode', u'Beginning new loan for user [email protected] with device 12345.', { u'ou_changed_date': datetime.datetime(2019, 10, 22, 20, 43, 37), u'current_ou': u'/', u'shelf': None, u'due_date': datetime.datetime(2019, 10, 22, 20, 43, 37), u'chrome_device_id': u'unique_id_1', u'mark_pending_return_date': None, u'asset_tag': u'12345', u'last_known_healthy': datetime.datetime(2019, 10, 22, 20, 43, 37), u'locked': False, u'last_reminder': { u'count': 1, u'time': datetime.datetime(2019, 10, 22, 20, 43, 37), u'level': 1 }, u'next_reminder': None, u'device_model': u'Chromebook', u'enrolled': True, u'serial_number': u'123ABC', u'damaged': False, u'onboarded': True, u'assignment_date': datetime.datetime(2019, 10, 22, 20, 43, 37), u'damaged_reason': None, u'assigned_user': u'*****@*****.**', u'lost': False, u'last_heartbeat': datetime.datetime(2019, 10, 22, 20, 43, 37) }), (u"Key('Device', 5158133238333440)", datetime.datetime(2019, 10, 22, 20, 43, 37), u'*****@*****.**', u'enable_guest_mode', u'Beginning new loan for user [email protected] with device 12345.', { u'ou_changed_date': datetime.datetime(2019, 10, 22, 20, 43, 37), u'current_ou': u'/', u'shelf': None, u'due_date': datetime.datetime(2019, 10, 22, 20, 43, 37), u'chrome_device_id': u'unique_id_1', u'mark_pending_return_date': None, u'asset_tag': u'12345', u'last_known_healthy': datetime.datetime(2019, 10, 22, 20, 43, 37), u'locked': False, u'last_reminder': { u'count': 1, u'time': datetime.datetime(2019, 10, 22, 20, 43, 37), u'level': 1 }, u'next_reminder': None, u'device_model': u'Chromebook', u'enrolled': True, u'serial_number': u'123ABC', u'damaged': False, u'onboarded': True, u'assignment_date': datetime.datetime(2019, 10, 22, 20, 43, 37), u'damaged_reason': None, u'assigned_user': u'*****@*****.**', u'lost': False, u'last_heartbeat': datetime.datetime(2019, 10, 22, 20, 43, 37) }), ] mock_bigquery_client = mock.Mock() mock_bigquery_client.get_device_info.return_value = bigquery_response mock_bigquery.return_value = mock_bigquery_client actual_response = self.service.get_history(request) self.assertEqual(actual_response, expected_response) def test_get_device_errors(self): # No identifiers. with self.assertRaises(endpoints.BadRequestException): device_api._get_device(device_messages.DeviceRequest()) # URL-safe key that is still URL-safe, but technically not a key. with self.assertRaises(device_api.endpoints.BadRequestException): device_api._get_device( device_messages.DeviceRequest(urlkey='bad-key'))