113 lines
2.7 KiB
JavaScript
113 lines
2.7 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { requestRefreshToken } from '../refresh-client.js';
|
|
|
|
function mockResponse({ ok, status, jsonData, textData, headers }) {
|
|
const normalizedHeaders = {};
|
|
if (headers) {
|
|
for (const [key, value] of Object.entries(headers)) {
|
|
normalizedHeaders[key.toLowerCase()] = value;
|
|
}
|
|
}
|
|
|
|
return {
|
|
ok,
|
|
status,
|
|
json: async () => jsonData ?? {},
|
|
text: async () => textData ?? '',
|
|
headers: {
|
|
get: (name) => normalizedHeaders[name.toLowerCase()] ?? null
|
|
}
|
|
};
|
|
}
|
|
|
|
test('refresh retries on 500 then succeeds', async () => {
|
|
let calls = 0;
|
|
const fetchImpl = async () => {
|
|
calls += 1;
|
|
if (calls === 1) {
|
|
return mockResponse({ ok: false, status: 500, textData: 'boom' });
|
|
}
|
|
return mockResponse({
|
|
ok: true,
|
|
status: 200,
|
|
jsonData: { access_token: 'access', refresh_token: 'refresh' }
|
|
});
|
|
};
|
|
|
|
const data = await requestRefreshToken({
|
|
refreshUrl: 'https://example.com',
|
|
refreshToken: 'refresh_token',
|
|
clientId: 'client',
|
|
timeoutMs: 20,
|
|
maxRetries: 1,
|
|
retryDelayMs: 1,
|
|
fetchImpl
|
|
});
|
|
|
|
assert.equal(data.access_token, 'access');
|
|
assert.equal(calls, 2);
|
|
});
|
|
|
|
test('refresh does not retry on 400', async () => {
|
|
let calls = 0;
|
|
const fetchImpl = async () => {
|
|
calls += 1;
|
|
return mockResponse({ ok: false, status: 400, textData: 'bad request' });
|
|
};
|
|
|
|
await assert.rejects(
|
|
() => requestRefreshToken({
|
|
refreshUrl: 'https://example.com',
|
|
refreshToken: 'refresh_token',
|
|
clientId: 'client',
|
|
timeoutMs: 20,
|
|
maxRetries: 2,
|
|
retryDelayMs: 1,
|
|
fetchImpl
|
|
}),
|
|
(err) => err?.status === 400
|
|
);
|
|
|
|
assert.equal(calls, 1);
|
|
});
|
|
|
|
test('refresh retries on timeout abort', async () => {
|
|
let calls = 0;
|
|
const fetchImpl = async (url, options) => {
|
|
calls += 1;
|
|
return new Promise((resolve, reject) => {
|
|
if (!options?.signal) {
|
|
resolve(mockResponse({ ok: true, status: 200, jsonData: {} }));
|
|
return;
|
|
}
|
|
if (options.signal.aborted) {
|
|
const error = new Error('Aborted');
|
|
error.name = 'AbortError';
|
|
reject(error);
|
|
return;
|
|
}
|
|
options.signal.addEventListener('abort', () => {
|
|
const error = new Error('Aborted');
|
|
error.name = 'AbortError';
|
|
reject(error);
|
|
});
|
|
});
|
|
};
|
|
|
|
await assert.rejects(
|
|
() => requestRefreshToken({
|
|
refreshUrl: 'https://example.com',
|
|
refreshToken: 'refresh_token',
|
|
clientId: 'client',
|
|
timeoutMs: 5,
|
|
maxRetries: 1,
|
|
retryDelayMs: 1,
|
|
fetchImpl
|
|
}),
|
|
(err) => err?.name === 'AbortError'
|
|
);
|
|
|
|
assert.equal(calls, 2);
|
|
});
|