Skip to content

Commit 07376bd

Browse files
committed
Add non-200 non-error response code tests
1 parent 07bd5ac commit 07376bd

File tree

2 files changed

+151
-10
lines changed

2 files changed

+151
-10
lines changed

firebase_admin/messaging.py

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -453,10 +453,17 @@ def _message_data(self, message, dry_run):
453453
return data
454454

455455
def _postproc(self, resp, body):
456-
if resp.status == 200:
457-
return json.loads(body.decode())
458-
else:
459-
raise Exception('unexpected response')
456+
if resp.status is not 200:
457+
data = {}
458+
try:
459+
parsed_body = json.loads(body.decode())
460+
if isinstance(parsed_body, dict):
461+
data = parsed_body
462+
except ValueError:
463+
pass
464+
code, msg = _MessagingService._parse_fcm_error(data, body, resp.status)
465+
raise ApiCallError(code, msg)
466+
return json.loads(body.decode())
460467

461468
def _handle_fcm_error(self, error):
462469
"""Handles errors received from the FCM API."""
@@ -468,8 +475,9 @@ def _handle_fcm_error(self, error):
468475
except ValueError:
469476
pass
470477

471-
raise _MessagingService._parse_fcm_error(
472-
data, error.response.content, error.response.status_code, error)
478+
code, msg = _MessagingService._parse_fcm_error(
479+
data, error.response.content, error.response.status_code)
480+
raise ApiCallError(code, msg, error)
473481

474482
def _handle_iid_error(self, error):
475483
"""Handles errors received from the Instance ID API."""
@@ -502,10 +510,12 @@ def _parse_batch_error(self, error):
502510
data = parsed_body
503511
except ValueError:
504512
pass
505-
return _MessagingService._parse_fcm_error(data, error.content, error.resp.status, error)
513+
514+
code, msg = _MessagingService._parse_fcm_error(data, error.content, error.resp.status)
515+
return ApiCallError(code, msg, error)
506516

507517
@classmethod
508-
def _parse_fcm_error(cls, data, content, status_code, error):
518+
def _parse_fcm_error(cls, data, content, status_code):
509519
"""Parses an error response from the FCM API to a ApiCallError."""
510520
error_dict = data.get('error', {})
511521
server_code = None
@@ -521,5 +531,4 @@ def _parse_fcm_error(cls, data, content, status_code, error):
521531
if not msg:
522532
msg = 'Unexpected HTTP response with status: {0}; body: {1}'.format(
523533
status_code, content.decode())
524-
525-
return ApiCallError(code, msg, error)
534+
return code, msg

tests/test_messaging.py

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1418,6 +1418,72 @@ def test_send_all(self):
14181418
assert all([r.success for r in batch_response.responses])
14191419
assert not any([r.exception for r in batch_response.responses])
14201420

