Skip to content

Commit 1022f46

Browse files
fix: expose isRequestRetryable as a static method on Client and add unit tests
1 parent 33eb7d6 commit 1022f46

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

lib/client.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ module.exports = function (dependencies) {
146146
* @param {string} [error.error.message] - The error message which may indicate specific conditions such as 'ExpiredProviderToken' or transient write failures.
147147
* @returns {boolean} - Returns true if the request is considered retryable based on the error, otherwise false.
148148
*/
149-
function isRequestRetryable(error) {
149+
Client.isRequestRetryable = function isRequestRetryable(error) {
150150
const isStatusCodeRetryable = [408, 429, 500, 502, 503, 504].includes(error.status);
151151

152152
const isProviderTokenExpired =
@@ -156,7 +156,7 @@ module.exports = function (dependencies) {
156156
const isTransientWriteFailure = !!error.error?.message?.startsWith('apn write failed');
157157

158158
return isStatusCodeRetryable || isProviderTokenExpired || isTransientWriteFailure;
159-
}
159+
};
160160

161161
Client.prototype.write = async function write(notification, subDirectory, type, method, count) {
162162
const retryCount = count || 0;
@@ -217,7 +217,7 @@ module.exports = function (dependencies) {
217217
);
218218
return { ...subDirectoryInformation, ...sentRequest };
219219
} catch (error) {
220-
if (isRequestRetryable(error)) {
220+
if (Client.isRequestRetryable(error)) {
221221
try {
222222
const resentRequest = await this.retryRequest(
223223
error,
@@ -268,7 +268,7 @@ module.exports = function (dependencies) {
268268
);
269269
return { ...subDirectoryInformation, ...sentRequest };
270270
} catch (error) {
271-
if (isRequestRetryable(error)) {
271+
if (Client.isRequestRetryable(error)) {
272272
try {
273273
const resentRequest = await this.retryRequest(
274274
error,

test/isRequestRetryable.unit.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
const { expect } = require('chai');
2+
const Client = require('../lib/client')({
3+
logger: { enabled: false, log: () => {} },
4+
errorLogger: { enabled: false, log: () => {} },
5+
config: () => ({}),
6+
http2: { constants: {} },
7+
});
8+
9+
describe('Client.isRequestRetryable', () => {
10+
it('returns true for error.message starting with "apn write failed"', () => {
11+
const error = { error: { message: 'apn write failed: socket hang up' } };
12+
expect(Client.isRequestRetryable(error)).to.be.true;
13+
});
14+
15+
it('returns false for error.message starting with "apn write timeout" (not retryable)', () => {
16+
const error = { error: { message: 'apn write timeout: timed out' } };
17+
expect(Client.isRequestRetryable(error)).to.be.false;
18+
});
19+
20+
it('returns false for error.message starting with "apn write aborted" (not retryable)', () => {
21+
const error = { error: { message: 'apn write aborted: aborted' } };
22+
expect(Client.isRequestRetryable(error)).to.be.false;
23+
});
24+
25+
it('returns false for error.message with other apn messages', () => {
26+
const error = { error: { message: 'apn connection closed' } };
27+
expect(Client.isRequestRetryable(error)).to.be.false;
28+
});
29+
30+
it('returns false for null error.error', () => {
31+
const error = { error: null };
32+
expect(Client.isRequestRetryable(error)).to.be.false;
33+
});
34+
35+
it('returns false for undefined error.error', () => {
36+
const error = {};
37+
expect(Client.isRequestRetryable(error)).to.be.false;
38+
});
39+
40+
it('returns false for error.error.message not matching', () => {
41+
const error = { error: { message: 'some other error' } };
42+
expect(Client.isRequestRetryable(error)).to.be.false;
43+
});
44+
45+
it('returns true for retryable status codes', () => {
46+
[408, 429, 500, 502, 503, 504].forEach(status => {
47+
expect(Client.isRequestRetryable({ status })).to.be.true;
48+
});
49+
});
50+
51+
it('returns true for ExpiredProviderToken', () => {
52+
const error = { status: 403, error: { message: 'ExpiredProviderToken' } };
53+
expect(Client.isRequestRetryable(error)).to.be.true;
54+
});
55+
56+
it('returns false for non-retryable status codes', () => {
57+
[200, 201, 400, 404, 401, 403].forEach(status => {
58+
expect(Client.isRequestRetryable({ status })).to.be.false;
59+
});
60+
});
61+
});

0 commit comments

Comments
 (0)