curltastic/e2e/e2e_multi_client.cjs
Jason Dekarske 56ab1fb2e3 test(e2e): align suite with PR-B protocol and physics
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-07-10 23:47:17 -07:00

80 lines
2.8 KiB
JavaScript

const WebSocket = require('ws')
const DRAW_VEL = 2.38
const room = 'MULTI' + Math.floor(Math.random() * 1000)
const base = 'ws://127.0.0.1:3000/ws?room=' + room
function throwFor(ws, team, broom_x, broom_y, velocity = DRAW_VEL, curl = 0, friction = 1.0) {
ws.send(JSON.stringify({ type: 'throw', team, broom_x, broom_y, velocity, curl, friction }))
}
function connect(name) {
return new Promise((resolve, reject) => {
const ws = new WebSocket(base)
const messages = []
ws.on('message', (data) => {
const msg = JSON.parse(data.toString())
messages.push(msg)
console.log(`[${name}]`, msg.type)
if (msg.type === 'joined') {
resolve({ ws, messages })
}
})
ws.on('open', () => console.log(`[${name}] open`))
ws.on('error', (e) => { console.error(`[${name}] error`, e.message); reject(e) })
ws.on('close', (code) => console.log(`[${name}] close`, code))
})
}
function waitFor(client, pred, timeout = 10000) {
const start = Date.now()
return new Promise((resolve, reject) => {
const check = () => {
if (pred(client.messages)) return resolve(undefined)
if (Date.now() - start > timeout) return reject(new Error('timeout'))
setTimeout(check, 50)
}
check()
})
}
;(async () => {
// 3 clients join the same room
const p1 = await connect('p1')
const p2 = await connect('p2')
const p3 = await connect('p3')
// All 3 receive game_state after join
await waitFor(p1, msgs => msgs.some(m => m.type === 'game_state'), 5000)
await waitFor(p2, msgs => msgs.some(m => m.type === 'game_state'), 5000)
await waitFor(p3, msgs => msgs.some(m => m.type === 'game_state'), 5000)
console.log('All 3 clients received game_state')
// p1 throws for the current turn_team
const state = p1.messages.find(m => m.type === 'game_state')
const turn = state.turn_team
console.log(`p1 throwing for team ${turn}`)
throwFor(p1.ws, turn, 0.0, 38.5, DRAW_VEL, 0, 1.0)
// All 3 clients eventually see trajectories (plural) or updated game_state
await waitFor(p1, msgs => msgs.some(m => m.type === 'trajectories'), 15000)
await waitFor(p2, msgs => msgs.some(m => m.type === 'trajectories'), 15000)
await waitFor(p3, msgs => msgs.some(m => m.type === 'trajectories'), 15000)
console.log('All 3 clients received trajectories')
// All 3 see an updated game_state after the throw
await waitFor(p1, msgs => msgs.slice(-1)[0]?.type === 'game_state', 15000)
await waitFor(p2, msgs => msgs.slice(-1)[0]?.type === 'game_state', 15000)
await waitFor(p3, msgs => msgs.slice(-1)[0]?.type === 'game_state', 15000)
console.log('All 3 clients received updated game_state after throw')
console.log('Multi-client observer test passed')
p1.ws.close()
p2.ws.close()
p3.ws.close()
process.exit(0)
})().catch(err => {
console.error(err)
process.exit(1)
})