mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Sync upstream Gemini-CLI v0.8.2 (#838)
This commit is contained in:
@@ -56,7 +56,9 @@ describe('ShellTool', () => {
|
||||
getDebugMode: vi.fn().mockReturnValue(false),
|
||||
getTargetDir: vi.fn().mockReturnValue('/test/dir'),
|
||||
getSummarizeToolOutputConfig: vi.fn().mockReturnValue(undefined),
|
||||
getWorkspaceContext: () => createMockWorkspaceContext('.'),
|
||||
getWorkspaceContext: vi
|
||||
.fn()
|
||||
.mockReturnValue(createMockWorkspaceContext('/test/dir')),
|
||||
getGeminiClient: vi.fn(),
|
||||
getGitCoAuthor: vi.fn().mockReturnValue({
|
||||
enabled: true,
|
||||
@@ -115,17 +117,41 @@ describe('ShellTool', () => {
|
||||
).toThrow('Command cannot be empty.');
|
||||
});
|
||||
|
||||
it('should throw an error for a non-existent directory', () => {
|
||||
vi.mocked(fs.existsSync).mockReturnValue(false);
|
||||
it('should throw an error for a relative directory path', () => {
|
||||
expect(() =>
|
||||
shellTool.build({
|
||||
command: 'ls',
|
||||
directory: 'rel/path',
|
||||
is_background: false,
|
||||
}),
|
||||
).toThrow(
|
||||
"Directory 'rel/path' is not a registered workspace directory.",
|
||||
).toThrow('Directory must be an absolute path.');
|
||||
});
|
||||
|
||||
it('should throw an error for a directory outside the workspace', () => {
|
||||
(mockConfig.getWorkspaceContext as Mock).mockReturnValue(
|
||||
createMockWorkspaceContext('/test/dir', ['/another/workspace']),
|
||||
);
|
||||
expect(() =>
|
||||
shellTool.build({
|
||||
command: 'ls',
|
||||
directory: '/not/in/workspace',
|
||||
is_background: false,
|
||||
}),
|
||||
).toThrow(
|
||||
"Directory '/not/in/workspace' is not within any of the registered workspace directories.",
|
||||
);
|
||||
});
|
||||
|
||||
it('should return an invocation for a valid absolute directory path', () => {
|
||||
(mockConfig.getWorkspaceContext as Mock).mockReturnValue(
|
||||
createMockWorkspaceContext('/test/dir', ['/another/workspace']),
|
||||
);
|
||||
const invocation = shellTool.build({
|
||||
command: 'ls',
|
||||
directory: '/test/dir/subdir',
|
||||
is_background: false,
|
||||
});
|
||||
expect(invocation).toBeDefined();
|
||||
});
|
||||
|
||||
it('should include background indicator in description when is_background is true', () => {
|
||||
@@ -182,12 +208,11 @@ describe('ShellTool', () => {
|
||||
const wrappedCommand = `{ my-command & }; __code=$?; pgrep -g 0 >${tmpFile} 2>&1; exit $__code;`;
|
||||
expect(mockShellExecutionService).toHaveBeenCalledWith(
|
||||
wrappedCommand,
|
||||
expect.any(String),
|
||||
'/test/dir',
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
expect(result.llmContent).toContain('Background PIDs: 54322');
|
||||
expect(vi.mocked(fs.unlinkSync)).toHaveBeenCalledWith(tmpFile);
|
||||
@@ -214,8 +239,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -240,8 +264,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -266,8 +289,32 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
it('should use the provided directory as cwd', async () => {
|
||||
(mockConfig.getWorkspaceContext as Mock).mockReturnValue(
|
||||
createMockWorkspaceContext('/test/dir'),
|
||||
);
|
||||
const invocation = shellTool.build({
|
||||
command: 'ls',
|
||||
directory: '/test/dir/subdir',
|
||||
is_background: false,
|
||||
});
|
||||
const promise = invocation.execute(mockAbortSignal);
|
||||
resolveShellExecution();
|
||||
await promise;
|
||||
|
||||
const tmpFile = path.join(os.tmpdir(), 'shell_pgrep_abcdef.tmp');
|
||||
const wrappedCommand = `{ ls; }; __code=$?; pgrep -g 0 >${tmpFile} 2>&1; exit $__code;`;
|
||||
expect(mockShellExecutionService).toHaveBeenCalledWith(
|
||||
wrappedCommand,
|
||||
'/test/dir/subdir',
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -291,12 +338,11 @@ describe('ShellTool', () => {
|
||||
await promise;
|
||||
expect(mockShellExecutionService).toHaveBeenCalledWith(
|
||||
'dir',
|
||||
expect.any(String),
|
||||
'/test/dir',
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -349,16 +395,13 @@ describe('ShellTool', () => {
|
||||
});
|
||||
|
||||
it('should throw an error for invalid directory', () => {
|
||||
vi.mocked(fs.existsSync).mockReturnValue(false);
|
||||
expect(() =>
|
||||
shellTool.build({
|
||||
command: 'ls',
|
||||
directory: 'nonexistent',
|
||||
is_background: false,
|
||||
}),
|
||||
).toThrow(
|
||||
`Directory 'nonexistent' is not a registered workspace directory.`,
|
||||
);
|
||||
).toThrow('Directory must be an absolute path.');
|
||||
});
|
||||
|
||||
it('should summarize output when configured', async () => {
|
||||
@@ -424,46 +467,6 @@ describe('ShellTool', () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('should throttle text output updates', async () => {
|
||||
const invocation = shellTool.build({
|
||||
command: 'stream',
|
||||
is_background: false,
|
||||
});
|
||||
const promise = invocation.execute(mockAbortSignal, updateOutputMock);
|
||||
|
||||
// First chunk, should be throttled.
|
||||
mockShellOutputCallback({
|
||||
type: 'data',
|
||||
chunk: 'hello ',
|
||||
});
|
||||
expect(updateOutputMock).not.toHaveBeenCalled();
|
||||
|
||||
// Advance time past the throttle interval.
|
||||
await vi.advanceTimersByTimeAsync(OUTPUT_UPDATE_INTERVAL_MS + 1);
|
||||
|
||||
// Send a second chunk. THIS event triggers the update with the CUMULATIVE content.
|
||||
mockShellOutputCallback({
|
||||
type: 'data',
|
||||
chunk: 'hello world',
|
||||
});
|
||||
|
||||
// It should have been called once now with the combined output.
|
||||
expect(updateOutputMock).toHaveBeenCalledOnce();
|
||||
expect(updateOutputMock).toHaveBeenCalledWith('hello world');
|
||||
|
||||
resolveExecutionPromise({
|
||||
rawOutput: Buffer.from(''),
|
||||
output: '',
|
||||
exitCode: 0,
|
||||
signal: null,
|
||||
error: null,
|
||||
aborted: false,
|
||||
pid: 12345,
|
||||
executionMethod: 'child_process',
|
||||
});
|
||||
await promise;
|
||||
});
|
||||
|
||||
it('should immediately show binary detection message and throttle progress', async () => {
|
||||
const invocation = shellTool.build({
|
||||
command: 'cat img',
|
||||
@@ -541,8 +544,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -572,8 +574,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -603,8 +604,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -633,8 +633,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -663,8 +662,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -694,8 +692,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -731,8 +728,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -769,8 +765,7 @@ describe('ShellTool', () => {
|
||||
expect.any(Function),
|
||||
mockAbortSignal,
|
||||
false,
|
||||
undefined,
|
||||
undefined,
|
||||
{},
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -837,76 +832,3 @@ describe('ShellTool', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateToolParams', () => {
|
||||
it('should return null for valid directory', () => {
|
||||
const config = {
|
||||
getCoreTools: () => undefined,
|
||||
getExcludeTools: () => undefined,
|
||||
getTargetDir: () => '/root',
|
||||
getWorkspaceContext: () =>
|
||||
createMockWorkspaceContext('/root', ['/users/test']),
|
||||
} as unknown as Config;
|
||||
const shellTool = new ShellTool(config);
|
||||
const result = shellTool.validateToolParams({
|
||||
command: 'ls',
|
||||
directory: 'test',
|
||||
is_background: false,
|
||||
});
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should return error for directory outside workspace', () => {
|
||||
const config = {
|
||||
getCoreTools: () => undefined,
|
||||
getExcludeTools: () => undefined,
|
||||
getTargetDir: () => '/root',
|
||||
getWorkspaceContext: () =>
|
||||
createMockWorkspaceContext('/root', ['/users/test']),
|
||||
} as unknown as Config;
|
||||
const shellTool = new ShellTool(config);
|
||||
const result = shellTool.validateToolParams({
|
||||
command: 'ls',
|
||||
directory: 'test2',
|
||||
is_background: false,
|
||||
});
|
||||
expect(result).toContain('is not a registered workspace directory');
|
||||
});
|
||||
});
|
||||
|
||||
describe('build', () => {
|
||||
it('should return an invocation for valid directory', () => {
|
||||
const config = {
|
||||
getCoreTools: () => undefined,
|
||||
getExcludeTools: () => undefined,
|
||||
getTargetDir: () => '/root',
|
||||
getWorkspaceContext: () =>
|
||||
createMockWorkspaceContext('/root', ['/users/test']),
|
||||
} as unknown as Config;
|
||||
const shellTool = new ShellTool(config);
|
||||
const invocation = shellTool.build({
|
||||
command: 'ls',
|
||||
directory: 'test',
|
||||
is_background: false,
|
||||
});
|
||||
expect(invocation).toBeDefined();
|
||||
});
|
||||
|
||||
it('should throw an error for directory outside workspace', () => {
|
||||
const config = {
|
||||
getCoreTools: () => undefined,
|
||||
getExcludeTools: () => undefined,
|
||||
getTargetDir: () => '/root',
|
||||
getWorkspaceContext: () =>
|
||||
createMockWorkspaceContext('/root', ['/users/test']),
|
||||
} as unknown as Config;
|
||||
const shellTool = new ShellTool(config);
|
||||
expect(() =>
|
||||
shellTool.build({
|
||||
command: 'ls',
|
||||
directory: 'test2',
|
||||
is_background: false,
|
||||
}),
|
||||
).toThrow('is not a registered workspace directory');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user