alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
417 lines (325 loc) • 13.5 kB
text/typescript
import { Readable } from "node:stream";
import { Alepha } from "alepha";
import { beforeEach, describe, it } from "vitest";
import { AlephaSystem } from "../index.ts";
import { NodeFileSystemProvider } from "../providers/NodeFileSystemProvider.ts";
describe("NodeFileSystem", () => {
let fs: NodeFileSystemProvider;
beforeEach(() => {
const alepha = Alepha.create().with(AlephaSystem);
fs = alepha.inject(NodeFileSystemProvider);
});
describe("createFile with object API", () => {
describe("from text", () => {
it("should create a file from text content", async ({ expect }) => {
const file = fs.createFile({
text: "Hello, world!",
name: "greeting.txt",
});
expect(file.name).toBe("greeting.txt");
expect(file.type).toBe("text/plain");
expect(file.size).toBe(13);
expect(await file.text()).toBe("Hello, world!");
});
it("should use default name if not provided", async ({ expect }) => {
const file = fs.createFile({
text: "Hello",
});
expect(file.name).toBe("file.txt");
expect(file.type).toBe("text/plain");
});
it("should allow custom type", async ({ expect }) => {
const file = fs.createFile({
text: '{"key": "value"}',
name: "data.json",
type: "application/json",
});
expect(file.type).toBe("application/json");
});
});
describe("from buffer", () => {
it("should create a file from a Buffer", async ({ expect }) => {
const buffer = Buffer.from("Hello, buffer!");
const file = fs.createFile({
buffer,
name: "test.txt",
});
expect(file.name).toBe("test.txt");
expect(file.type).toBe("text/plain");
expect(file.size).toBe(14);
expect(await file.text()).toBe("Hello, buffer!");
});
it("should detect content type from filename", async ({ expect }) => {
const buffer = Buffer.from("PNG data");
const file = fs.createFile({
buffer,
name: "image.png",
});
expect(file.type).toBe("image/png");
});
it("should allow type override", async ({ expect }) => {
const buffer = Buffer.from("data");
const file = fs.createFile({
buffer,
name: "file.bin",
type: "application/custom",
});
expect(file.type).toBe("application/custom");
});
});
describe("from arrayBuffer", () => {
it("should create a file from an ArrayBuffer", async ({ expect }) => {
const arrayBuffer = new ArrayBuffer(8);
const view = new Uint8Array(arrayBuffer);
view[0] = 72; // 'H'
view[1] = 101; // 'e'
view[2] = 108; // 'l'
view[3] = 108; // 'l'
view[4] = 111; // 'o'
const file = fs.createFile({
arrayBuffer,
name: "test.txt",
});
expect(file.name).toBe("test.txt");
expect(file.type).toBe("text/plain");
const text = await file.text();
expect(text.substring(0, 5)).toBe("Hello");
});
});
describe("from stream", () => {
it("should create a file from a Readable stream", async ({ expect }) => {
const stream = Readable.from(Buffer.from("Stream data"));
const file = fs.createFile({
stream,
name: "data.txt",
size: 11,
});
expect(file.name).toBe("data.txt");
expect(file.type).toBe("text/plain");
expect(file.size).toBe(11);
expect(await file.text()).toBe("Stream data");
});
it("should detect content type from filename", async ({ expect }) => {
const stream = Readable.from(Buffer.from("{}"));
const file = fs.createFile({
stream,
name: "config.json",
});
expect(file.type).toBe("application/json");
});
});
describe("from Web File", () => {
it("should create a file from a Web File object", async ({ expect }) => {
// Create a mock Web File
const webFile = new File(["Hello from File"], "document.txt", {
type: "text/plain",
});
const file = fs.createFile({
file: webFile,
});
expect(file.name).toBe("document.txt");
expect(file.type).toBe("text/plain");
expect(await file.text()).toBe("Hello from File");
});
it("should allow overriding Web File properties", async ({ expect }) => {
const webFile = new File(["content"], "old.txt", {
type: "text/plain",
});
const file = fs.createFile({
file: webFile,
name: "new.txt",
type: "text/custom",
});
expect(file.name).toBe("new.txt");
expect(file.type).toBe("text/custom");
});
});
describe("from URL", () => {
it("should create a file from a file:// URL", async ({ expect }) => {
// This test would require an actual file, so we just test the structure
const file = fs.createFile({
url: "file:///tmp/test.txt",
name: "test.txt",
});
expect(file.name).toBe("test.txt");
expect(file.type).toBe("text/plain");
});
it("should extract filename from URL path", async ({ expect }) => {
const file = fs.createFile({
url: "https://example.com/path/to/image.png",
});
expect(file.name).toBe("image.png");
expect(file.type).toBe("image/png");
});
it("should allow name override", async ({ expect }) => {
const file = fs.createFile({
url: "https://example.com/download",
name: "custom.pdf",
});
expect(file.name).toBe("custom.pdf");
expect(file.type).toBe("application/pdf");
});
});
describe("error handling", () => {
it("should throw error for invalid options", ({ expect }) => {
expect(() => {
// @ts-expect-error - testing invalid options
fs.createFile({});
}).toThrow("Invalid createFile options");
});
});
});
describe("filesystem operations", () => {
const tmpDir = "/tmp/alepha-file-test";
describe("mkdir", () => {
it("should create a directory", async ({ expect }) => {
// Ensure tmpDir exists
await fs.mkdir(tmpDir, { recursive: true });
const testDir = `${tmpDir}/mkdir-test-${Date.now()}`;
await fs.mkdir(testDir);
// Verify directory was created
const files = await fs.ls(tmpDir);
expect(files.some((f) => f.includes("mkdir-test"))).toBe(true);
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should create nested directories with recursive option", async ({
expect,
}) => {
const testDir = `${tmpDir}/nested/path/to/dir-${Date.now()}`;
await fs.mkdir(testDir, { recursive: true });
// Verify directory was created
const files = await fs.ls(`${tmpDir}/nested/path/to`);
expect(files.some((f) => f.includes("dir-"))).toBe(true);
// Cleanup
await fs.rm(`${tmpDir}/nested`, { recursive: true, force: true });
});
});
describe("ls", () => {
it("should list files in a directory", async ({ expect }) => {
const testDir = `${tmpDir}/ls-test-${Date.now()}`;
await fs.mkdir(testDir, { recursive: true });
// Create test files using Node.js fs
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/file1.txt`, "content1");
await writeFile(`${testDir}/file2.txt`, "content2");
await writeFile(`${testDir}/.hidden`, "hidden");
const files = await fs.ls(testDir);
expect(files).toContain("file1.txt");
expect(files).toContain("file2.txt");
expect(files).not.toContain(".hidden"); // Hidden files excluded by default
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should list hidden files with hidden option", async ({ expect }) => {
const testDir = `${tmpDir}/ls-hidden-test-${Date.now()}`;
await fs.mkdir(testDir, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/.hidden`, "hidden");
const files = await fs.ls(testDir, { hidden: true });
expect(files).toContain(".hidden");
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should list files recursively", async ({ expect }) => {
const testDir = `${tmpDir}/ls-recursive-test-${Date.now()}`;
await fs.mkdir(`${testDir}/subdir`, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/file1.txt`, "content1");
await writeFile(`${testDir}/subdir/file2.txt`, "content2");
const files = await fs.ls(testDir, { recursive: true });
expect(files).toContain("file1.txt");
expect(files).toContain("subdir");
expect(files.some((f) => f.includes("subdir/file2.txt"))).toBe(true);
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
});
describe("cp", () => {
it("should copy a file", async ({ expect }) => {
const testDir = `${tmpDir}/cp-test-${Date.now()}`;
await fs.mkdir(testDir, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/source.txt`, "content");
await fs.cp(`${testDir}/source.txt`, `${testDir}/dest.txt`);
const files = await fs.ls(testDir);
expect(files).toContain("source.txt");
expect(files).toContain("dest.txt");
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should copy a directory recursively", async ({ expect }) => {
const testDir = `${tmpDir}/cp-dir-test-${Date.now()}`;
await fs.mkdir(`${testDir}/source/subdir`, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/source/file.txt`, "content");
await writeFile(`${testDir}/source/subdir/nested.txt`, "nested");
await fs.cp(`${testDir}/source`, `${testDir}/dest`);
const destFiles = await fs.ls(`${testDir}/dest`, { recursive: true });
expect(destFiles).toContain("file.txt");
expect(destFiles.some((f) => f.includes("subdir/nested.txt"))).toBe(
true,
);
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
});
describe("mv", () => {
it("should move/rename a file", async ({ expect }) => {
const testDir = `${tmpDir}/mv-test-${Date.now()}`;
await fs.mkdir(testDir, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/old.txt`, "content");
await fs.mv(`${testDir}/old.txt`, `${testDir}/new.txt`);
const files = await fs.ls(testDir);
expect(files).not.toContain("old.txt");
expect(files).toContain("new.txt");
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should move a directory", async ({ expect }) => {
const testDir = `${tmpDir}/mv-dir-test-${Date.now()}`;
await fs.mkdir(`${testDir}/olddir`, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/olddir/file.txt`, "content");
await fs.mv(`${testDir}/olddir`, `${testDir}/newdir`);
const files = await fs.ls(testDir);
expect(files).not.toContain("olddir");
expect(files).toContain("newdir");
const newDirFiles = await fs.ls(`${testDir}/newdir`);
expect(newDirFiles).toContain("file.txt");
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
});
describe("rm", () => {
it("should remove a file", async ({ expect }) => {
const testDir = `${tmpDir}/rm-test-${Date.now()}`;
await fs.mkdir(testDir, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/file.txt`, "content");
await fs.rm(`${testDir}/file.txt`);
const files = await fs.ls(testDir);
expect(files).not.toContain("file.txt");
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should remove a directory recursively", async ({ expect }) => {
const testDir = `${tmpDir}/rm-dir-test-${Date.now()}`;
await fs.mkdir(`${testDir}/dir/subdir`, { recursive: true });
const { writeFile } = await import("node:fs/promises");
await writeFile(`${testDir}/dir/file.txt`, "content");
await fs.rm(`${testDir}/dir`, { recursive: true });
const files = await fs.ls(testDir);
expect(files).not.toContain("dir");
// Cleanup
await fs.rm(testDir, { recursive: true, force: true });
});
it("should not throw error with force option", async ({ expect }) => {
await expect(
fs.rm(`${tmpDir}/non-existent-${Date.now()}.txt`, { force: true }),
).resolves.not.toThrow();
});
});
});
});