def test_send_fcm_unavailable_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None, UnavailableError('code', 'message'))
        ])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response), patch(
                              'logging.error') as mock_error:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            self.assertEqual(exit_code, 0)
            mock_error.assert_called_once_with(
                'FCM unavailable - retrying client...')

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        # Check that we queue'd for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 1)

        # Make sure our taskqueue tasks execute what we expect
        with patch.object(TBANSHelper, '_send_fcm') as mock_send_fcm:
            deferred.run(tasks[0].payload)
            mock_send_fcm.assert_called_once_with([client], ANY, 1)
    def test_send_fcm_unhandled_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        batch_response = messaging.BatchResponse(
            [messaging.SendResponse(None, FirebaseError('code', 'message'))])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response), patch(
                              'logging.error') as mock_error:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            self.assertEqual(exit_code, 0)
            mock_error.assert_called_once_with(
                'Unhandled FCM error for messaging_id - code / message')

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        # Check that we didn't queue for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 0)
    def test_send_fcm_invalid_argument_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None,
                                   InvalidArgumentError('code', 'message'))
        ])
        with patch.object(FCMRequest, 'send',
                          return_value=batch_response), patch(
                              'logging.critical') as mock_critical:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            self.assertEqual(exit_code, 0)
            mock_critical.assert_called_once_with(
                'Invalid argument when sending to FCM - code')

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        # Make sure we haven't queued for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 0)
    def test_send_fcm_sender_id_mismatch_error(self):
        client = MobileClient(parent=ndb.Key(Account, 'user_id'),
                              user_id='user_id',
                              messaging_id='messaging_id',
                              client_type=ClientType.OS_IOS)
        client.put()

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'),
                         ['messaging_id'])

        batch_response = messaging.BatchResponse([
            messaging.SendResponse(None,
                                   SenderIdMismatchError('code', 'message'))
        ])
        with patch.object(FCMRequest, 'send', return_value=batch_response), \
            patch.object(MobileClient, 'delete_for_messaging_id', wraps=MobileClient.delete_for_messaging_id) as mock_delete, \
                patch('logging.info') as mock_info:
            exit_code = TBANSHelper._send_fcm([client], MockNotification())
            mock_delete.assert_called_once_with('messaging_id')
            self.assertEqual(exit_code, 0)
            mock_info.assert_called_with(
                'Deleting mismatched client with ID: messaging_id')

        # Sanity check
        self.assertEqual(MobileClient.fcm_messaging_ids('user_id'), [])

        # Make sure we haven't queued for a retry
        tasks = self.taskqueue_stub.get_filtered_tasks(
            queue_names='push-notifications')
        self.assertEqual(len(tasks), 0)
    def test_delete_for_messaging_id(self):
        user_id_one = 'user_id_one'
        messaging_id_one = 'messaging_id1'
        messaging_id_two = 'messaging_id2'

        user_id_two = 'user_id_two'
        messaging_id_three = 'messaging_id3'

        for (user_id, messaging_ids) in [(user_id_one, [messaging_id_one, messaging_id_two]), (user_id_two, [messaging_id_three])]:
            for messaging_id in messaging_ids:
                MobileClient(
                    parent=ndb.Key(Account, user_id),
                    user_id=user_id,
                    messaging_id=messaging_id,
                    client_type=ClientType.OS_IOS,
                    device_uuid=messaging_id[::-1],
                    display_name='Phone').put()

        MobileClient.delete_for_messaging_id(messaging_id_one)

        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_one), [messaging_id_two])
        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_two), [messaging_id_three])

        MobileClient.delete_for_messaging_id(messaging_id_two)

        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_one), [])
        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_two), [messaging_id_three])

        MobileClient.delete_for_messaging_id('does_not_exist')
    def test_fcm_messaging_ids_unsupported_type(self):
        user_id = 'user_id'

        for (token, os) in [('a', ClientType.OS_ANDROID), ('b', ClientType.OS_IOS), ('c', ClientType.WEBHOOK), ('d', ClientType.WEB)]:
            MobileClient(
                parent=ndb.Key(Account, user_id),
                user_id=user_id,
                messaging_id=token,
                client_type=os,
                device_uuid=token,
                display_name=token).put()

        self.assertEqual(MobileClient.fcm_messaging_ids(user_id), ['b', 'd'])
    def test_fcm_messaging_ids(self):
        user_id_one = 'user_id_one'
        token_one = 'token1'
        token_two = 'token2'

        user_id_two = 'user_id_two'
        token_three = 'token3'

        user_id_three = 'user_id_three'

        for (user_id, tokens) in [(user_id_one, [token_one, token_two]), (user_id_two, [token_three])]:
            for token in tokens:
                MobileClient(
                    parent=ndb.Key(Account, user_id),
                    user_id=user_id,
                    messaging_id=token,
                    client_type=ClientType.OS_IOS,
                    device_uuid=token[::-1],
                    display_name='Phone').put()

        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_one), [token_one, token_two])
        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_two), [token_three])
        self.assertEqual(MobileClient.fcm_messaging_ids(user_id_three), [])