97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { MetricsRegistry } from "../src/metrics.ts";
|
|
import {
|
|
createOperationTracer,
|
|
createOtlpLogExporter,
|
|
createPerformanceProfiler,
|
|
createPrometheusPushExporter,
|
|
createSentryCompatibleReporter,
|
|
createZipkinExporter,
|
|
renderPrometheus,
|
|
} from "../src/integrations.ts";
|
|
|
|
describe("observability integrations", () => {
|
|
test("records every framework operation kind and failures", async () => {
|
|
const spans: any[] = [];
|
|
const tracer = createOperationTracer({
|
|
exporter: {
|
|
export: (records) => {
|
|
spans.push(...records);
|
|
},
|
|
},
|
|
now: (() => {
|
|
let time = 0;
|
|
return () => (time += 5);
|
|
})(),
|
|
});
|
|
for (const kind of [
|
|
"database",
|
|
"cache",
|
|
"queue",
|
|
"realtime",
|
|
"server-action",
|
|
"application",
|
|
] as const)
|
|
expect(await tracer.span(kind, `${kind}.work`, async () => kind)).toBe(kind);
|
|
await expect(
|
|
tracer.span("application", "failure", async () => {
|
|
throw new Error("boom");
|
|
}),
|
|
).rejects.toThrow("boom");
|
|
expect(spans.map((span) => span.attributes["wrnexus.span.kind"])).toContain("database");
|
|
expect(spans.at(-1).status).toBe("error");
|
|
});
|
|
|
|
test("exports Prometheus, Zipkin/Jaeger, OTLP logs and Sentry-compatible errors", async () => {
|
|
const requests: Array<{ url: string; init?: RequestInit }> = [];
|
|
const send = (async (url: URL | RequestInfo, init?: RequestInit) => {
|
|
requests.push({ url: String(url), init });
|
|
return new Response(null, { status: 200 });
|
|
}) as typeof fetch;
|
|
const registry = new MetricsRegistry(() => 10);
|
|
registry.increment("http.requests", 1, { route: "/" });
|
|
expect(renderPrometheus(registry.snapshot())).toContain('http_requests{route="/"} 1');
|
|
await createPrometheusPushExporter("https://prom.test", { fetch: send }).export(
|
|
registry.snapshot(),
|
|
);
|
|
const span = {
|
|
name: "db",
|
|
traceId: "1".repeat(32),
|
|
spanId: "2".repeat(16),
|
|
sampled: true,
|
|
startTime: 1,
|
|
endTime: 2,
|
|
durationMs: 1,
|
|
status: "ok" as const,
|
|
attributes: {},
|
|
};
|
|
await createZipkinExporter("https://zipkin.test", { fetch: send }).export([span]);
|
|
await createOtlpLogExporter("https://otlp.test", { fetch: send }).export([
|
|
{
|
|
timestamp: new Date(0).toISOString(),
|
|
level: "info",
|
|
message: "ready",
|
|
service: "app",
|
|
attributes: {},
|
|
},
|
|
]);
|
|
expect(
|
|
await createSentryCompatibleReporter("https://sentry.test", { fetch: send }).capture(
|
|
new Error("bad"),
|
|
),
|
|
).toHaveLength(32);
|
|
expect(requests).toHaveLength(4);
|
|
});
|
|
|
|
test("profiles application operations", async () => {
|
|
const profiles: any[] = [];
|
|
let time = 0;
|
|
const profile = createPerformanceProfiler({
|
|
now: () => (time += 4),
|
|
onProfile: (value) => profiles.push(value),
|
|
});
|
|
expect(await profile("render", () => "ok")).toBe("ok");
|
|
expect(profiles[0]).toMatchObject({ name: "render", durationMs: 4 });
|
|
});
|
|
});
|