test: verify human challenge race condition fix ordering (BUG-S2)
waitForHumanResponse synchronously stores pending challenge before returning, ensuring SSE emit happens after storage. Tests verify ordering, choice consistency, promise resolution, and cleanup. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
8468c89352
commit
e48a984d96
@@ -0,0 +1,80 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { waitForHumanResponse, getPendingChallenge, submitHumanResponse } from './human-responses.js'
|
||||
import type { Challenge } from './challenges.js'
|
||||
|
||||
const testChallenge: Challenge = {
|
||||
type: 'bitcoin_trivia',
|
||||
label: 'Bitcoin Trivia',
|
||||
prompt: 'What year was Bitcoin launched?',
|
||||
answers: ['2009'],
|
||||
scoring: 'factual',
|
||||
timeout_ms: 8000,
|
||||
difficulty: 'easy',
|
||||
choices: ['2009', '2010', '2008'],
|
||||
}
|
||||
|
||||
describe('waitForHumanResponse ordering', () => {
|
||||
it('pending challenge is stored synchronously before promise is awaited', () => {
|
||||
const fightId = `fight-ordering-${Date.now()}`
|
||||
const botId = 'bot-ordering-test'
|
||||
|
||||
// Call waitForHumanResponse — the pending entry must be stored synchronously
|
||||
const { promise, choices } = waitForHumanResponse(fightId, botId, testChallenge, 1)
|
||||
|
||||
// IMMEDIATELY check that getPendingChallenge returns data (before awaiting)
|
||||
const pending = getPendingChallenge(fightId, botId)
|
||||
expect(pending).not.toBeNull()
|
||||
expect(pending!.prompt).toBe('What year was Bitcoin launched?')
|
||||
expect(pending!.roundNumber).toBe(1)
|
||||
expect(choices).toBeInstanceOf(Array)
|
||||
expect(choices.length).toBe(3)
|
||||
|
||||
// Clean up: submit answer to resolve the promise
|
||||
submitHumanResponse(fightId, botId, '2009')
|
||||
// Consume promise to prevent unhandled rejection
|
||||
return promise
|
||||
})
|
||||
|
||||
it('choices returned by waitForHumanResponse match pending challenge choices', () => {
|
||||
const fightId = `fight-choices-${Date.now()}`
|
||||
const botId = 'bot-choices-test'
|
||||
|
||||
const { promise, choices } = waitForHumanResponse(fightId, botId, testChallenge, 2)
|
||||
|
||||
const pending = getPendingChallenge(fightId, botId)
|
||||
expect(pending).not.toBeNull()
|
||||
expect(pending!.choices).toEqual(choices)
|
||||
|
||||
submitHumanResponse(fightId, botId, '2009')
|
||||
return promise
|
||||
})
|
||||
|
||||
it('submitHumanResponse resolves the waiting promise', async () => {
|
||||
const fightId = `fight-submit-${Date.now()}`
|
||||
const botId = 'bot-submit-test'
|
||||
|
||||
const { promise } = waitForHumanResponse(fightId, botId, testChallenge, 3)
|
||||
|
||||
// Submit after a microtask delay to simulate browser response
|
||||
setTimeout(() => submitHumanResponse(fightId, botId, '2009', 'nice!'), 10)
|
||||
|
||||
const result = await promise
|
||||
expect(result.answer).toBe('2009')
|
||||
expect(result.trashTalk).toBe('nice!')
|
||||
expect(result.timedOut).toBe(false)
|
||||
})
|
||||
|
||||
it('pending is cleared after submit', () => {
|
||||
const fightId = `fight-clear-${Date.now()}`
|
||||
const botId = 'bot-clear-test'
|
||||
|
||||
const { promise } = waitForHumanResponse(fightId, botId, testChallenge, 4)
|
||||
|
||||
submitHumanResponse(fightId, botId, '2009')
|
||||
|
||||
const pending = getPendingChallenge(fightId, botId)
|
||||
expect(pending).toBeNull()
|
||||
|
||||
return promise
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user