mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-13 23:51:16 -07:00
feat(core): experimental in-progress steering hints (1 of 3) (#19008)
This commit is contained in:
140
packages/cli/src/test-utils/MockShellExecutionService.ts
Normal file
140
packages/cli/src/test-utils/MockShellExecutionService.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { vi } from 'vitest';
|
||||
import type {
|
||||
ShellExecutionHandle,
|
||||
ShellExecutionResult,
|
||||
ShellOutputEvent,
|
||||
ShellExecutionConfig,
|
||||
} from '@google/gemini-cli-core';
|
||||
|
||||
export interface MockShellCommand {
|
||||
command: string | RegExp;
|
||||
result: Partial<ShellExecutionResult>;
|
||||
events?: ShellOutputEvent[];
|
||||
}
|
||||
|
||||
type ShellExecutionServiceExecute = (
|
||||
commandToExecute: string,
|
||||
cwd: string,
|
||||
onOutputEvent: (event: ShellOutputEvent) => void,
|
||||
abortSignal: AbortSignal,
|
||||
shouldUseNodePty: boolean,
|
||||
shellExecutionConfig: ShellExecutionConfig,
|
||||
) => Promise<ShellExecutionHandle>;
|
||||
|
||||
export class MockShellExecutionService {
|
||||
private static mockCommands: MockShellCommand[] = [];
|
||||
private static originalExecute: ShellExecutionServiceExecute | undefined;
|
||||
private static passthroughEnabled = false;
|
||||
|
||||
/**
|
||||
* Registers the original implementation to allow falling back to real shell execution.
|
||||
*/
|
||||
static setOriginalImplementation(
|
||||
implementation: ShellExecutionServiceExecute,
|
||||
) {
|
||||
this.originalExecute = implementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables or disables passthrough to the real implementation when no mock matches.
|
||||
*/
|
||||
static setPassthrough(enabled: boolean) {
|
||||
this.passthroughEnabled = enabled;
|
||||
}
|
||||
|
||||
static setMockCommands(commands: MockShellCommand[]) {
|
||||
this.mockCommands = commands;
|
||||
}
|
||||
|
||||
static reset() {
|
||||
this.mockCommands = [];
|
||||
this.passthroughEnabled = false;
|
||||
this.writeToPty.mockClear();
|
||||
this.kill.mockClear();
|
||||
this.background.mockClear();
|
||||
this.resizePty.mockClear();
|
||||
this.scrollPty.mockClear();
|
||||
}
|
||||
|
||||
static async execute(
|
||||
commandToExecute: string,
|
||||
cwd: string,
|
||||
onOutputEvent: (event: ShellOutputEvent) => void,
|
||||
abortSignal: AbortSignal,
|
||||
shouldUseNodePty: boolean,
|
||||
shellExecutionConfig: ShellExecutionConfig,
|
||||
): Promise<ShellExecutionHandle> {
|
||||
const mock = this.mockCommands.find((m) =>
|
||||
typeof m.command === 'string'
|
||||
? m.command === commandToExecute
|
||||
: m.command.test(commandToExecute),
|
||||
);
|
||||
|
||||
const pid = Math.floor(Math.random() * 10000);
|
||||
|
||||
if (mock) {
|
||||
if (mock.events) {
|
||||
for (const event of mock.events) {
|
||||
onOutputEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
const result: ShellExecutionResult = {
|
||||
rawOutput: Buffer.from(mock.result.output || ''),
|
||||
output: mock.result.output || '',
|
||||
exitCode: mock.result.exitCode ?? 0,
|
||||
signal: mock.result.signal ?? null,
|
||||
error: mock.result.error ?? null,
|
||||
aborted: false,
|
||||
pid,
|
||||
executionMethod: 'none',
|
||||
...mock.result,
|
||||
};
|
||||
|
||||
return {
|
||||
pid,
|
||||
result: Promise.resolve(result),
|
||||
};
|
||||
}
|
||||
|
||||
if (this.passthroughEnabled && this.originalExecute) {
|
||||
return this.originalExecute(
|
||||
commandToExecute,
|
||||
cwd,
|
||||
onOutputEvent,
|
||||
abortSignal,
|
||||
shouldUseNodePty,
|
||||
shellExecutionConfig,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
pid,
|
||||
result: Promise.resolve({
|
||||
rawOutput: Buffer.from(''),
|
||||
output: `Command not found: ${commandToExecute}`,
|
||||
exitCode: 127,
|
||||
signal: null,
|
||||
error: null,
|
||||
aborted: false,
|
||||
pid,
|
||||
executionMethod: 'none',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
static writeToPty = vi.fn();
|
||||
static isPtyActive = vi.fn(() => false);
|
||||
static onExit = vi.fn(() => () => {});
|
||||
static kill = vi.fn();
|
||||
static background = vi.fn();
|
||||
static subscribe = vi.fn(() => () => {});
|
||||
static resizePty = vi.fn();
|
||||
static scrollPty = vi.fn();
|
||||
}
|
||||
Reference in New Issue
Block a user