Files
gemini-cli/integration-tests/background_shell_output.test.ts
2026-03-10 11:15:18 -07:00

88 lines
2.7 KiB
TypeScript

/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestRig } from './test-helper.js';
import fs from 'node:fs';
import path from 'node:path';
describe('Background Shell Output Logging', () => {
let rig: TestRig;
beforeEach(() => {
rig = new TestRig();
});
afterEach(async () => await rig.cleanup());
it('should log background process output to a file', async () => {
await rig.setup('should log background process output to a file', {
settings: { tools: { core: ['run_shell_command'] } },
});
// We use a command that outputs something, then backgrounds, then outputs more.
// Since we're in the test rig, we have to be careful with how we background.
// The run_shell_command tool backgrounds if is_background: true is passed.
const prompt = `Please run the command "echo start; sleep 1; echo end" in the background and tell me the PID.`;
const result = await rig.run({
args: [prompt],
// approvalMode: 'yolo' is needed to avoid interactive prompt in tests
approvalMode: 'yolo',
});
// Extract PID from result
const cleanResult = result.replace(
// eslint-disable-next-line no-control-regex
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
'',
);
const pidMatch = cleanResult.match(/PID.*?\s*(\d+)/i);
expect(pidMatch, `Expected PID in output: ${cleanResult}`).toBeTruthy();
const pid = parseInt(pidMatch![1], 10);
const logDir = path.join(
rig.homeDir!,
'.gemini',
'tmp',
'background-processes',
);
const logFilePath = path.join(logDir, `background-${pid}.log`);
// Wait for the process to finish and log output
// We'll poll the log file
let logContent = '';
const maxRetries = 40;
let retries = 0;
while (retries < maxRetries) {
if (fs.existsSync(logFilePath)) {
logContent = fs.readFileSync(logFilePath, 'utf8');
if (logContent.includes('end')) {
break;
}
}
await new Promise((resolve) => setTimeout(resolve, 500));
retries++;
}
expect(logContent).toContain('start');
expect(logContent).toContain('end');
// Verify no ANSI escape codes are present (starting with \x1b[ or \u001b[)
const ansiRegex =
// eslint-disable-next-line no-control-regex
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
expect(logContent).not.toMatch(ansiRegex);
// Cleanup the log file after test
if (fs.existsSync(logFilePath)) {
fs.unlinkSync(logFilePath);
}
});
});