@xec-sh/core
Version:
Universal shell execution engine
154 lines • 5.78 kB
JavaScript
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