1421+
def test_send_all_non_error_non_200(self):
1422+
success_payload = json.dumps({'name': 'message-id'})
1423+
error_payload = json.dumps({})
1424+
_ = self._instrument_batch_messaging_service(
1425+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1426+
msg = messaging.Message(topic='foo')
1427+
with pytest.raises(messaging.ApiCallError) as excinfo:
1428+
messaging.send_all([msg, msg], dry_run=True)
1429+
assert str(excinfo.value) == 'Unexpected HTTP response with status: 202; body: {}\r\n'
1430+
assert str(excinfo.value.code) == messaging._MessagingService.UNKNOWN_ERROR
1431+
1432+
def test_send_all_non_error_non_200_detailed_error(self):
1433+
success_payload = json.dumps({'name': 'message-id'})
1434+
error_payload = json.dumps({
1435+
'error': {
1436+
'status': 'INVALID_ARGUMENT',
1437+
'message': 'test error'
1438+
}
1439+
})
1440+
_ = self._instrument_batch_messaging_service(
1441+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1442+
msg = messaging.Message(topic='foo')
1443+
with pytest.raises(messaging.ApiCallError) as excinfo:
1444+
batch_response = messaging.send_all([msg, msg], dry_run=True)
1445+
assert str(excinfo.value) == 'test error'
1446+
assert str(excinfo.value.code) == 'invalid-argument'
1447+
1448+
def test_send_all_non_error_non_200_canonical_error_code(self):
1449+
success_payload = json.dumps({'name': 'message-id'})
1450+
error_payload = json.dumps({
1451+
'error': {
1452+
'status': 'NOT_FOUND',
1453+
'message': 'test error'
1454+
}
1455+
})
1456+
_ = self._instrument_batch_messaging_service(
1457+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1458+
msg = messaging.Message(topic='foo')
1459+
with pytest.raises(messaging.ApiCallError) as excinfo:
1460+
messaging.send_all([msg, msg], dry_run=True)
1461+
expected = 'send_all messages must not contain more than 100 messages.'
1462+
assert str(excinfo.value) == 'test error'
1463+
assert str(excinfo.value.code) == 'registration-token-not-registered'
1464+
1465+
def test_send_all_non_error_non_200_fcm_error_code(self):
1466+
success_payload = json.dumps({'name': 'message-id'})
1467+
error_payload = json.dumps({
1468+
'error': {
1469+
'status': 'INVALID_ARGUMENT',
1470+
'message': 'test error',
1471+
'details': [
1472+
{
1473+
'@type': 'type.googleapis.com/google.firebase.fcm.v1.FcmError',
1474+
'errorCode': 'UNREGISTERED',
1475+
},
1476+
],
1477+
}
1478+
})
1479+
_ = self._instrument_batch_messaging_service(
1480+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1481+
msg = messaging.Message(topic='foo')
1482+
with pytest.raises(messaging.ApiCallError) as excinfo:
1483+
messaging.send_all([msg, msg], dry_run=True)
1484+
assert str(excinfo.value) == 'test error'
1485+
assert str(excinfo.value.code) == 'registration-token-not-registered'
1486+
14211487
@pytest.mark.parametrize('status', HTTP_ERRORS)
14221488
def test_send_all_detailed_error(self, status):
14231489
success_payload = json.dumps({'name': 'message-id'})
@@ -1599,6 +1665,72 @@ def test_send_multicast(self):
15991665
assert all([r.success for r in batch_response.responses])
16001666
assert not any([r.exception for r in batch_response.responses])
16011667

1668+
def test_send_multicast_non_error_non_200(self):
1669+
success_payload = json.dumps({'name': 'message-id'})
1670+
error_payload = json.dumps({})
1671+
_ = self._instrument_batch_messaging_service(
1672+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1673+
msg = messaging.MulticastMessage(tokens=['foo', 'foo'])
1674+
with pytest.raises(messaging.ApiCallError) as excinfo:
1675+
messaging.send_multicast(msg, dry_run=True)
1676+
expected = 'Unexpected HTTP response with status: 202; body: {}\r\n'
1677+
assert str(excinfo.value) == expected
1678+
assert str(excinfo.value.code) == messaging._MessagingService.UNKNOWN_ERROR
1679+
1680+
def test_send_multicast_non_error_non_200_detailed_error(self):
1681+
success_payload = json.dumps({'name': 'message-id'})
1682+
error_payload = json.dumps({
1683+
'error': {
1684+
'status': 'INVALID_ARGUMENT',
1685+
'message': 'test error'
1686+
}
1687+
})
1688+
_ = self._instrument_batch_messaging_service(
1689+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1690+
msg = messaging.MulticastMessage(tokens=['foo', 'foo'])
1691+
with pytest.raises(messaging.ApiCallError) as excinfo:
1692+
batch_response = messaging.send_multicast(msg, dry_run=True)
1693+
assert str(excinfo.value) == 'test error'
1694+
assert str(excinfo.value.code) == 'invalid-argument'
1695+
1696+
def test_send_multicast_non_error_non_200_canonical_error_code(self):
1697+
success_payload = json.dumps({'name': 'message-id'})
1698+
error_payload = json.dumps({
1699+
'error': {
1700+
'status': 'NOT_FOUND',
1701+
'message': 'test error'
1702+
}
1703+
})
1704+
_ = self._instrument_batch_messaging_service(
1705+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1706+
msg = messaging.MulticastMessage(tokens=['foo', 'foo'])
1707+
with pytest.raises(messaging.ApiCallError) as excinfo:
1708+
messaging.send_multicast(msg, dry_run=True)
1709+
assert str(excinfo.value) == 'test error'
1710+
assert str(excinfo.value.code) == 'registration-token-not-registered'
1711+
1712+
def test_send_multicast_non_error_non_200_fcm_error_code(self):
1713+
success_payload = json.dumps({'name': 'message-id'})
1714+
error_payload = json.dumps({
1715+
'error': {
1716+
'status': 'INVALID_ARGUMENT',
1717+
'message': 'test error',
1718+
'details': [
1719+
{
1720+
'@type': 'type.googleapis.com/google.firebase.fcm.v1.FcmError',
1721+
'errorCode': 'UNREGISTERED',
1722+
},
1723+
],
1724+
}
1725+
})
1726+
_ = self._instrument_batch_messaging_service(
1727+
payload=self._batch_payload([(200, success_payload), (202, error_payload)]))
1728+
msg = messaging.MulticastMessage(tokens=['foo', 'foo'])
1729+
with pytest.raises(messaging.ApiCallError) as excinfo:
1730+
messaging.send_multicast(msg, dry_run=True)
1731+
assert str(excinfo.value) == 'test error'
1732+
assert str(excinfo.value.code) == 'registration-token-not-registered'
1733+
16021734
@pytest.mark.parametrize('status', HTTP_ERRORS)
16031735
def test_send_multicast_detailed_error(self, status):
16041736
success_payload = json.dumps({'name': 'message-id'})

0 commit comments

Comments
 (0)