{"version":3,"sources":["../src/testing.ts"],"names":["SpanStatusCode","name","startTime","otelTrace","context","configure"],"mappings":"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","file":"testing.cjs","sourcesContent":["/**\n * Testing Utilities\n *\n * Helpers for testing instrumented code and verifying telemetry.\n * Perfect for integration tests and QA in production validation.\n *\n * @example Verify traces are created\n * ```typescript\n * import { assertTraceCreated, collectTraces } from '@your-org/otel-decorators/testing'\n *\n * describe('UserService', () => {\n *   it('should create trace for user creation', async () => {\n *     const collector = collectTraces()\n *\n *     const service = new UserService()\n *     await service.createUser({ email: 'test@example.com' })\n *\n *     assertTraceCreated(collector, 'user.createUser')\n *   })\n * })\n * ```\n */\n\nimport {\n  SpanStatusCode,\n  type SpanStatus,\n  type Attributes,\n  type AttributeValue,\n  context,\n  trace as otelTrace,\n  type Span,\n  type SpanContext,\n  type TimeInput,\n  type Exception,\n  type SpanOptions,\n  type Context as OtelContext,\n  type Tracer,\n} from '@opentelemetry/api';\nimport { type Logger } from './logger';\nimport { configure } from './config';\n\n// Re-export events testing utilities\nexport {\n  createEventCollector,\n  assertEventTracked,\n  assertOutcomeTracked,\n  type EventCollector,\n  type EventData,\n  type EventsFunnelStep,\n  type EventsOutcome,\n  type EventsValue,\n} from './event-testing';\n\n/**\n * Note: OpenTelemetry exporters and processors have moved to dedicated modules\n * for better semantic clarity.\n *\n * For exporters (ConsoleSpanExporter, InMemorySpanExporter):\n * @see {@link autotel/exporters}\n *\n * For processors (SimpleSpanProcessor, BatchSpanProcessor):\n * @see {@link autotel/processors}\n *\n * This module focuses on high-level testing utilities with assertion helpers\n * and trace collectors.\n *\n * @example High-level testing (recommended)\n * ```typescript\n * import { createTraceCollector, assertTraceCreated } from 'autotel/testing'\n *\n * const collector = createTraceCollector()\n * await myService.doSomething()\n * assertTraceCreated(collector, 'myService.doSomething')\n * ```\n *\n * @example Low-level testing (when you need raw OTel spans)\n * ```typescript\n * import { InMemorySpanExporter } from 'autotel/exporters'\n * import { SimpleSpanProcessor } from 'autotel/processors'\n *\n * const exporter = new InMemorySpanExporter()\n * init({ service: 'test', spanProcessor: new SimpleSpanProcessor(exporter) })\n * ```\n */\n\n/**\n * Simplified span representation for testing\n */\nexport interface TestSpan {\n  name: string;\n  status: SpanStatus;\n  attributes: Attributes;\n  startTime: number;\n  endTime: number;\n  duration: number;\n}\n\n/**\n * In-memory trace collector for testing\n */\nexport interface TraceCollector {\n  /** Get all collected spans */\n  getSpans(): TestSpan[];\n  /** Get spans matching a name */\n  getSpansByName(name: string): TestSpan[];\n  /** Get spans matching attributes */\n  getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[];\n  /** Clear all collected spans */\n  clear(): void;\n  /** Record a span (internal use) */\n  recordSpan(span: TestSpan): void;\n}\n\n/**\n * Create an in-memory trace collector for testing\n *\n * IMPORTANT: This automatically configures the global tracer to record spans.\n * Call this in your test's beforeEach() to ensure proper setup.\n *\n * @example\n * ```typescript\n * import { createTraceCollector } from 'autotel/testing'\n *\n * describe('MyService', () => {\n *   let collector: TraceCollector\n *\n *   beforeEach(() => {\n *     collector = createTraceCollector()\n *   })\n *\n *   it('should trace operations', async () => {\n *     await myService.doSomething()\n *\n *     const spans = collector.getSpansByName('myService.doSomething')\n *     expect(spans).toHaveLength(1)\n *   })\n * })\n * ```\n */\nexport function createTraceCollector(): TraceCollector {\n  const spans: TestSpan[] = [];\n\n  // Create mock span that captures data and implements full Span interface\n  const createMockSpan = (name: string, startTime: number): Span => {\n    const spanData: Partial<TestSpan> = {\n      name,\n      startTime,\n      attributes: {},\n      status: { code: SpanStatusCode.OK },\n    };\n\n    const spanContextData: SpanContext = {\n      traceId: '1234567890abcdef1234567890abcdef', // 128-bit trace ID (32 hex chars)\n      spanId: '1234567890abcdef', // 64-bit span ID (16 hex chars)\n      traceFlags: 1,\n      isRemote: false,\n    };\n\n    const mockSpan: Span = {\n      spanContext: () => spanContextData,\n\n      setStatus(status: SpanStatus) {\n        spanData.status = status;\n        return this;\n      },\n\n      setAttributes(attributes: Attributes) {\n        spanData.attributes = { ...spanData.attributes, ...attributes };\n        return this;\n      },\n\n      setAttribute(key: string, value: AttributeValue) {\n        spanData.attributes = spanData.attributes || {};\n        spanData.attributes[key] = value;\n        return this;\n      },\n\n      addEvent(\n        name: string,\n        attributesOrStartTime?: Attributes | TimeInput,\n        startTime?: TimeInput,\n      ) {\n        void name;\n        void attributesOrStartTime;\n        void startTime;\n        return this;\n      },\n\n      addLink(link: { context: SpanContext; attributes?: Attributes }) {\n        void link;\n        return this;\n      },\n\n      addLinks(\n        links: Array<{ context: SpanContext; attributes?: Attributes }>,\n      ) {\n        void links;\n        return this;\n      },\n\n      updateName(newName: string) {\n        spanData.name = newName;\n        return this;\n      },\n\n      isRecording() {\n        return true;\n      },\n\n      recordException(exception: Exception, time?: TimeInput) {\n        void exception;\n        void time;\n      },\n\n      end(endTimeArg?: TimeInput) {\n        void endTimeArg;\n        const endTime = performance.now();\n        spans.push({\n          name: spanData.name!,\n          status: spanData.status!,\n          attributes: spanData.attributes || {},\n          startTime: spanData.startTime!,\n          endTime,\n          duration: endTime - spanData.startTime!,\n        });\n      },\n    };\n\n    return mockSpan;\n  };\n\n  // Create mock tracer\n  const mockTracer: Tracer = {\n    startSpan(name: string, options?: SpanOptions, ctx?: OtelContext): Span {\n      void options;\n      void ctx;\n      const startTime = performance.now();\n      return createMockSpan(name, startTime);\n    },\n\n    startActiveSpan<F extends (span: Span) => unknown>(\n      name: string,\n      optionsOrFn: SpanOptions | F,\n      contextOrFn?: OtelContext | F,\n      fn?: F,\n    ): ReturnType<F> {\n      const callback = (() => {\n        if (typeof optionsOrFn === 'function') {\n          return optionsOrFn;\n        }\n        if (typeof contextOrFn === 'function') {\n          return contextOrFn;\n        }\n        if (fn) {\n          return fn;\n        }\n        throw new Error('startActiveSpan requires a callback');\n      })();\n\n      const startTime = performance.now();\n      const mockSpan = createMockSpan(name, startTime);\n\n      // Set span as active in context (makes otelTrace.getActiveSpan() work)\n      const ctx = otelTrace.setSpan(context.active(), mockSpan);\n      return context.with(ctx, () => callback(mockSpan)) as ReturnType<F>;\n    },\n  };\n\n  // Auto-configure global tracer\n  configure({ tracer: mockTracer });\n\n  return {\n    getSpans(): TestSpan[] {\n      return [...spans];\n    },\n\n    getSpansByName(name: string): TestSpan[] {\n      return spans.filter((span) => span.name === name);\n    },\n\n    getSpansByAttributes(attributes: Record<string, unknown>): TestSpan[] {\n      return spans.filter((span) => {\n        return Object.entries(attributes).every(\n          ([key, value]) => span.attributes[key] === value,\n        );\n      });\n    },\n\n    clear(): void {\n      spans.length = 0;\n    },\n\n    recordSpan(span: TestSpan): void {\n      spans.push(span);\n    },\n  };\n}\n\n/**\n * Assert that a trace was created for an operation\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param options - Optional assertion options\n * @throws Error if trace was not found or doesn't match expectations\n *\n * @example\n * ```typescript\n * assertTraceCreated(collector, 'user.createUser')\n * assertTraceCreated(collector, 'user.createUser', {\n *   minCount: 1,\n *   maxCount: 1,\n *   status: SpanStatusCode.OK,\n *   attributes: { 'user.email': 'test@example.com' }\n * })\n * ```\n */\nexport function assertTraceCreated(\n  collector: TraceCollector,\n  operationName: string,\n  options?: {\n    minCount?: number;\n    maxCount?: number;\n    status?: SpanStatusCode;\n    attributes?: Record<string, unknown>;\n  },\n): void {\n  const spans = collector.getSpansByName(operationName);\n\n  if (options?.minCount !== undefined && spans.length < options.minCount) {\n    throw new Error(\n      `Expected at least ${options.minCount} traces for ${operationName}, got ${spans.length}`,\n    );\n  }\n\n  if (options?.maxCount !== undefined && spans.length > options.maxCount) {\n    throw new Error(\n      `Expected at most ${options.maxCount} traces for ${operationName}, got ${spans.length}`,\n    );\n  }\n\n  if (spans.length === 0) {\n    throw new Error(`No traces found for operation: ${operationName}`);\n  }\n\n  if (options?.status !== undefined) {\n    const matchingSpans = spans.filter(\n      (span) => span.status.code === options.status,\n    );\n    if (matchingSpans.length === 0) {\n      throw new Error(\n        `No traces with status ${options.status} found for ${operationName}`,\n      );\n    }\n  }\n\n  if (options?.attributes) {\n    const matchingSpans = spans.filter((span) => {\n      return Object.entries(options.attributes!).every(\n        ([key, value]) => span.attributes[key] === value,\n      );\n    });\n    if (matchingSpans.length === 0) {\n      throw new Error(\n        `No traces with attributes ${JSON.stringify(options.attributes)} found for ${operationName}`,\n      );\n    }\n  }\n}\n\n/**\n * Assert that no errors were logged\n *\n * Use this in smoke tests to verify critical paths don't have errors.\n *\n * @param collector - Trace collector\n * @throws Error if any error traces are found\n *\n * @example\n * ```typescript\n * // Run critical user flows\n * await runSmokeTests()\n *\n * // Verify no errors occurred\n * assertNoErrors(collector)\n * ```\n */\nexport function assertNoErrors(collector: TraceCollector): void {\n  const errorSpans = collector\n    .getSpans()\n    .filter((span) => span.status.code === SpanStatusCode.ERROR);\n\n  if (errorSpans.length > 0) {\n    const errorSummary = errorSpans\n      .map((span) => `${span.name}: ${span.status.message}`)\n      .join('\\n');\n    throw new Error(`Found ${errorSpans.length} error spans:\\n${errorSummary}`);\n  }\n}\n\n/**\n * Assert that a trace was created and succeeded\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n *\n * @example\n * ```typescript\n * assertTraceSucceeded(collector, 'user.createUser')\n * ```\n */\nexport function assertTraceSucceeded(\n  collector: TraceCollector,\n  operationName: string,\n): void {\n  assertTraceCreated(collector, operationName, { status: SpanStatusCode.OK });\n}\n\n/**\n * Assert that a trace was created and failed\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param errorMessage - Optional expected error message\n *\n * @example\n * ```typescript\n * assertTraceFailed(collector, 'user.createUser', 'Invalid email')\n * ```\n */\nexport function assertTraceFailed(\n  collector: TraceCollector,\n  operationName: string,\n  errorMessage?: string,\n): void {\n  const spans = collector.getSpansByName(operationName);\n\n  if (spans.length === 0) {\n    throw new Error(`No traces found for operation: ${operationName}`);\n  }\n\n  const errorSpans = spans.filter(\n    (span) => span.status.code === SpanStatusCode.ERROR,\n  );\n\n  if (errorSpans.length === 0) {\n    throw new Error(`No error traces found for operation: ${operationName}`);\n  }\n\n  if (errorMessage) {\n    const matchingSpans = errorSpans.filter(\n      (span) => span.status.message === errorMessage,\n    );\n    if (matchingSpans.length === 0) {\n      throw new Error(\n        `No error traces with message \"${errorMessage}\" found for ${operationName}`,\n      );\n    }\n  }\n}\n\n/**\n * In-memory log collector for testing\n */\nexport interface LogCollector {\n  /** Get all collected logs */\n  getLogs(): LogEntry[];\n  /** Get logs by level */\n  getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[];\n  /** Get logs containing a message */\n  getLogsByMessage(message: string): LogEntry[];\n  /** Clear all collected logs */\n  clear(): void;\n}\n\n/**\n * Log entry\n */\nexport interface LogEntry {\n  level: 'info' | 'warn' | 'error' | 'debug';\n  message: string;\n  extra?: Record<string, unknown>;\n  error?: Error;\n}\n\n/**\n * Create an in-memory log collector for testing\n *\n * @example\n * ```typescript\n * const logger = createMockLogger()\n *\n * // Use logger in your code\n * service.log = logger\n * await service.doSomething()\n *\n * // Assert logs were created\n * const logs = logger.getLogs()\n * expect(logs).toHaveLength(2)\n * expect(logs[0].message).toBe('Operation started')\n * ```\n */\nexport function createMockLogger(): Logger & LogCollector {\n  const logs: LogEntry[] = [];\n\n  // Pino-compatible signature: supports both:\n  // - logger.info('message') - string only\n  // - logger.info({ extra }, 'message') - object first with optional message\n  const createLogMethod = (level: 'info' | 'warn' | 'debug') => {\n    return (objOrMsg: Record<string, unknown> | string, msg?: string): void => {\n      if (typeof objOrMsg === 'string') {\n        // String-only call: logger.info('message')\n        logs.push({\n          level,\n          message: objOrMsg,\n          extra: undefined,\n        });\n      } else {\n        // Pino style: logger.info({ extra }, 'message')\n        logs.push({\n          level,\n          message: msg || '',\n          extra: objOrMsg,\n        });\n      }\n    };\n  };\n\n  return {\n    info: createLogMethod('info'),\n    warn: createLogMethod('warn'),\n    debug: createLogMethod('debug'),\n\n    error(objOrMsg: Record<string, unknown> | string, msg?: string): void {\n      if (typeof objOrMsg === 'string') {\n        // String-only call: logger.error('message')\n        logs.push({\n          level: 'error',\n          message: objOrMsg,\n          extra: undefined,\n          error: undefined,\n        });\n        return;\n      }\n\n      // Pino style: logger.error({ err, ...extra }, 'message')\n      // Extract err from extra if present (Pino convention)\n      const { err, ...rest } = objOrMsg as Record<string, unknown> & {\n        err?: unknown;\n      };\n      logs.push({\n        level: 'error',\n        message: msg || '',\n        error: err instanceof Error ? err : undefined,\n        extra:\n          err !== undefined && !(err instanceof Error)\n            ? { err, ...rest }\n            : rest,\n      });\n    },\n\n    getLogs(): LogEntry[] {\n      return [...logs];\n    },\n\n    getLogsByLevel(level: 'info' | 'warn' | 'error' | 'debug'): LogEntry[] {\n      return logs.filter((log) => log.level === level);\n    },\n\n    getLogsByMessage(message: string): LogEntry[] {\n      return logs.filter((log) => log.message.includes(message));\n    },\n\n    clear(): void {\n      logs.length = 0;\n    },\n  };\n}\n\n/**\n * Assert that no error logs were created\n *\n * @param logger - Log collector\n * @throws Error if any error logs are found\n *\n * @example\n * ```typescript\n * assertNoErrorsLogged(logger)\n * ```\n */\nexport function assertNoErrorsLogged(logger: LogCollector): void {\n  const errorLogs = logger.getLogsByLevel('error');\n\n  if (errorLogs.length > 0) {\n    const errorSummary = errorLogs\n      .map(\n        (log) => `${log.message}${log.error ? ': ' + log.error.message : ''}`,\n      )\n      .join('\\n');\n    throw new Error(`Found ${errorLogs.length} error logs:\\n${errorSummary}`);\n  }\n}\n\n/**\n * Wait for a specific trace to be created\n *\n * Useful for async operations where you need to wait for telemetry.\n *\n * @param collector - Trace collector\n * @param operationName - Expected operation name\n * @param timeoutMs - Timeout in milliseconds (default 5000)\n * @returns Promise that resolves when trace is found\n * @throws Error if timeout is reached\n *\n * @example\n * ```typescript\n * // Start async operation\n * const promise = service.doAsyncWork()\n *\n * // Wait for trace\n * await waitForTrace(collector, 'service.doAsyncWork', 1000)\n *\n * // Now you can assert on the trace\n * assertTraceSucceeded(collector, 'service.doAsyncWork')\n * ```\n */\nexport async function waitForTrace(\n  collector: TraceCollector,\n  operationName: string,\n  timeoutMs: number = 5000,\n): Promise<void> {\n  const startTime = Date.now();\n\n  while (Date.now() - startTime < timeoutMs) {\n    const spans = collector.getSpansByName(operationName);\n    if (spans.length > 0) {\n      return;\n    }\n    await new Promise((resolve) => setTimeout(resolve, 10));\n  }\n\n  throw new Error(\n    `Timeout waiting for trace ${operationName} after ${timeoutMs}ms`,\n  );\n}\n\n/**\n * Get trace duration in milliseconds\n *\n * @param collector - Trace collector\n * @param operationName - Operation name\n * @returns Duration in milliseconds, or undefined if trace not found\n *\n * @example\n * ```typescript\n * const duration = getTraceDuration(collector, 'user.createUser')\n * expect(duration).toBeLessThan(1000) // Should be < 1s\n * ```\n */\nexport function getTraceDuration(\n  collector: TraceCollector,\n  operationName: string,\n): number | undefined {\n  const spans = collector.getSpansByName(operationName);\n  if (spans.length === 0) {\n    return undefined;\n  }\n\n  return spans[0]?.duration;\n}\n\n/**\n * Assert that an operation completed within a time threshold\n *\n * Perfect for performance testing and SLO validation.\n *\n * @param collector - Trace collector\n * @param operationName - Operation name\n * @param maxDurationMs - Maximum allowed duration in milliseconds\n * @throws Error if operation took too long\n *\n * @example\n * ```typescript\n * // Verify operation meets SLO\n * await service.createUser({ email: 'test@example.com' })\n * assertTraceDuration(collector, 'user.createUser', 500) // Must be < 500ms\n * ```\n */\nexport function assertTraceDuration(\n  collector: TraceCollector,\n  operationName: string,\n  maxDurationMs: number,\n): void {\n  const duration = getTraceDuration(collector, operationName);\n\n  if (duration === undefined) {\n    throw new Error(`No trace found for operation: ${operationName}`);\n  }\n\n  if (duration > maxDurationMs) {\n    throw new Error(\n      `Operation ${operationName} took ${duration.toFixed(2)}ms, exceeding ${maxDurationMs}ms threshold`,\n    );\n  }\n}\n"]}