/* Unit tests for otlp json covering encodeOtlpMetrics; nowUnixNano. It also guards the documented public contract and regression-sensitive edge cases. Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only. RFC-1311: Compass header sweep — declares the otlp suite's contract for test-file scaffold coverage. */ import { describe, it, expect } from "vitest"; import { encodeOtlpMetrics, nowUnixNano, type OtlpMetricPoint } from "../otlp-json.ts"; import type { OtlpKeyValue } from "../conventions.ts"; const resourceAttrs: OtlpKeyValue[] = [ { key: "service.name", value: { stringValue: "fleet-probe-runner" } }, { key: "deployment.environment", value: { stringValue: "production" } }, { key: "warpgogol.layer", value: { stringValue: "probe" } }, { key: "warpgogol.site_id", value: { stringValue: "warpgogol-com" } }, ]; describe("encodeOtlpMetrics", () => { it("encodes a gauge metric", () => { const points: OtlpMetricPoint[] = [ { name: "warpgogol_probe_up", kind: "gauge", gaugePoints: [ { asDouble: 1, timeUnixNano: "1751884800000000000", attributes: [{ key: "site_id", value: { stringValue: "warpgogol-com" } }], }, ], }, ]; const env = encodeOtlpMetrics( resourceAttrs, points, "@warpgogol/werkstatt-shared/observability", "1", ); expect(env.resourceMetrics).toHaveLength(1); expect(env.resourceMetrics[0]!.resource.attributes).toEqual(resourceAttrs); const scope = env.resourceMetrics[0]!.scopeMetrics[0]!; expect(scope.scope.name).toBe("@warpgogol/werkstatt-shared/observability"); expect(scope.scope.version).toBe("1"); expect(scope.metrics).toHaveLength(1); const metric = scope.metrics[0] as Record; expect(metric["name"]).toBe("warpgogol_probe_up"); expect(metric["gauge"]).toBeDefined(); const gauge = metric["gauge"] as { dataPoints: unknown[] }; expect(gauge.dataPoints).toHaveLength(1); const dp = gauge.dataPoints[0] as Record; expect(dp["asDouble"]).toBe(1); expect(dp["timeUnixNano"]).toBe("1751884800000000000"); }); it("encodes a delta sum (counter) with aggregationTemporality 1", () => { const points: OtlpMetricPoint[] = [ { name: "warpgogol_probe_http_status_class_total", kind: "sum", unit: "1", isMonotonic: true, sumPoints: [ { asDouble: 3, startTimeUnixNano: "1751884500000000000", timeUnixNano: "1751884800000000000", attributes: [{ key: "status_class", value: { stringValue: "2xx" } }], }, ], }, ]; const env = encodeOtlpMetrics( resourceAttrs, points, "@warpgogol/werkstatt-shared/observability", "1", ); const metric = env.resourceMetrics[0]!.scopeMetrics[0]!.metrics[0] as Record; expect(metric["name"]).toBe("warpgogol_probe_http_status_class_total"); expect(metric["unit"]).toBe("1"); const sum = metric["sum"] as { aggregationTemporality: number; isMonotonic: boolean; dataPoints: unknown[]; }; expect(sum.aggregationTemporality).toBe(1); expect(sum.isMonotonic).toBe(true); const dp = sum.dataPoints[0] as Record; expect(dp["asDouble"]).toBe(3); expect(dp["startTimeUnixNano"]).toBe("1751884500000000000"); expect(dp["timeUnixNano"]).toBe("1751884800000000000"); }); it("encodes a delta histogram with correct bucketCounts and explicitBounds", () => { const points: OtlpMetricPoint[] = [ { name: "warpgogol_factory_command_duration_seconds", kind: "histogram", unit: "s", histogramPoints: [ { startTimeUnixNano: "1751884500000000000", timeUnixNano: "1751884800000000000", count: "3", sum: 42.5, bucketCounts: ["0", "1", "1", "1", "0", "0", "0", "0", "0", "0", "0"], explicitBounds: [0.5, 1, 2, 5, 10, 30, 60, 120, 300, 600], }, ], }, ]; const env = encodeOtlpMetrics( resourceAttrs, points, "@warpgogol/werkstatt-shared/observability", "1", ); const metric = env.resourceMetrics[0]!.scopeMetrics[0]!.metrics[0] as Record; expect(metric["name"]).toBe("warpgogol_factory_command_duration_seconds"); expect(metric["unit"]).toBe("s"); const hist = metric["histogram"] as { aggregationTemporality: number; dataPoints: unknown[] }; expect(hist.aggregationTemporality).toBe(1); const dp = hist.dataPoints[0] as Record; expect(dp["count"]).toBe("3"); expect(dp["sum"]).toBe(42.5); expect(dp["bucketCounts"]).toEqual(["0", "1", "1", "1", "0", "0", "0", "0", "0", "0", "0"]); expect(dp["explicitBounds"]).toEqual([0.5, 1, 2, 5, 10, 30, 60, 120, 300, 600]); }); }); describe("nowUnixNano", () => { it("returns a string of digits representing unix nanoseconds", () => { const nano = nowUnixNano(); expect(typeof nano).toBe("string"); expect(nano).toMatch(/^\d+$/); // Should be approximately Date.now() * 1e6 const approx = Date.now() * 1_000_000; const parsed = Number(nano); expect(Math.abs(parsed - approx)).toBeLessThan(5_000_000_000); }); });