120 lines
3.6 KiB
TypeScript
120 lines
3.6 KiB
TypeScript
|
|
import { describe, test, expect } from 'bun:test';
|
||
|
|
import {
|
||
|
|
ShadeError,
|
||
|
|
InvalidSignatureError,
|
||
|
|
DecryptionError,
|
||
|
|
NoSessionError,
|
||
|
|
MaxSkipExceededError,
|
||
|
|
DuplicateMessageError,
|
||
|
|
UntrustedIdentityError,
|
||
|
|
PreKeyNotFoundError,
|
||
|
|
ReplayError,
|
||
|
|
IdentityRotationError,
|
||
|
|
NetworkError,
|
||
|
|
StorageError,
|
||
|
|
ValidationError,
|
||
|
|
TimeoutError,
|
||
|
|
RateLimitError,
|
||
|
|
ConfigurationError,
|
||
|
|
UnauthorizedError,
|
||
|
|
errorToHttpStatus,
|
||
|
|
} from '../src/errors.js';
|
||
|
|
|
||
|
|
describe('Error hierarchy', () => {
|
||
|
|
test('all errors extend ShadeError', () => {
|
||
|
|
const errors = [
|
||
|
|
new InvalidSignatureError(),
|
||
|
|
new DecryptionError(),
|
||
|
|
new NoSessionError('addr'),
|
||
|
|
new MaxSkipExceededError(1001, 1000),
|
||
|
|
new DuplicateMessageError(),
|
||
|
|
new UntrustedIdentityError('addr'),
|
||
|
|
new PreKeyNotFoundError(1, 'signed'),
|
||
|
|
new ReplayError(),
|
||
|
|
new IdentityRotationError(),
|
||
|
|
new NetworkError('net'),
|
||
|
|
new StorageError('storage'),
|
||
|
|
new ValidationError('val'),
|
||
|
|
new TimeoutError(),
|
||
|
|
new RateLimitError(),
|
||
|
|
new ConfigurationError('cfg'),
|
||
|
|
new UnauthorizedError(),
|
||
|
|
];
|
||
|
|
for (const e of errors) {
|
||
|
|
expect(e).toBeInstanceOf(ShadeError);
|
||
|
|
expect(e).toBeInstanceOf(Error);
|
||
|
|
expect(e.code).toMatch(/^SHADE_/);
|
||
|
|
expect(e.name).not.toBe('ShadeError');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
test('toJSON produces serializable form', () => {
|
||
|
|
const e = new InvalidSignatureError('bad sig');
|
||
|
|
const json = e.toJSON();
|
||
|
|
expect(json.name).toBe('InvalidSignatureError');
|
||
|
|
expect(json.code).toBe('SHADE_INVALID_SIGNATURE');
|
||
|
|
expect(json.message).toBe('bad sig');
|
||
|
|
|
||
|
|
const str = JSON.stringify(e);
|
||
|
|
const parsed = JSON.parse(str);
|
||
|
|
expect(parsed.code).toBe('SHADE_INVALID_SIGNATURE');
|
||
|
|
});
|
||
|
|
|
||
|
|
test('NetworkError carries status code', () => {
|
||
|
|
const e = new NetworkError('server error', 503);
|
||
|
|
expect(e.statusCode).toBe(503);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('RateLimitError carries retry-after', () => {
|
||
|
|
const e = new RateLimitError('too many', 60);
|
||
|
|
expect(e.retryAfterSeconds).toBe(60);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('ValidationError carries field name', () => {
|
||
|
|
const e = new ValidationError('must be positive', 'count');
|
||
|
|
expect(e.field).toBe('count');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('errorToHttpStatus', () => {
|
||
|
|
test('unknown error → 500', () => {
|
||
|
|
expect(errorToHttpStatus(new Error('plain'))).toBe(500);
|
||
|
|
expect(errorToHttpStatus('string error')).toBe(500);
|
||
|
|
expect(errorToHttpStatus(null)).toBe(500);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('auth errors → 401/403', () => {
|
||
|
|
expect(errorToHttpStatus(new InvalidSignatureError())).toBe(401);
|
||
|
|
expect(errorToHttpStatus(new UnauthorizedError())).toBe(401);
|
||
|
|
expect(errorToHttpStatus(new UntrustedIdentityError('x'))).toBe(403);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('not-found errors → 404', () => {
|
||
|
|
expect(errorToHttpStatus(new NoSessionError('x'))).toBe(404);
|
||
|
|
expect(errorToHttpStatus(new PreKeyNotFoundError(1, 'signed'))).toBe(404);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('validation → 400', () => {
|
||
|
|
expect(errorToHttpStatus(new ValidationError('bad'))).toBe(400);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('replay/duplicate → 409', () => {
|
||
|
|
expect(errorToHttpStatus(new ReplayError())).toBe(409);
|
||
|
|
expect(errorToHttpStatus(new DuplicateMessageError())).toBe(409);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('rate limit → 429', () => {
|
||
|
|
expect(errorToHttpStatus(new RateLimitError())).toBe(429);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('infra errors → 503', () => {
|
||
|
|
expect(errorToHttpStatus(new NetworkError('x'))).toBe(503);
|
||
|
|
expect(errorToHttpStatus(new StorageError('x'))).toBe(503);
|
||
|
|
expect(errorToHttpStatus(new ConfigurationError('x'))).toBe(503);
|
||
|
|
});
|
||
|
|
|
||
|
|
test('timeout → 504', () => {
|
||
|
|
expect(errorToHttpStatus(new TimeoutError())).toBe(504);
|
||
|
|
});
|
||
|
|
});
|