import { describe, expect, test } from "bun:test" import fs from "fs/promises" import path from "path" import { GrepTool } from "../../src/tool/grep" import { Instance } from "../../src/project/instance" import { tmpdir } from "../fixture/fixture" import { SessionID, MessageID } from "../../src/session/schema" async function write(file: string, body: string) { await fs.mkdir(path.dirname(file), { recursive: true }) await fs.writeFile(file, body) } const ctx = { sessionID: SessionID.make("ses_test"), messageID: MessageID.make(""), callID: "", agent: "build", abort: AbortSignal.any([]), messages: [], metadata: () => {}, ask: async () => {}, } const projectRoot = path.join(__dirname, "../..") describe("tool.grep", () => { test("basic search", async () => { await Instance.provide({ directory: projectRoot, fn: async () => { const grep = await GrepTool.init() const result = await grep.execute( { pattern: "export", path: path.join(projectRoot, "src/tool"), include: "*.ts", }, ctx, ) expect(result.metadata.matches).toBeGreaterThan(0) expect(result.output).toContain("Found") }, }) }) test("no matches returns correct output", async () => { await using tmp = await tmpdir({ init: async (dir) => { await write(path.join(dir, "test.txt"), "hello world") }, }) await Instance.provide({ directory: tmp.path, fn: async () => { const grep = await GrepTool.init() const result = await grep.execute( { pattern: "xyznonexistentpatternxyz123", path: tmp.path, }, ctx, ) expect(result.metadata.matches).toBe(0) expect(result.output).toBe("No files found") }, }) }) test("handles CRLF line endings in output", async () => { // This test verifies the regex split handles both \n and \r\n await using tmp = await tmpdir({ init: async (dir) => { await write(path.join(dir, "test.txt"), "line1\nline2\nline3") }, }) await Instance.provide({ directory: tmp.path, fn: async () => { const grep = await GrepTool.init() const result = await grep.execute( { pattern: "line", path: tmp.path, }, ctx, ) expect(result.metadata.matches).toBeGreaterThan(0) }, }) }) test("broadens multi-word query when exact has no match", async () => { await using tmp = await tmpdir({ init: async (dir) => { await write(path.join(dir, "test.txt"), "upload completed\n") }, }) await Instance.provide({ directory: tmp.path, fn: async () => { const grep = await GrepTool.init() const result = await grep.execute( { pattern: "prepare upload", path: tmp.path, }, ctx, ) expect(result.metadata.matches).toBeGreaterThan(0) expect(result.output).toContain("Broadened query") }, }) }) test("suggests path when content has no match", async () => { await using tmp = await tmpdir({ init: async (dir) => { await write(path.join(dir, "src", "server", "auth.ts"), "export const token = 1\n") }, }) await Instance.provide({ directory: tmp.path, fn: async () => { const grep = await GrepTool.init() const result = await grep.execute( { pattern: "src/server/auth.ts", path: tmp.path, }, ctx, ) expect(result.metadata.matches).toBe(0) expect(result.output).toContain("relevant file path") }, }) }) })