cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/portal-dist-prebuild
goq / portal / network-diagnostics.test.mjs
network-diagnostics.test.mjs 6.7K
1import test from 'node:test';2import assert from 'node:assert/strict';3import {4 networkDiagnosticsPresentation,5 normalizeNetworkDiagnostics,6} from './network-diagnostics.mjs';78function leg(overrides = {}) {9 return {10 mode: 'direct',11 sample_count: 3,12 direct_samples: 3,13 relay_samples: 0,14 custom_samples: 0,15 unknown_samples: 0,16 path_epoch: 1,17 path_switch_total: 0,18 mode_transition_total: 0,19 rtt_sample_count: 3,20 rtt_current_ms: 9.25,21 rtt_p50_ms: 8,22 rtt_p95_ms: 12.5,23 rtt_max_ms: 13,24 rtt_overflow_total: 0,25 tx_datagrams: 20,26 tx_bytes: 2048,27 rx_datagrams: 18,28 rx_bytes: 1024,29 lost_packets: 1,30 lost_bytes: 64,31 cwnd_bytes: 65536,32 mtu: 1200,33 congestion_events: 2,34 black_holes_detected: 0,35 counter_regression_total: 0,36 complete: true,37 ...overrides,38 };39}4041function snapshot(overrides = {}) {42 return {43 version: 2,44 session_elapsed_ms: 12345,45 media: leg(),46 input: leg({ mode: 'relay', direct_samples: 0, relay_samples: 3 }),47 audio: null,48 input_ack: {49 negotiated: true,50 sent_total: 10,51 acknowledged_total: 9,52 pending_count: 1,53 pending_capacity: 64,54 duplicate_total: 1,55 untracked_total: 0,56 malformed: false,57 closed: false,58 latency_sample_count: 3,59 latency_p50_ms: 4,60 latency_p95_ms: 9,61 latency_max_ms: 10,62 latency_overflow_total: 0,63 complete: true,64 },65 adaptive: {66 scope: 'local_viewer',67 local_pressure: 'pressured',68 aggregate_target_kbps: 6000,69 aggregate_state: 'decrease',70 recovery_state: 'active',71 applied: true,72 },73 session: {74 mode: 'moq_multi_viewer_v2',75 local_handle: 'viewer-one',76 focus_generation: 7,77 roster_revision: 9,78 roster_age_ms: 25,79 subscription_expires_at_unix: 2_000_000_000,80 subscription_seconds_remaining: 30,81 stale_snapshot_total: 0,82 invalid_snapshot_total: 0,83 },84 ...overrides,85 };86}8788test('normalizes a bounded v2 snapshot and keeps transport legs separate', () => {89 const diagnostics = normalizeNetworkDiagnostics(snapshot());90 assert.equal(diagnostics.version, 2);91 assert.equal(diagnostics.sessionElapsedMs, 12345);92 assert.equal(diagnostics.media.mode, 'direct');93 assert.equal(diagnostics.input.mode, 'relay');94 assert.equal(diagnostics.audio, null);95 assert.equal(diagnostics.media.rttCurrentMs, 9.25);96 assert.deepEqual(diagnostics.media.rtt, {97 sampleCount: 3,98 p50Ms: 8,99 p95Ms: 12.5,100 maxMs: 13,101 overflowTotal: 0,102 });103 assert.equal(diagnostics.inputAck.pendingCount, 1);104 assert.deepEqual(diagnostics.adaptive, {105 scope: 'local-viewer',106 localPressure: 'pressured',107 aggregateTargetKbps: 6000,108 aggregateState: 'decrease',109 recoveryState: 'active',110 applied: true,111 });112 assert.equal(diagnostics.session.localHandle, 'viewer-one');113});114115test('validates exact counters, classified samples, percentiles, and completeness', () => {116 assert.throws(() => normalizeNetworkDiagnostics(snapshot({117 media: leg({ tx_bytes: Number.MAX_SAFE_INTEGER + 1 }),118 })), /exact unsigned integer/);119 assert.throws(() => normalizeNetworkDiagnostics(snapshot({120 media: leg({ direct_samples: 2 }),121 })), /exactly match sample_count/);122 assert.throws(() => normalizeNetworkDiagnostics(snapshot({123 media: leg({ rtt_p50_ms: 14 }),124 })), /percentiles must be monotonic/);125 assert.throws(() => normalizeNetworkDiagnostics(snapshot({126 media: leg({ complete: 'yes' }),127 })), /must be a boolean/);128 assert.throws(() => normalizeNetworkDiagnostics(snapshot({129 input_ack: { ...snapshot().input_ack, pending_count: 65 },130 })), /pending_count cannot exceed pending_capacity/);131});132133test('requires null percentiles for an empty bounded window', () => {134 const empty = leg({135 rtt_sample_count: 0,136 rtt_current_ms: null,137 rtt_p50_ms: null,138 rtt_p95_ms: null,139 rtt_max_ms: null,140 });141 assert.equal(normalizeNetworkDiagnostics(snapshot({ media: empty })).media.rtt.sampleCount, 0);142 assert.throws(() => normalizeNetworkDiagnostics(snapshot({143 media: { ...empty, rtt_p50_ms: 0 },144 })), /require at least one retained sample/);145});146147test('accepts nullable path gauges and represents bounded histogram overflow', () => {148 const overflowed = leg({149 sample_count: 2,150 direct_samples: 0,151 unknown_samples: 2,152 mode: 'unknown',153 rtt_sample_count: 2,154 rtt_current_ms: null,155 rtt_p50_ms: 12,156 rtt_p95_ms: null,157 rtt_max_ms: 6000,158 rtt_overflow_total: 1,159 cwnd_bytes: null,160 mtu: null,161 congestion_events: null,162 black_holes_detected: null,163 complete: false,164 });165 const diagnostics = normalizeNetworkDiagnostics(snapshot({ media: overflowed }));166 assert.equal(diagnostics.media.rtt.p50Ms, 12);167 assert.equal(diagnostics.media.rtt.p95Ms, null);168 assert.equal(diagnostics.media.cwndBytes, null);169 assert.match(170 networkDiagnosticsPresentation(diagnostics).media,171 /RTT current — · history 12\.0 \/ — \/ 6000\.0 ms/,172 );173});174175test('renders legs and ACK health without exposing identifiers or addresses', () => {176 const raw = snapshot({177 peer_id: 'secret-peer',178 remote_address: '192.0.2.1:443',179 media: leg({ endpoint_id: 'secret-endpoint' }),180 });181 const presentation = networkDiagnosticsPresentation(normalizeNetworkDiagnostics(raw));182 assert.match(presentation.media, /^direct · RTT/);183 assert.match(presentation.input, /^relay · RTT/);184 assert.equal(presentation.audio, 'unavailable');185 assert.match(presentation.inputAck, /^negotiated · 9\/10 acknowledged/);186 assert.match(presentation.adaptive, /^local-viewer · local pressured · aggregate 6000 kbps decrease/);187 assert.match(presentation.session, /native MoQ multi-viewer v2 · viewer-one/);188 assert.doesNotMatch(JSON.stringify(presentation), /secret|192\.0\.2\.1|endpoint/i);189});190191test('formats a missing v4 snapshot as explicitly unavailable', () => {192 assert.deepEqual(networkDiagnosticsPresentation(null), {193 session: 'unavailable',194 media: 'unavailable',195 input: 'unavailable',196 audio: 'unavailable',197 inputAck: 'unavailable',198 adaptive: 'unavailable',199 });200});201202test('adaptive diagnostics expose only local contribution and aggregate decision', () => {203 const diagnostics = normalizeNetworkDiagnostics(snapshot({204 adaptive: {205 scope: 'local_viewer',206 local_pressure: 'recovering',207 aggregate_target_kbps: 1000,208 aggregate_state: 'hold',209 recovery_state: 'floor_limited',210 applied: false,211 other_viewer_rtt_ms: 900,212 contributor_peer: 'secret-peer',213 },214 }));215 assert.equal(diagnostics.adaptive.localPressure, 'recovering');216 assert.equal(diagnostics.adaptive.recoveryState, 'floor-limited');217 assert.doesNotMatch(JSON.stringify(diagnostics.adaptive), /other|peer|rtt/i);218});