Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,205 @@
import type {
Counter,
Histogram,
Meter,
MeterProvider,
} from "@opentelemetry/api";
import {
afterEach,
beforeEach,
describe,
expect,
it,
} from "@suaveplan/testing/runner";
import {
getInstruments,
recordOp,
resetInstruments,
setStorageBrowserOpfsMeterProvider,
} from "./otel.js";
interface RecordedHistogram {
name: string;
samples: { value: number; attrs: Record<string, unknown> | undefined }[];
}
interface RecordedCounter {
name: string;
additions: { value: number; attrs: Record<string, unknown> | undefined }[];
}
class StubHistogram implements Histogram {
public readonly entry: RecordedHistogram;
public constructor(name: string, bag: RecordedHistogram[]) {
this.entry = { name, samples: [] };
bag.push(this.entry);
}
public record(value: number, attrs?: Record<string, unknown>): void {
this.entry.samples.push({ value, attrs });
}
}
class StubCounter implements Counter {
public readonly entry: RecordedCounter;
public constructor(name: string, bag: RecordedCounter[]) {
this.entry = { name, additions: [] };
bag.push(this.entry);
}
public add(value: number, attrs?: Record<string, unknown>): void {
this.entry.additions.push({ value, attrs });
}
}
class StubMeter implements Meter {
public histograms: RecordedHistogram[] = [];
public counters: RecordedCounter[] = [];
public createHistogram(name: string): Histogram {
return new StubHistogram(name, this.histograms);
}
public createCounter(name: string): Counter {
return new StubCounter(name, this.counters);
}
public createObservableGauge(): never {
throw new Error("unused");
}
public createUpDownCounter(): never {
throw new Error("unused");
}
public createObservableCounter(): never {
throw new Error("unused");
}
public createObservableUpDownCounter(): never {
throw new Error("unused");
}
public addBatchObservableCallback(): void {}
public removeBatchObservableCallback(): void {}
public createGauge(): never {
throw new Error("unused");
}
}
class StubMeterProvider implements MeterProvider {
public meters: StubMeter[] = [];
public getMeter(): Meter {
const meter = new StubMeter();
this.meters.push(meter);
return meter;
}
}
describe("storage-browser-opfs otel", () => {
let provider: StubMeterProvider;
beforeEach(() => {
provider = new StubMeterProvider();
setStorageBrowserOpfsMeterProvider(provider);
});
afterEach(() => {
setStorageBrowserOpfsMeterProvider(undefined);
resetInstruments();
});
function findCounter(name: string): RecordedCounter | undefined {
return provider.meters[0]?.counters.find((c) => c.name === name);
}
function findHistogram(name: string): RecordedHistogram | undefined {
return provider.meters[0]?.histograms.find((h) => h.name === name);
}
it("recordOp emits the documented counter and histogram set", {
timeout: 5000,
}, () => {
recordOp({
op: "set",
outcome: "success",
durationMs: 12,
bytes: 100,
direction: "write",
});
const dur = findHistogram("storage_browser_opfs.op_duration_ms");
expect(dur?.samples).toEqual([
{ value: 12, attrs: { op: "set", outcome: "success" } },
]);
const writes = findCounter("storage_browser_opfs.writes_total");
expect(writes?.additions).toEqual([
{ value: 1, attrs: { op: "set", outcome: "success" } },
]);
const io = findHistogram("storage_browser_opfs.io_bytes");
expect(io?.samples).toEqual([
{ value: 100, attrs: { op: "set", direction: "write" } },
]);
});
it("records error counter on a failed op", { timeout: 5000 }, () => {
recordOp({ op: "get", outcome: "error", durationMs: 3 });
const errs = findCounter("storage_browser_opfs.errors_total");
expect(errs?.additions).toEqual([
{ value: 1, attrs: { op: "get", outcome: "error" } },
]);
});
it("records aborted operations on the errors counter", {
timeout: 5000,
}, () => {
recordOp({ op: "get", outcome: "aborted", durationMs: 0 });
expect(
findCounter("storage_browser_opfs.errors_total")?.additions[0]
?.attrs
).toEqual({ op: "get", outcome: "aborted" });
});
it("increments reads/writes/deletes counters by op kind", {
timeout: 5000,
}, () => {
recordOp({ op: "get", outcome: "success", durationMs: 1 });
recordOp({ op: "getPartial", outcome: "success", durationMs: 1 });
recordOp({ op: "getWithMetadata", outcome: "success", durationMs: 1 });
recordOp({ op: "set", outcome: "success", durationMs: 1 });
recordOp({ op: "setPartial", outcome: "success", durationMs: 1 });
recordOp({ op: "delete", outcome: "success", durationMs: 1 });
recordOp({ op: "clear", outcome: "success", durationMs: 1 });
recordOp({ op: "exists", outcome: "success", durationMs: 1 });
recordOp({ op: "list", outcome: "success", durationMs: 1 });
expect(
findCounter("storage_browser_opfs.reads_total")?.additions
).toHaveLength(3);
expect(
findCounter("storage_browser_opfs.writes_total")?.additions
).toHaveLength(2);
expect(
findCounter("storage_browser_opfs.deletes_total")?.additions
).toHaveLength(2);
});
it("returns a cached instrument set on repeat getInstruments calls", {
timeout: 5000,
}, () => {
const a = getInstruments();
const b = getInstruments();
expect(a).toBe(b);
});
it("rebuilds the instrument set when getInstruments(true) is called", {
timeout: 5000,
}, () => {
const a = getInstruments();
const b = getInstruments(true);
expect(a).not.toBe(b);
});
it("omits the io_bytes record when bytes is undefined", {
timeout: 5000,
}, () => {
recordOp({ op: "list", outcome: "success", durationMs: 5 });
expect(
findHistogram("storage_browser_opfs.io_bytes")?.samples ?? []
).toHaveLength(0);
});
it("uses the global metrics fallback when provider is unset", {
timeout: 5000,
}, () => {
setStorageBrowserOpfsMeterProvider(undefined);
// Should not throw — falls back to the global no-op meter provider.
const inst = getInstruments(true);
expect(inst.meter).toBeDefined();
});
});