mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-19 08:28:06 +02:00
feat(telemetry): include error details for failures (#254)
This commit is contained in:
parent
494618ab14
commit
6da8c3452a
18 changed files with 1259 additions and 999 deletions
|
|
@ -34,4 +34,23 @@ describe('telemetry command hook', () => {
|
|||
resetCommandSpan();
|
||||
expect(completeCommandSpan({ completedAt: 200, outcome: 'ok' })).toBeUndefined();
|
||||
});
|
||||
|
||||
it('captures errorClass and raw errorDetail on a failed command', () => {
|
||||
resetCommandSpan();
|
||||
beginCommandSpan({
|
||||
commandPath: ['ktx', 'ingest'],
|
||||
flagsPresent: {},
|
||||
hasProject: true,
|
||||
attachProjectGroup: false,
|
||||
startedAt: 0,
|
||||
});
|
||||
|
||||
class KtxConnectionError extends Error {}
|
||||
const error = new KtxConnectionError('connect ECONNREFUSED 127.0.0.1:5432');
|
||||
|
||||
const completed = completeCommandSpan({ completedAt: 10, outcome: 'error', error });
|
||||
expect(completed?.outcome).toBe('error');
|
||||
expect(completed?.errorClass).toBe('KtxConnectionError');
|
||||
expect(completed?.errorDetail).toBe('connect ECONNREFUSED 127.0.0.1:5432');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { scrubErrorClass } from '../../src/telemetry/scrubber.js';
|
||||
import { formatErrorDetail, scrubErrorClass } from '../../src/telemetry/scrubber.js';
|
||||
|
||||
class KtxProjectMissingAbortError extends Error {}
|
||||
|
||||
|
|
@ -23,3 +23,39 @@ describe('scrubErrorClass', () => {
|
|||
expect(scrubErrorClass(null)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatErrorDetail', () => {
|
||||
it('prefixes a string or numeric .code onto the message', () => {
|
||||
const refused = new Error('connect failed');
|
||||
(refused as { code?: unknown }).code = 'ECONNREFUSED';
|
||||
expect(formatErrorDetail(refused)).toBe('ECONNREFUSED: connect failed');
|
||||
|
||||
const forbidden = new Error('forbidden');
|
||||
(forbidden as { code?: unknown }).code = 403;
|
||||
expect(formatErrorDetail(forbidden)).toBe('403: forbidden');
|
||||
});
|
||||
|
||||
it('uses the bare message when there is no .code', () => {
|
||||
expect(formatErrorDetail(new Error('password authentication failed for user "x"'))).toBe(
|
||||
'password authentication failed for user "x"',
|
||||
);
|
||||
});
|
||||
|
||||
it('accepts non-Error values', () => {
|
||||
expect(formatErrorDetail('boom')).toBe('boom');
|
||||
});
|
||||
|
||||
it('collapses whitespace to a single line', () => {
|
||||
expect(formatErrorDetail(new Error('line one\n line two'))).toBe('line one line two');
|
||||
});
|
||||
|
||||
it('caps the length at 1000 characters', () => {
|
||||
expect(formatErrorDetail(new Error('x'.repeat(2000)))?.length).toBe(1000);
|
||||
});
|
||||
|
||||
it('returns undefined for empty, null, or undefined input', () => {
|
||||
expect(formatErrorDetail(new Error(' '))).toBeUndefined();
|
||||
expect(formatErrorDetail(null)).toBeUndefined();
|
||||
expect(formatErrorDetail(undefined)).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue