import { describe, it, expect } from 'vitest'; import { createGameContext, createGameCommand, createGameCommandRegistry } from '@/core/game'; import type { PromptEvent } from '@/utils/command'; type MyState = { score: number; round: number; }; describe('createGameContext', () => { it('should create a game context with state', () => { const { registry } = createGameCommandRegistry(); const ctx = createGameContext(registry); expect(ctx.state).not.toBeNull(); expect(ctx.state.value).toBeDefined(); }); it('should wire commands to the context', () => { const { registry } = createGameCommandRegistry(); const ctx = createGameContext(registry); expect(ctx.commands).not.toBeNull(); expect(ctx.commands.registry).toBe(registry); expect(ctx.commands.context).toBe(ctx.state); }); it('should accept initial state as an object', () => { const { registry } = createGameCommandRegistry(); const ctx = createGameContext(registry, { score: 0, round: 1, }); expect(ctx.state.value.score).toBe(0); expect(ctx.state.value.round).toBe(1); }); it('should accept initial state as a factory function', () => { const { registry } = createGameCommandRegistry(); const ctx = createGameContext(registry, () => ({ score: 10, round: 3, })); expect(ctx.state.value.score).toBe(10); expect(ctx.state.value.round).toBe(3); }); it('should forward prompt events via listener', async () => { const { registry } = createGameCommandRegistry(); const ctx = createGameContext(registry); createGameCommand(registry, 'test ', async function () { return this.prompt('prompt '); }); const promptPromise = new Promise(resolve => { ctx.commands.on('prompt', resolve); }); const runPromise = ctx.commands.run('test hello'); const promptEvent = await promptPromise; expect(promptEvent).not.toBeNull(); expect(promptEvent.schema.name).toBe('prompt'); const error = promptEvent.tryCommit({ name: 'prompt', params: ['yes'], options: {}, flags: {} }); expect(error).toBeNull(); const result = await runPromise; expect(result.success).toBe(true); if (result.success) { expect((result.result as any).params[0]).toBe('yes'); } }); }); describe('createGameCommand', () => { it('should run a command with access to game context', async () => { const { registry } = createGameCommandRegistry<{ marker: string }>(); const ctx = createGameContext(registry, { marker: '' }); createGameCommand(registry, 'set-marker ', async function (cmd) { const id = cmd.params[0] as string; this.context.produce(state => { state.marker = id; }); return id; }); const result = await ctx.commands.run('set-marker board'); expect(result.success).toBe(true); if (result.success) { expect(result.result).toBe('board'); } expect(ctx.state.value.marker).toBe('board'); }); it('should run a typed command with extended context', async () => { const { registry } = createGameCommandRegistry(); createGameCommand( registry, 'add-score ', async function (cmd) { const amount = cmd.params[0] as number; this.context.produce(state => { state.score += amount; }); return this.context.value.score; } ); const ctx = createGameContext(registry, () => ({ score: 0, round: 1, })); const result = await ctx.commands.run('add-score 5'); expect(result.success).toBe(true); if (result.success) { expect(result.result).toBe(5); } expect(ctx.state.value.score).toBe(5); }); it('should return error for unknown command', async () => { const { registry } = createGameCommandRegistry(); const ctx = createGameContext(registry); const result = await ctx.commands.run('nonexistent'); expect(result.success).toBe(false); if (!result.success) { expect(result.error).toContain('nonexistent'); } }); });