100 lines
3.3 KiB
TypeScript
100 lines
3.3 KiB
TypeScript
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<Record<string, unknown>>;
|
|
}[];
|
|
}
|
|
|
|
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<Record<string, unknown>>
|
|
) {
|
|
r.addCalls.push({ value: v, attributes: attrs });
|
|
},
|
|
} as ReturnType<Meter["createCounter"]>;
|
|
},
|
|
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",
|
|
]);
|
|
});
|
|
});
|