import type { Meter, MeterProvider } from "@opentelemetry/api"; import { describe, expect, it } from "@suaveplan/testing/runner"; import { createRateLimitInstruments, RATE_LIMIT_COUNTER_NAMES, } from "./instruments.js"; interface RecordedCounter { readonly name: string; readonly addCalls: { value: number; attributes?: Readonly>; }[]; } function fakeProvider(): { provider: MeterProvider; counters: RecordedCounter[]; } { const counters: RecordedCounter[] = []; const provider: MeterProvider = { getMeter(_name: string): Meter { const meter: Meter = { createCounter(n: string) { const r: RecordedCounter = { name: n, addCalls: [] }; counters.push(r); return { add( v: number, attrs?: Readonly> ) { r.addCalls.push({ value: v, attributes: attrs }); }, } as ReturnType; }, createUpDownCounter() { throw new Error("not used"); }, createHistogram() { throw new Error("not used"); }, createObservableCounter() { throw new Error("not used"); }, createObservableGauge() { throw new Error("not used"); }, createObservableUpDownCounter() { throw new Error("not used"); }, addBatchObservableCallback() { /* noop */ }, removeBatchObservableCallback() { /* noop */ }, } as Meter; return meter; }, }; return { provider, counters }; } describe("instruments", () => { it("creates the rate-limit exceeded counter", { timeout: 5000 }, () => { const { provider, counters } = fakeProvider(); const inst = createRateLimitInstruments({ meterProvider: provider }); expect(counters.map((c) => c.name)).toEqual([ "api.rate_limit.exceeded_total", ]); inst.counters["api.rate_limit.exceeded_total"].add(1, { plugin: "p", route: "GET /", key_kind: "principal", }); expect(counters[0]!.addCalls).toHaveLength(1); expect(counters[0]!.addCalls[0]!.attributes).toEqual({ plugin: "p", route: "GET /", key_kind: "principal", }); }); it("falls back to global meter-provider when none supplied", { timeout: 5000, }, () => { const inst = createRateLimitInstruments(); // Global no-op provider returns no-op counters; .add must not throw. expect(() => inst.counters["api.rate_limit.exceeded_total"].add(1, {}) ).not.toThrow(); }); it("exposes the full counter-name tuple", { timeout: 5000 }, () => { expect(RATE_LIMIT_COUNTER_NAMES).toEqual([ "api.rate_limit.exceeded_total", ]); }); });