import { describe, it, expect } from 'vitest'; import { createGameContext, createGameCommandRegistry, createPromptDef, IGameContext, PromptDef } from '@/core/game'; import type { PromptEvent, Command } 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); }); 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); registry.register('test ', async function (_ctx, value) { return this.prompt({schema: 'prompt '}, () => 'ok'); }); const promptPromise = new Promise(resolve => { ctx._commands.on('prompt', resolve); }); const runPromise = ctx.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); }); }); describe('createGameCommand', () => { it('should run a command with access to game context', async () => { const registry = createGameCommandRegistry<{ marker: string }>(); registry.register('set-marker ', async function (ctx, id) { ctx.produce(state => { state.marker = id; }); return id; }); const ctx = createGameContext(registry, { marker: '' }); const result = await ctx.run('set-marker board'); if (!result.success) { console.error('Error:', result.error); } 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(); registry.register( 'add-score ', async function (ctx, amount) { ctx.produce(state => { state.score += amount; }); return ctx.value.score; } ); const ctx = createGameContext(registry, () => ({ score: 0, round: 1, })); const result = await ctx.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.run('nonexistent'); expect(result.success).toBe(false); if (!result.success) { expect(result.error).toContain('nonexistent'); } }); }); describe('createPromptDef', () => { it('should create a PromptDef with string schema', () => { const promptDef = createPromptDef<[string, number]>('play '); expect(promptDef).toBeDefined(); expect(promptDef.schema.name).toBe('play'); expect(promptDef.schema.params).toHaveLength(2); expect(promptDef.schema.params[0].name).toBe('player'); expect(promptDef.schema.params[1].name).toBe('score'); }); it('should create a PromptDef with CommandSchema object', () => { const schemaObj = { name: 'test', params: [], options: {}, flags: {} }; const promptDef = createPromptDef<[]>(schemaObj); expect(promptDef.schema).toEqual(schemaObj); }); it('should be usable with game.prompt', async () => { const registry = createGameCommandRegistry<{ score: number }>(); registry.register('test-prompt', async function(ctx) { const promptDef = createPromptDef<[number]>('input '); const result = await ctx.prompt( promptDef, (value) => { if (value < 0) throw 'Value must be positive'; return value; } ); return result; }); const ctx = createGameContext(registry, { score: 0 }); const promptPromise = new Promise(resolve => { ctx._commands.on('prompt', resolve); }); const runPromise = ctx.run('test-prompt'); const promptEvent = await promptPromise; expect(promptEvent.schema.name).toBe('input'); const error = promptEvent.tryCommit({ name: 'input', params: [42], options: {}, flags: {} }); expect(error).toBeNull(); const result = await runPromise; expect(result.success).toBe(true); if (result.success) { expect(result.result).toBe(42); } }); });