From b785bd4da0027b64bebdd1af1d6d149649061d15 Mon Sep 17 00:00:00 2001 From: Christian Gunderman Date: Thu, 5 Feb 2026 12:45:32 -0800 Subject: [PATCH] Consolidate tests. --- .../grep-total-max-matches.test.ts | 92 ------- integration-tests/ripgrep-max-matches.test.ts | 108 -------- integration-tests/ripgrep-real.test.ts | 107 -------- .../ripgrep-total-max-matches.test.ts | 100 -------- integration-tests/search-tools.test.ts | 235 ++++++++++++++++++ 5 files changed, 235 insertions(+), 407 deletions(-) delete mode 100644 integration-tests/grep-total-max-matches.test.ts delete mode 100644 integration-tests/ripgrep-max-matches.test.ts delete mode 100644 integration-tests/ripgrep-real.test.ts delete mode 100644 integration-tests/ripgrep-total-max-matches.test.ts create mode 100644 integration-tests/search-tools.test.ts diff --git a/integration-tests/grep-total-max-matches.test.ts b/integration-tests/grep-total-max-matches.test.ts deleted file mode 100644 index 445717b2fa..0000000000 --- a/integration-tests/grep-total-max-matches.test.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import * as path from 'node:path'; -import * as fs from 'node:fs/promises'; -import * as os from 'node:os'; -import { GrepTool } from '../packages/core/src/tools/grep.js'; -import { Config } from '../packages/core/src/config/config.js'; -import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js'; - -// Mock Config to provide necessary context -class MockConfig { - constructor(private targetDir: string) {} - - getTargetDir() { - return this.targetDir; - } - - getWorkspaceContext() { - return new WorkspaceContext(this.targetDir, [this.targetDir]); - } - - getDebugMode() { - return true; - } - - getFileFilteringOptions() { - return { - respectGitIgnore: true, - respectGeminiIgnore: true, - customIgnoreFilePaths: [], - }; - } - - getFileExclusions() { - return { - getGlobExcludes: () => [], - }; - } - - validatePathAccess() { - return null; - } -} - -describe('grep-total-max-matches', () => { - let tempDir: string; - let tool: GrepTool; - - beforeAll(async () => { - tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'grep-total-max-test-')); - - // Create a test file with multiple matches - const content = ` - match 1 - match 2 - match 3 - match 4 - match 5 - `; - await fs.writeFile(path.join(tempDir, 'many_matches.txt'), content); - - const config = new MockConfig(tempDir) as unknown as Config; - tool = new GrepTool(config, null!); - }); - - afterAll(async () => { - await fs.rm(tempDir, { recursive: true, force: true }); - }); - - it('should limit total matches when total_max_matches is set', async () => { - const invocation = tool.build({ - pattern: 'match', - total_max_matches: 3, - }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 3 matches'); - expect(result.llmContent).toContain('match 1'); - expect(result.llmContent).toContain('match 2'); - expect(result.llmContent).toContain('match 3'); - expect(result.llmContent).not.toContain('match 4'); - expect(result.llmContent).not.toContain('match 5'); - expect(result.llmContent).toContain( - '(results limited to 3 matches for performance)', - ); - }); -}); diff --git a/integration-tests/ripgrep-max-matches.test.ts b/integration-tests/ripgrep-max-matches.test.ts deleted file mode 100644 index d25eaa0a08..0000000000 --- a/integration-tests/ripgrep-max-matches.test.ts +++ /dev/null @@ -1,108 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import * as path from 'node:path'; -import * as fs from 'node:fs/promises'; -import * as os from 'node:os'; -import { RipGrepTool } from '../packages/core/src/tools/ripGrep.js'; -import { Config } from '../packages/core/src/config/config.js'; -import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js'; - -// Mock Config to provide necessary context -class MockConfig { - constructor(private targetDir: string) {} - - getTargetDir() { - return this.targetDir; - } - - getWorkspaceContext() { - return new WorkspaceContext(this.targetDir, [this.targetDir]); - } - - getDebugMode() { - return true; - } - - getFileFilteringRespectGeminiIgnore() { - return true; - } - - getFileFilteringOptions() { - return { - respectGitIgnore: true, - respectGeminiIgnore: true, - customIgnoreFilePaths: [], - }; - } - - validatePathAccess() { - return null; - } -} - -describe('ripgrep-max-matches', () => { - let tempDir: string; - let tool: RipGrepTool; - - beforeAll(async () => { - tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-max-test-')); - - // Create a test file with multiple matches - const content = ` - match 1 - filler - match 2 - filler - match 3 - filler - match 4 - `; - await fs.writeFile(path.join(tempDir, 'many_matches.txt'), content); - - const config = new MockConfig(tempDir) as unknown as Config; - tool = new RipGrepTool(config); - }); - - afterAll(async () => { - await fs.rm(tempDir, { recursive: true, force: true }); - }); - - it('should limit matches per file when max_matches_per_file is set', async () => { - const invocation = tool.build({ - pattern: 'match', - max_matches_per_file: 2, - }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 2 matches'); - expect(result.llmContent).toContain('many_matches.txt'); - expect(result.llmContent).toContain('match 1'); - expect(result.llmContent).toContain('match 2'); - expect(result.llmContent).not.toContain('match 3'); - expect(result.llmContent).not.toContain('match 4'); - }); - - it('should return all matches when max_matches_per_file is not set', async () => { - const invocation = tool.build({ pattern: 'match' }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 4 matches'); - expect(result.llmContent).toContain('match 1'); - expect(result.llmContent).toContain('match 2'); - expect(result.llmContent).toContain('match 3'); - expect(result.llmContent).toContain('match 4'); - }); - - it('should return context lines when requested', async () => { - const invocation = tool.build({ pattern: 'match 1', context: 1 }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('match 1'); - expect(result.llmContent).toContain('filler'); // Context line - }); -}); diff --git a/integration-tests/ripgrep-real.test.ts b/integration-tests/ripgrep-real.test.ts deleted file mode 100644 index fee76aedc1..0000000000 --- a/integration-tests/ripgrep-real.test.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @license - * Copyright 2025 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import * as path from 'node:path'; -import * as fs from 'node:fs/promises'; -import * as os from 'node:os'; -import { RipGrepTool } from '../packages/core/src/tools/ripGrep.js'; -import { Config } from '../packages/core/src/config/config.js'; -import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js'; - -// Mock Config to provide necessary context -class MockConfig { - constructor(private targetDir: string) {} - - getTargetDir() { - return this.targetDir; - } - - getWorkspaceContext() { - return new WorkspaceContext(this.targetDir, [this.targetDir]); - } - - getDebugMode() { - return true; - } - - getFileFilteringRespectGeminiIgnore() { - return true; - } - - getFileFilteringOptions() { - return { - respectGitIgnore: true, - respectGeminiIgnore: true, - customIgnoreFilePaths: [], - }; - } - - validatePathAccess() { - return null; - } -} - -describe('ripgrep-real-direct', () => { - let tempDir: string; - let tool: RipGrepTool; - - beforeAll(async () => { - tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-real-test-')); - - // Create test files - await fs.writeFile(path.join(tempDir, 'file1.txt'), 'hello world\n'); - await fs.mkdir(path.join(tempDir, 'subdir')); - await fs.writeFile( - path.join(tempDir, 'subdir', 'file2.txt'), - 'hello universe\n', - ); - await fs.writeFile(path.join(tempDir, 'file3.txt'), 'goodbye moon\n'); - - const config = new MockConfig(tempDir) as unknown as Config; - tool = new RipGrepTool(config); - }); - - afterAll(async () => { - await fs.rm(tempDir, { recursive: true, force: true }); - }); - - it('should find matches using the real ripgrep binary', async () => { - const invocation = tool.build({ pattern: 'hello' }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 2 matches'); - expect(result.llmContent).toContain('file1.txt'); - expect(result.llmContent).toContain('L1: hello world'); - expect(result.llmContent).toContain('subdir'); // Should show path - expect(result.llmContent).toContain('file2.txt'); - expect(result.llmContent).toContain('L1: hello universe'); - - expect(result.llmContent).not.toContain('goodbye moon'); - }); - - it('should handle no matches correctly', async () => { - const invocation = tool.build({ pattern: 'nonexistent_pattern_123' }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('No matches found'); - }); - - it('should respect include filters', async () => { - // Create a .js file - await fs.writeFile( - path.join(tempDir, 'script.js'), - 'console.log("hello");\n', - ); - - const invocation = tool.build({ pattern: 'hello', include: '*.js' }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 1 match'); - expect(result.llmContent).toContain('script.js'); - expect(result.llmContent).not.toContain('file1.txt'); - }); -}); diff --git a/integration-tests/ripgrep-total-max-matches.test.ts b/integration-tests/ripgrep-total-max-matches.test.ts deleted file mode 100644 index 0c87bd1b29..0000000000 --- a/integration-tests/ripgrep-total-max-matches.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @license - * Copyright 2026 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, it, expect, beforeAll, afterAll } from 'vitest'; -import * as path from 'node:path'; -import * as fs from 'node:fs/promises'; -import * as os from 'node:os'; -import { RipGrepTool } from '../packages/core/src/tools/ripGrep.js'; -import { Config } from '../packages/core/src/config/config.js'; -import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js'; - -// Mock Config to provide necessary context -class MockConfig { - constructor(private targetDir: string) {} - - getTargetDir() { - return this.targetDir; - } - - getWorkspaceContext() { - return new WorkspaceContext(this.targetDir, [this.targetDir]); - } - - getDebugMode() { - return true; - } - - getFileFilteringRespectGeminiIgnore() { - return true; - } - - getFileFilteringOptions() { - return { - respectGitIgnore: true, - respectGeminiIgnore: true, - customIgnoreFilePaths: [], - }; - } - - validatePathAccess() { - return null; - } -} - -describe('ripgrep-total-max-matches', () => { - let tempDir: string; - let tool: RipGrepTool; - - beforeAll(async () => { - tempDir = await fs.mkdtemp( - path.join(os.tmpdir(), 'ripgrep-total-max-test-'), - ); - - // Create a test file with multiple matches - const content = ` - match 1 - match 2 - match 3 - match 4 - match 5 - `; - await fs.writeFile(path.join(tempDir, 'many_matches.txt'), content); - - const config = new MockConfig(tempDir) as unknown as Config; - tool = new RipGrepTool(config); - }); - - afterAll(async () => { - await fs.rm(tempDir, { recursive: true, force: true }); - }); - - it('should limit total matches when total_max_matches is set', async () => { - const invocation = tool.build({ - pattern: 'match', - total_max_matches: 3, - }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 3 matches'); - expect(result.llmContent).toContain('match 1'); - expect(result.llmContent).toContain('match 2'); - expect(result.llmContent).toContain('match 3'); - expect(result.llmContent).not.toContain('match 4'); - expect(result.llmContent).not.toContain('match 5'); - expect(result.llmContent).toContain( - '(results limited to 3 matches for performance)', - ); - }); - - it('should use default limit when total_max_matches is not set', async () => { - // We can't easily test the default 100 without 100 matches, but we can verify it doesn't fail. - const invocation = tool.build({ pattern: 'match' }); - const result = await invocation.execute(new AbortController().signal); - - expect(result.llmContent).toContain('Found 5 matches'); - }); -}); diff --git a/integration-tests/search-tools.test.ts b/integration-tests/search-tools.test.ts new file mode 100644 index 0000000000..fbd712e7e2 --- /dev/null +++ b/integration-tests/search-tools.test.ts @@ -0,0 +1,235 @@ +/** + * @license + * Copyright 2026 Google LLC + * SPDX-License-Identifier: Apache-2.0 + */ + +import { describe, it, expect, beforeAll, afterAll } from 'vitest'; +import * as path from 'node:path'; +import * as fs from 'node:fs/promises'; +import * as os from 'node:os'; +import { GrepTool } from '../packages/core/src/tools/grep.js'; +import { RipGrepTool } from '../packages/core/src/tools/ripGrep.js'; +import { Config } from '../packages/core/src/config/config.js'; +import { WorkspaceContext } from '../packages/core/src/utils/workspaceContext.js'; + +// Mock Config to provide necessary context for both GrepTool and RipGrepTool +class MockConfig { + constructor(private targetDir: string) {} + + getTargetDir() { + return this.targetDir; + } + + getWorkspaceContext() { + return new WorkspaceContext(this.targetDir, [this.targetDir]); + } + + getDebugMode() { + return true; + } + + getFileFilteringRespectGeminiIgnore() { + return true; + } + + getFileFilteringOptions() { + return { + respectGitIgnore: true, + respectGeminiIgnore: true, + customIgnoreFilePaths: [], + }; + } + + getFileExclusions() { + return { + getGlobExcludes: () => [], + }; + } + + validatePathAccess() { + return null; + } +} + +describe('Search Tools Integration', () => { + describe('RipGrepTool', () => { + let tempDir: string; + let tool: RipGrepTool; + + beforeAll(async () => { + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'ripgrep-test-')); + + // Create test files + await fs.writeFile(path.join(tempDir, 'file1.txt'), 'hello world\n'); + await fs.mkdir(path.join(tempDir, 'subdir')); + await fs.writeFile( + path.join(tempDir, 'subdir', 'file2.txt'), + 'hello universe\n', + ); + await fs.writeFile(path.join(tempDir, 'file3.txt'), 'goodbye moon\n'); + await fs.writeFile( + path.join(tempDir, 'script.js'), + 'console.log("hello");\n', + ); + + // Create a file with multiple matches for limits testing + const manyMatches = ` + match 1 + filler + match 2 + filler + match 3 + filler + match 4 + match 5 + `; + await fs.writeFile(path.join(tempDir, 'many_matches.txt'), manyMatches); + + const config = new MockConfig(tempDir) as unknown as Config; + tool = new RipGrepTool(config); + }); + + afterAll(async () => { + await fs.rm(tempDir, { recursive: true, force: true }); + }); + + describe('Basic Functionality', () => { + it('should find matches using the real ripgrep binary', async () => { + const invocation = tool.build({ pattern: 'hello' }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('Found 3 matches'); // file1, file2, script.js + expect(result.llmContent).toContain('file1.txt'); + expect(result.llmContent).toContain('L1: hello world'); + expect(result.llmContent).toContain('subdir'); + expect(result.llmContent).toContain('file2.txt'); + expect(result.llmContent).toContain('L1: hello universe'); + expect(result.llmContent).not.toContain('goodbye moon'); + }); + + it('should handle no matches correctly', async () => { + const invocation = tool.build({ pattern: 'nonexistent_pattern_123' }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('No matches found'); + }); + + it('should respect include filters', async () => { + const invocation = tool.build({ pattern: 'hello', include: '*.js' }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('Found 1 match'); + expect(result.llmContent).toContain('script.js'); + expect(result.llmContent).not.toContain('file1.txt'); + }); + + it('should return context lines when requested', async () => { + const invocation = tool.build({ pattern: 'match 1', context: 1 }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('match 1'); + expect(result.llmContent).toContain('filler'); // Context line + }); + }); + + describe('Limits', () => { + it('should limit matches per file when max_matches_per_file is set', async () => { + const invocation = tool.build({ + pattern: 'match', + max_matches_per_file: 2, + }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('Found 2 matches'); + expect(result.llmContent).toContain('many_matches.txt'); + expect(result.llmContent).toContain('match 1'); + expect(result.llmContent).toContain('match 2'); + expect(result.llmContent).not.toContain('match 3'); + expect(result.llmContent).not.toContain('match 4'); + }); + + it('should return all matches when max_matches_per_file is not set', async () => { + // We test this on a file that has more matches than the previous limit + const invocation = tool.build({ pattern: 'match' }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('match 1'); + expect(result.llmContent).toContain('match 2'); + expect(result.llmContent).toContain('match 3'); + expect(result.llmContent).toContain('match 4'); + expect(result.llmContent).toContain('match 5'); + }); + + it('should limit total matches when total_max_matches is set', async () => { + const invocation = tool.build({ + pattern: 'match', + total_max_matches: 3, + }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('Found 3 matches'); + expect(result.llmContent).toContain('match 1'); + expect(result.llmContent).toContain('match 2'); + expect(result.llmContent).toContain('match 3'); + expect(result.llmContent).not.toContain('match 4'); + expect(result.llmContent).not.toContain('match 5'); + expect(result.llmContent).toContain( + '(results limited to 3 matches for performance)', + ); + }); + + it('should use default limit when total_max_matches is not set', async () => { + const invocation = tool.build({ pattern: 'match' }); + const result = await invocation.execute(new AbortController().signal); + + // Just verify it found everything available (5 matches) + expect(result.llmContent).toContain('Found 5 matches'); + }); + }); + }); + + describe('GrepTool', () => { + let tempDir: string; + let tool: GrepTool; + + beforeAll(async () => { + tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'grep-test-')); + + // Create a test file with multiple matches + const content = ` + match 1 + match 2 + match 3 + match 4 + match 5 + `; + await fs.writeFile(path.join(tempDir, 'many_matches.txt'), content); + + const config = new MockConfig(tempDir) as unknown as Config; + tool = new GrepTool(config, null!); + }); + + afterAll(async () => { + await fs.rm(tempDir, { recursive: true, force: true }); + }); + + it('should limit total matches when total_max_matches is set', async () => { + const invocation = tool.build({ + pattern: 'match', + total_max_matches: 3, + }); + const result = await invocation.execute(new AbortController().signal); + + expect(result.llmContent).toContain('Found 3 matches'); + expect(result.llmContent).toContain('match 1'); + expect(result.llmContent).toContain('match 2'); + expect(result.llmContent).toContain('match 3'); + expect(result.llmContent).not.toContain('match 4'); + expect(result.llmContent).not.toContain('match 5'); + expect(result.llmContent).toContain( + '(results limited to 3 matches for performance)', + ); + }); + }); +});