168 lines
5.2 KiB
TypeScript
168 lines
5.2 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { promises as fs } from "node:fs";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import { list_dir } from "../tools/impl/ListDirCodex.js";
|
|
|
|
describe("list_dir codex tool", () => {
|
|
let tempDir: string;
|
|
|
|
async function setupTempDir(): Promise<string> {
|
|
if (!tempDir) {
|
|
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "list-dir-test-"));
|
|
}
|
|
return tempDir;
|
|
}
|
|
|
|
async function createStructure(
|
|
structure: Record<string, string | null>,
|
|
): Promise<string> {
|
|
const dir = await setupTempDir();
|
|
|
|
for (const [relativePath, content] of Object.entries(structure)) {
|
|
const fullPath = path.join(dir, relativePath);
|
|
const parentDir = path.dirname(fullPath);
|
|
|
|
await fs.mkdir(parentDir, { recursive: true });
|
|
|
|
if (content !== null) {
|
|
// It's a file
|
|
await fs.writeFile(fullPath, content);
|
|
}
|
|
// If content is null, it's just a directory (already created by mkdir)
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
test("lists directory with default pagination", async () => {
|
|
const dir = await createStructure({
|
|
"file1.txt": "content1",
|
|
"file2.txt": "content2",
|
|
"subdir/file3.txt": "content3",
|
|
});
|
|
|
|
const result = await list_dir({ dir_path: dir });
|
|
|
|
expect(result.content).toContain(`Absolute path: ${dir}`);
|
|
expect(result.content).toContain("file1.txt");
|
|
expect(result.content).toContain("file2.txt");
|
|
expect(result.content).toContain("subdir/");
|
|
});
|
|
|
|
test("respects offset parameter (1-indexed)", async () => {
|
|
const dir = await createStructure({
|
|
"aaa.txt": "a",
|
|
"bbb.txt": "b",
|
|
"ccc.txt": "c",
|
|
"ddd.txt": "d",
|
|
});
|
|
|
|
// Skip first 2 entries
|
|
const result = await list_dir({ dir_path: dir, offset: 3, limit: 10 });
|
|
|
|
// Should not contain first two entries (when sorted alphabetically)
|
|
const lines = result.content.split("\n");
|
|
// First line is "Absolute path: ..."
|
|
expect(lines[0]).toContain("Absolute path:");
|
|
// Remaining lines should be limited entries
|
|
expect(lines.length).toBeGreaterThan(1);
|
|
});
|
|
|
|
test("respects limit parameter", async () => {
|
|
const dir = await createStructure({
|
|
"file1.txt": "1",
|
|
"file2.txt": "2",
|
|
"file3.txt": "3",
|
|
"file4.txt": "4",
|
|
"file5.txt": "5",
|
|
});
|
|
|
|
const result = await list_dir({ dir_path: dir, limit: 2 });
|
|
|
|
// Should have "More than 2 entries found" message
|
|
expect(result.content).toContain("More than 2 entries found");
|
|
});
|
|
|
|
test("respects depth parameter", async () => {
|
|
const dir = await createStructure({
|
|
"level1/level2/level3/deep.txt": "deep",
|
|
"level1/shallow.txt": "shallow",
|
|
"root.txt": "root",
|
|
});
|
|
|
|
// Depth 1 should only show immediate children
|
|
const result1 = await list_dir({ dir_path: dir, depth: 1, limit: 100 });
|
|
expect(result1.content).toContain("level1/");
|
|
expect(result1.content).toContain("root.txt");
|
|
expect(result1.content).not.toContain("level2");
|
|
expect(result1.content).not.toContain("shallow.txt");
|
|
|
|
// Depth 2 should show one level deeper
|
|
const result2 = await list_dir({ dir_path: dir, depth: 2, limit: 100 });
|
|
expect(result2.content).toContain("level1/");
|
|
expect(result2.content).toContain("shallow.txt");
|
|
expect(result2.content).toContain("level2/");
|
|
expect(result2.content).not.toContain("level3");
|
|
});
|
|
|
|
test("shows directories with trailing slash", async () => {
|
|
const dir = await createStructure({
|
|
"mydir/file.txt": "content",
|
|
});
|
|
|
|
const result = await list_dir({ dir_path: dir });
|
|
|
|
expect(result.content).toContain("mydir/");
|
|
});
|
|
|
|
test("accepts relative paths", async () => {
|
|
const relDir = await fs.mkdtemp(
|
|
path.join(process.cwd(), "list-dir-relative-test-"),
|
|
);
|
|
await fs.writeFile(path.join(relDir, "file.txt"), "content");
|
|
|
|
const relativePath = path.relative(process.cwd(), relDir);
|
|
expect(path.isAbsolute(relativePath)).toBe(false);
|
|
|
|
const result = await list_dir({ dir_path: relativePath });
|
|
|
|
expect(result.content).toContain(`Absolute path: ${relDir}`);
|
|
expect(result.content).toContain("file.txt");
|
|
|
|
await fs.rm(relDir, { recursive: true, force: true });
|
|
});
|
|
|
|
test("throws error for offset < 1", async () => {
|
|
const dir = await setupTempDir();
|
|
await expect(list_dir({ dir_path: dir, offset: 0 })).rejects.toThrow(
|
|
"offset must be a 1-indexed entry number",
|
|
);
|
|
});
|
|
|
|
test("throws error for limit < 1", async () => {
|
|
const dir = await setupTempDir();
|
|
await expect(list_dir({ dir_path: dir, limit: 0 })).rejects.toThrow(
|
|
"limit must be greater than zero",
|
|
);
|
|
});
|
|
|
|
test("throws error for depth < 1", async () => {
|
|
const dir = await setupTempDir();
|
|
await expect(list_dir({ dir_path: dir, depth: 0 })).rejects.toThrow(
|
|
"depth must be greater than zero",
|
|
);
|
|
});
|
|
|
|
test("handles empty directory", async () => {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "empty-dir-test-"));
|
|
|
|
const result = await list_dir({ dir_path: dir });
|
|
|
|
expect(result.content).toContain(`Absolute path: ${dir}`);
|
|
// Should only have the header line
|
|
const lines = result.content.split("\n").filter((l) => l.trim() !== "");
|
|
expect(lines.length).toBe(1);
|
|
});
|
|
});
|