import { mkdtemp, rm } from 'node:fs/promises' import { tmpdir } from 'node:os' import path from 'node:path' import { afterAll, describe, expect, it } from 'vitest' import { openGameDb, type GameDb } from '@/lib/db/client' import * as schema from '@/lib/db/schema' import { getWorldState } from './save' import { DEFAULT_LIMIT, EventError, MAX_LIMIT, getEvents, parseEventLimit } from './events' const dirs: string[] = [] async function freshFile(): Promise<{ db: GameDb; file: string }> { const dir = await mkdtemp(path.join(tmpdir(), 'sw-events-test-')) dirs.push(dir) const file = path.join(dir, 'test.db') return { db: await openGameDb(file), file } } afterAll(async () => { for (const d of dirs) await rm(d, { recursive: true, force: true }) }) // Appends `count` synthetic events (turns 1..count) on top of the turn-0 new_game event. async function seedEvents(db: GameDb, count: number): Promise { const [save] = await db.db.select().from(schema.saves).limit(1) for (let i = 1; i <= count; i++) { await db.db.insert(schema.events).values({ saveId: save.id, turn: i, type: 'test', message: `event ${i}`, createdAt: new Date(Date.UTC(2026, 8, 29, 0, i)).toISOString() }) } } describe('parseEventLimit', () => { it('defaults to DEFAULT_LIMIT when the param is absent', () => { expect(parseEventLimit(null)).toBe(DEFAULT_LIMIT) }) it('accepts integers in [1, MAX_LIMIT]', () => { expect(parseEventLimit('1')).toBe(1) expect(parseEventLimit(String(MAX_LIMIT))).toBe(MAX_LIMIT) expect(parseEventLimit('25')).toBe(25) }) it('rejects non-integers and out-of-range values with a 400', () => { for (const bad of ['abc', '', '0', '-3', String(MAX_LIMIT + 1), '2.5']) { try { parseEventLimit(bad) expect.unreachable(`limit ${JSON.stringify(bad)} should have been rejected`) } catch (err) { expect(err).toBeInstanceOf(EventError) expect((err as EventError).status).toBe(400) } } }) }) describe('getEvents', () => { it('returns the latest events newest-first, sliced to limit', async () => { const { db } = await freshFile() await getWorldState(db) // auto-initializes the save (turn-0 new_game event) await seedEvents(db, 5) const all = await getEvents(db, DEFAULT_LIMIT) expect(all.map((e) => e.turn)).toEqual([5, 4, 3, 2, 1, 0]) // newest first, turn-0 line included expect(all[all.length - 1].type).toBe('new_game') const two = await getEvents(db, 2) expect(two.map((e) => [e.turn, e.message])).toEqual([[5, 'event 5'], [4, 'event 4']]) }) })