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