cat / goq
clone

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

fix/release-build-and-order
goq / portal / pointer-feedback.test.mjs
pointer-feedback.test.mjs 10.1K
1import assert from 'node:assert/strict';2import { readFileSync } from 'node:fs';3import test from 'node:test';45import {6 createPointerFeedbackRuntime,7 newPointerSession,8 parsePointerFeedbackMessage,9} from './pointer-feedback.mjs';1011const main = readFileSync(new URL('./main.js', import.meta.url), 'utf8');1213function runtimeHarness() {14 let activeSession = newPointerSession();15 let capabilitySnapshot = { connected: false, pointerPositionFeedback: true };16 let surface = { width: 1280, height: 800 };17 let renders = 0;18 let disconnects = 0;19 const errors = [];20 const runtime = createPointerFeedbackRuntime({21 getActiveSession: () => activeSession,22 getCapabilities: () => capabilitySnapshot,23 getSurface: () => surface,24 render: () => { renders++; },25 disconnect: () => { disconnects++; },26 logger: { error: (...args) => errors.push(args) },27 });28 return {29 runtime,30 errors,31 get session() { return activeSession; },32 get renders() { return renders; },33 get disconnects() { return disconnects; },34 setActiveSession(session) { activeSession = session; },35 setCapabilities(snapshot) { capabilitySnapshot = snapshot; },36 setSurface(nextSurface) { surface = nextSurface; },37 };38}3940test('new pointer sessions pin feedback, channel, surface, and render defaults', () => {41 assert.deepEqual(newPointerSession(), {42 received: false,43 latest: null,44 failed: false,45 failureDetail: null,46 closing: false,47 channel: null,48 surfaceDimensions: null,49 remotePosition: null,50 remoteVisible: false,51 });52});5354test('new pointer sessions isolate staged and rendered feedback state', () => {55 const staged = newPointerSession();56 const fresh = newPointerSession();57 staged.received = true;58 staged.latest = { sequence: 3, position: { x: 4, y: 5 }, pointer_visible: true };59 staged.surfaceDimensions = { width: 1280, height: 800 };60 staged.remotePosition = { x: 4, y: 5 };61 staged.remoteVisible = true;6263 assert.deepEqual(fresh, newPointerSession());64 assert.equal(staged.received, true);65 assert.equal(staged.remoteVisible, true);66});6768test('pointer feedback position envelopes preserve validated coordinates', () => {69 assert.deepEqual(70 parsePointerFeedbackMessage(71 { type: 'position', sequence: 7, position: { x: 1280, y: 800 } },72 { width: 2560, height: 1600 },73 ),74 {75 type: 'position',76 feedback: { sequence: 7, position: { x: 1280, y: 800 }, pointer_visible: true },77 },78 );79 assert.deepEqual(80 parsePointerFeedbackMessage(81 {82 type: 'position',83 sequence: 8,84 position: { x: 1280, y: 800 },85 pointer_visible: false,86 },87 { width: 2560, height: 1600 },88 ),89 {90 type: 'position',91 feedback: { sequence: 8, position: { x: 1280, y: 800 }, pointer_visible: false },92 },93 );94});9596test('pointer feedback terminal envelopes are finite and explicit', () => {97 assert.deepEqual(98 parsePointerFeedbackMessage({ type: 'terminal', reason: 'eof' }),99 { type: 'terminal', reason: 'eof' },100 );101 assert.deepEqual(102 parsePointerFeedbackMessage({ type: 'terminal', reason: 'malformed' }),103 { type: 'terminal', reason: 'malformed' },104 );105 assert.throws(106 () => parsePointerFeedbackMessage({ type: 'terminal', reason: 'unknown' }),107 /invalid pointer feedback message/,108 );109});110111test('pointer feedback envelopes reject malformed or out-of-surface positions', () => {112 assert.throws(113 () => parsePointerFeedbackMessage({ sequence: 1, position: null }),114 /invalid pointer feedback message/,115 );116 assert.throws(117 () => parsePointerFeedbackMessage(118 { type: 'position', sequence: 2, position: { x: 2560, y: 0 } },119 { width: 2560, height: 1600 },120 ),121 /outside the negotiated surface/,122 );123});124125test('runtime stages pre-connect feedback without applying or rendering it', () => {126 const fixture = runtimeHarness();127 fixture.runtime.handleMessage({128 type: 'position',129 sequence: 1,130 position: { x: 50, y: 60 },131 pointer_visible: true,132 }, fixture.session);133134 assert.equal(fixture.session.received, true);135 assert.deepEqual(fixture.session.latest, {136 sequence: 1,137 position: { x: 50, y: 60 },138 pointer_visible: true,139 });140 assert.equal(fixture.session.remotePosition, null);141 assert.equal(fixture.session.remoteVisible, false);142 assert.equal(fixture.renders, 0);143 assert.equal(fixture.disconnects, 0);144});145146test('runtime applies active connected feedback only with capability and a surface', () => {147 const fixture = runtimeHarness();148 fixture.setCapabilities({ connected: true, pointerPositionFeedback: true });149 fixture.runtime.handleMessage({150 type: 'position',151 sequence: 2,152 position: { x: 100, y: 200 },153 pointer_visible: false,154 }, fixture.session);155 assert.deepEqual(fixture.session.remotePosition, { x: 100, y: 200 });156 assert.equal(fixture.session.remoteVisible, false);157 assert.equal(fixture.renders, 1);158159 fixture.setCapabilities({ connected: true, pointerPositionFeedback: false });160 fixture.runtime.handleMessage({161 type: 'position',162 sequence: 3,163 position: { x: 101, y: 201 },164 }, fixture.session);165 assert.equal(fixture.session.latest.sequence, 3);166 assert.deepEqual(fixture.session.remotePosition, { x: 100, y: 200 });167 assert.equal(fixture.renders, 1);168169 fixture.setCapabilities({ connected: true, pointerPositionFeedback: true });170 fixture.setSurface(null);171 fixture.runtime.handleMessage({172 type: 'position',173 sequence: 4,174 position: { x: 102, y: 202 },175 }, fixture.session);176 assert.equal(fixture.session.latest.sequence, 4);177 assert.deepEqual(fixture.session.remotePosition, { x: 100, y: 200 });178 assert.equal(fixture.renders, 1);179});180181test('runtime ignores stale, failed, and closing session deliveries', () => {182 const fixture = runtimeHarness();183 const stale = newPointerSession();184 fixture.setCapabilities({ connected: true, pointerPositionFeedback: true });185 fixture.runtime.applyPosition({186 sequence: 0, position: { x: 0, y: 0 }, pointer_visible: true,187 }, stale);188 assert.equal(stale.remotePosition, null);189 assert.equal(fixture.renders, 0);190191 fixture.runtime.handleMessage({192 type: 'position', sequence: 1, position: { x: 1, y: 1 },193 }, stale);194 assert.equal(stale.received, false);195196 fixture.session.failed = true;197 fixture.runtime.handleMessage({198 type: 'position', sequence: 2, position: { x: 2, y: 2 },199 }, fixture.session);200 assert.equal(fixture.session.received, false);201202 const closing = newPointerSession();203 closing.closing = true;204 fixture.setActiveSession(closing);205 fixture.runtime.handleMessage({206 type: 'position', sequence: 3, position: { x: 3, y: 3 },207 }, closing);208 assert.equal(closing.received, false);209 assert.equal(fixture.renders, 0);210});211212test('terminal feedback clears render state and disconnects only while connected', () => {213 for (const [reason, detail] of [214 ['eof', 'Pointer feedback ended'],215 ['malformed', 'Pointer feedback was malformed'],216 ]) {217 const fixture = runtimeHarness();218 fixture.session.remotePosition = { x: 4, y: 5 };219 fixture.session.remoteVisible = true;220 fixture.runtime.handleMessage({ type: 'terminal', reason }, fixture.session);221 assert.equal(fixture.session.failed, true);222 assert.equal(fixture.session.failureDetail, detail);223 assert.equal(fixture.session.remotePosition, null);224 assert.equal(fixture.session.remoteVisible, false);225 assert.equal(fixture.renders, 1);226 assert.deepEqual(fixture.errors, [[detail]]);227 assert.equal(fixture.disconnects, 0);228229 const connected = runtimeHarness();230 connected.setCapabilities({ connected: true, pointerPositionFeedback: true });231 connected.runtime.handleMessage({ type: 'terminal', reason }, connected.session);232 assert.equal(connected.disconnects, 1);233 }234});235236test('invalid and out-of-surface feedback fail, clear state, and preserve error logging', () => {237 const fixture = runtimeHarness();238 fixture.setCapabilities({ connected: true, pointerPositionFeedback: true });239 fixture.session.remotePosition = { x: 3, y: 4 };240 fixture.session.remoteVisible = true;241 fixture.runtime.handleMessage({242 type: 'position',243 sequence: 7,244 position: { x: 1280, y: 0 },245 }, fixture.session);246247 assert.equal(fixture.session.received, true);248 assert.equal(fixture.session.latest.sequence, 7);249 assert.equal(fixture.session.failed, true);250 assert.match(fixture.session.failureDetail, /outside the negotiated surface/);251 assert.equal(fixture.session.remotePosition, null);252 assert.equal(fixture.session.remoteVisible, false);253 assert.equal(fixture.renders, 1);254 assert.equal(fixture.errors[0][0], 'invalid pointer-position feedback:');255 assert.match(String(fixture.errors[0][1]), /outside the negotiated surface/);256 assert.equal(fixture.disconnects, 1);257});258259test('runtime validates its injected ownership and side-effect boundaries', () => {260 const required = {261 getActiveSession: () => null,262 getCapabilities: () => ({}),263 getSurface: () => null,264 render: () => {},265 disconnect: () => {},266 logger: { error: () => {} },267 };268 for (const name of ['getActiveSession', 'getCapabilities', 'getSurface', 'render', 'disconnect']) {269 assert.throws(270 () => createPointerFeedbackRuntime({ ...required, [name]: null }),271 new RegExp(`${name} must be a function`),272 );273 }274 assert.throws(275 () => createPointerFeedbackRuntime({ ...required, logger: {} }),276 /logger must provide error/,277 );278});279280test('Portal delegates pointer feedback while retaining session, surface, and DOM rendering', () => {281 assert.match(main, /const pointerFeedbackRuntime = createPointerFeedbackRuntime\(\{/);282 assert.match(main, /getActiveSession: \(\) => activePointerSession/);283 assert.match(main, /getSurface: currentPointerSurfaceSize/);284 assert.match(main, /render: renderRemotePointer/);285 assert.match(286 main,287 /pointerFeedbackRuntime\.handleMessage\(message, pointerSession\)/,288 );289 assert.match(main, /let activePointerSession = null/);290 assert.match(main, /function currentPointerSurfaceSize\(\)/);291 assert.match(main, /function renderRemotePointer\(\)/);292 assert.doesNotMatch(main, /function applyPointerPositionFeedback\(/);293 assert.doesNotMatch(main, /function handlePointerPositionFeedback\(/);294});