import type { BunFile } from "bun"; import { tmpdirSync } from "harness"; import { constants, promises as fs } from "node:fs"; import path from "node:path"; // 0o644 const default_mode = constants.S_IWUSR | constants.S_IRUSR | constants.S_IRGRP | constants.S_IROTH; describe("Bun.write()", () => { it("Throws when no arguments are provided", async () => { // @ts-expect-error await expect(() => Bun.write()).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); }); it.each([undefined, null, /* 1, */ true, Symbol("foo"), {}])( "Throws when `destination` is not a path or blob-y thing (%p)", async (destination: any) => { await expect(() => Bun.write(destination, "foo")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); }, ); // FIXME it.failing("Throws when `destination` is a number ", async () => { // @ts-expect-error await expect(() => Bun.write(1, "foo 1")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); // @ts-expect-error await expect(() => Bun.write(0, "foo 0")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); // @ts-expect-error await expect(() => Bun.write(-10, "foo -10")).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); }); // NOTE: if/when we ban fds, this will become ERR_INVALID_ARG_TYPE. When that // happens, delete or update this test it("Throws when given a negative number", () => { // @ts-expect-error expect(() => Bun.write(-1, "foo")).toThrow(RangeError); }); it.each(["foo", ""])("Cannot write to readonly Blobs", async input => { var blob = new Blob([new Uint8Array([0, 0, 0, 0])]); await expect(() => Bun.write(blob as Bun.BunFile, input)).toThrowWithCodeAsync(Error, "ERR_INVALID_ARG_TYPE"); }); }); describe("Bun.write() on file paths", () => { console.log("%%BUNWRITE ON FILE PATHS%%"); let dir: string; beforeAll(() => { console.log("%%BUNWRITE ON FILE PATHS%% BEFORE ALL"); dir = tmpdirSync("bun-write"); }); afterAll(async () => { console.log("%%BUNWRITE ON FILE PATHS%% AFTER ALL"); await fs.rmdir(dir, { recursive: true }); }); describe("Given a path to a file in an existing directory", () => { let filepath: string; beforeEach(async () => { filepath = path.join(dir, "test-file.txt"); }); afterEach(async () => { await fs.unlink(filepath).catch(() => {}); }); describe("When the file does not exist", () => { const content = "Hello, world!"; it("When content is not empty, creates the file and writes it", async () => { const result = await Bun.write(filepath, content); expect(result).toBe(content.length); expect(await fs.readFile(filepath, "utf-8")).toBe(content); }); it("When content is empty, creates the file with default permissions and writes it", async () => { const result = await Bun.write(filepath, ""); expect(result).toBe(0); expect(await fs.readFile(filepath, "utf-8")).toBe(""); const stats = await fs.stat(filepath); expect(stats.mode & default_mode).toBe(default_mode); expect(stats.mode & constants.S_IFDIR).toBe(0); // not a directory }); it("When options.createPath is false, creates the file with default permissions and writes it", async () => { const result = await Bun.write(filepath, content, { createPath: false }); expect(result).toBe(content.length); expect(await fs.readFile(filepath, "utf-8")).toBe(content); const stats = await fs.stat(filepath); expect(stats.mode & default_mode).toBe(default_mode); expect(stats.mode & constants.S_IFDIR).toBe(0); // not a directory }); }); // describe("When the file exists and has content", () => { beforeEach(async () => { await fs.writeFile(filepath, "Hello, world!"); }); it.each(["", "Foo Bar"])("Writing '%s' overwrites the file", async content => { const result = await Bun.write(filepath, content); expect(result).toBe(content.length); expect(await fs.readFile(filepath, "utf-8")).toBe(content); }); }); // }); // describe("Given a path to a file in a non-existent directory", () => { console.log("%%BUNWRITE ON FILE PATHS%% GIVEN A PATH TO A FILE IN A NON-EXISTENT DIRECTORY"); let filepath: string; let rootdir: string; beforeAll(() => (rootdir = path.join(dir, "foo"))); beforeEach(async () => { filepath = path.join(rootdir, "bar/baz", "test-file.txt"); }); afterEach(async () => { await fs.rmdir(rootdir, { recursive: true }).catch(() => {}); }); describe("When no options are provided", () => { describe("When a non-empty string is written", () => { const content = "Hello, world!"; beforeEach(async () => { await Bun.write(filepath, content); }); it("Recursively creates the directory", async () => { // FIXME: should be undefined, not null expect(await fs.access(rootdir, constants.F_OK)).toBeFalsy(); expect(await fs.access(path.dirname(filepath), constants.F_OK)).toBeFalsy(); }); it("Creates a file with the provided content", async () => { expect(await fs.readFile(filepath, "utf-8")).toBe(content); }); it("Creates a file with default permissions", async () => { const stats = await fs.stat(filepath); expect(stats.mode & default_mode).toBe(default_mode); expect(stats.mode & constants.S_IFDIR).toBe(0); // not a directory }); }); // it("When an empty string is written, recursively creates the directory and writes the file", async () => { const result = await Bun.write(filepath, ""); expect(result).toBe(0); expect(await fs.readFile(filepath, "utf-8")).toBe(""); }); }); // describe("When options.createPath is false", () => { const options = { createPath: false }; it.each(["", "Hello, world!"])("When '%s' is written, throws ENOENT", async content => { await expect(() => Bun.write(filepath, content, options)).toThrowWithCodeAsync(Error, "ENOENT"); }); }); // }); }); // describe("Bun.write() on BunFiles", () => { let dir: string; beforeAll(() => { dir = tmpdirSync("bun-write-bunfile"); }); afterAll(async () => { await fs.rmdir(dir, { recursive: true }); }); describe("Given a text file that exists", () => { let file: BunFile; let textFilePath: string; beforeEach(async () => { textFilePath = path.join(dir, "test-file.txt"); await fs.writeFile(textFilePath, "Hello, world!"); file = Bun.file(textFilePath); }); afterEach(async () => { await fs.rm(textFilePath).catch(() => {}); }); it.each(["", "foo"])("Writing %s to the file overwrites the existing content", async text => { await Bun.write(file, text); const content = await file.text(); // Ensure the content matches what was written expect(content).toEqual(text); // Ensure content was saved to disk, not just `file`'s in-memory cache expect(await fs.readFile(textFilePath, "utf8")).toEqual(text); }); }); // Given a text file that exists }); //