Exemplo n.º 1
0
    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)
Exemplo n.º 2
0
  def testListUserDeviceResponse(self):
    device1 = device_messages.Device(serial_number='FAKE-DEVICE-SERIAL-1')
    device2 = device_messages.Device(serial_number='FAKE-DEVICE-SERIAL-2')

    list_device_resp = device_messages.ListUserDeviceResponse(
        devices=[device1, device2])

    self.assertEqual(list_device_resp.devices[0].serial_number,
                     'FAKE-DEVICE-SERIAL-1')
    self.assertEqual(list_device_resp.devices[1].serial_number,
                     'FAKE-DEVICE-SERIAL-2')
Exemplo n.º 3
0
 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)
Exemplo n.º 4
0
  def testListDevicesResponse(self):
    device1 = device_messages.Device(serial_number='FAKE-DEVICE-SERIAL-1')
    device2 = device_messages.Device(serial_number='FAKE-DEVICE-SERIAL-2')
    list_device_resp = device_messages.ListDevicesResponse(
        devices=[device1, device2],
        has_additional_results=True,
        page_token='FAKE-PAGE-TOKEN')

    self.assertTrue(list_device_resp.has_additional_results)
    self.assertEqual(list_device_resp.devices[0].serial_number,
                     'FAKE-DEVICE-SERIAL-1')
    self.assertEqual(list_device_resp.devices[1].serial_number,
                     'FAKE-DEVICE-SERIAL-2')
    self.assertEqual(list_device_resp.page_token, 'FAKE-PAGE-TOKEN')
Exemplo n.º 5
0
 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)
Exemplo n.º 6
0
 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_build_device_message_from_model(self):
     """Test the construction of a device message from a device entity."""
     test_device = device_model.Device(
         serial_number='test_serial_value',
         asset_tag='test_asset_tag_value',
         enrolled=True,
         device_model='test model value',
         due_date=datetime.datetime(year=2018, month=1, day=1),
         last_known_healthy=datetime.datetime(year=2018, month=1, day=2),
         shelf=self.test_shelf_model.key,
         assigned_user='******',
         assignment_date=datetime.datetime(year=2018, month=1, day=3),
         current_ou=constants.ORG_UNIT_DICT['GUEST'],
         ou_changed_date=datetime.datetime(year=2018, month=1, day=4),
         locked=True,
         lost=False,
         mark_pending_return_date=datetime.datetime(year=2018,
                                                    month=1,
                                                    day=5),
         chrome_device_id='device id value',
         last_heartbeat=datetime.datetime(year=2018, month=1, day=6),
         damaged=None,
         damaged_reason='Not damaged',
         last_reminder=device_model.Reminder(level=1),
         next_reminder=device_model.Reminder(level=2),
     ).put().get()
     expected_message = device_messages.Device(
         serial_number='test_serial_value',
         asset_tag='test_asset_tag_value',
         identifier='test_asset_tag_value',
         enrolled=True,
         device_model='test model value',
         due_date=datetime.datetime(year=2018, month=1, day=1),
         last_known_healthy=datetime.datetime(year=2018, month=1, day=2),
         shelf=self.expected_shelf_message,
         assigned_user='******',
         assignment_date=datetime.datetime(year=2018, month=1, day=3),
         current_ou=constants.ORG_UNIT_DICT['GUEST'],
         ou_changed_date=datetime.datetime(year=2018, month=1, day=4),
         locked=True,
         lost=False,
         mark_pending_return_date=datetime.datetime(year=2018,
                                                    month=1,
                                                    day=5),
         chrome_device_id='device id value',
         last_heartbeat=datetime.datetime(year=2018, month=1, day=6),
         damaged=None,
         damaged_reason='Not damaged',
         last_reminder=device_messages.Reminder(level=1),
         next_reminder=device_messages.Reminder(level=2),
         guest_permitted=True,
         guest_enabled=True,
         max_extend_date=device_model.calculate_return_dates(
             test_device.assignment_date).max,
         overdue=True,
     )
     actual_message = api_utils.build_device_message_from_model(
         test_device, True)
     self.assertEqual(actual_message, expected_message)
