test(harness): cover Node 20 WebSocket capability boundary

This commit is contained in:
ruv committed 2026-10-12 12:20:40 -04:00
1 parent b265454b93
commit b064acf57c
2 files changed
+31 -4

No files matched your search

+18 -3
View File
@@ -4,6 +4,9 @@ import http from 'node:http';
import { createHash } from 'node:crypto';
import { readIphoneLidar } from '../src/devices/lidar.js';
const hasNativeWebSocket = typeof globalThis.WebSocket === 'function';
const needsWebSocket = { skip: !hasNativeWebSocket && 'requires the native WebSocket client (Node 22+)' };
// A real owned loopback WebSocket producer: upgrade, optional depth frame,
// close handshake. No injected client event callbacks or hardware.
async function relay(t, { frame = true, code = 1000 } = {}) {
@@ -28,7 +31,19 @@ async function relay(t, { frame = true, code = 1000 } = {}) {
return `ws://127.0.0.1:${server.address().port}/ws/lidar`;
}
test('public iPhone reader completes when a successful relay stream closes', async t => {
test('public iPhone reader reports an unavailable WebSocket without connecting', { skip: hasNativeWebSocket && 'native WebSocket is available' }, async t => {
const server = http.createServer();
let connections = 0;
server.on('connection', socket => { connections++; socket.destroy(); });
await new Promise(r => server.listen(0, '127.0.0.1', r));
t.after(() => new Promise(r => server.close(r)));
const result = await readIphoneLidar({ url: `ws://127.0.0.1:${server.address().port}/ws/lidar`, seconds: 2 });
assert.deepEqual(result, { ok: false, reason: 'websocket_unavailable', remedy: 'Use Node 22+ (built-in WebSocket client).' });
await new Promise(r => setImmediate(r));
assert.equal(connections, 0, 'unavailable transport must not open a connection');
});
test('public iPhone reader completes when a successful relay stream closes', needsWebSocket, async t => {
const url = await relay(t);
const started = performance.now();
const result = await readIphoneLidar({ url, seconds: 2 });
@@ -38,7 +53,7 @@ test('public iPhone reader completes when a successful relay stream closes', asy
assert.ok(performance.now() - started < 1000, 'closed relay must finish before the capture timeout');
});
test('normal empty relay close completes as no_frames', async t => {
test('normal empty relay close completes as no_frames', needsWebSocket, async t => {
const url = await relay(t, { frame: false });
const started = performance.now();
const result = await readIphoneLidar({ url, seconds: 2 });
@@ -47,7 +62,7 @@ test('normal empty relay close completes as no_frames', async t => {
assert.ok(performance.now() - started < 1000, 'empty closed relay must finish before timeout');
});
test('abnormal empty relay close and packet limit controls retain semantics', async t => {
test('abnormal empty relay close and packet limit controls retain semantics', needsWebSocket, async t => {
const denied = await readIphoneLidar({ url: await relay(t, { frame: false, code: 1008 }), seconds: 2 });
assert.equal(denied.reason, 'relay_closed');
assert.match(denied.detail, /1008/);
@@ -10,6 +10,7 @@ const str = (f, s) => { const b = Buffer.from(s); return Buffer.concat([encodeVa
const key = (f, n) => { const b = Buffer.alloc(4); b.writeUInt32LE(n); return Buffer.concat([encodeVarint((f << 3) | 5), b]); };
const float = (f, v) => { const b = Buffer.alloc(4); b.writeFloatLE(v); return Buffer.concat([encodeVarint((f << 3) | 5), b]); };
const pause = ms => new Promise(r => setTimeout(r, ms));
const hasNativeWebSocket = typeof globalThis.WebSocket === 'function';
async function ownServer(t, server) {
const sockets = new Set();
@@ -57,7 +58,18 @@ test('public SDK accepts spectrum options and returns real UDP amplitude rows',
assert.deepEqual(result.spectrum[0].frames, [Array(8).fill(5)]);
});
test('public SDK accepts iPhone max_frames and terminates at the packet cap', async t => {
test('public SDK reports unavailable iPhone transport without connecting', { skip: hasNativeWebSocket && 'native WebSocket is available' }, async t => {
const server = http.createServer();
let connections = 0;
server.on('connection', socket => { connections++; socket.destroy(); });
const port = await ownServer(t, server);
const result = await createRuView().devices.lidar({ source: 'iphone', url: `ws://127.0.0.1:${port}/ws/lidar`, seconds: 2, max_frames: 1 });
assert.deepEqual(result, { ok: false, reason: 'websocket_unavailable', remedy: 'Use Node 22+ (built-in WebSocket client).' });
await new Promise(r => setImmediate(r));
assert.equal(connections, 0, 'unavailable transport must not open a connection');
});
test('public SDK accepts iPhone max_frames and terminates at the packet cap', { skip: !hasNativeWebSocket && 'requires the native WebSocket client (Node 22+)' }, async t => {
const server = http.createServer();
server.on('upgrade', (req, sock) => {
const accept = createHash('sha1').update(req.headers['sec-websocket-key'] + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11').digest('base64');