/**
* Unit tests for Error classes
*/
import { describe, expect, it } from 'vitest';
import {
HNAPIError,
HNError,
ItemNotFoundError,
NetworkError,
RateLimitError,
ValidationError,
} from '../../src/lib/errors.js';
describe('Error Classes', () => {
describe('HNError', () => {
it('should create base error with code and context', () => {
const error = new HNError('Test message', 'TEST_CODE', { key: 'value' });
expect(error.message).toBe('Test message');
expect(error.code).toBe('TEST_CODE');
expect(error.context).toEqual({ key: 'value' });
expect(error.name).toBe('HNError');
});
it('should create error without context', () => {
const error = new HNError('Test message', 'TEST_CODE');
expect(error.message).toBe('Test message');
expect(error.code).toBe('TEST_CODE');
expect(error.context).toBeUndefined();
});
it('should convert to MCP error format', () => {
const error = new HNError('Test message', 'TEST_CODE', { key: 'value' });
const mcpError = error.toMCPError();
expect(mcpError).toEqual({
content: [
{
type: 'text',
text: 'Error: Test message\nCode: TEST_CODE\nContext: {\n "key": "value"\n}',
},
],
isError: true,
});
});
it('should convert to MCP error without context', () => {
const error = new HNError('Test message', 'TEST_CODE');
const mcpError = error.toMCPError();
expect(mcpError).toEqual({
content: [
{
type: 'text',
text: 'Error: Test message\nCode: TEST_CODE',
},
],
isError: true,
});
});
});
describe('HNAPIError', () => {
it('should create API error with status and response', () => {
const error = new HNAPIError('API failed', 500, 'error response', { url: 'test.com' });
expect(error.message).toBe('API failed');
expect(error.code).toBe('HN_API_ERROR');
expect(error.statusCode).toBe(500);
expect(error.response).toBe('error response');
expect(error.context).toEqual({
url: 'test.com',
statusCode: 500,
response: 'error response',
});
expect(error.name).toBe('HNAPIError');
});
});
describe('RateLimitError', () => {
it('should create rate limit error with reset time', () => {
const resetTime = new Date('2024-01-01T12:00:00Z');
const error = new RateLimitError(9500, 10000, resetTime);
expect(error.message).toBe(
'Rate limit exceeded: 9500/10000 requests per hour. Resets at 2024-01-01T12:00:00.000Z'
);
expect(error.code).toBe('RATE_LIMIT_EXCEEDED');
expect(error.current).toBe(9500);
expect(error.limit).toBe(10000);
expect(error.context).toEqual({
current: 9500,
limit: 10000,
resetTime: '2024-01-01T12:00:00.000Z',
});
expect(error.name).toBe('RateLimitError');
});
it('should create rate limit error without reset time', () => {
const error = new RateLimitError(9500, 10000);
expect(error.message).toBe('Rate limit exceeded: 9500/10000 requests per hour.');
expect(error.code).toBe('RATE_LIMIT_EXCEEDED');
expect(error.current).toBe(9500);
expect(error.limit).toBe(10000);
expect(error.context).toEqual({
current: 9500,
limit: 10000,
resetTime: undefined,
});
});
});
describe('ValidationError', () => {
it('should create validation error with field and value', () => {
const error = new ValidationError('Invalid value', 'username', 'invalid@user', { source: 'input' });
expect(error.message).toBe('Invalid value');
expect(error.code).toBe('VALIDATION_ERROR');
expect(error.field).toBe('username');
expect(error.value).toBe('invalid@user');
expect(error.context).toEqual({
source: 'input',
field: 'username',
value: 'invalid@user',
});
expect(error.name).toBe('ValidationError');
});
});
describe('NetworkError', () => {
it('should create network error with cause', () => {
const cause = new Error('Connection failed');
const error = new NetworkError('Network request failed', cause, { url: 'test.com' });
expect(error.message).toBe('Network request failed');
expect(error.code).toBe('NETWORK_ERROR');
expect(error.context).toEqual({
url: 'test.com',
cause: 'Connection failed',
});
expect(error.name).toBe('NetworkError');
expect(error.stack).toContain('Caused by:');
});
it('should create network error without cause', () => {
const error = new NetworkError('Network request failed');
expect(error.message).toBe('Network request failed');
expect(error.code).toBe('NETWORK_ERROR');
expect(error.context).toEqual({
cause: undefined,
});
expect(error.name).toBe('NetworkError');
});
});
describe('ItemNotFoundError', () => {
it('should create item not found error for story', () => {
const error = new ItemNotFoundError('story', 123, { source: 'api' });
expect(error.message).toBe('story not found: 123');
expect(error.code).toBe('ITEM_NOT_FOUND');
expect(error.statusCode).toBe(404);
expect(error.context).toEqual({
source: 'api',
itemType: 'story',
identifier: 123,
statusCode: 404,
response: undefined,
});
expect(error.name).toBe('ItemNotFoundError');
});
it('should create item not found error for user', () => {
const error = new ItemNotFoundError('user', 'testuser');
expect(error.message).toBe('user not found: testuser');
expect(error.code).toBe('ITEM_NOT_FOUND');
expect(error.statusCode).toBe(404);
expect(error.context).toEqual({
itemType: 'user',
identifier: 'testuser',
statusCode: 404,
response: undefined,
});
});
});
});