import { describe, it, expect } from 'vitest' import { mockResponse } from './mock.js' import { pickChallenge, getAllChallengeTypes } from './challenges.js' describe('mockResponse', () => { it('returns a valid response structure', () => { const challenge = pickChallenge(new Set(), null) const resp = mockResponse(challenge, 'confident', 1500) expect(resp).toHaveProperty('answer') expect(resp).toHaveProperty('trashTalk') expect(resp).toHaveProperty('timeMs') expect(resp).toHaveProperty('timedOut') expect(resp).toHaveProperty('error') expect(typeof resp.timeMs).toBe('number') expect(typeof resp.timedOut).toBe('boolean') expect(typeof resp.error).toBe('boolean') }) it('high ELO bots answer correctly more often (factual)', () => { let highCorrect = 0 let lowCorrect = 0 const runs = 200 for (let i = 0; i < runs; i++) { const challenge = pickChallenge(new Set(), null) if (challenge.scoring !== 'factual' || !challenge.answers?.length) continue const highResp = mockResponse(challenge, 'confident', 1900) const lowResp = mockResponse(challenge, 'clueless', 900) if (!highResp.timedOut && !highResp.error && challenge.answers.includes(highResp.answer)) highCorrect++ if (!lowResp.timedOut && !lowResp.error && challenge.answers.includes(lowResp.answer)) lowCorrect++ } // High ELO should answer correctly more often expect(highCorrect).toBeGreaterThan(lowCorrect) }) it('low ELO bots time out / error more often', () => { let highFails = 0 let lowFails = 0 const runs = 500 for (let i = 0; i < runs; i++) { const challenge = pickChallenge(new Set(), null) const highResp = mockResponse(challenge, 'confident', 1900) const lowResp = mockResponse(challenge, 'clueless', 900) if (highResp.timedOut || highResp.error) highFails++ if (lowResp.timedOut || lowResp.error) lowFails++ } expect(lowFails).toBeGreaterThan(highFails) }) it('high ELO bots respond faster on average', () => { let highTotal = 0 let lowTotal = 0 let highCount = 0 let lowCount = 0 const runs = 200 for (let i = 0; i < runs; i++) { const challenge = pickChallenge(new Set(), null) const highResp = mockResponse(challenge, 'confident', 1900) const lowResp = mockResponse(challenge, 'clueless', 900) if (!highResp.timedOut && !highResp.error) { highTotal += highResp.timeMs; highCount++ } if (!lowResp.timedOut && !lowResp.error) { lowTotal += lowResp.timeMs; lowCount++ } } const highAvg = highTotal / Math.max(highCount, 1) const lowAvg = lowTotal / Math.max(lowCount, 1) expect(highAvg).toBeLessThan(lowAvg) }) it('creative challenges return answers (most non-empty at high elo)', () => { let nonEmpty = 0 let total = 0 for (let i = 0; i < 50; i++) { const challenge = pickChallenge(new Set(), null) if (challenge.scoring !== 'creative') continue const resp = mockResponse(challenge, 'witty', 1800) if (!resp.timedOut && !resp.error) { total++ if (resp.answer.length > 0) nonEmpty++ } } // At elo 1800, bad answer chance is very low; most should be non-empty expect(nonEmpty).toBeGreaterThan(total * 0.7) }) it('trash talk is always a string', () => { for (let i = 0; i < 20; i++) { const challenge = pickChallenge(new Set(), null) const resp = mockResponse(challenge, 'confident', 1900) expect(typeof resp.trashTalk).toBe('string') } }) it('timeMs is always positive', () => { for (let i = 0; i < 50; i++) { const challenge = pickChallenge(new Set(), null) const resp = mockResponse(challenge, 'confident', 1500) expect(resp.timeMs).toBeGreaterThan(0) } }) it('produces valid responses for all 16 challenge types', () => { const types = getAllChallengeTypes() expect(types.length).toBe(16) for (const type of types) { // Force pick a challenge of this type by using all other types const otherTypes = new Set(types.filter(t => t !== type)) const challenge = pickChallenge(otherTypes, null) // May not get exact type if creative/factual split causes fallback, but should still work const resp = mockResponse(challenge, 'confident', 1500) expect(resp).toHaveProperty('answer') expect(resp).toHaveProperty('timeMs') expect(resp.timeMs).toBeGreaterThan(0) } }) it('answer is empty string on timeout', () => { // Run many times with very low elo to trigger timeouts let foundTimeout = false for (let i = 0; i < 200; i++) { const challenge = pickChallenge(new Set(), null) const resp = mockResponse(challenge, 'clueless', 500) if (resp.timedOut) { expect(resp.answer).toBe('') foundTimeout = true } } expect(foundTimeout).toBe(true) }) it('answer is empty string on error', () => { let foundError = false for (let i = 0; i < 200; i++) { const challenge = pickChallenge(new Set(), null) const resp = mockResponse(challenge, 'clueless', 500) if (resp.error) { expect(resp.answer).toBe('') foundError = true } } expect(foundError).toBe(true) }) })