test: add queue test suite with 8 cases (cooldown, join, leave, snapshot)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-12 23:42:11 +00:00
co-authored by Claude Opus 4.6
parent 21f9650585
commit 6144fa7910
+140
View File
@@ -0,0 +1,140 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
// Mock orchestrator
vi.mock('./orchestrator.js', () => ({
runFightAsync: vi.fn().mockResolvedValue('fight-123'),
isInFight: vi.fn().mockReturnValue(false),
getActiveFightId: vi.fn().mockReturnValue(null),
}))
// Mock mock.ts
vi.mock('./mock.js', () => ({
seedMockBots: vi.fn().mockResolvedValue(undefined),
}))
// Mock db
const mockBotRows = vi.fn()
vi.mock('../db/index.js', () => ({
db: {
select: () => ({
from: () => ({
where: () => ({
limit: () => mockBotRows(),
}),
// For matchAgainstMock's full select (no where clause on this chain)
}),
}),
},
schema: {
bots: {
id: 'id',
name: 'name',
secretHash: 'secretHash',
webhookUrl: 'webhookUrl',
eloRating: 'eloRating',
isActive: 'isActive',
},
},
}))
const { joinQueue, leaveQueue, getQueueSize, getQueueSnapshot, setCooldown } = await import('./queue.js')
const { isInFight } = await import('./orchestrator.js')
const mockBot = (id: string, name = 'TestBot', elo = 1200) => [{
id,
name,
webhookUrl: 'https://example.com/webhook',
eloRating: elo,
isActive: true,
secretHash: 'abc',
ownerPubkey: 'pub123',
wins: 0,
losses: 0,
draws: 0,
winStreak: 0,
createdAt: new Date().toISOString(),
}]
describe('queue', () => {
beforeEach(() => {
vi.clearAllMocks()
// Drain queue
while (getQueueSize() > 0) {
const snap = getQueueSnapshot()
if (snap[0]) leaveQueue(snap[0].botId)
}
})
it('getQueueSize returns 0 when empty', () => {
expect(getQueueSize()).toBe(0)
})
it('setCooldown prevents joining during cooldown', async () => {
mockBotRows.mockResolvedValue(mockBot('bot-cool'))
setCooldown('bot-cool')
await expect(joinQueue('bot-cool')).rejects.toThrow('Cooldown active')
})
it('prevents bot already in fight from joining', async () => {
mockBotRows.mockResolvedValue(mockBot('bot-fighting'))
vi.mocked(isInFight).mockReturnValueOnce(true)
await expect(joinQueue('bot-fighting')).rejects.toThrow('already in a fight')
})
it('throws for non-existent bot', async () => {
mockBotRows.mockResolvedValue([])
await expect(joinQueue('nonexistent')).rejects.toThrow('Bot not found')
})
it('throws for deactivated bot', async () => {
const bot = mockBot('bot-inactive')
bot[0].isActive = false
mockBotRows.mockResolvedValue(bot)
await expect(joinQueue('bot-inactive')).rejects.toThrow('deactivated')
})
it('leaveQueue returns false for unknown bot', () => {
expect(leaveQueue('not-in-queue')).toBe(false)
})
it('getQueueSnapshot returns queued bot info', async () => {
mockBotRows.mockResolvedValue(mockBot('bot-snap', 'SnapBot', 1400))
// joinQueue will wait in queue since no opponent — don't await
const promise = joinQueue('bot-snap')
// Flush microtasks so the async DB query resolves and bot enters queue
await new Promise(r => setTimeout(r, 0))
const snap = getQueueSnapshot()
expect(snap.length).toBe(1)
expect(snap[0].botId).toBe('bot-snap')
expect(snap[0].botName).toBe('SnapBot')
expect(snap[0].eloRating).toBe(1400)
// Cleanup: leave queue
leaveQueue('bot-snap')
await promise.catch(() => {}) // swallow 'Left queue' rejection
})
it('leaveQueue removes bot and returns true', async () => {
mockBotRows.mockResolvedValue(mockBot('bot-leave'))
const promise = joinQueue('bot-leave')
// Flush microtasks so the async DB query resolves and bot enters queue
await new Promise(r => setTimeout(r, 0))
expect(getQueueSize()).toBe(1)
const left = leaveQueue('bot-leave')
expect(left).toBe(true)
expect(getQueueSize()).toBe(0)
await promise.catch(() => {}) // swallow rejection
})
})