import { describe, it, expect, beforeEach } from 'vitest'; import { CommandLog, createCommandLog } from '../../src/commands/CommandLog'; import { Command, CommandActionType, CommandExecutionResult, StepResult } from '../../src/commands/Command'; describe('CommandLog', () => { let log: CommandLog; beforeEach(() => { log = createCommandLog(); }); const sampleCommand: Command = { id: 'test-command', name: 'Test Command', steps: [ { action: CommandActionType.CreateMeeple, params: { id: 'm1', color: 'red' } }, ], }; const sampleResult: CommandExecutionResult = { success: true, executedSteps: 1, totalSteps: 1, }; const sampleStepResults: StepResult[] = [ { stepIndex: 0, action: CommandActionType.CreateMeeple, success: true, params: { id: 'm1', color: 'red' }, }, ]; describe('log', () => { it('should log a command execution', () => { log.log(sampleCommand, sampleResult, sampleStepResults); const entries = log.getEntries(); expect(entries.length).toBe(1); expect(entries[0].commandId).toBe('test-command'); expect(entries[0].commandName).toBe('Test Command'); expect(entries[0].result.success).toBe(true); }); it('should add timestamp to log entry', () => { const beforeTime = Date.now(); log.log(sampleCommand, sampleResult, sampleStepResults); const afterTime = Date.now(); const entry = log.getEntries()[0]; expect(entry.timestamp).toBeGreaterThanOrEqual(beforeTime); expect(entry.timestamp).toBeLessThanOrEqual(afterTime); }); it('should log multiple entries', () => { log.log(sampleCommand, sampleResult, sampleStepResults); log.log(sampleCommand, sampleResult, sampleStepResults); log.log(sampleCommand, sampleResult, sampleStepResults); expect(log.getEntries().length).toBe(3); }); }); describe('getFilteredEntries', () => { it('should filter by commandId', () => { const command1: Command = { ...sampleCommand, id: 'cmd-1', name: 'Command 1' }; const command2: Command = { ...sampleCommand, id: 'cmd-2', name: 'Command 2' }; log.log(command1, sampleResult, sampleStepResults); log.log(command2, sampleResult, sampleStepResults); log.log(command1, sampleResult, sampleStepResults); const filtered = log.getFilteredEntries({ commandId: 'cmd-1' }); expect(filtered.length).toBe(2); }); it('should filter by success status', () => { const successResult: CommandExecutionResult = { success: true, executedSteps: 1, totalSteps: 1 }; const failResult: CommandExecutionResult = { success: false, executedSteps: 0, totalSteps: 1, error: 'Failed' }; log.log(sampleCommand, successResult, sampleStepResults); log.log(sampleCommand, failResult, []); log.log(sampleCommand, successResult, sampleStepResults); const failed = log.getFilteredEntries({ success: false }); expect(failed.length).toBe(1); const successful = log.getFilteredEntries({ success: true }); expect(successful.length).toBe(2); }); it('should filter by time range', () => { const startTime = Date.now(); log.log(sampleCommand, sampleResult, sampleStepResults); // 模拟时间流逝 const midTime = Date.now() + 100; // 手动创建一个带时间戳的条目来测试时间过滤 const entries = log.getEntries(); expect(entries[0].timestamp).toBeGreaterThanOrEqual(startTime); }); }); describe('getCommandHistory', () => { it('should return history for a specific command', () => { const command1: Command = { ...sampleCommand, id: 'cmd-1', name: 'Command 1' }; const command2: Command = { ...sampleCommand, id: 'cmd-2', name: 'Command 2' }; log.log(command1, sampleResult, sampleStepResults); log.log(command2, sampleResult, sampleStepResults); log.log(command1, sampleResult, sampleStepResults); const history = log.getCommandHistory('cmd-1'); expect(history.length).toBe(2); }); }); describe('getFailedCommands', () => { it('should return only failed commands', () => { const successResult: CommandExecutionResult = { success: true, executedSteps: 1, totalSteps: 1 }; const failResult: CommandExecutionResult = { success: false, executedSteps: 0, totalSteps: 1, error: 'Error' }; log.log(sampleCommand, successResult, sampleStepResults); log.log(sampleCommand, failResult, []); log.log(sampleCommand, successResult, sampleStepResults); const failed = log.getFailedCommands(); expect(failed.length).toBe(1); expect(failed[0].result.success).toBe(false); }); }); describe('getSuccessfulCommands', () => { it('should return only successful commands', () => { const successResult: CommandExecutionResult = { success: true, executedSteps: 1, totalSteps: 1 }; const failResult: CommandExecutionResult = { success: false, executedSteps: 0, totalSteps: 1, error: 'Error' }; log.log(sampleCommand, successResult, sampleStepResults); log.log(sampleCommand, failResult, []); log.log(sampleCommand, successResult, sampleStepResults); const successful = log.getSuccessfulCommands(); expect(successful.length).toBe(2); }); }); describe('clear', () => { it('should clear all log entries', () => { log.log(sampleCommand, sampleResult, sampleStepResults); log.log(sampleCommand, sampleResult, sampleStepResults); expect(log.getEntries().length).toBe(2); log.clear(); expect(log.getEntries().length).toBe(0); }); }); describe('exportToJson', () => { it('should export logs as JSON string', () => { log.log(sampleCommand, sampleResult, sampleStepResults); const json = log.exportToJson(); const parsed = JSON.parse(json); expect(Array.isArray(parsed)).toBe(true); expect(parsed.length).toBe(1); expect(parsed[0].commandId).toBe('test-command'); }); }); describe('getCount', () => { it('should return the number of log entries', () => { expect(log.getCount()).toBe(0); log.log(sampleCommand, sampleResult, sampleStepResults); expect(log.getCount()).toBe(1); log.log(sampleCommand, sampleResult, sampleStepResults); expect(log.getCount()).toBe(2); }); }); describe('getLastEntry', () => { it('should return the last log entry', () => { const command1: Command = { ...sampleCommand, id: 'cmd-1', name: 'First' }; const command2: Command = { ...sampleCommand, id: 'cmd-2', name: 'Last' }; log.log(command1, sampleResult, sampleStepResults); log.log(command2, sampleResult, sampleStepResults); const lastEntry = log.getLastEntry(); expect(lastEntry).not.toBeNull(); expect(lastEntry?.commandId).toBe('cmd-2'); }); it('should return null when log is empty', () => { const lastEntry = log.getLastEntry(); expect(lastEntry).toBeNull(); }); }); describe('Queue management', () => { describe('enqueue', () => { it('should add command to queue', () => { const queued = log.enqueue(sampleCommand); expect(queued.id).toBe('test-command'); expect(log.getQueueLength()).toBe(1); }); it('should set initial status to Pending', () => { const queued = log.enqueue(sampleCommand); expect(queued.status).toBe('pending'); }); }); describe('dequeue', () => { it('should remove and return the first command from queue', () => { const command1: Command = { ...sampleCommand, id: 'cmd-1' }; const command2: Command = { ...sampleCommand, id: 'cmd-2' }; log.enqueue(command1); log.enqueue(command2); const dequeued = log.dequeue(); expect(dequeued?.command.id).toBe('cmd-1'); expect(log.getQueueLength()).toBe(1); }); it('should return null when queue is empty', () => { const dequeued = log.dequeue(); expect(dequeued).toBeNull(); }); }); describe('updateQueueStatus', () => { it('should update command status in queue', () => { log.enqueue(sampleCommand); log.updateQueueStatus('test-command', 'executing'); const queue = log.getQueue(); expect(queue[0].status).toBe('executing'); }); it('should set executedAt and result when status is Completed', () => { const result: CommandExecutionResult = { success: true, executedSteps: 1, totalSteps: 1 }; log.enqueue(sampleCommand); const beforeTime = Date.now(); log.updateQueueStatus('test-command', 'completed', result); const afterTime = Date.now(); const queue = log.getQueue(); expect(queue[0].status).toBe('completed'); expect(queue[0].result).toEqual(result); expect(queue[0].executedAt).toBeGreaterThanOrEqual(beforeTime); expect(queue[0].executedAt).toBeLessThanOrEqual(afterTime); }); }); describe('clearQueue', () => { it('should clear all queued commands', () => { log.enqueue(sampleCommand); log.enqueue(sampleCommand); expect(log.getQueueLength()).toBe(2); log.clearQueue(); expect(log.getQueueLength()).toBe(0); }); }); }); });