UNPKG

@xec-sh/core

Version:

Universal shell execution engine

154 lines 5.78 kB
import { BaseAdapter } from './base-adapter.js'; import { AdapterError, CommandError, TimeoutError } from '../core/error.js'; export class MockAdapter extends BaseAdapter { constructor(config = {}) { super(config); this.adapterName = 'mock'; this.responses = new Map(); this.regexResponses = []; this.defaultResponse = { stdout: '', stderr: '', exitCode: 0 }; this.executedCommands = []; this.name = this.adapterName; this.mockConfig = { recordCommands: config.recordCommands ?? true, defaultDelay: config.defaultDelay ?? 10, ...config }; } async isAvailable() { return true; } async execute(command) { const mergedCommand = this.mergeCommand(command); let commandString; if (mergedCommand.shell) { const shellCmd = typeof mergedCommand.shell === 'string' ? mergedCommand.shell : 'sh'; commandString = `${shellCmd} -c "${this.buildCommandString(mergedCommand)}"`; } else { commandString = this.buildCommandString(mergedCommand); } const startTime = Date.now(); if (this.mockConfig.recordCommands) { this.executedCommands.push(commandString); } try { const mockResponse = this.findMockResponse(commandString); const delay = mockResponse.delay ?? this.mockConfig.defaultDelay ?? 10; if (delay > 0) { await this.delay(delay); } if (command.signal?.aborted) { throw new AdapterError(this.adapterName, 'execute', new Error('Operation aborted')); } if (mockResponse.error) { throw mockResponse.error; } const endTime = Date.now(); return this.createResult(mockResponse.stdout ?? '', mockResponse.stderr ?? '', mockResponse.exitCode ?? 0, mockResponse.signal, commandString, startTime, endTime, { originalCommand: mergedCommand }); } catch (error) { if (error instanceof CommandError || error instanceof TimeoutError || error instanceof AdapterError) { throw error; } throw new AdapterError(this.adapterName, 'execute', error instanceof Error ? error : new Error(String(error))); } } mockCommand(command, response) { if (typeof command === 'string') { this.responses.set(command, response); } else { this.regexResponses.push({ pattern: command, response }); } } mockDefault(response) { this.defaultResponse = response; } clearMocks() { this.responses.clear(); this.regexResponses = []; this.executedCommands = []; this.defaultResponse = { stdout: '', stderr: '', exitCode: 0 }; } getExecutedCommands() { return [...this.executedCommands]; } wasCommandExecuted(command) { if (typeof command === 'string') { return this.executedCommands.includes(command); } else { return this.executedCommands.some(cmd => command.test(cmd)); } } getCommandExecutionCount(command) { if (typeof command === 'string') { return this.executedCommands.filter(cmd => cmd === command).length; } else { return this.executedCommands.filter(cmd => command.test(cmd)).length; } } findMockResponse(command) { const exactMatch = this.responses.get(command); if (exactMatch) { return exactMatch; } for (const { pattern, response } of this.regexResponses) { if (pattern.test(command)) { return response; } } return this.defaultResponse; } async delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } mockSuccess(command, stdout = '') { this.mockCommand(command, { stdout, stderr: '', exitCode: 0 }); } mockFailure(command, stderr = 'Command failed', exitCode = 1) { this.mockCommand(command, { stdout: '', stderr, exitCode }); } mockTimeout(command, delay = 5000) { this.mockCommand(command, { delay, error: new TimeoutError('Command timed out', delay) }); } assertCommandExecuted(command) { if (!this.wasCommandExecuted(command)) { throw new Error(`Expected command "${command}" to be executed, but it was not`); } } assertCommandNotExecuted(command) { if (this.wasCommandExecuted(command)) { throw new Error(`Expected command "${command}" not to be executed, but it was`); } } assertCommandExecutedTimes(command, times) { const count = this.getCommandExecutionCount(command); if (count !== times) { throw new Error(`Expected command "${command}" to be executed ${times} times, but it was executed ${count} times`); } } assertCommandsExecutedInOrder(commands) { let lastIndex = -1; for (const command of commands) { const index = this.executedCommands.findIndex((cmd, i) => { if (i <= lastIndex) return false; return typeof command === 'string' ? cmd === command : command.test(cmd); }); if (index === -1) { throw new Error(`Expected command "${command}" to be executed in order, but it was not found after index ${lastIndex}`); } lastIndex = index; } } async dispose() { this.clearMocks(); } } //# sourceMappingURL=mock-adapter.js.map