104 lines
3.5 KiB
TypeScript
104 lines
3.5 KiB
TypeScript
import { tap, expect } from '@git.zone/tstest/tapbundle';
|
|
import { AcmeError } from '../ts/acme/acme.classes.error.js';
|
|
|
|
// --- Basic error properties ---
|
|
|
|
tap.test('AcmeError extends Error', async () => {
|
|
const err = new AcmeError({ status: 400 });
|
|
expect(err).toBeInstanceOf(Error);
|
|
expect(err).toBeInstanceOf(AcmeError);
|
|
});
|
|
|
|
tap.test('AcmeError has correct name', async () => {
|
|
const err = new AcmeError({ status: 400 });
|
|
expect(err.name).toEqual('AcmeError');
|
|
});
|
|
|
|
tap.test('message includes type, status, url, and detail', async () => {
|
|
const err = new AcmeError({
|
|
status: 403,
|
|
type: 'urn:ietf:params:acme:error:unauthorized',
|
|
detail: 'Account is deactivated',
|
|
url: 'https://acme.example/order/1',
|
|
});
|
|
expect(err.message).toInclude('403');
|
|
expect(err.message).toInclude('urn:ietf:params:acme:error:unauthorized');
|
|
expect(err.message).toInclude('https://acme.example/order/1');
|
|
expect(err.message).toInclude('Account is deactivated');
|
|
});
|
|
|
|
tap.test('preserves all properties including subproblems', async () => {
|
|
const subproblems = [
|
|
{ type: 'urn:ietf:params:acme:error:caa', detail: 'CAA record prevents issuance', identifier: { type: 'dns', value: 'example.com' } },
|
|
];
|
|
const err = new AcmeError({
|
|
status: 403,
|
|
type: 'urn:ietf:params:acme:error:unauthorized',
|
|
detail: 'Forbidden',
|
|
subproblems,
|
|
url: 'https://acme.example/chall/1',
|
|
retryAfter: 30,
|
|
});
|
|
expect(err.status).toEqual(403);
|
|
expect(err.type).toEqual('urn:ietf:params:acme:error:unauthorized');
|
|
expect(err.detail).toEqual('Forbidden');
|
|
expect(err.subproblems.length).toEqual(1);
|
|
expect(err.subproblems[0].type).toEqual('urn:ietf:params:acme:error:caa');
|
|
expect(err.url).toEqual('https://acme.example/chall/1');
|
|
expect(err.retryAfter).toEqual(30);
|
|
});
|
|
|
|
tap.test('default values for optional fields', async () => {
|
|
const err = new AcmeError({ status: 500 });
|
|
expect(err.type).toEqual('');
|
|
expect(err.detail).toEqual('');
|
|
expect(err.subproblems).toEqual([]);
|
|
expect(err.url).toEqual('');
|
|
expect(err.retryAfter).toEqual(0);
|
|
});
|
|
|
|
// --- isRateLimited ---
|
|
|
|
tap.test('isRateLimited is true for status 429', async () => {
|
|
const err = new AcmeError({ status: 429 });
|
|
expect(err.isRateLimited).toBeTrue();
|
|
});
|
|
|
|
tap.test('isRateLimited is true for rateLimited type URN', async () => {
|
|
const err = new AcmeError({
|
|
status: 403,
|
|
type: 'urn:ietf:params:acme:error:rateLimited',
|
|
});
|
|
expect(err.isRateLimited).toBeTrue();
|
|
});
|
|
|
|
tap.test('isRateLimited is false for regular 400', async () => {
|
|
const err = new AcmeError({ status: 400, type: 'urn:ietf:params:acme:error:malformed' });
|
|
expect(err.isRateLimited).toBeFalse();
|
|
});
|
|
|
|
// --- isRetryable ---
|
|
|
|
tap.test('isRetryable is true for 429', async () => {
|
|
const err = new AcmeError({ status: 429 });
|
|
expect(err.isRetryable).toBeTrue();
|
|
});
|
|
|
|
tap.test('isRetryable is true for 503 and 500', async () => {
|
|
expect(new AcmeError({ status: 503 }).isRetryable).toBeTrue();
|
|
expect(new AcmeError({ status: 500 }).isRetryable).toBeTrue();
|
|
});
|
|
|
|
tap.test('isRetryable is true for badNonce type', async () => {
|
|
const err = new AcmeError({ status: 400, type: 'urn:ietf:params:acme:error:badNonce' });
|
|
expect(err.isRetryable).toBeTrue();
|
|
});
|
|
|
|
tap.test('isRetryable is false for 403, 404, 409', async () => {
|
|
expect(new AcmeError({ status: 403 }).isRetryable).toBeFalse();
|
|
expect(new AcmeError({ status: 404 }).isRetryable).toBeFalse();
|
|
expect(new AcmeError({ status: 409 }).isRetryable).toBeFalse();
|
|
});
|
|
|
|
export default tap.start();
|