connection-seconds.test.ts111 lines · main
1import { beforeEach, describe, expect, test } from 'bun:test';
2
3import {
4 _resetConnectionSecondsBaseline,
5 diffConnectionSeconds,
6 parseConnectionSecondsMetrics,
7} from './connection-seconds.js';
8
9describe('parseConnectionSecondsMetrics', () => {
10 test('parses one project gauge', () => {
11 const body = `# HELP briven_realtime_connection_seconds_total cumulative
12# TYPE briven_realtime_connection_seconds_total gauge
13briven_realtime_connection_seconds_total{project="p_abc123"} 42.5
14`;
15 const out = parseConnectionSecondsMetrics(body);
16 expect(out.size).toBe(1);
17 expect(out.get('p_abc123')).toBe(42.5);
18 });
19
20 test('parses multiple projects', () => {
21 const body =
22 'briven_realtime_connection_seconds_total{project="p_a"} 10\n' +
23 'briven_realtime_connection_seconds_total{project="p_b"} 20\n' +
24 'briven_realtime_connection_seconds_total{project="p_c"} 30.75\n';
25 const out = parseConnectionSecondsMetrics(body);
26 expect(out.size).toBe(3);
27 expect(out.get('p_a')).toBe(10);
28 expect(out.get('p_b')).toBe(20);
29 expect(out.get('p_c')).toBe(30.75);
30 });
31
32 test('ignores comment lines, empty lines, and other metrics', () => {
33 const body = `# HELP something else
34# TYPE briven_realtime_subscriptions_active gauge
35briven_realtime_subscriptions_active 5
36
37briven_realtime_connection_seconds_total{project="p_x"} 7
38briven_realtime_other_total{project="p_y"} 99
39`;
40 const out = parseConnectionSecondsMetrics(body);
41 expect(out.size).toBe(1);
42 expect(out.get('p_x')).toBe(7);
43 });
44
45 test('rejects negative or non-finite values', () => {
46 const body =
47 'briven_realtime_connection_seconds_total{project="p_a"} -1\n' +
48 'briven_realtime_connection_seconds_total{project="p_b"} NaN\n' +
49 'briven_realtime_connection_seconds_total{project="p_c"} 5\n';
50 const out = parseConnectionSecondsMetrics(body);
51 expect(out.size).toBe(1);
52 expect(out.get('p_c')).toBe(5);
53 });
54
55 test('handles missing label brace gracefully', () => {
56 const body = 'briven_realtime_connection_seconds_total 42\n';
57 const out = parseConnectionSecondsMetrics(body);
58 expect(out.size).toBe(0);
59 });
60});
61
62describe('diffConnectionSeconds', () => {
63 beforeEach(() => {
64 _resetConnectionSecondsBaseline();
65 });
66
67 test('first scrape returns no deltas (baseline-only)', () => {
68 const deltas = diffConnectionSeconds(new Map([['p_a', 100]]));
69 expect(deltas.size).toBe(0);
70 });
71
72 test('second scrape returns the delta', () => {
73 diffConnectionSeconds(new Map([['p_a', 100]]));
74 const deltas = diffConnectionSeconds(new Map([['p_a', 175]]));
75 expect(deltas.size).toBe(1);
76 expect(deltas.get('p_a')).toBe(75);
77 });
78
79 test('counter going backwards (realtime restart) → current treated as delta', () => {
80 diffConnectionSeconds(new Map([['p_a', 1000]]));
81 const deltas = diffConnectionSeconds(new Map([['p_a', 10]]));
82 // realtime restarted; we lost the seconds between 1000 (api-side
83 // last-seen) and the restart, but the 10 seconds since restart are
84 // surfaced as a delta so we don't drop them too.
85 expect(deltas.get('p_a')).toBe(10);
86 });
87
88 test('zero delta is dropped (no row emitted)', () => {
89 diffConnectionSeconds(new Map([['p_a', 50]]));
90 const deltas = diffConnectionSeconds(new Map([['p_a', 50]]));
91 expect(deltas.size).toBe(0);
92 });
93
94 test('multiple projects are tracked independently', () => {
95 diffConnectionSeconds(
96 new Map([
97 ['p_a', 100],
98 ['p_b', 200],
99 ]),
100 );
101 const deltas = diffConnectionSeconds(
102 new Map([
103 ['p_a', 150], // +50
104 ['p_b', 200], // 0 — drops
105 ['p_c', 30], // new — baselines, drops
106 ]),
107 );
108 expect(deltas.size).toBe(1);
109 expect(deltas.get('p_a')).toBe(50);
110 });
111});