mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-22 11:04:42 -07:00
Use consistent param names (#12517)
This commit is contained in:
committed by
GitHub
parent
5f1208ad81
commit
f05d937f39
@@ -17,19 +17,12 @@ import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
||||
import { StandardFileSystemService } from '../services/fileSystemService.js';
|
||||
import { createMockWorkspaceContext } from '../test-utils/mockWorkspaceContext.js';
|
||||
import type { ToolInvocation, ToolResult } from './tools.js';
|
||||
import { WorkspaceContext } from '../utils/workspaceContext.js';
|
||||
|
||||
vi.mock('../telemetry/loggers.js', () => ({
|
||||
logFileOperation: vi.fn(),
|
||||
}));
|
||||
|
||||
interface ReadFileParameterSchema {
|
||||
properties: {
|
||||
absolute_path: {
|
||||
description: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
describe('ReadFileTool', () => {
|
||||
let tempRootDir: string;
|
||||
let tool: ReadFileTool;
|
||||
@@ -37,9 +30,8 @@ describe('ReadFileTool', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a unique temporary root directory for each test run
|
||||
tempRootDir = await fsp.mkdtemp(
|
||||
path.join(os.tmpdir(), 'read-file-tool-root-'),
|
||||
);
|
||||
const realTmp = await fsp.realpath(os.tmpdir());
|
||||
tempRootDir = await fsp.mkdtemp(path.join(realTmp, 'read-file-tool-root-'));
|
||||
|
||||
const mockConfigInstance = {
|
||||
getFileService: () => new FileDiscoveryService(tempRootDir),
|
||||
@@ -67,24 +59,30 @@ describe('ReadFileTool', () => {
|
||||
describe('build', () => {
|
||||
it('should return an invocation for valid params (absolute path within root)', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: path.join(tempRootDir, 'test.txt'),
|
||||
file_path: path.join(tempRootDir, 'test.txt'),
|
||||
};
|
||||
const result = tool.build(params);
|
||||
expect(typeof result).not.toBe('string');
|
||||
});
|
||||
|
||||
it('should throw error if file path is relative', () => {
|
||||
it('should return an invocation for valid params (relative path within root)', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: 'relative/path.txt',
|
||||
file_path: 'test.txt',
|
||||
};
|
||||
expect(() => tool.build(params)).toThrow(
|
||||
'File path must be absolute, but was relative: relative/path.txt. You must provide an absolute path.',
|
||||
const result = tool.build(params);
|
||||
expect(typeof result).not.toBe('string');
|
||||
const invocation = result as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
expect(invocation.toolLocations()[0].path).toBe(
|
||||
path.join(tempRootDir, 'test.txt'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error if path is outside root', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: '/outside/root.txt',
|
||||
file_path: '/outside/root.txt',
|
||||
};
|
||||
expect(() => tool.build(params)).toThrow(
|
||||
/File path must be within one of the workspace directories/,
|
||||
@@ -94,7 +92,7 @@ describe('ReadFileTool', () => {
|
||||
it('should allow access to files in project temp directory', () => {
|
||||
const tempDir = path.join(tempRootDir, '.temp');
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: path.join(tempDir, 'temp-file.txt'),
|
||||
file_path: path.join(tempDir, 'temp-file.txt'),
|
||||
};
|
||||
const result = tool.build(params);
|
||||
expect(typeof result).not.toBe('string');
|
||||
@@ -102,7 +100,7 @@ describe('ReadFileTool', () => {
|
||||
|
||||
it('should show temp directory in error message when path is outside workspace and temp dir', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: '/completely/outside/path.txt',
|
||||
file_path: '/completely/outside/path.txt',
|
||||
};
|
||||
expect(() => tool.build(params)).toThrow(
|
||||
/File path must be within one of the workspace directories.*or within the project temp directory/,
|
||||
@@ -111,16 +109,16 @@ describe('ReadFileTool', () => {
|
||||
|
||||
it('should throw error if path is empty', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: '',
|
||||
file_path: '',
|
||||
};
|
||||
expect(() => tool.build(params)).toThrow(
|
||||
/The 'absolute_path' parameter must be non-empty./,
|
||||
/The 'file_path' parameter must be non-empty./,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw error if offset is negative', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: path.join(tempRootDir, 'test.txt'),
|
||||
file_path: path.join(tempRootDir, 'test.txt'),
|
||||
offset: -1,
|
||||
};
|
||||
expect(() => tool.build(params)).toThrow(
|
||||
@@ -130,7 +128,7 @@ describe('ReadFileTool', () => {
|
||||
|
||||
it('should throw error if limit is zero or negative', () => {
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: path.join(tempRootDir, 'test.txt'),
|
||||
file_path: path.join(tempRootDir, 'test.txt'),
|
||||
limit: 0,
|
||||
};
|
||||
expect(() => tool.build(params)).toThrow(
|
||||
@@ -143,7 +141,7 @@ describe('ReadFileTool', () => {
|
||||
it('should return relative path without limit/offset', () => {
|
||||
const subDir = path.join(tempRootDir, 'sub', 'dir');
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: path.join(subDir, 'file.txt'),
|
||||
file_path: path.join(subDir, 'file.txt'),
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
@@ -168,7 +166,7 @@ describe('ReadFileTool', () => {
|
||||
'limit',
|
||||
'file.txt',
|
||||
);
|
||||
const params: ReadFileToolParams = { absolute_path: deepPath };
|
||||
const params: ReadFileToolParams = { file_path: deepPath };
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
const desc = (
|
||||
@@ -181,7 +179,7 @@ describe('ReadFileTool', () => {
|
||||
it('should handle non-normalized file paths correctly', () => {
|
||||
const subDir = path.join(tempRootDir, 'sub', 'dir');
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: path.join(subDir, '..', 'dir', 'file.txt'),
|
||||
file_path: path.join(subDir, '..', 'dir', 'file.txt'),
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
@@ -193,7 +191,7 @@ describe('ReadFileTool', () => {
|
||||
});
|
||||
|
||||
it('should return . if path is the root directory', () => {
|
||||
const params: ReadFileToolParams = { absolute_path: tempRootDir };
|
||||
const params: ReadFileToolParams = { file_path: tempRootDir };
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
expect(
|
||||
@@ -204,42 +202,26 @@ describe('ReadFileTool', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should use windows-style path examples on windows', () => {
|
||||
vi.spyOn(process, 'platform', 'get').mockReturnValue('win32');
|
||||
|
||||
const tool = new ReadFileTool({} as unknown as Config);
|
||||
const schema = tool.schema;
|
||||
expect(
|
||||
(schema.parametersJsonSchema as ReadFileParameterSchema).properties
|
||||
.absolute_path.description,
|
||||
).toBe(
|
||||
"The absolute path to the file to read (e.g., 'C:\\Users\\project\\file.txt'). Relative paths are not supported. You must provide an absolute path.",
|
||||
);
|
||||
});
|
||||
|
||||
it('should use unix-style path examples on non-windows platforms', () => {
|
||||
vi.spyOn(process, 'platform', 'get').mockReturnValue('linux');
|
||||
|
||||
const tool = new ReadFileTool({} as unknown as Config);
|
||||
const schema = tool.schema;
|
||||
expect(
|
||||
(schema.parametersJsonSchema as ReadFileParameterSchema).properties
|
||||
.absolute_path.description,
|
||||
).toBe(
|
||||
"The absolute path to the file to read (e.g., '/home/user/project/file.txt'). Relative paths are not supported. You must provide an absolute path.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
it('should successfully read a file with a relative path', async () => {
|
||||
const filePath = path.join(tempRootDir, 'textfile.txt');
|
||||
const fileContent = 'This is a test file.';
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { file_path: 'textfile.txt' };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
>;
|
||||
|
||||
expect(await invocation.execute(abortSignal)).toEqual({
|
||||
llmContent: fileContent,
|
||||
returnDisplay: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error if file does not exist', async () => {
|
||||
const filePath = path.join(tempRootDir, 'nonexistent.txt');
|
||||
const params: ReadFileToolParams = { absolute_path: filePath };
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -261,7 +243,7 @@ describe('ReadFileTool', () => {
|
||||
const filePath = path.join(tempRootDir, 'textfile.txt');
|
||||
const fileContent = 'This is a test file.';
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { absolute_path: filePath };
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -276,7 +258,7 @@ describe('ReadFileTool', () => {
|
||||
it('should return error if path is a directory', async () => {
|
||||
const dirPath = path.join(tempRootDir, 'directory');
|
||||
await fsp.mkdir(dirPath);
|
||||
const params: ReadFileToolParams = { absolute_path: dirPath };
|
||||
const params: ReadFileToolParams = { file_path: dirPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -299,7 +281,7 @@ describe('ReadFileTool', () => {
|
||||
// 21MB of content exceeds 20MB limit
|
||||
const largeContent = 'x'.repeat(21 * 1024 * 1024);
|
||||
await fsp.writeFile(filePath, largeContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { absolute_path: filePath };
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -318,7 +300,7 @@ describe('ReadFileTool', () => {
|
||||
const longLine = 'a'.repeat(2500); // Exceeds MAX_LINE_LENGTH_TEXT_FILE (2000)
|
||||
const fileContent = `Short line\n${longLine}\nAnother short line`;
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { absolute_path: filePath };
|
||||
const params: ReadFileToolParams = { file_path: filePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -339,7 +321,7 @@ describe('ReadFileTool', () => {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a,
|
||||
]);
|
||||
await fsp.writeFile(imagePath, pngHeader);
|
||||
const params: ReadFileToolParams = { absolute_path: imagePath };
|
||||
const params: ReadFileToolParams = { file_path: imagePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -360,7 +342,7 @@ describe('ReadFileTool', () => {
|
||||
// Minimal PDF header
|
||||
const pdfHeader = Buffer.from('%PDF-1.4');
|
||||
await fsp.writeFile(pdfPath, pdfHeader);
|
||||
const params: ReadFileToolParams = { absolute_path: pdfPath };
|
||||
const params: ReadFileToolParams = { file_path: pdfPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -381,7 +363,7 @@ describe('ReadFileTool', () => {
|
||||
// Binary data with null bytes
|
||||
const binaryData = Buffer.from([0x00, 0xff, 0x00, 0xff]);
|
||||
await fsp.writeFile(binPath, binaryData);
|
||||
const params: ReadFileToolParams = { absolute_path: binPath };
|
||||
const params: ReadFileToolParams = { file_path: binPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -398,7 +380,7 @@ describe('ReadFileTool', () => {
|
||||
const svgPath = path.join(tempRootDir, 'image.svg');
|
||||
const svgContent = '<svg><circle cx="50" cy="50" r="40"/></svg>';
|
||||
await fsp.writeFile(svgPath, svgContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { absolute_path: svgPath };
|
||||
const params: ReadFileToolParams = { file_path: svgPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -414,7 +396,7 @@ describe('ReadFileTool', () => {
|
||||
// Create SVG content larger than 1MB
|
||||
const largeContent = '<svg>' + 'x'.repeat(1024 * 1024 + 1) + '</svg>';
|
||||
await fsp.writeFile(svgPath, largeContent, 'utf-8');
|
||||
const params: ReadFileToolParams = { absolute_path: svgPath };
|
||||
const params: ReadFileToolParams = { file_path: svgPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -432,7 +414,7 @@ describe('ReadFileTool', () => {
|
||||
it('should handle empty file', async () => {
|
||||
const emptyPath = path.join(tempRootDir, 'empty.txt');
|
||||
await fsp.writeFile(emptyPath, '', 'utf-8');
|
||||
const params: ReadFileToolParams = { absolute_path: emptyPath };
|
||||
const params: ReadFileToolParams = { file_path: emptyPath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -450,7 +432,7 @@ describe('ReadFileTool', () => {
|
||||
await fsp.writeFile(filePath, fileContent, 'utf-8');
|
||||
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: filePath,
|
||||
file_path: filePath,
|
||||
offset: 5, // Start from line 6
|
||||
limit: 3,
|
||||
};
|
||||
@@ -481,7 +463,7 @@ describe('ReadFileTool', () => {
|
||||
const tempFileContent = 'This is temporary output content';
|
||||
await fsp.writeFile(tempFilePath, tempFileContent, 'utf-8');
|
||||
|
||||
const params: ReadFileToolParams = { absolute_path: tempFilePath };
|
||||
const params: ReadFileToolParams = { file_path: tempFilePath };
|
||||
const invocation = tool.build(params) as ToolInvocation<
|
||||
ReadFileToolParams,
|
||||
ToolResult
|
||||
@@ -498,13 +480,27 @@ describe('ReadFileTool', () => {
|
||||
path.join(tempRootDir, '.geminiignore'),
|
||||
['foo.*', 'ignored/'].join('\n'),
|
||||
);
|
||||
const mockConfigInstance = {
|
||||
getFileService: () => new FileDiscoveryService(tempRootDir),
|
||||
getFileSystemService: () => new StandardFileSystemService(),
|
||||
getTargetDir: () => tempRootDir,
|
||||
getWorkspaceContext: () => new WorkspaceContext(tempRootDir),
|
||||
getFileFilteringOptions: () => ({
|
||||
respectGitIgnore: true,
|
||||
respectGeminiIgnore: true,
|
||||
}),
|
||||
storage: {
|
||||
getProjectTempDir: () => path.join(tempRootDir, '.temp'),
|
||||
},
|
||||
} as unknown as Config;
|
||||
tool = new ReadFileTool(mockConfigInstance);
|
||||
});
|
||||
|
||||
it('should throw error if path is ignored by a .geminiignore pattern', async () => {
|
||||
const ignoredFilePath = path.join(tempRootDir, 'foo.bar');
|
||||
await fsp.writeFile(ignoredFilePath, 'content', 'utf-8');
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: ignoredFilePath,
|
||||
file_path: ignoredFilePath,
|
||||
};
|
||||
const expectedError = `File path '${ignoredFilePath}' is ignored by configured ignore patterns.`;
|
||||
expect(() => tool.build(params)).toThrow(expectedError);
|
||||
@@ -516,7 +512,7 @@ describe('ReadFileTool', () => {
|
||||
const ignoredFilePath = path.join(ignoredDirPath, 'file.txt');
|
||||
await fsp.writeFile(ignoredFilePath, 'content', 'utf-8');
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: ignoredFilePath,
|
||||
file_path: ignoredFilePath,
|
||||
};
|
||||
const expectedError = `File path '${ignoredFilePath}' is ignored by configured ignore patterns.`;
|
||||
expect(() => tool.build(params)).toThrow(expectedError);
|
||||
@@ -526,7 +522,7 @@ describe('ReadFileTool', () => {
|
||||
const allowedFilePath = path.join(tempRootDir, 'allowed.txt');
|
||||
await fsp.writeFile(allowedFilePath, 'content', 'utf-8');
|
||||
const params: ReadFileToolParams = {
|
||||
absolute_path: allowedFilePath,
|
||||
file_path: allowedFilePath,
|
||||
};
|
||||
const invocation = tool.build(params);
|
||||
expect(typeof invocation).not.toBe('string');
|
||||
|
||||
Reference in New Issue
Block a user