Files
oh-my-pi/packages/coding-agent/test/tools/grouped-file-output.test.ts
T
can1357 9d457f73d9 test: migrated test imports to package subpath exports
- Replaced relative `../src` imports with `@oh-my-pi/pi-ai` and `@oh-my-pi/pi-agent-core` subpaths.
2026-06-08 19:03:55 +02:00

123 lines
4.4 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { formatFindGroupedOutput } from "@oh-my-pi/pi-coding-agent/tools/find";
import {
classifyGroupedLines,
formatGroupedFiles,
groupLineIndicesByBlank,
} from "@oh-my-pi/pi-coding-agent/tools/grouped-file-output";
describe("formatFindGroupedOutput", () => {
it("folds a shared absolute prefix into one heading and nests the rest", () => {
const output = formatFindGroupedOutput([
"/Users/me/proj/shared/wasm/llvm.hpp",
"/Users/me/proj/shared/wasm/vm.hpp",
"/Users/me/proj/shared/xstd.hpp",
"/Users/me/proj/shared/apollo/details/hash.hpp",
"/Users/me/proj/flash/main.cpp",
]);
expect(output).toBe(
[
"# /Users/me/proj/",
"## shared/",
"xstd.hpp",
"### wasm/",
"llvm.hpp",
"vm.hpp",
"### apollo/details/",
"hash.hpp",
"## flash/",
"main.cpp",
].join("\n"),
);
});
it("lists a directory's own files before its subdirectories", () => {
const output = formatFindGroupedOutput(["pkg/sub/deep.txt", "pkg/top.txt"]);
// `top.txt` is a direct child of pkg; `sub/` is a subdirectory. Files first.
expect(output).toBe(["# pkg/", "top.txt", "## sub/", "deep.txt"].join("\n"));
});
it("emits a single root-level file with no directory heading", () => {
expect(formatFindGroupedOutput(["single.txt"])).toBe("single.txt");
});
it("keeps matched directories (trailing slash) as headings", () => {
expect(formatFindGroupedOutput(["alpha/tests/", "beta/tests/"])).toBe(
["# alpha/tests/", "# beta/tests/"].join("\n"),
);
});
});
describe("formatGroupedFiles", () => {
it("nests subdirectories with deeper headings and blank-separates top groups", () => {
const { model } = formatGroupedFiles(["pkg/ai/CHANGELOG.md", "pkg/ai/src/util/x.ts", "README.md"], file => ({
modelLines: [` ${file}`],
headerSuffix: " (1)",
}));
expect(model).toEqual([
"# README.md (1)",
" README.md",
"",
"# pkg/ai/",
"## CHANGELOG.md (1)",
" pkg/ai/CHANGELOG.md",
"",
"## src/util/",
"### x.ts (1)",
" pkg/ai/src/util/x.ts",
]);
});
it("omits skipped files and their now-empty directories", () => {
const { model } = formatGroupedFiles(["a/keep.ts", "a/drop.ts"], file => ({
modelLines: [` ${file}`],
skip: file.endsWith("drop.ts"),
}));
expect(model).toEqual(["# a/", "## keep.ts", " a/keep.ts"]);
});
});
describe("classifyGroupedLines", () => {
it("reconstructs absolute paths across a nested directory stack", () => {
const lines = ["# pkg/ai/", "## CHANGELOG.md", " match", "## src/util/", "### x.ts", "*12│const y = 1;"];
const ctx = classifyGroupedLines(lines, "/repo");
expect(ctx[0]).toMatchObject({ kind: "dir", headerPath: "/repo/pkg/ai" });
expect(ctx[1]).toMatchObject({ kind: "file", headerPath: "/repo/pkg/ai/CHANGELOG.md" });
expect(ctx[2]).toMatchObject({ kind: "content", filePath: "/repo/pkg/ai/CHANGELOG.md" });
// `src/util/` is a folded subdirectory chain under `pkg/ai/`, not the root.
expect(ctx[3]).toMatchObject({ kind: "dir", headerPath: "/repo/pkg/ai/src/util" });
expect(ctx[4]).toMatchObject({ kind: "file", headerPath: "/repo/pkg/ai/src/util/x.ts" });
expect(ctx[5]).toMatchObject({ kind: "content", filePath: "/repo/pkg/ai/src/util/x.ts" });
});
it("keeps an absolute folded prefix instead of joining it onto the search base", () => {
const ctx = classifyGroupedLines(["# /outside/dir/", "## file.txt"], "/repo");
expect(ctx[0]).toMatchObject({ kind: "dir", headerPath: "/outside/dir" });
expect(ctx[1]).toMatchObject({ kind: "file", headerPath: "/outside/dir/file.txt" });
});
it("links body lines before any header to the single-file search base", () => {
const ctx = classifyGroupedLines(["*7│needle();"], "/repo/file.ts");
expect(ctx[0]).toMatchObject({ kind: "content", filePath: "/repo/file.ts" });
});
it("flags url-like headers for caller-side resolution without a filesystem path", () => {
const ctx = classifyGroupedLines(["# omp://docs/", " body"], "/repo");
expect(ctx[0]).toMatchObject({ kind: "file", isUrl: true });
expect(ctx[0]?.headerPath).toBeUndefined();
});
});
describe("groupLineIndicesByBlank", () => {
it("breaks on blank-line runs and keeps original indices", () => {
expect(groupLineIndicesByBlank(["a", "b", "", "", "c"])).toEqual([[0, 1], [4]]);
});
it("returns a single group of non-empty lines when no blanks are present", () => {
expect(groupLineIndicesByBlank(["a", "b", "c"])).toEqual([[0, 1, 2]]);
});
});