import { metrics } from "@opentelemetry/api"; import { AggregationTemporality, InMemoryMetricExporter, MeterProvider, MetricReader, } from "@opentelemetry/sdk-metrics"; import { expect } from "vitest"; // biome-ignore lint/suspicious/noConfusingVoidType: Test helper export function waitFor(callback: () => boolean | void) { return new Promise((resolve, reject) => { const checkPassed = () => { try { return { ok: callback(), error: null }; } catch (error) { return { ok: false, error }; } }; let retries = 0; const interval = setInterval(() => { const { ok, error } = checkPassed(); if (ok !== false) { clearInterval(interval); resolve(); } if (++retries > 10) { clearInterval(interval); reject(error); } }, 100); }); } /** * This is a test metric reader that uses an in-memory metric exporter and exposes a method to get the value of a metric given its name and attributes. * * This is useful for testing the values of metrics that are collected by the SDK. * * TODO: We may want to rethink how we access metrics (see `getMetricValue` method) to make it more flexible. */ class TestMetricReader extends MetricReader { private _exporter = new InMemoryMetricExporter( AggregationTemporality.CUMULATIVE, ); protected onShutdown(): Promise { throw new Error("Method not implemented."); } protected onForceFlush(): Promise { throw new Error("Method not implemented."); } async getMetricValue( name: string, attributes: { [key: string]: string | number } = {}, ) { await this.collectAndExport(); const resourceMetrics = this._exporter.getMetrics(); const metric = resourceMetrics .flatMap((rm) => rm.scopeMetrics) .flatMap((sm) => sm.metrics) .find((m) => m.descriptor.name === name); const dp = metric?.dataPoints.find( (dp) => JSON.stringify(dp.attributes) === JSON.stringify(attributes), ); this._exporter.reset(); return dp?.value; } async collectAndExport(): Promise { const result = await this.collect(); await new Promise((resolve, reject) => { this._exporter.export(result.resourceMetrics, (result) => { if (result.error != null) { reject(result.error); } else { resolve(); } }); }); } } export function createTestMetricReader() { const metricReader = new TestMetricReader(); const success = metrics.setGlobalMeterProvider( new MeterProvider({ readers: [metricReader], }), ); expect(success).toBe(true); return metricReader; } export function tearDownTestMetricReader() { metrics.disable(); }