Files
oh-my-pi/packages/coding-agent/test/tools/grep-directory-selector.test.ts
T
can1357 0297202989 fix(tool): bounded ranged grep native fetch budgets
- Replaced the unbounded native fetch (no total cap; no per-file cap for
  open-ended ranges) adopted from PR #4903 with finite budgets: per-file
  fetch covers bounded ranges up to endLine and open-ended ranges up to
  startLine-1 plus the kept window, clamped to the native file-size
  ceiling; the global ceiling scales by the same amplification.
- Threaded the scaled ceiling through mergeGrepResults so mixed
  native+virtual ranged searches are not re-truncated pre-filter.
- Dropped the unrelated workflow-notice.md ellipsis churn from the PR.
- Added open-ended directory selector coverage.

Fixes #4898
2026-07-09 18:27:21 +02:00

100 lines
3.5 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "bun:test";
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import { Settings } from "@oh-my-pi/pi-coding-agent/config/settings";
import type { ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
import { GrepTool } from "@oh-my-pi/pi-coding-agent/tools/grep";
import { removeWithRetries } from "@oh-my-pi/pi-utils";
function resultText(result: { content: Array<{ type: string; text?: string }> }): string {
return result.content
.filter(entry => entry.type === "text")
.map(entry => entry.text ?? "")
.join("\n");
}
describe("grep explicit line selector on directory searches", () => {
let testDir: string;
beforeEach(async () => {
testDir = await fs.mkdtemp(path.join(os.tmpdir(), "grep-directory-selector-"));
});
afterEach(async () => {
await removeWithRetries(testDir);
});
function createSession(): ToolSession {
return {
cwd: testDir,
hasUI: false,
getSessionFile: () => null,
getSessionSpawns: () => "*",
settings: Settings.isolated({ "grep.contextBefore": 0, "grep.contextAfter": 0 }),
};
}
it("filters matches by per-file line number instead of rejecting the directory", async () => {
const appDir = path.join(testDir, "scripts", "app");
await fs.mkdir(appDir, { recursive: true });
await Bun.write(path.join(appDir, "one.ts"), "outside one\ninside one\noutside one again\n");
await Bun.write(path.join(appDir, "two.ts"), "outside two\ninside two\noutside two again\n");
const result = await new GrepTool(createSession()).execute("grep-directory-selector", {
pattern: "inside|outside",
path: "scripts/app",
selector: "2-2",
});
const text = resultText(result);
expect(text).toContain("inside one");
expect(text).toContain("inside two");
expect(text).not.toContain("outside one");
expect(text).not.toContain("outside two");
expect(text).not.toContain("Line-range selector requires a single file");
});
it("fetches enough directory matches before applying an explicit later line selector", async () => {
const appDir = path.join(testDir, "scripts", "hot");
await fs.mkdir(appDir, { recursive: true });
const content = `${Array.from(
{ length: 30 },
(_, index) => `cap-needle line ${String(index + 1).padStart(2, "0")}`,
).join("\n")}\n`;
await Bun.write(path.join(appDir, "many.ts"), content);
const result = await new GrepTool(createSession()).execute("grep-directory-selector-cap", {
pattern: "cap-needle",
path: "scripts/hot",
selector: "25-25",
});
const text = resultText(result);
expect(text).toContain("cap-needle line 25");
expect(text).not.toContain("cap-needle line 24");
expect(text).not.toContain("cap-needle line 26");
});
it("supports open-ended selectors on directories with a finite fetch budget", async () => {
const appDir = path.join(testDir, "scripts", "tail");
await fs.mkdir(appDir, { recursive: true });
const content = `${Array.from(
{ length: 60 },
(_, index) => `open-needle line ${String(index + 1).padStart(2, "0")}`,
).join("\n")}\n`;
await Bun.write(path.join(appDir, "long.ts"), content);
const result = await new GrepTool(createSession()).execute("grep-directory-selector-open", {
pattern: "open-needle",
path: "scripts/tail",
selector: "35-",
});
const text = resultText(result);
expect(text).toContain("open-needle line 35");
expect(text).not.toContain("open-needle line 34");
expect(text).not.toContain("Line-range selector requires a single file");
});
});