bend_puzzle/test/helpers/realtime-client.js
2026-07-30 15:13:53 +09:00

52 lines
2.2 KiB
JavaScript

'use strict';
const assert=require('assert/strict');
async function connectRealtime(base,session,{timeout=4000}={}){
assert(session?.playerId&&session?.token,'Realtime session is required');
const url=base.replace(/^http/,'ws')+'/api/realtime';
const socket=new WebSocket(url);
const queue=[];
const waiters=[];
let closed=false;
function dispatch(message){
for(let index=0;index<waiters.length;index++){
const waiter=waiters[index];
if(waiter.predicate(message)){
waiters.splice(index,1);clearTimeout(waiter.timer);waiter.resolve(message);return;
}
}
queue.push(message);
}
socket.addEventListener('message',event=>{
try{dispatch(JSON.parse(String(event.data)))}catch(error){dispatch({type:'test-parse-error',error})}
});
socket.addEventListener('close',()=>{closed=true;for(const waiter of waiters.splice(0)){clearTimeout(waiter.timer);waiter.reject(new Error('Realtime socket closed'))}});
await new Promise((resolve,reject)=>{
const timer=setTimeout(()=>reject(new Error('Realtime socket open timed out')),timeout);
socket.addEventListener('open',()=>{clearTimeout(timer);resolve()},{once:true});
socket.addEventListener('error',()=>{clearTimeout(timer);reject(new Error('Realtime socket failed'))},{once:true});
});
socket.send(JSON.stringify({type:'hello',playerId:session.playerId,token:session.token}));
async function waitFor(predicateOrType,waitTimeout=timeout){
const predicate=typeof predicateOrType==='function'?predicateOrType:message=>message?.type===predicateOrType;
const found=queue.findIndex(predicate);
if(found>=0)return queue.splice(found,1)[0];
if(closed)throw new Error('Realtime socket is closed');
return new Promise((resolve,reject)=>{
const waiter={predicate,resolve,reject,timer:null};
waiter.timer=setTimeout(()=>{const index=waiters.indexOf(waiter);if(index>=0)waiters.splice(index,1);reject(new Error('Realtime message timed out'))},waitTimeout);
waiters.push(waiter);
});
}
const ready=await waitFor('ready');
return{
socket,ready,
send(message){socket.send(JSON.stringify(message))},
waitFor,
close(){try{socket.close()}catch(_){}},
queued:queue
};
}
module.exports={connectRealtime};