autotel
Version:
Write Once, Observe Anywhere
292 lines (288 loc) • 8.82 kB
text/typescript
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 };