import assert from "node:assert"; import * as fs from "node:fs/promises"; import path from "node:path"; import { describe, test } from "node:test"; import { fileURLToPath } from "node:url"; import FileTree from "../../src/drivers/FileTree.js"; import { ObjectTree, Tree } from "../../src/internal.js"; const dirname = path.dirname(fileURLToPath(import.meta.url)); const tempDirectory = path.join(dirname, "fixtures/temp"); const textDecoder = new TextDecoder(); describe("FileTree", async () => { test("can get the keys of the tree", async () => { const fixture = createFixture("fixtures/markdown"); assert.deepEqual(Array.from(await fixture.keys()), [ "Alice.md", "Bob.md", "Carol.md", "subfolder/", ]); }); test("can get the value for a key", async () => { const fixture = createFixture("fixtures/markdown"); const buffer = await fixture.get("Alice.md"); const text = textDecoder.decode(buffer); assert.equal(text, "Hello, **Alice**."); }); test("getting an unsupported key returns undefined", async () => { const fixture = createFixture("fixtures/markdown"); assert.equal(await fixture.get("xyz"), undefined); }); test("getting empty key returns undefined", async () => { const fixture = createFixture("fixtures/markdown"); assert.equal(await fixture.get(""), undefined); }); test("getting a null/undefined key throws an exception", async () => { const fixture = createFixture("fixtures/markdown"); await assert.rejects(async () => { await fixture.get(null); }); await assert.rejects(async () => { await fixture.get(undefined); }); }); test("can retrieve values with optional trailing slash", async () => { const fixture = createFixture("fixtures/markdown"); assert(await fixture.get("Alice.md")); assert(await fixture.get("Alice.md/")); assert(await fixture.get("subfolder")); assert(await fixture.get("subfolder/")); }); test("sets parent on subtrees", async () => { const fixture = createFixture("fixtures"); const markdown = await fixture.get("markdown"); assert.equal(markdown.parent, fixture); }); test("can write out a file via set()", async () => { await createTempDirectory(); // Write out a file. const fileName = "file1"; const fileText = "This is the first file."; const tempFiles = new FileTree(tempDirectory); await tempFiles.set(fileName, fileText); // Read it back in. const filePath = path.join(tempDirectory, fileName); const actualText = String(await fs.readFile(filePath)); assert.equal(fileText, actualText); await removeTempDirectory(); }); test("create subfolder via set() with empty object value", async () => { await createTempDirectory(); // Write out new, empty folder called "empty". const tempFiles = new FileTree(tempDirectory); await tempFiles.set("empty", {}); // Verify folder exists and has no contents. const folderPath = path.join(tempDirectory, "empty"); const stats = await fs.stat(folderPath); assert(stats.isDirectory()); const files = await fs.readdir(folderPath); assert.deepEqual(files, []); await removeTempDirectory(); }); test("create subfolder via set() with empty tree value", async () => { await createTempDirectory(); // Write out new, empty folder called "empty". const tempFiles = new FileTree(tempDirectory); await tempFiles.set("empty", new ObjectTree({})); // Verify folder exists and has no contents. const folderPath = path.join(tempDirectory, "empty"); const stats = await fs.stat(folderPath); assert(stats.isDirectory()); const files = await fs.readdir(folderPath); assert.deepEqual(files, []); await removeTempDirectory(); }); test("can write out subfolder via set()", async () => { await createTempDirectory(); // Create a tiny set of "files". const obj = { file1: "This is the first file.", subfolder: { file2: "This is the second file.", }, }; // Write out files as a new folder called "folder". const tempFiles = new FileTree(tempDirectory); await tempFiles.set("folder", obj); // Read them back in. const actualFiles = await tempFiles.get("folder"); const strings = Tree.map(actualFiles, { deep: true, value: (buffer) => textDecoder.decode(buffer), }); const plain = await Tree.plain(strings); assert.deepEqual(plain, obj); await removeTempDirectory(); }); test("can delete a file via set()", async () => { await createTempDirectory(); const tempFile = path.join(tempDirectory, "file"); await fs.writeFile(tempFile, ""); const tempFiles = new FileTree(tempDirectory); await tempFiles.set("file", undefined); let stats; try { stats = await fs.stat(tempFile); } catch (/** @type {any} */ error) { if (error.code !== "ENOENT") { throw error; } } assert(stats === undefined); await removeTempDirectory(); }); test("can delete a folder via set()", async () => { await createTempDirectory(); const folder = path.join(tempDirectory, "folder"); await fs.mkdir(folder); const tempFiles = new FileTree(tempDirectory); await tempFiles.set("folder", undefined); let stats; try { stats = await fs.stat(folder); } catch (/** @type {any} */ error) { if (error.code !== "ENOENT") { throw error; } } assert(stats === undefined); await removeTempDirectory(); }); }); function createFixture(fixturePath) { return new FileTree(path.join(dirname, fixturePath)); } async function createTempDirectory() { await fs.mkdir(tempDirectory, { recursive: true }); } async function removeTempDirectory() { await fs.rm(tempDirectory, { recursive: true }); }