139 lines
3.7 KiB
TypeScript
139 lines
3.7 KiB
TypeScript
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")
|
|
},
|
|
})
|
|
})
|
|
})
|