light-sentry-sdk/tests/error-safety.test.ts

271 lines
6.8 KiB
TypeScript

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import Client from '../src/core/Client';
import { setAnonymousId } from '../src/utils/identity';
describe('SDK Error Safety', () => {
const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
beforeEach(() => {
vi.clearAllMocks();
});
afterAll(() => {
consoleWarnSpy.mockRestore();
consoleLogSpy.mockRestore();
});
describe('Client public methods should not throw', () => {
let client: Client;
beforeEach(() => {
client = new Client({
dsn: 'https://proj_abc123@log.example.com/1001',
});
});
afterEach(() => {
client.destroy();
});
it('captureException should not throw on invalid input', () => {
expect(() => {
client.captureException(null as unknown as Error);
}).not.toThrow();
});
it('captureException should not throw on undefined input', () => {
expect(() => {
client.captureException(undefined);
}).not.toThrow();
});
it('captureException should not throw when SDK is disabled', () => {
client.disable();
expect(() => {
client.captureException(new Error('test'));
}).not.toThrow();
});
it('captureMessage should not throw on empty message', () => {
expect(() => {
client.captureMessage('');
}).not.toThrow();
});
it('captureMessage should not throw when SDK is disabled', () => {
client.disable();
expect(() => {
client.captureMessage('test');
}).not.toThrow();
});
it('captureEvent should not throw on minimal event', () => {
expect(() => {
client.captureEvent({ type: 'custom' });
}).not.toThrow();
});
it('captureEvent should not throw when SDK is disabled', () => {
client.disable();
expect(() => {
client.captureEvent({ type: 'custom' });
}).not.toThrow();
});
it('setUser should not throw on null input', () => {
expect(() => {
client.setUser(null);
}).not.toThrow();
});
it('setUser should not throw on valid input', () => {
expect(() => {
client.setUser({ id: '123', username: 'test' });
}).not.toThrow();
});
it('setTag should not throw', () => {
expect(() => {
client.setTag('key', 'value');
}).not.toThrow();
});
it('setTags should not throw', () => {
expect(() => {
client.setTags({ key1: 'val1', key2: 'val2' });
}).not.toThrow();
});
it('setExtra should not throw', () => {
expect(() => {
client.setExtra('key', { nested: true });
}).not.toThrow();
});
it('addBreadcrumb should not throw', () => {
expect(() => {
client.addBreadcrumb({
type: 'default',
message: 'test breadcrumb',
level: 'info',
});
}).not.toThrow();
});
it('addBreadcrumb should not throw on empty breadcrumb', () => {
expect(() => {
client.addBreadcrumb({
type: 'default',
});
}).not.toThrow();
});
it('startTransaction should not throw', () => {
expect(() => {
client.startTransaction({ name: 'test-tx', op: 'test' });
}).not.toThrow();
});
it('startTransaction should not throw when SDK is disabled', () => {
client.disable();
const result = client.startTransaction({ name: 'test-tx', op: 'test' });
expect(result).toBeNull();
});
it('getCurrentTransaction should not throw', () => {
expect(() => {
client.getCurrentTransaction();
}).not.toThrow();
});
it('getCurrentSpan should not throw', () => {
expect(() => {
client.getCurrentSpan();
}).not.toThrow();
});
it('setSpan should not throw', () => {
expect(() => {
client.setSpan(null);
}).not.toThrow();
});
it('startSpan should not throw', () => {
expect(() => {
client.startSpan({ name: 'test-span', op: 'test' });
}).not.toThrow();
});
it('startSpan should not throw when SDK is disabled', () => {
client.disable();
const result = client.startSpan({ name: 'test-span', op: 'test' });
expect(result).toBeNull();
});
it('disable should not throw', () => {
expect(() => {
client.disable();
}).not.toThrow();
});
it('enable should not throw', () => {
expect(() => {
client.enable();
}).not.toThrow();
});
it('use should not throw on valid plugin', () => {
const plugin = {
name: 'test-plugin',
setup: vi.fn(),
};
expect(() => {
client.use(plugin);
}).not.toThrow();
});
it('use should not throw when plugin setup throws', () => {
const plugin = {
name: 'bad-plugin',
setup: () => {
throw new Error('setup failed');
},
};
expect(() => {
client.use(plugin);
}).not.toThrow();
});
it('flush should not throw', async () => {
await expect(client.flush()).resolves.not.toThrow();
});
it('destroy should not throw', () => {
expect(() => {
client.destroy();
}).not.toThrow();
});
it('init should not throw', () => {
const newClient = new Client({
dsn: 'https://proj_abc123@log.example.com/1001',
});
expect(() => {
newClient.init();
}).not.toThrow();
newClient.destroy();
});
it('setAnonymousId should not throw', () => {
expect(() => {
client.setAnonymousId('test-anon-id');
}).not.toThrow();
});
it('on/off/emit should not throw', () => {
const handler = vi.fn();
expect(() => {
client.on('test', handler);
}).not.toThrow();
expect(() => {
client.emit('test', 'data');
}).not.toThrow();
expect(() => {
client.off('test', handler);
}).not.toThrow();
});
it('event handler errors should not affect other handlers', () => {
const handler1 = vi.fn(() => {
throw new Error('handler1 failed');
});
const handler2 = vi.fn();
client.on('test-event', handler1);
client.on('test-event', handler2);
expect(() => {
client.emit('test-event', 'data');
}).not.toThrow();
expect(handler1).toHaveBeenCalled();
expect(handler2).toHaveBeenCalled();
});
});
describe('Config edge cases', () => {
it('should throw on missing DSN (config error, not SDK bug)', () => {
expect(() => {
new Client({ dsn: '' });
}).toThrow();
});
it('should throw on undefined DSN', () => {
expect(() => {
new Client({ dsn: undefined as unknown as string });
}).toThrow();
});
});
});