mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-03 09:50:40 -07:00
106 lines
3.7 KiB
TypeScript
106 lines
3.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, beforeEach, afterEach } from 'vitest';
|
|
import { TestRig } from './test-helper.js';
|
|
import { join, dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
describe('shell-background-tools', () => {
|
|
let rig: TestRig;
|
|
|
|
beforeEach(() => {
|
|
rig = new TestRig();
|
|
});
|
|
|
|
afterEach(async () => await rig.cleanup());
|
|
|
|
it('should run a command in the background, list it, and read its output', async () => {
|
|
// We use a fake responses file to make the test deterministic and run in CI.
|
|
rig.setup('shell-background-workflow', {
|
|
fakeResponsesPath: join(__dirname, 'shell-background.responses'),
|
|
settings: {
|
|
tools: {
|
|
core: [
|
|
'run_shell_command',
|
|
'list_background_processes',
|
|
'read_background_output',
|
|
],
|
|
},
|
|
hooksConfig: {
|
|
enabled: true,
|
|
},
|
|
hooks: {
|
|
BeforeTool: [
|
|
{
|
|
matcher: 'run_shell_command',
|
|
hooks: [
|
|
{
|
|
type: 'command',
|
|
// This hook intercepts run_shell_command.
|
|
// If is_background is true, it returns a mock result with PID 12345.
|
|
// It also creates the mock log file that read_background_output expects.
|
|
command: `node -e "
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const input = JSON.parse(fs.readFileSync(0, 'utf-8'));
|
|
const args = JSON.parse(input.tool_call.args);
|
|
|
|
if (args.is_background) {
|
|
const logDir = path.join(process.env.GEMINI_CLI_HOME, 'background-processes');
|
|
if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
|
|
fs.writeFileSync(path.join(logDir, 'background-12345.log'), 'hello-from-background\\n');
|
|
|
|
console.log(JSON.stringify({
|
|
decision: 'replace',
|
|
hookSpecificOutput: {
|
|
result: {
|
|
llmContent: 'Command moved to background (PID: 12345). Output hidden. Press Ctrl+B to view.',
|
|
data: { pid: 12345, command: args.command }
|
|
}
|
|
}
|
|
}));
|
|
} else {
|
|
console.log(JSON.stringify({ decision: 'allow' }));
|
|
}
|
|
"`,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
|
|
const run = await rig.runInteractive({ approvalMode: 'yolo' });
|
|
|
|
// 1. Start a background process
|
|
// We use a command that stays alive for a bit to ensure it shows up in lists
|
|
await run.type(
|
|
"Run 'sleep 10 && echo hello-from-background' in the background.",
|
|
);
|
|
await run.type('\r');
|
|
|
|
// Wait for the model's canned response acknowledging the start
|
|
await run.expectText('background', 30000);
|
|
|
|
// 2. List background processes
|
|
await run.type('List my background processes.');
|
|
await run.type('\r');
|
|
// Wait for the model's canned response showing the list
|
|
await run.expectText('hello-from-background', 30000);
|
|
|
|
// 3. Read the output
|
|
await run.type('Read the output of that process.');
|
|
await run.type('\r');
|
|
// Wait for the model's canned response showing the output
|
|
await run.expectText('hello-from-background', 30000);
|
|
}, 60000);
|
|
});
|