import { describe, it, expect, vi, beforeEach } from "vitest";
import path from "path";
import {
listFilesHandler,
readFileSnippetHandler,
searchFilesHandler,
fileTreeHandler,
} from "./filesystem.js";
import fs from "fs";
// Explicitly mock 'fs'
vi.mock("fs", () => {
const readdirSyncMock = vi.fn();
const readdirMock = vi.fn();
const readFileMock = vi.fn();
return {
default: {
readdirSync: readdirSyncMock,
promises: {
readdir: readdirMock,
readFile: readFileMock,
},
},
};
});
describe("Filesystem Tools", () => {
const cwd = "/mock/cwd";
beforeEach(() => {
vi.clearAllMocks();
});
describe("listFilesHandler", () => {
it("should list files in current directory", async () => {
// Mock readdirSync return value (Dirent-like objects)
(fs.readdirSync as any).mockReturnValue([
{ name: "package.json", isDirectory: () => false, path: cwd },
{ name: "src", isDirectory: () => true, path: cwd },
]);
const result = await listFilesHandler({
path: cwd,
recursive: false,
});
expect(result.content[0].text).toContain("package.json");
expect(result.content[0].text).toContain("src");
// Verify call
expect(fs.readdirSync).toHaveBeenCalledWith(cwd, expect.anything());
});
});
describe("readFileSnippetHandler", () => {
it("should read snippet from package.json", async () => {
(fs.promises.readFile as any).mockResolvedValue(`{
"name": "test-project",
"version": "1.0.0",
"scripts": {
"test": "vitest"
}
}`);
const result = await readFileSnippetHandler({
filePath: path.join(cwd, "package.json"),
startLine: 1,
endLine: 5,
});
expect(result.content[0].text).toContain("test-project");
expect(result.content[0].text).toContain("scripts");
});
});
describe("searchFilesHandler", () => {
it("should find text in files", async () => {
// Mock readdir (recursive logic uses this)
// Step 1: Root dir
(fs.promises.readdir as any).mockResolvedValueOnce([
{
name: "package.json",
isDirectory: () => false,
isFile: () => true,
},
]);
// Mock readFile
(fs.promises.readFile as any).mockResolvedValue(
'dependencies: { "vitest": "^1.0.0" }',
);
const result = await searchFilesHandler({
path: cwd,
pattern: "vitest",
exclude: ["node_modules", ".git"],
});
// Note: searchFilesHandler implementation might need 'path' in dirent or joins it manually.
// Looking at code: it uses path.join(dir, entry.name).
expect(result.content[0].text).toContain("package.json");
expect(result.content[0].text).toContain("vitest");
});
});
describe("fileTreeHandler", () => {
it("should generate file tree", async () => {
(fs.promises.readdir as any).mockResolvedValue([
{ name: "src", isDirectory: () => true },
{ name: "package.json", isDirectory: () => false },
]);
// Mock src subdirectory call just in case (though depth might limit it)
(fs.promises.readdir as any)
.mockResolvedValueOnce([
{ name: "src", isDirectory: () => true },
{ name: "package.json", isDirectory: () => false },
])
.mockResolvedValueOnce([]); // Empty src
const result = await fileTreeHandler({
path: cwd,
depth: 1,
});
expect(result.content[0].text).toContain("src");
expect(result.content[0].text).toContain("package.json");
});
});
});