refactor(file): add ripgrep search service (#22295)
This commit is contained in:
@@ -1,22 +1,26 @@
|
||||
import { describe, expect, test } from "bun:test"
|
||||
import { describe, expect } from "bun:test"
|
||||
import path from "path"
|
||||
import { Effect, Layer, ManagedRuntime } from "effect"
|
||||
import { Effect, Layer } from "effect"
|
||||
import { GrepTool } from "../../src/tool/grep"
|
||||
import { Instance } from "../../src/project/instance"
|
||||
import { tmpdir } from "../fixture/fixture"
|
||||
import { provideInstance, provideTmpdirInstance } from "../fixture/fixture"
|
||||
import { SessionID, MessageID } from "../../src/session/schema"
|
||||
import * as CrossSpawnSpawner from "../../src/effect/cross-spawn-spawner"
|
||||
import { Truncate } from "../../src/tool/truncate"
|
||||
import { Agent } from "../../src/agent/agent"
|
||||
import { Ripgrep } from "../../src/file/ripgrep"
|
||||
import { AppFileSystem } from "../../src/filesystem"
|
||||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const runtime = ManagedRuntime.make(
|
||||
Layer.mergeAll(CrossSpawnSpawner.defaultLayer, Truncate.defaultLayer, Agent.defaultLayer),
|
||||
const it = testEffect(
|
||||
Layer.mergeAll(
|
||||
CrossSpawnSpawner.defaultLayer,
|
||||
AppFileSystem.defaultLayer,
|
||||
Ripgrep.defaultLayer,
|
||||
Truncate.defaultLayer,
|
||||
Agent.defaultLayer,
|
||||
),
|
||||
)
|
||||
|
||||
function initGrep() {
|
||||
return runtime.runPromise(GrepTool.pipe(Effect.flatMap((info) => info.init())))
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
sessionID: SessionID.make("ses_test"),
|
||||
messageID: MessageID.make(""),
|
||||
@@ -31,99 +35,59 @@ const ctx = {
|
||||
const projectRoot = path.join(__dirname, "../..")
|
||||
|
||||
describe("tool.grep", () => {
|
||||
test("basic search", async () => {
|
||||
await Instance.provide({
|
||||
directory: projectRoot,
|
||||
fn: async () => {
|
||||
const grep = await initGrep()
|
||||
const result = await Effect.runPromise(
|
||||
grep.execute(
|
||||
{
|
||||
pattern: "export",
|
||||
path: path.join(projectRoot, "src/tool"),
|
||||
include: "*.ts",
|
||||
},
|
||||
ctx,
|
||||
),
|
||||
)
|
||||
expect(result.metadata.matches).toBeGreaterThan(0)
|
||||
expect(result.output).toContain("Found")
|
||||
},
|
||||
})
|
||||
})
|
||||
it.live("basic search", () =>
|
||||
Effect.gen(function* () {
|
||||
const info = yield* GrepTool
|
||||
const grep = yield* info.init()
|
||||
const result = yield* provideInstance(projectRoot)(
|
||||
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 Bun.write(path.join(dir, "test.txt"), "hello world")
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const grep = await initGrep()
|
||||
const result = await Effect.runPromise(
|
||||
grep.execute(
|
||||
{
|
||||
pattern: "xyznonexistentpatternxyz123",
|
||||
path: tmp.path,
|
||||
},
|
||||
ctx,
|
||||
),
|
||||
it.live("no matches returns correct output", () =>
|
||||
provideTmpdirInstance((dir) =>
|
||||
Effect.gen(function* () {
|
||||
yield* Effect.promise(() => Bun.write(path.join(dir, "test.txt"), "hello world"))
|
||||
const info = yield* GrepTool
|
||||
const grep = yield* info.init()
|
||||
const result = yield* grep.execute(
|
||||
{
|
||||
pattern: "xyznonexistentpatternxyz123",
|
||||
path: dir,
|
||||
},
|
||||
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) => {
|
||||
// Create a test file with content
|
||||
await Bun.write(path.join(dir, "test.txt"), "line1\nline2\nline3")
|
||||
},
|
||||
})
|
||||
await Instance.provide({
|
||||
directory: tmp.path,
|
||||
fn: async () => {
|
||||
const grep = await initGrep()
|
||||
const result = await Effect.runPromise(
|
||||
grep.execute(
|
||||
{
|
||||
pattern: "line",
|
||||
path: tmp.path,
|
||||
},
|
||||
ctx,
|
||||
),
|
||||
it.live("finds matches in tmp instance", () =>
|
||||
provideTmpdirInstance((dir) =>
|
||||
Effect.gen(function* () {
|
||||
yield* Effect.promise(() => Bun.write(path.join(dir, "test.txt"), "line1\nline2\nline3"))
|
||||
const info = yield* GrepTool
|
||||
const grep = yield* info.init()
|
||||
const result = yield* grep.execute(
|
||||
{
|
||||
pattern: "line",
|
||||
path: dir,
|
||||
},
|
||||
ctx,
|
||||
)
|
||||
expect(result.metadata.matches).toBeGreaterThan(0)
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("CRLF regex handling", () => {
|
||||
test("regex correctly splits Unix line endings", () => {
|
||||
const unixOutput = "file1.txt|1|content1\nfile2.txt|2|content2\nfile3.txt|3|content3"
|
||||
const lines = unixOutput.trim().split(/\r?\n/)
|
||||
expect(lines.length).toBe(3)
|
||||
expect(lines[0]).toBe("file1.txt|1|content1")
|
||||
expect(lines[2]).toBe("file3.txt|3|content3")
|
||||
})
|
||||
|
||||
test("regex correctly splits Windows CRLF line endings", () => {
|
||||
const windowsOutput = "file1.txt|1|content1\r\nfile2.txt|2|content2\r\nfile3.txt|3|content3"
|
||||
const lines = windowsOutput.trim().split(/\r?\n/)
|
||||
expect(lines.length).toBe(3)
|
||||
expect(lines[0]).toBe("file1.txt|1|content1")
|
||||
expect(lines[2]).toBe("file3.txt|3|content3")
|
||||
})
|
||||
|
||||
test("regex handles mixed line endings", () => {
|
||||
const mixedOutput = "file1.txt|1|content1\nfile2.txt|2|content2\r\nfile3.txt|3|content3"
|
||||
const lines = mixedOutput.trim().split(/\r?\n/)
|
||||
expect(lines.length).toBe(3)
|
||||
})
|
||||
}),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user