test: add mock and arena tests — 21 more tests (86 total)
- mock.test.ts: 7 tests for ELO-weighted response quality, failure rates, timing - arenas.test.ts: 14 tests for arena data integrity, pickArena, randomArena Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
f837153771
commit
728afacc5c
@@ -0,0 +1,105 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { ARENAS, pickArena, randomArena } from './arenas.js'
|
||||
|
||||
describe('ARENAS', () => {
|
||||
it('has 25 arenas', () => {
|
||||
expect(ARENAS.length).toBe(25)
|
||||
})
|
||||
|
||||
it('all arenas have required fields', () => {
|
||||
for (const arena of ARENAS) {
|
||||
expect(arena.id).toBeTruthy()
|
||||
expect(arena.name).toBeTruthy()
|
||||
expect(arena.description).toBeTruthy()
|
||||
expect(typeof arena.id).toBe('string')
|
||||
expect(typeof arena.name).toBe('string')
|
||||
expect(typeof arena.description).toBe('string')
|
||||
}
|
||||
})
|
||||
|
||||
it('all arena IDs are unique', () => {
|
||||
const ids = ARENAS.map(a => a.id)
|
||||
expect(new Set(ids).size).toBe(ids.length)
|
||||
})
|
||||
|
||||
it('all arena names are unique', () => {
|
||||
const names = ARENAS.map(a => a.name)
|
||||
expect(new Set(names).size).toBe(names.length)
|
||||
})
|
||||
|
||||
it('arenas with modifier also have modifierDescription', () => {
|
||||
for (const arena of ARENAS) {
|
||||
if (arena.modifier) {
|
||||
expect(arena.modifierDescription).toBeTruthy()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('arenas without modifier have null modifierDescription', () => {
|
||||
for (const arena of ARENAS) {
|
||||
if (!arena.modifier) {
|
||||
expect(arena.modifierDescription).toBeNull()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('known modifiers are present', () => {
|
||||
const modifiers = ARENAS.map(a => a.modifier).filter(Boolean)
|
||||
expect(modifiers).toContain('speed_2x')
|
||||
expect(modifiers).toContain('roast_2x')
|
||||
expect(modifiers).toContain('food_2x')
|
||||
expect(modifiers).toContain('meme_2x')
|
||||
})
|
||||
})
|
||||
|
||||
describe('pickArena', () => {
|
||||
it('same choices return The Singularity', () => {
|
||||
const arena = pickArena(5, 5)
|
||||
expect(arena.id).toBe('the_singularity')
|
||||
})
|
||||
|
||||
it('different choices return a non-singularity arena', () => {
|
||||
const arena = pickArena(3, 7)
|
||||
expect(arena.id).not.toBe('the_singularity')
|
||||
})
|
||||
|
||||
it('deterministic for same inputs', () => {
|
||||
const a1 = pickArena(10, 20)
|
||||
const a2 = pickArena(10, 20)
|
||||
expect(a1.id).toBe(a2.id)
|
||||
})
|
||||
|
||||
it('different inputs give different arenas (most of the time)', () => {
|
||||
const arenas = new Set<string>()
|
||||
for (let i = 0; i < 50; i++) {
|
||||
arenas.add(pickArena(i, i + 1).id)
|
||||
}
|
||||
expect(arenas.size).toBeGreaterThan(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe('randomArena', () => {
|
||||
it('never returns The Singularity', () => {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const arena = randomArena()
|
||||
expect(arena.id).not.toBe('the_singularity')
|
||||
}
|
||||
})
|
||||
|
||||
it('returns variety', () => {
|
||||
const arenas = new Set<string>()
|
||||
for (let i = 0; i < 100; i++) {
|
||||
arenas.add(randomArena().id)
|
||||
}
|
||||
expect(arenas.size).toBeGreaterThan(5)
|
||||
})
|
||||
|
||||
it('returns valid arena objects', () => {
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const arena = randomArena()
|
||||
expect(arena.id).toBeTruthy()
|
||||
expect(arena.name).toBeTruthy()
|
||||
expect(arena.description).toBeTruthy()
|
||||
}
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,104 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { mockResponse } from './mock.js'
|
||||
import { pickChallenge } 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 non-empty answers', () => {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
const challenge = pickChallenge(new Set(), null)
|
||||
if (challenge.scoring !== 'creative') continue
|
||||
|
||||
const resp = mockResponse(challenge, 'witty', 1500)
|
||||
if (!resp.timedOut && !resp.error) {
|
||||
expect(resp.answer.length).toBeGreaterThan(0)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user