mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-17 00:31:44 -07:00
105 lines
3.4 KiB
TypeScript
105 lines
3.4 KiB
TypeScript
|
|
/**
|
||
|
|
* @license
|
||
|
|
* Copyright 2026 Google LLC
|
||
|
|
* SPDX-License-Identifier: Apache-2.0
|
||
|
|
*/
|
||
|
|
|
||
|
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||
|
|
import { ShellExecutionService } from '../services/shellExecutionService.js';
|
||
|
|
import {
|
||
|
|
ListBackgroundProcessesTool,
|
||
|
|
ReadBackgroundOutputTool,
|
||
|
|
} from './shellBackgroundTools.js';
|
||
|
|
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
|
||
|
|
import { NoopSandboxManager } from '../services/sandboxManager.js';
|
||
|
|
import type { AgentLoopContext } from '../config/agent-loop-context.js';
|
||
|
|
|
||
|
|
// Integration test simulating model interaction cycle
|
||
|
|
describe('Background Tools Integration', () => {
|
||
|
|
const bus = createMockMessageBus();
|
||
|
|
let listTool: ListBackgroundProcessesTool;
|
||
|
|
let readTool: ReadBackgroundOutputTool;
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
vi.clearAllMocks();
|
||
|
|
const mockContext = {
|
||
|
|
config: { getSessionId: () => 'default' },
|
||
|
|
} as unknown as AgentLoopContext;
|
||
|
|
listTool = new ListBackgroundProcessesTool(mockContext, bus);
|
||
|
|
readTool = new ReadBackgroundOutputTool(mockContext, bus);
|
||
|
|
|
||
|
|
// Clear history to avoid state leakage from previous runs
|
||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
|
|
(ShellExecutionService as any).backgroundProcessHistory.clear();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should support interaction cycle: start background -> list -> read logs', async () => {
|
||
|
|
const controller = new AbortController();
|
||
|
|
|
||
|
|
// 1. Start a backgroundable process
|
||
|
|
// We use node to print continuous logs until killed
|
||
|
|
const commandString = `${process.execPath} -e "setInterval(() => console.log('Log line'), 50)"`;
|
||
|
|
|
||
|
|
const realHandle = await ShellExecutionService.execute(
|
||
|
|
commandString,
|
||
|
|
'/',
|
||
|
|
() => {},
|
||
|
|
controller.signal,
|
||
|
|
true,
|
||
|
|
{
|
||
|
|
originalCommand: 'node continuous_log',
|
||
|
|
sessionId: 'default',
|
||
|
|
sanitizationConfig: {
|
||
|
|
allowedEnvironmentVariables: [],
|
||
|
|
blockedEnvironmentVariables: [],
|
||
|
|
enableEnvironmentVariableRedaction: false,
|
||
|
|
},
|
||
|
|
sandboxManager: new NoopSandboxManager(),
|
||
|
|
},
|
||
|
|
);
|
||
|
|
|
||
|
|
const pid = realHandle.pid;
|
||
|
|
if (pid === undefined) {
|
||
|
|
throw new Error('pid is undefined');
|
||
|
|
}
|
||
|
|
expect(pid).toBeGreaterThan(0);
|
||
|
|
|
||
|
|
// 2. Simulate model triggering background operations
|
||
|
|
ShellExecutionService.background(pid, 'default', 'node continuous_log');
|
||
|
|
|
||
|
|
// 3. Model decides to inspect list
|
||
|
|
const listInvocation = listTool.build({});
|
||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
|
|
(listInvocation as any).context = {
|
||
|
|
config: { getSessionId: () => 'default' },
|
||
|
|
};
|
||
|
|
const listResult = await listInvocation.execute(
|
||
|
|
new AbortController().signal,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(listResult.llmContent).toContain(
|
||
|
|
`[PID ${pid}] RUNNING: \`node continuous_log\``,
|
||
|
|
);
|
||
|
|
|
||
|
|
// 4. Give it time to write output to interval
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
||
|
|
|
||
|
|
// 5. Model decides to read logs
|
||
|
|
const readInvocation = readTool.build({ pid, lines: 2 });
|
||
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||
|
|
(readInvocation as any).context = {
|
||
|
|
config: { getSessionId: () => 'default' },
|
||
|
|
};
|
||
|
|
const readResult = await readInvocation.execute(
|
||
|
|
new AbortController().signal,
|
||
|
|
);
|
||
|
|
|
||
|
|
expect(readResult.llmContent).toContain('Showing last');
|
||
|
|
expect(readResult.llmContent).toContain('Log line');
|
||
|
|
|
||
|
|
// Cleanup
|
||
|
|
await ShellExecutionService.kill(pid);
|
||
|
|
controller.abort();
|
||
|
|
});
|
||
|
|
});
|