UNPKG

autotel

Version:
292 lines (288 loc) 8.82 kB
import { SpanStatus, Attributes, SpanStatusCode } from '@opentelemetry/api'; import { Logger } from './logger.cjs'; export { EventCollector, EventData, EventsFunnelStep, EventsOutcome, EventsValue, assertEventTracked, assertOutcomeTracked, createEventCollector } from './event-testing.cjs'; import 'pino'; import './event-subscriber.cjs'; /** * Testing Utilities * * Helpers for testing instrumented code and verifying telemetry. * Perfect for integration tests and QA in production validation. * * @example Verify traces are created * ```typescript * import { assertTraceCreated, collectTraces } from '@your-org/otel-decorators/testing' * * describe('UserService', () => { * it('should create trace for user creation', async () => { * const collector = collectTraces() * * const service = new UserService() * await service.createUser({ email: 'test@example.com' }) * * assertTraceCreated(collector, 'user.createUser') * }) * }) * ``` */ /** * Note: OpenTelemetry exporters and processors have moved to dedicated modules * for better semantic clarity. * * For exporters (ConsoleSpanExporter, InMemorySpanExporter): * @see {@link autotel/exporters} * * For processors (SimpleSpanProcessor, BatchSpanProcessor): * @see {@link autotel/processors} * * This module focuses on high-level testing utilities with assertion helpers * and trace collectors. * * @example High-level testing (recommended) * ```typescript * import { createTraceCollector, assertTraceCreated } from 'autotel/testing' * * const collector = createTraceCollector() * await myService.doSomething() * assertTraceCreated(collector, 'myService.doSomething') * ``` * * @example Low-level testing (when you need raw OTel spans) * ```typescript * import { InMemorySpanExporter } from 'autotel/exporters' * import { SimpleSpanProcessor } from 'autotel/processors' * * const exporter = new InMemorySpanExporter() * init({ service: 'test', spanProcessor: new SimpleSpanProcessor(exporter) }) * ``` */ /** * Simplified span representation for testing */ interface TestSpan { name: string; status: SpanStatus; attributes: Attributes; startTime: number; endTime: number; duration: number; } /** * In-memory trace collector for testing */ interface TraceCollector { /** Get all collected spans */ getSpans(): TestSpan[]; /** Get spans matching a name */ getSpansByName(name: string): TestSpan[]; /** Get spans matching attributes */ getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[]; /** Clear all collected spans */ clear(): void; /** Record a span (internal use) */ recordSpan(span: TestSpan): void; } /** * Create an in-memory trace collector for testing * * IMPORTANT: This automatically configures the global tracer to record spans. * Call this in your test's beforeEach() to ensure proper setup. * * @example * ```typescript * import { createTraceCollector } from 'autotel/testing' * * describe('MyService', () => { * let collector: TraceCollector * * beforeEach(() => { * collector = createTraceCollector() * }) * * it('should trace operations', async () => { * await myService.doSomething() * * const spans = collector.getSpansByName('myService.doSomething') * expect(spans).toHaveLength(1) * }) * }) * ``` */ declare function createTraceCollector(): TraceCollector; /** * Assert that a trace was created for an operation * * @param collector - Trace collector * @param operationName - Expected operation name * @param options - Optional assertion options * @throws Error if trace was not found or doesn't match expectations * * @example * ```typescript * assertTraceCreated(collector, 'user.createUser') * assertTraceCreated(collector, 'user.createUser', { * minCount: 1, * maxCount: 1, * status: SpanStatusCode.OK, * attributes: { 'user.email': 'test@example.com' } * }) * ``` */ declare function assertTraceCreated(collector: TraceCollector, operationName: string, options?: { minCount?: number; maxCount?: number; status?: SpanStatusCode; attributes?: Record<string, unknown>; }): void; /** * Assert that no errors were logged * * Use this in smoke tests to verify critical paths don't have errors. * * @param collector - Trace collector * @throws Error if any error traces are found * * @example * ```typescript * // Run critical user flows * await runSmokeTests() * * // Verify no errors occurred * assertNoErrors(collector) * ``` */ declare function assertNoErrors(collector: TraceCollector): void; /** * Assert that a trace was created and succeeded * * @param collector - Trace collector * @param operationName - Expected operation name * * @example * ```typescript * assertTraceSucceeded(collector, 'user.createUser') * ``` */ declare function assertTraceSucceeded(collector: TraceCollector, operationName: string): void; /** * Assert that a trace was created and failed * * @param collector - Trace collector * @param operationName - Expected operation name * @param errorMessage - Optional expected error message * * @example * ```typescript * assertTraceFailed(collector, 'user.createUser', 'Invalid email') * ``` */ declare function assertTraceFailed(collector: TraceCollector, operationName: string, errorMessage?: string): void; /** * In-memory log collector for testing */ interface LogCollector { /** Get all collected logs */ getLogs(): LogEntry[]; /** Get logs by level */ getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[]; /** Get logs containing a message */ getLogsByMessage(message: string): LogEntry[]; /** Clear all collected logs */ clear(): void; } /** * Log entry */ interface LogEntry { level: 'info' | 'warn' | 'error' | 'debug'; message: string; extra?: Record<string, unknown>; error?: Error; } /** * Create an in-memory log collector for testing * * @example * ```typescript * const logger = createMockLogger() * * // Use logger in your code * service.log = logger * await service.doSomething() * * // Assert logs were created * const logs = logger.getLogs() * expect(logs).toHaveLength(2) * expect(logs[0].message).toBe('Operation started') * ``` */ declare function createMockLogger(): Logger & LogCollector; /** * Assert that no error logs were created * * @param logger - Log collector * @throws Error if any error logs are found * * @example * ```typescript * assertNoErrorsLogged(logger) * ``` */ declare function assertNoErrorsLogged(logger: LogCollector): void; /** * Wait for a specific trace to be created * * Useful for async operations where you need to wait for telemetry. * * @param collector - Trace collector * @param operationName - Expected operation name * @param timeoutMs - Timeout in milliseconds (default 5000) * @returns Promise that resolves when trace is found * @throws Error if timeout is reached * * @example * ```typescript * // Start async operation * const promise = service.doAsyncWork() * * // Wait for trace * await waitForTrace(collector, 'service.doAsyncWork', 1000) * * // Now you can assert on the trace * assertTraceSucceeded(collector, 'service.doAsyncWork') * ``` */ declare function waitForTrace(collector: TraceCollector, operationName: string, timeoutMs?: number): Promise<void>; /** * Get trace duration in milliseconds * * @param collector - Trace collector * @param operationName - Operation name * @returns Duration in milliseconds, or undefined if trace not found * * @example * ```typescript * const duration = getTraceDuration(collector, 'user.createUser') * expect(duration).toBeLessThan(1000) // Should be < 1s * ``` */ declare function getTraceDuration(collector: TraceCollector, operationName: string): number | undefined; /** * Assert that an operation completed within a time threshold * * Perfect for performance testing and SLO validation. * * @param collector - Trace collector * @param operationName - Operation name * @param maxDurationMs - Maximum allowed duration in milliseconds * @throws Error if operation took too long * * @example * ```typescript * // Verify operation meets SLO * await service.createUser({ email: 'test@example.com' }) * assertTraceDuration(collector, 'user.createUser', 500) // Must be < 500ms * ``` */ declare function assertTraceDuration(collector: TraceCollector, operationName: string, maxDurationMs: number): void; export { type LogCollector, type LogEntry, type TestSpan, type TraceCollector, assertNoErrors, assertNoErrorsLogged, assertTraceCreated, assertTraceDuration, assertTraceFailed, assertTraceSucceeded, createMockLogger, createTraceCollector, getTraceDuration, waitForTrace };