1import { describe, it, expect } from "vitest";
2import { Metrics, mergeExposition } from "../src/metrics.js";
3
4
8
9const LOCAL = [
10 "# HELP uptime_seconds Process uptime in seconds.",
11 "# TYPE uptime_seconds gauge",
12 "uptime_seconds 42",
13 "# HELP ws_connections Live WebSocket connections, by room type.",
14 "# TYPE ws_connections gauge",
15 'ws_connections{room="page"} 3',
16 "# HELP ws_messages_total Frames received, by frame type.",
17 "# TYPE ws_messages_total counter",
18 'ws_messages_total{frame="update"} 10',
19].join("\n") + "\n";
20
21const OTEL = [
22 "# HELP target_info Target metadata.",
23 "# TYPE target_info gauge",
24 'target_info{service_name="factory-realtime"} 1',
25 "# HELP ws_active_connections Live WebSocket connections, total.",
26 "# TYPE ws_active_connections gauge",
27 "ws_active_connections 5",
28 "# HELP ws_messages_total Frames received (OTel, no labels).",
29 "# TYPE ws_messages_total counter",
30 "ws_messages_total 7",
31].join("\n") + "\n";
32
33describe("mergeExposition", () => {
34 it("returns the local text unchanged when OTel text is empty (disabled)", () => {
35 expect(mergeExposition(LOCAL, "")).toBe(LOCAL);
36 });
37
38 it("appends OTel-only families (target_info, ws_active_connections)", () => {
39 const out = mergeExposition(LOCAL, OTEL);
40 expect(out).toContain('target_info{service_name="factory-realtime"} 1');
41 expect(out).toContain("ws_active_connections 5");
42 expect(out).toContain("# TYPE target_info gauge");
43 });
44
45 it("keeps the LOCAL form on a family collision and drops the OTel one", () => {
46 const out = mergeExposition(LOCAL, OTEL);
47
48 expect(out).toContain('ws_messages_total{frame="update"} 10');
49
50 expect(out).not.toContain("# HELP ws_messages_total Frames received (OTel, no labels).");
51 expect(out).not.toMatch(/^ws_messages_total \d+/m);
52
53 expect(out.match(/# TYPE ws_messages_total /g)).toHaveLength(1);
54 });
55
56 it("preserves the local ordering first, OTel additions last", () => {
57 const out = mergeExposition(LOCAL, OTEL);
58 expect(out.indexOf("uptime_seconds 42")).toBeLessThan(out.indexOf("target_info"));
59 });
60
61 it("round-trips the local registry's own render() output", () => {
62 const reg = new Metrics(() => 1000);
63 reg.inc("ws_messages_total", { frame: "ping" });
64 const local = reg.render();
65 const out = mergeExposition(local, OTEL);
66 expect(out).toContain('ws_messages_total{frame="ping"} 1');
67 expect(out.match(/# TYPE ws_messages_total /g)).toHaveLength(1);
68 });
69});
70