async def send_notification(self, request): response = NotificationResult(request.notification_id, APNS_RESPONSE_CODE.BAD_REQUEST) response.description = "connection error" logger.debug('Notification %s: waiting for connection', request.notification_id) try: connection = await self.acquire() except ConnectionError: logger.warning('Could not send notification %s: ConnectionError', request.notification_id) return response logger.debug('Notification %s: connection %s acquired', request.notification_id, connection) response.description = "internal error" try: response = await connection.send_notification(request) return response except NoAvailableStreamIDError: connection.close() except ConnectionClosed: logger.warning('Could not send notification %s: ConnectionClosed', request.notification_id) except FlowControlError: logger.debug('Got FlowControlError for notification %s', request.notification_id) except: logger.debug('internal error for %s', request.notification_id) return response
def test_expected_badge_only_with_default_payload(self): """ Tests the expected fallback case: a good response from APNS means we pass on a good response to the homeserver. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "200")) # Act resp = self._request( self._make_dummy_notification_badge_only( [DEVICE_EXAMPLE_WITH_DEFAULT_PAYLOAD])) # Assert self.assertEqual(1, method.call_count) ((notification_req, ), _kwargs) = method.call_args self.assertEqual( {"aps": { "badge": 2 }}, notification_req.message, ) self.assertEqual({"rejected": []}, resp)
def on_response_received(self, headers): notification_id = headers.get(b'apns-id').decode('utf8') status = headers.get(b':status').decode('utf8') if status == APNS_RESPONSE_CODE.SUCCESS: request = self.requests.pop(notification_id, None) if request: result = NotificationResult(notification_id, status) request.set_result(result) else: logger.warning( 'Got response for unknown notification request %s', notification_id) else: self.request_statuses[notification_id] = status
def test_no_retry_on_4xx(self): """ Test that we don't retry when we get a 4xx error but do not mark as rejected. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "429", description="TooManyRequests")) # Act resp = self._request(self._make_dummy_notification([DEVICE_EXAMPLE])) # Assert self.assertEqual(1, method.call_count) self.assertEqual(502, resp)
def test_rejection(self): """ Tests the rejection case: a rejection response from APNS leads to us passing on a rejection to the homeserver. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "410", description="Unregistered")) # Act resp = self._request(self._make_dummy_notification([DEVICE_EXAMPLE])) # Assert self.assertEqual(1, method.call_count) self.assertEqual({"rejected": ["spqr"]}, resp)
def test_retry_on_5xx(self): """ Test that we DO retry when we get a 5xx error and do not mark as rejected. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "503", description="ServiceUnavailable") ) # Act resp = self._request(self._make_dummy_notification([DEVICE_EXAMPLE])) # Assert self.assertGreater(method.call_count, 1) self.assertEqual(502, resp)
def test_with_matching_appid(self): """ Tests the matching case: A matching app id (only one time) must be processed. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "200")) # Act resp = self._request( self._make_dummy_notification([DEVICE_EXAMPLE_MATCHING])) # Assert # method should be called one time self.assertEqual(1, method.call_count) self.assertEqual({"rejected": []}, resp)
def on_data_received(self, data, stream_id): data = json.loads(data.decode()) reason = data.get('reason', '') if not reason: return notification_id = self.request_streams.pop(stream_id, None) if notification_id: request = self.requests.pop(notification_id, None) if request: # TODO: Теоретически здесь может быть ошибка, если нет ключа status = self.request_statuses.pop(notification_id) result = NotificationResult(notification_id, status, description=reason) request.set_result(result) else: logger.warning('Could not find request %s', notification_id) else: logger.warning('Could not find notification by stream %s', stream_id)
def test_payload_truncation(self): """ Tests that APNS message bodies will be truncated to fit the limits of APNS. """ # Arrange method = self.apns_pushkin_snotif method.return_value = testutils.make_async_magic_mock( NotificationResult("notID", "200")) self.sygnal.pushkins[PUSHKIN_ID].MAX_JSON_BODY_SIZE = 240 # Act self._request(self._make_dummy_notification([DEVICE_EXAMPLE])) # Assert self.assertEqual(1, method.call_count) ((notification_req, ), _kwargs) = method.call_args payload = notification_req.message self.assertLessEqual(len(apnstruncate.json_encode(payload)), 240)
def test_expected_full_with_default_payload(self): """ Tests the expected fallback case: a good response from APNS means we pass on a good response to the homeserver. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "200")) # Act resp = self._request( self._make_dummy_notification( [DEVICE_EXAMPLE_WITH_DEFAULT_PAYLOAD])) # Assert self.assertEqual(1, method.call_count) ((notification_req, ), _kwargs) = method.call_args self.assertEqual( { "room_id": "!slw48wfj34rtnrf:example.com", "event_id": "$qTOWWTEL48yPm3uT-gdNhFcoHxfKbZuqRVnnWWSkGBs", "aps": { "alert": { "loc-key": "MSG_FROM_USER_IN_ROOM_WITH_CONTENT", "loc-args": [ "Major Tom", "Mission Control", "I'm floating in a most peculiar way.", ], }, "badge": 3, "mutable-content": 1, }, }, notification_req.message, ) self.assertEqual({"rejected": []}, resp)
def test_payload_truncation_test_validity(self): """ This tests that L{test_payload_truncation_success} is a valid test by showing that not limiting the truncation size would result in a longer message. """ # Arrange method = self.apns_pushkin_snotif method.return_value = testutils.make_async_magic_mock( NotificationResult("notID", "200")) self.sygnal.pushkins[PUSHKIN_ID].MAX_JSON_BODY_SIZE = 4096 # Act self._request(self._make_dummy_notification([DEVICE_EXAMPLE])) # Assert self.assertEqual(1, method.call_count) ((notification_req, ), _kwargs) = method.call_args payload = notification_req.message self.assertGreater(len(apnstruncate.json_encode(payload)), 200)
def test_expected(self): """ Tests the expected case: a good response from APNS means we pass on a good response to the homeserver. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "200")) # Act resp = self._request(self._make_dummy_notification([DEVICE_EXAMPLE])) # Assert self.assertEquals(1, method.call_count) ((notification_req, ), _kwargs) = method.call_args self.assertEquals( { "room_id": "!slw48wfj34rtnrf:example.com", "aps": { "alert": { "loc-key": "MSG_FROM_USER_IN_ROOM_WITH_CONTENT", "loc-args": [ "Major Tom", "Mission Control", "I'm floating in a most peculiar way.", ], }, "badge": 3, "content-available": 1, }, }, notification_req.message, ) self.assertEquals({"rejected": []}, resp)
def test_expected_event_id_only_with_default_payload(self): """ Tests the expected fallback case: a good response from APNS means we pass on a good response to the homeserver. """ # Arrange method = self.apns_pushkin_snotif method.side_effect = testutils.make_async_magic_mock( NotificationResult("notID", "200")) # Act resp = self._request( self._make_dummy_notification_event_id_only( [DEVICE_EXAMPLE_WITH_DEFAULT_PAYLOAD])) # Assert self.assertEqual(1, method.call_count) ((notification_req, ), _kwargs) = method.call_args self.assertEqual( { "room_id": "!slw48wfj34rtnrf:example.com", "event_id": "$qTOWWTEL48yPm3uT-gdNhFcoHxfKbZuqRVnnWWSkGBs", "unread_count": 2, "aps": { "alert": { "loc-key": "SINGLE_UNREAD", "loc-args": [] }, "mutable-content": 1, }, }, notification_req.message, ) self.assertEqual({"rejected": []}, resp)