diff --git a/server/src/engine/lifecycle.test.ts b/server/src/engine/lifecycle.test.ts index c7be637..60cf71a 100644 --- a/server/src/engine/lifecycle.test.ts +++ b/server/src/engine/lifecycle.test.ts @@ -505,6 +505,62 @@ describe('fight lifecycle', () => { } }) + it('both bots timing out every round produces draw with zero damage', () => { + const botA = { id: 'to-a', name: 'TimeoutA' } + const botB = { id: 'to-b', name: 'TimeoutB' } + const usedTypes = new Set() + const timeout = { answer: '', timeMs: 8000, timedOut: true, error: false } + + for (let r = 0; r < 10; r++) { + const challenge = pickChallenge(usedTypes, null, undefined, r + 1) + usedTypes.add(challenge.type) + const result = scoreRound(challenge, botA, botB, timeout, timeout, null, 0, 0) + + // Both timeout = draw, no damage + expect(result.winnerId).toBeNull() + expect(result.botADamage).toBe(0) + expect(result.botBDamage).toBe(0) + expect(result.botAScore).toBe(0) + expect(result.botBScore).toBe(0) + } + }) + + it('perfect victory (10-0) — correct ELO calculation', () => { + // Bot A wins every round, Bot B always wrong + const eloA = 1200 + const eloB = 1200 + + // Simulate 10 rounds where A always correct, B always wrong + const botA = { id: 'pv-a', name: 'PerfA' } + const botB = { id: 'pv-b', name: 'PerfB' } + const usedTypes = new Set() + let aRoundWins = 0 + + for (let r = 0; r < 10; r++) { + const challenge = pickChallenge(usedTypes, null, undefined, r + 1) + usedTypes.add(challenge.type) + const resp = mockResponse(challenge, 'confident', 1800) + const result = scoreRound( + challenge, botA, botB, + { answer: resp.answer, timeMs: 200, timedOut: false, error: false }, + { answer: 'wrong answer completely', timeMs: 200, timedOut: false, error: false }, + null, aRoundWins, 0, + ) + if (result.winnerId === botA.id) aRoundWins++ + } + + expect(aRoundWins).toBeGreaterThan(5) // A should win most rounds + + // ELO calculation for equal-elo matchup with clear winner + const { newWinnerElo, newLoserElo } = calculateElo(eloA, eloB) + expect(newWinnerElo).toBeGreaterThan(eloA) + expect(newLoserElo).toBeLessThan(eloB) + // ELO change should be moderate for equal-elo matchup + const change = newWinnerElo - eloA + expect(change).toBeGreaterThan(5) + expect(change).toBeLessThan(40) + }) + it('fight loop throughput: >500 fights/second (no I/O)', () => { const fights = 5000 const start = performance.now()