Files
smartacme/test/test.acme-error.ts

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();