autotel
Version:
Write Once, Observe Anywhere
289 lines (287 loc) • 8.76 kB
JavaScript
export { assertEventTracked, assertOutcomeTracked, createEventCollector } from './chunk-BZHG5IZ4.js';
import './chunk-55ER2KD5.js';
import { configure } from './chunk-J5QENANM.js';
import './chunk-HA2WBOGQ.js';
import './chunk-DGUM43GV.js';
import { SpanStatusCode, trace, context } from '@opentelemetry/api';
function createTraceCollector() {
const spans = [];
const createMockSpan = (name, startTime) => {
const spanData = {
name,
startTime,
attributes: {},
status: { code: SpanStatusCode.OK }
};
const spanContextData = {
traceId: "1234567890abcdef1234567890abcdef",
// 128-bit trace ID (32 hex chars)
spanId: "1234567890abcdef",
// 64-bit span ID (16 hex chars)
traceFlags: 1,
isRemote: false
};
const mockSpan = {
spanContext: () => spanContextData,
setStatus(status) {
spanData.status = status;
return this;
},
setAttributes(attributes) {
spanData.attributes = { ...spanData.attributes, ...attributes };
return this;
},
setAttribute(key, value) {
spanData.attributes = spanData.attributes || {};
spanData.attributes[key] = value;
return this;
},
addEvent(name2, attributesOrStartTime, startTime2) {
return this;
},
addLink(link) {
return this;
},
addLinks(links) {
return this;
},
updateName(newName) {
spanData.name = newName;
return this;
},
isRecording() {
return true;
},
recordException(exception, time) {
},
end(endTimeArg) {
const endTime = performance.now();
spans.push({
name: spanData.name,
status: spanData.status,
attributes: spanData.attributes || {},
startTime: spanData.startTime,
endTime,
duration: endTime - spanData.startTime
});
}
};
return mockSpan;
};
const mockTracer = {
startSpan(name, options, ctx) {
const startTime = performance.now();
return createMockSpan(name, startTime);
},
startActiveSpan(name, optionsOrFn, contextOrFn, fn) {
const callback = (() => {
if (typeof optionsOrFn === "function") {
return optionsOrFn;
}
if (typeof contextOrFn === "function") {
return contextOrFn;
}
if (fn) {
return fn;
}
throw new Error("startActiveSpan requires a callback");
})();
const startTime = performance.now();
const mockSpan = createMockSpan(name, startTime);
const ctx = trace.setSpan(context.active(), mockSpan);
return context.with(ctx, () => callback(mockSpan));
}
};
configure({ tracer: mockTracer });
return {
getSpans() {
return [...spans];
},
getSpansByName(name) {
return spans.filter((span) => span.name === name);
},
getSpansByAttributes(attributes) {
return spans.filter((span) => {
return Object.entries(attributes).every(
([key, value]) => span.attributes[key] === value
);
});
},
clear() {
spans.length = 0;
},
recordSpan(span) {
spans.push(span);
}
};
}
function assertTraceCreated(collector, operationName, options) {
const spans = collector.getSpansByName(operationName);
if (options?.minCount !== void 0 && spans.length < options.minCount) {
throw new Error(
`Expected at least ${options.minCount} traces for ${operationName}, got ${spans.length}`
);
}
if (options?.maxCount !== void 0 && spans.length > options.maxCount) {
throw new Error(
`Expected at most ${options.maxCount} traces for ${operationName}, got ${spans.length}`
);
}
if (spans.length === 0) {
throw new Error(`No traces found for operation: ${operationName}`);
}
if (options?.status !== void 0) {
const matchingSpans = spans.filter(
(span) => span.status.code === options.status
);
if (matchingSpans.length === 0) {
throw new Error(
`No traces with status ${options.status} found for ${operationName}`
);
}
}
if (options?.attributes) {
const matchingSpans = spans.filter((span) => {
return Object.entries(options.attributes).every(
([key, value]) => span.attributes[key] === value
);
});
if (matchingSpans.length === 0) {
throw new Error(
`No traces with attributes ${JSON.stringify(options.attributes)} found for ${operationName}`
);
}
}
}
function assertNoErrors(collector) {
const errorSpans = collector.getSpans().filter((span) => span.status.code === SpanStatusCode.ERROR);
if (errorSpans.length > 0) {
const errorSummary = errorSpans.map((span) => `${span.name}: ${span.status.message}`).join("\n");
throw new Error(`Found ${errorSpans.length} error spans:
${errorSummary}`);
}
}
function assertTraceSucceeded(collector, operationName) {
assertTraceCreated(collector, operationName, { status: SpanStatusCode.OK });
}
function assertTraceFailed(collector, operationName, errorMessage) {
const spans = collector.getSpansByName(operationName);
if (spans.length === 0) {
throw new Error(`No traces found for operation: ${operationName}`);
}
const errorSpans = spans.filter(
(span) => span.status.code === SpanStatusCode.ERROR
);
if (errorSpans.length === 0) {
throw new Error(`No error traces found for operation: ${operationName}`);
}
if (errorMessage) {
const matchingSpans = errorSpans.filter(
(span) => span.status.message === errorMessage
);
if (matchingSpans.length === 0) {
throw new Error(
`No error traces with message "${errorMessage}" found for ${operationName}`
);
}
}
}
function createMockLogger() {
const logs = [];
const createLogMethod = (level) => {
return (objOrMsg, msg) => {
if (typeof objOrMsg === "string") {
logs.push({
level,
message: objOrMsg,
extra: void 0
});
} else {
logs.push({
level,
message: msg || "",
extra: objOrMsg
});
}
};
};
return {
info: createLogMethod("info"),
warn: createLogMethod("warn"),
debug: createLogMethod("debug"),
error(objOrMsg, msg) {
if (typeof objOrMsg === "string") {
logs.push({
level: "error",
message: objOrMsg,
extra: void 0,
error: void 0
});
return;
}
const { err, ...rest } = objOrMsg;
logs.push({
level: "error",
message: msg || "",
error: err instanceof Error ? err : void 0,
extra: err !== void 0 && !(err instanceof Error) ? { err, ...rest } : rest
});
},
getLogs() {
return [...logs];
},
getLogsByLevel(level) {
return logs.filter((log) => log.level === level);
},
getLogsByMessage(message) {
return logs.filter((log) => log.message.includes(message));
},
clear() {
logs.length = 0;
}
};
}
function assertNoErrorsLogged(logger) {
const errorLogs = logger.getLogsByLevel("error");
if (errorLogs.length > 0) {
const errorSummary = errorLogs.map(
(log) => `${log.message}${log.error ? ": " + log.error.message : ""}`
).join("\n");
throw new Error(`Found ${errorLogs.length} error logs:
${errorSummary}`);
}
}
async function waitForTrace(collector, operationName, timeoutMs = 5e3) {
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const spans = collector.getSpansByName(operationName);
if (spans.length > 0) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 10));
}
throw new Error(
`Timeout waiting for trace ${operationName} after ${timeoutMs}ms`
);
}
function getTraceDuration(collector, operationName) {
const spans = collector.getSpansByName(operationName);
if (spans.length === 0) {
return void 0;
}
return spans[0]?.duration;
}
function assertTraceDuration(collector, operationName, maxDurationMs) {
const duration = getTraceDuration(collector, operationName);
if (duration === void 0) {
throw new Error(`No trace found for operation: ${operationName}`);
}
if (duration > maxDurationMs) {
throw new Error(
`Operation ${operationName} took ${duration.toFixed(2)}ms, exceeding ${maxDurationMs}ms threshold`
);
}
}
export { assertNoErrors, assertNoErrorsLogged, assertTraceCreated, assertTraceDuration, assertTraceFailed, assertTraceSucceeded, createMockLogger, createTraceCollector, getTraceDuration, waitForTrace };
//# sourceMappingURL=testing.js.map
//# sourceMappingURL=testing.js.map