Exemplo n.º 8
0
  def testHistoryResponse(self):
    now = datetime.datetime.now()
    device1 = device_messages.Device(serial_number='FAKE-DEVICE-SERIAL-1')
    device2 = device_messages.Device(serial_number='FAKE-DEVICE-SERIAL-2')

    hist_resp = device_messages.HistoryResponse(
        devices=[device1, device2],
        timestamp=[now],
        actor=['FAKE-ACTOR-1'],
        summary=['FAKE-SUMMARY-1', 'FAKE-SUMMARY-2'])

    self.assertListEqual(hist_resp.timestamp, [now])
    self.assertListEqual(hist_resp.actor, ['FAKE-ACTOR-1'])
    self.assertListEqual(hist_resp.summary,
                         ['FAKE-SUMMARY-1', 'FAKE-SUMMARY-2'])
    self.assertEqual(hist_resp.devices[0].serial_number, 'FAKE-DEVICE-SERIAL-1')
    self.assertEqual(hist_resp.devices[1].serial_number, 'FAKE-DEVICE-SERIAL-2')
Exemplo n.º 9
0
def build_device_message_from_model(device, guest_permitted):
    """Builds a device_messages.Device ProtoRPC message.

  Args:
    device: device_model.Device, a device entity to convert into a message.
    guest_permitted: bool, whether or not guest is permitted for this
        organization.

  Returns:
    A populated device_messages.Device ProtoRPC message.
  """
    message = device_messages.Device(
        serial_number=device.serial_number,
        asset_tag=device.asset_tag,
        identifier=device.identifier,
        enrolled=device.enrolled,
        device_model=device.device_model,
        due_date=device.due_date,
        last_known_healthy=device.last_known_healthy,
        assigned_user=device.assigned_user,
        assignment_date=device.assignment_date,
        current_ou=device.current_ou,
        ou_changed_date=device.ou_changed_date,
        locked=device.locked,
        lost=device.lost,
        mark_pending_return_date=device.mark_pending_return_date,
        chrome_device_id=device.chrome_device_id,
        last_heartbeat=device.last_heartbeat,
        damaged=device.damaged,
        damaged_reason=device.damaged_reason,
        guest_enabled=device.guest_enabled,
        guest_permitted=guest_permitted,
        overdue=device.overdue,
    )
    if device.last_reminder:
        message.last_reminder = build_reminder_message_from_model(
            device.last_reminder)
    if device.next_reminder:
        message.next_reminder = build_reminder_message_from_model(
            device.next_reminder)
    if device.is_assigned:
        message.max_extend_date = device.return_dates.max
    if device.shelf:
        message.shelf = build_shelf_message_from_model(device.shelf.get())
    for tag_data in device.tags:
        tag_data_message = tag_messages.TagData()
        urlsafe_key = tag_model.Tag.get(tag_data.tag.name).key.urlsafe()
        tag_data_message.tag = tag_messages.Tag(
            name=tag_data.tag.name,
            hidden=tag_data.tag.hidden,
            color=tag_data.tag.color,
            protect=tag_data.tag.protect,
            description=tag_data.tag.description,
            urlsafe_key=urlsafe_key)
        tag_data_message.more_info = tag_data.more_info
        message.tags.append(tag_data_message)

    return message
Exemplo n.º 10
0
 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)
Exemplo n.º 11
0
 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)
         ],
         has_additional_results=False)
     self.assertEqual(expected_response, response)
Exemplo n.º 12
0
 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)
         ],
         has_additional_results=False)
     self.assertEqual(response, expected_response)
def build_device_message_from_model(device, guest_permitted):
    """Builds a device_messages.Device ProtoRPC message.

  Args:
    device: device_model.Device, a device entity to convert into a message.
    guest_permitted: bool, whether or not guest is permitted for this
        organization.

  Returns:
    A populated device_messages.Device ProtoRPC message.
  """
    message = device_messages.Device(
        serial_number=device.serial_number,
        asset_tag=device.asset_tag,
        identifier=device.identifier,
        enrolled=device.enrolled,
        device_model=device.device_model,
        due_date=device.due_date,
        last_known_healthy=device.last_known_healthy,
        assigned_user=device.assigned_user,
        assignment_date=device.assignment_date,
        current_ou=device.current_ou,
        ou_changed_date=device.ou_changed_date,
        locked=device.locked,
        lost=device.lost,
        mark_pending_return_date=device.mark_pending_return_date,
        chrome_device_id=device.chrome_device_id,
        last_heartbeat=device.last_heartbeat,
        damaged=device.damaged,
        damaged_reason=device.damaged_reason,
        guest_enabled=device.guest_enabled,
        guest_permitted=guest_permitted,
        overdue=device.overdue,
    )
    if device.last_reminder:
        message.last_reminder = build_reminder_message_from_model(
            device.last_reminder)
    if device.next_reminder:
        message.next_reminder = build_reminder_message_from_model(
            device.next_reminder)
    if device.is_assigned:
        message.max_extend_date = device_model.calculate_return_dates(
            device.assignment_date).max
    if device.shelf:
        message.shelf = build_shelf_message_from_model(device.shelf.get())
    return message
Exemplo n.º 14
0
    def list_devices(self, request):
        """Lists all devices based on any device attribute."""
        self.check_xsrf_token(self.request_state)
        if request.page_size <= 0:
            raise endpoints.BadRequestException(
                'The value for page_size must be greater than 0.')
        query, sort_options, returned_fields = (
            search_utils.set_search_query_options(request.query))
        if not query:
            shelf_query = ''
            if request.shelf:
                shelf_urlsafe_key = request.shelf.shelf_request.urlsafe_key
                if not shelf_urlsafe_key:
                    shelf_urlsafe_key = shelf_api.get_shelf(
                        request.shelf.shelf_request).key.urlsafe()
                request.shelf = None
                shelf_query = ':'.join(('shelf', shelf_urlsafe_key))
            query = search_utils.to_query(request, device_model.Device)
            query = ' '.join((query, shelf_query))

        offset = search_utils.calculate_page_offset(
            page_size=request.page_size, page_number=request.page_number)

        search_results = device_model.Device.search(
            query_string=query,
            query_limit=request.page_size,
            offset=offset,
            sort_options=sort_options,
            returned_fields=returned_fields)
        total_pages = search_utils.calculate_total_pages(
            page_size=request.page_size,
            total_results=search_results.number_found)
        guest_permitted = config_model.Config.get('allow_guest_mode')
        messages = []
        for document in search_results.results:
            message = search_utils.document_to_message(
                document, device_messages.Device())
            message.guest_permitted = guest_permitted
            messages.append(message)

        return device_messages.ListDevicesResponse(
            devices=messages,
            total_results=search_results.number_found,
            total_pages=total_pages)
Exemplo n.º 15
0
 def get_history(self, request):
     """Gets historical data for a given device."""
     self.check_xsrf_token(self.request_state)
     client = bigquery.BigQueryClient()
     device = _get_device(request.device)
     serial = device.serial_number
     info = client.get_device_info(serial)
     if not info:
         raise endpoints.NotFoundException(
             'No history for the requested serial number.')
     historical_device = device_messages.Device()
     response = device_messages.HistoryResponse()
     historical_device.asset_tag = info[0][5]['asset_tag']
     for row in info:
         response.devices.append(historical_device)
         response.timestamp.append(row[1])
         response.actor.append(row[2])
         response.summary.append(row[4])
     return response
Exemplo n.º 16
0
    def list_devices(self, request):
        """Lists all devices based on any device attribute."""
        self.check_xsrf_token(self.request_state)
        query, sort_options, returned_fields = (
            search_utils.set_search_query_options(request.query))
        if not query:
            shelf_query = ''
            if request.shelf:
                shelf_urlsafe_key = request.shelf.shelf_request.urlsafe_key
                if not shelf_urlsafe_key:
                    shelf_urlsafe_key = shelf_api.get_shelf(
                        request.shelf.shelf_request).key.urlsafe()
                request.shelf = None
                shelf_query = ':'.join(('shelf', shelf_urlsafe_key))
            query = search_utils.to_query(request, device_model.Device)
            query = ' '.join((query, shelf_query))

        cursor = search_utils.get_search_cursor(request.page_token)

        search_results = device_model.Device.search(
            query_string=query,
            query_limit=request.page_size,
            cursor=cursor,
            sort_options=sort_options,
            returned_fields=returned_fields)
        new_search_cursor = None
        if search_results.cursor:
            new_search_cursor = search_results.cursor.web_safe_string
        guest_permitted = config_model.Config.get('allow_guest_mode')
        messages = []
        for document in search_results.results:
            message = search_utils.document_to_message(
                document, device_messages.Device())
            message.guest_permitted = guest_permitted
            if message.current_ou == constants.ORG_UNIT_DICT['GUEST']:
                message.guest_enabled = True
            messages.append(message)

        return device_messages.ListDevicesResponse(
            devices=messages,
            has_additional_results=bool(new_search_cursor),
            page_token=new_search_cursor)
