Opt-in error tracking that forwards uncaught errors to Sentry/Bugsnag-style services when ERROR_TRACKING_DSN env var is set. Without DSN, disabled. Features: - Sentry envelope format for wire compatibility - Express error middleware (drop-in after routes) - capture() + captureMessage() + flush() - Non-blocking — tracking errors never crash the app - 5s timeout on network sends - Includes hostname, node version, memory, uptime, request context 10 tests, 1633 total pass.
92 lines
2.8 KiB
JavaScript
92 lines
2.8 KiB
JavaScript
/**
|
|
* DC-071: Error tracker tests
|
|
*/
|
|
const errorTracker = require('../src/utilities/error-tracker');
|
|
|
|
describe('DC-071: Error Tracker', () => {
|
|
beforeEach(() => {
|
|
// Reset to clean state
|
|
errorTracker.dsn = null;
|
|
errorTracker.enabled = false;
|
|
});
|
|
|
|
describe('init()', () => {
|
|
it('is disabled without DSN', () => {
|
|
const enabled = errorTracker.init({});
|
|
expect(enabled).toBe(false);
|
|
expect(errorTracker.enabled).toBe(false);
|
|
});
|
|
|
|
it('enables with DSN', () => {
|
|
const enabled = errorTracker.init({
|
|
dsn: 'https://abc123@sentry.io/123',
|
|
release: '1.15.0',
|
|
});
|
|
expect(enabled).toBe(true);
|
|
expect(errorTracker.enabled).toBe(true);
|
|
expect(errorTracker.release).toBe('1.15.0');
|
|
});
|
|
|
|
it('reads DSN from env', () => {
|
|
process.env.ERROR_TRACKING_DSN = 'https://key@sentry.io/456';
|
|
const enabled = errorTracker.init({});
|
|
expect(enabled).toBe(true);
|
|
delete process.env.ERROR_TRACKING_DSN;
|
|
});
|
|
});
|
|
|
|
describe('capture()', () => {
|
|
it('returns undefined when disabled', () => {
|
|
const result = errorTracker.capture(new Error('test'));
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('returns event ID when enabled', () => {
|
|
errorTracker.init({ dsn: 'https://key@sentry.io/123' });
|
|
const eventId = errorTracker.capture(new Error('test'));
|
|
expect(eventId).toBeTruthy();
|
|
expect(typeof eventId).toBe('string');
|
|
});
|
|
|
|
it('handles null error gracefully', () => {
|
|
errorTracker.init({ dsn: 'https://key@sentry.io/123' });
|
|
const result = errorTracker.capture(null);
|
|
expect(result).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('captureMessage()', () => {
|
|
it('returns undefined when disabled', () => {
|
|
const result = errorTracker.captureMessage('test');
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('returns event ID when enabled', () => {
|
|
errorTracker.init({ dsn: 'https://key@sentry.io/123' });
|
|
const eventId = errorTracker.captureMessage('test info', 'info');
|
|
expect(eventId).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('middleware()', () => {
|
|
it('calls next(err) after capturing', () => {
|
|
errorTracker.init({ dsn: 'https://key@sentry.io/123' });
|
|
const middleware = errorTracker.middleware();
|
|
const err = new Error('middleware test');
|
|
const req = { url: '/test', method: 'GET', headers: {}, path: '/test' };
|
|
const res = {};
|
|
let nextCalled = false;
|
|
let nextArg = null;
|
|
middleware(err, req, res, (e) => { nextCalled = true; nextArg = e; });
|
|
expect(nextCalled).toBe(true);
|
|
expect(nextArg).toBe(err);
|
|
});
|
|
});
|
|
|
|
describe('flush()', () => {
|
|
it('resolves without error', async () => {
|
|
await expect(errorTracker.flush(100)).resolves.toBeUndefined();
|
|
});
|
|
});
|
|
});
|