| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228 |
- /// <reference types="bun-types" />
- import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
- import { mkdir, rm, writeFile } from 'node:fs/promises';
- import { tmpdir } from 'node:os';
- import { join } from 'node:path';
- import { runRg, runRgCount } from './cli';
- import { grep } from './tools';
- import { formatGrepResult } from './utils';
- describe('grep tool', () => {
- const testDir = join(tmpdir(), `grep-test-${Date.now()}`);
- const testFile1 = join(testDir, 'test1.txt');
- const testFile2 = join(testDir, 'test2.ts');
- beforeAll(async () => {
- await mkdir(testDir, { recursive: true });
- await writeFile(
- testFile1,
- 'Hello world\nThis is a test file\nAnother line with match',
- );
- await writeFile(
- testFile2,
- "const x = 'Hello world';\nconsole.log('test');",
- );
- });
- afterAll(async () => {
- await rm(testDir, { recursive: true, force: true });
- });
- describe('formatGrepResult', () => {
- test('formats empty results', () => {
- const result = {
- matches: [],
- totalMatches: 0,
- filesSearched: 10,
- truncated: false,
- };
- expect(formatGrepResult(result)).toBe('No matches found.');
- });
- test('formats error results', () => {
- const result = {
- matches: [],
- totalMatches: 0,
- filesSearched: 0,
- truncated: false,
- error: 'Something went wrong',
- };
- expect(formatGrepResult(result)).toBe('Error: Something went wrong');
- });
- test('formats matches correctly', () => {
- const result = {
- matches: [
- { file: 'file1.ts', line: 10, text: "const foo = 'bar'" },
- { file: 'file1.ts', line: 15, text: 'console.log(foo)' },
- { file: 'file2.ts', line: 5, text: "import { foo } from './file1'" },
- ],
- totalMatches: 3,
- filesSearched: 2,
- truncated: false,
- };
- const output = formatGrepResult(result);
- expect(output).toContain('file1.ts:');
- expect(output).toContain(" 10: const foo = 'bar'");
- expect(output).toContain(' 15: console.log(foo)');
- expect(output).toContain('file2.ts:');
- expect(output).toContain(" 5: import { foo } from './file1'");
- expect(output).toContain('Found 3 matches in 2 files');
- });
- test('indicates truncation', () => {
- const result = {
- matches: [{ file: 'foo.txt', line: 1, text: 'bar' }],
- totalMatches: 100,
- filesSearched: 50,
- truncated: true,
- };
- expect(formatGrepResult(result)).toContain('(output truncated)');
- });
- });
- describe('runRg', () => {
- test('finds matches in files', async () => {
- const result = await runRg({
- pattern: 'Hello',
- paths: [testDir],
- });
- expect(result.totalMatches).toBeGreaterThanOrEqual(2);
- expect(
- result.matches.some(
- (m) => m.file.includes('test1.txt') && m.text.includes('Hello'),
- ),
- ).toBe(true);
- expect(
- result.matches.some(
- (m) => m.file.includes('test2.ts') && m.text.includes('Hello'),
- ),
- ).toBe(true);
- });
- test('respects file inclusion patterns', async () => {
- const result = await runRg({
- pattern: 'Hello',
- paths: [testDir],
- globs: ['*.txt'],
- });
- expect(result.matches.some((m) => m.file.includes('test1.txt'))).toBe(
- true,
- );
- expect(result.matches.some((m) => m.file.includes('test2.ts'))).toBe(
- false,
- );
- });
- test('handles no matches', async () => {
- const result = await runRg({
- pattern: 'NonExistentString12345',
- paths: [testDir],
- });
- expect(result.totalMatches).toBe(0);
- expect(result.matches).toHaveLength(0);
- });
- test('respects case sensitivity', async () => {
- // Test with exact case match
- const resultExact = await runRg({
- pattern: 'Hello',
- paths: [testDir],
- });
- expect(resultExact.totalMatches).toBeGreaterThan(0);
- // Test with caseSensitive flag set to true (should not match lowercase pattern)
- const resultSensitive = await runRg({
- pattern: 'hello', // File has "Hello"
- paths: [testDir],
- caseSensitive: true,
- });
- expect(resultSensitive.totalMatches).toBe(0);
- });
- test('respects whole word match', async () => {
- const resultPartial = await runRg({
- pattern: 'Hell',
- paths: [testDir],
- wholeWord: false,
- });
- expect(resultPartial.totalMatches).toBeGreaterThan(0);
- const resultWhole = await runRg({
- pattern: 'Hell',
- paths: [testDir],
- wholeWord: true,
- });
- expect(resultWhole.totalMatches).toBe(0);
- });
- test('respects max count', async () => {
- const result = await runRg({
- pattern: 'Hello',
- paths: [testDir],
- maxCount: 1,
- });
- // maxCount is per file
- expect(
- result.matches.filter((m) => m.file.includes('test1.txt')).length,
- ).toBeLessThanOrEqual(1);
- });
- });
- describe('runRgCount', () => {
- test('counts matches correctly', async () => {
- const results = await runRgCount({
- pattern: 'Hello',
- paths: [testDir],
- });
- expect(results.length).toBeGreaterThan(0);
- const file1Result = results.find((r) => r.file.includes('test1.txt'));
- expect(file1Result).toBeDefined();
- expect(file1Result?.count).toBe(1);
- });
- });
- describe('grep tool execute', () => {
- test('executes successfully', async () => {
- // @ts-expect-error
- const result = await grep.execute({
- pattern: 'Hello',
- path: testDir,
- });
- expect(typeof result).toBe('string');
- expect(result).toContain('Found');
- expect(result).toContain('matches');
- });
- test('handles errors gracefully', async () => {
- // @ts-expect-error
- const result = await grep.execute({
- pattern: 'Hello',
- path: '/non/existent/path/12345',
- });
- // Depending on implementation, it might return "No matches found" or an error string
- // But it should not throw
- expect(typeof result).toBe('string');
- });
- test('respects include pattern in execute', async () => {
- // @ts-expect-error
- const result = await grep.execute({
- pattern: 'Hello',
- path: testDir,
- include: '*.txt',
- });
- expect(result).toContain('test1.txt');
- expect(result).not.toContain('test2.ts');
- });
- });
- });
|