// Smoke test: two bot clients join, aim at each other, and fight. // Verifies welcome/state flow, phases, hits, deaths, round ends, scoring. // Usage: start the server first, then `node tools/smoke.mjs` import WebSocket from 'ws'; const URL = process.env.SMOKE_URL || 'ws://localhost:3000/ws'; const seen = { phases: new Set(), events: new Set(), scores: new Set() }; function bot(name) { const ws = new WebSocket(URL); let me = null; let latest = null; ws.on('open', () => ws.send(JSON.stringify({ t: 'join', name }))); ws.on('message', (d) => { const m = JSON.parse(d.toString()); if (m.t === 'welcome') { me = m.id; console.log(`[${name}] welcome, id=${m.id}, seed=${m.seed}`); return; } if (m.t !== 'state') return; latest = m; seen.phases.add(m.phase); for (const e of m.events) { seen.events.add(e.kind); if (['die', 'roundEnd', 'matchEnd'].includes(e.kind)) { console.log(`[${name}] event:`, JSON.stringify(e)); } } const mine = m.players.find((p) => p.id === me); if (mine) seen.scores.add(`${name}:${mine.score}/${mine.hp}`); }); ws.on('error', (e) => { console.error(`[${name}] ws error:`, e.message); }); // aim at nearest opponent, shuffle around, hold fire setInterval(() => { if (ws.readyState !== 1 || !latest) return; const mine = latest.players.find((p) => p.id === me); if (!mine) return; const others = latest.players.filter((p) => p.id !== me && p.alive && p.inRound); let angle = Math.random() * Math.PI * 2; if (others.length > 0) { const t = others[0]; angle = Math.atan2(t.y - mine.y, t.x - mine.x); } ws.send(JSON.stringify({ t: 'input', up: Math.random() < 0.3, down: Math.random() < 0.3, left: Math.random() < 0.3, right: Math.random() < 0.3, angle, fire: true, })); }, 60); } bot('Alpha'); setTimeout(() => bot('Bravo'), 400); setTimeout(() => { console.log('\n--- smoke summary ---'); console.log('phases seen :', [...seen.phases].join(', ')); console.log('events seen :', [...seen.events].join(', ')); console.log('score/hp samples:', [...seen.scores].slice(0, 12).join(' ')); const ok = seen.phases.has('round') && seen.events.has('throw') && seen.events.has('hit'); console.log(ok ? '✅ SMOKE OK' : '⚠️ smoke inconclusive (check output above)'); process.exit(0); }, Number(process.env.SMOKE_MS || 25000));