Exemplo n.º 17
0
 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)
Exemplo n.º 18
0
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)
Exemplo n.º 19
0
 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)
Exemplo n.º 20
0
    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)
Exemplo n.º 21
0
  def testDevice(self):
    due_date = datetime.datetime.now()
    last_heartbeat = datetime.datetime.now()
    assignment_date = datetime.datetime.now()
    ou_changed_date = datetime.datetime.now()
    last_known_healthy = datetime.datetime.now()
    mark_pending_return_date = datetime.datetime.now()
    max_extend_date = datetime.datetime.now()

    device = device_messages.Device(
        serial_number='FAKE-SERIAL-NUMBER',
        asset_tag='FAKE-TAG',
        identifier='FAKE-IDENTIFIER',
        urlkey='FAKE-URL-KEY',
        enrolled=False,
        device_model='FAKE-DEVICE-MODEL',
        due_date=due_date,
        last_known_healthy=last_known_healthy,
        assigned_user='******',
        assignment_date=assignment_date,
        current_ou='FAKE-CURRENT-OU',
        ou_changed_date=ou_changed_date,
        locked=True,
        lost=True,
        mark_pending_return_date=mark_pending_return_date,
        chrome_device_id='FAKE-CHROME-DEVICE-ID',
        last_heartbeat=last_heartbeat,
        damaged=True,
        damaged_reason='FAKE-DAMAGED-REASON',
        page_token='FAKE-PAGE-TOKEN',
        page_size=50,
        max_extend_date=max_extend_date,
        guest_enabled=False,
        guest_permitted=True,
        given_name='FAKE-GIVEN-NAME',
        overdue=True,
        onboarded=True)

    self.assertTrue(device.lost)
    self.assertTrue(device.locked)
    self.assertTrue(device.overdue)
    self.assertTrue(device.damaged)
    self.assertFalse(device.enrolled)
    self.assertTrue(device.onboarded)
    self.assertEqual(device.page_size, 50)
    self.assertFalse(device.guest_enabled)
    self.assertTrue(device.guest_permitted)
    self.assertEqual(device.due_date, due_date)

    self.assertEqual(device.asset_tag, 'FAKE-TAG')
    self.assertEqual(device.urlkey, 'FAKE-URL-KEY')
    self.assertEqual(device.identifier, 'FAKE-IDENTIFIER')
    self.assertEqual(device.current_ou, 'FAKE-CURRENT-OU')
    self.assertEqual(device.page_token, 'FAKE-PAGE-TOKEN')
    self.assertEqual(device.given_name, 'FAKE-GIVEN-NAME')
    self.assertEqual(device.last_heartbeat, last_heartbeat)
    self.assertEqual(device.assignment_date, assignment_date)
    self.assertEqual(device.ou_changed_date, ou_changed_date)
    self.assertEqual(device.max_extend_date, max_extend_date)
    self.assertEqual(device.device_model, 'FAKE-DEVICE-MODEL')
    self.assertEqual(device.serial_number, 'FAKE-SERIAL-NUMBER')
    self.assertEqual(device.assigned_user, 'FAKE-ASSIGNED-USER')
    self.assertEqual(device.damaged_reason, 'FAKE-DAMAGED-REASON')
    self.assertEqual(device.last_known_healthy, last_known_healthy)
    self.assertEqual(device.chrome_device_id, 'FAKE-CHROME-DEVICE-ID')
    self.assertEqual(device.mark_pending_return_date, mark_pending_return_date)
Exemplo n.º 22
0
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'))
Exemplo n.º 23
0
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'))