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(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(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_retry_backoff_time(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='messaging_id', client_type=ClientType.OS_IOS) client.put() import time batch_response = messaging.BatchResponse([ messaging.SendResponse(None, QuotaExceededError('code', 'message')) ]) for i in range(0, 6): with patch.object( FCMRequest, 'send', return_value=batch_response), patch('logging.error'): call_time = time.time() TBANSHelper._send_fcm([client], MockNotification(), i) # Check that we queue'd for a retry with the proper countdown time tasks = self.taskqueue_stub.get_filtered_tasks( queue_names='push-notifications') if i > 0: self.assertGreater(tasks[0].eta_posix - call_time, 0) # 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, i + 1) self.taskqueue_stub.FlushQueue('push-notifications')
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(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(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_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(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(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_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(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(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 send_message( self, message: messaging.Message, skip_registration_id_lookup: bool = False, additional_registration_ids: Sequence[str] = None, app: "firebase_admin.App" = SETTINGS["DEFAULT_FIREBASE_APP"], **more_send_message_kwargs, ) -> FirebaseResponseDict: """ Send notification of single message for all active devices in queryset and deactivate if DELETE_INACTIVE_DEVICES setting is set to True. Bulk sends using firebase.messaging.send_all. For every 500 messages, we send a single HTTP request to Firebase (the 500 is set by the firebase-sdk). :param message: firebase.messaging.Message. If `message` includes a token/id, it will be overridden. :param skip_registration_id_lookup: skips the QuerySet lookup and solely uses the list of IDs from additional_registration_ids :param additional_registration_ids: specific registration_ids to add to the :param app: firebase_admin.App. Specify a specific app to use QuerySet lookup :param more_send_message_kwargs: Parameters for firebase.messaging.send_all() - dry_run: bool. Whether to actually send the notification to the device If there are any new parameters, you can still specify them here. :raises FirebaseError :returns FirebaseResponseDict """ registration_ids = self.get_registration_ids( skip_registration_id_lookup, additional_registration_ids, ) if not registration_ids: return self.get_default_send_message_response() responses: List[messaging.SendResponse] = [] for i in range(0, len(registration_ids), MAX_MESSAGES_PER_BATCH): messages = [ self._prepare_message(m, t) for m, t in zip( repeat(message, MAX_MESSAGES_PER_BATCH), registration_ids[i:i + MAX_MESSAGES_PER_BATCH], ) ] responses.extend( messaging.send_all(messages, app=app, **more_send_message_kwargs).responses) return FirebaseResponseDict( response=messaging.BatchResponse(responses), registration_ids_sent=registration_ids, deactivated_registration_ids=self. deactivate_devices_with_error_results(registration_ids, responses), )
def test_send_webhook_multiple(self): clients = [ MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='{}'.format(i), client_type=ClientType.WEBHOOK) for i in range(3) ] batch_response = messaging.BatchResponse([]) with patch.object(WebhookRequest, 'send', return_value=batch_response) as mock_send: exit_code = TBANSHelper._send_webhook(clients, MockNotification()) self.assertEqual(mock_send.call_count, 3) self.assertEqual(exit_code, 0)
def test_send_failed(self): batch_response = messaging.BatchResponse( [messaging.SendResponse(None, 'a')]) request = FCMRequest(app=self.app, notification=MockNotification(), tokens=['abc', 'def']) with patch.object( messaging, 'send_multicast', return_value=batch_response) as mock_send, patch.object( request, 'defer_track_notification') as mock_track: response = request.send() mock_send.assert_called_once() mock_track.assert_not_called() self.assertEqual(response, batch_response)
def test_ping_fcm_fail(self): client = MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='token', client_type=ClientType.OS_IOS, device_uuid='uuid', display_name='Phone') batch_response = messaging.BatchResponse( [messaging.SendResponse(None, FirebaseError(500, 'testing'))]) with patch.object(FCMRequest, 'send', return_value=batch_response) as mock_send: success = TBANSHelper._ping_client(client) mock_send.assert_called_once() self.assertFalse(success)
def test_send_fcm_batch(self): clients = [ MobileClient(parent=ndb.Key(Account, 'user_id'), user_id='user_id', messaging_id='{}'.format(i), client_type=ClientType.OS_IOS) for i in range(3) ] batch_response = messaging.BatchResponse([]) with patch('models.notifications.requests.fcm_request.MAXIMUM_TOKENS', 2), patch.object(FCMRequest, 'send', return_value=batch_response) as mock_send: exit_code = TBANSHelper._send_fcm(clients, MockNotification()) self.assertEqual(mock_send.call_count, 2) self.assertEqual(exit_code, 0)
def get_default_send_message_response() -> FirebaseResponseDict: return FirebaseResponseDict( response=messaging.BatchResponse([]), registration_ids_sent=[], deactivated_registration_ids=[], )
def _patched_send_multicast(*args, **kwargs): return messaging.BatchResponse(responses)