import { mkdtemp, open, readFile, readdir, rename, rm, writeFile, } from "node:fs/promises"; import { constants } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { z } from "zod"; import { createValidatedJsonArtifactWriter, writeValidatedJsonArtifact, } from "../../scripts/lib/validated-json-artifact.ts"; const artifactSchema = z .object({ schemaVersion: z.literal(1), name: z.string().min(1) }) .strict(); const temporaryDirectories: string[] = []; async function temporaryDirectory(): Promise { const directory = await mkdtemp( path.join(tmpdir(), "validated-json-artifact-"), ); temporaryDirectories.push(directory); return directory; } afterEach(async () => { await Promise.all( temporaryDirectories.splice(0).map((directory) => rm(directory, { force: true, recursive: true }), ), ); }); describe("validated JSON artifact writer", () => { it("returns the exact schema-validated bytes used by durable publication", async () => { const artifact = await import("../../scripts/lib/validated-json-artifact.ts") as Record; expect(artifact.serializeValidatedJsonArtifact).toBeTypeOf("function"); const serializeValidatedJsonArtifact = artifact.serializeValidatedJsonArtifact as ( input: Readonly<{ path: string; schema: z.ZodType; value: unknown }>, ) => Buffer; expect(serializeValidatedJsonArtifact({ path: "/unused/artifact.json", schema: z.object({ value: z.string() }).strict(), value: { value: "sealed" }, })).toEqual(Buffer.from('{\n "value": "sealed"\n}\n')); expect(() => serializeValidatedJsonArtifact({ path: "/unused/artifact.json", schema: z.object({ value: z.string() }).strict(), value: { value: 7 }, })).toThrow(); }); it("syncs an O_NOFOLLOW exclusive temp and its directory around rename", async () => { const events: string[] = []; let openFlags = 0; let openMode = 0; const writer = createValidatedJsonArtifactWriter({ createNonce: () => "durable", fileSystem: { open: async (_target, flags, mode) => { openFlags = flags; openMode = mode; events.push(`open:${flags}`); return { writeFile: async () => { events.push("write"); }, sync: async () => { events.push("file-sync"); }, close: async () => { events.push("file-close"); }, }; }, openDirectory: async () => ({ sync: async () => { events.push("directory-sync"); }, close: async () => { events.push("directory-close"); }, }), rename: async () => { events.push("rename"); }, rm: async () => {}, }, }); await writer({ path: "/tmp/report.json", schema: artifactSchema, value: { schemaVersion: 1, name: "valid" }, }); expect(events).toHaveLength(7); expect(events[0]).toMatch(/^open:\d+$/); expect(openFlags & constants.O_WRONLY).toBe(constants.O_WRONLY); expect(openFlags & constants.O_CREAT).toBe(constants.O_CREAT); expect(openFlags & constants.O_EXCL).toBe(constants.O_EXCL); expect(openFlags & constants.O_NOFOLLOW).toBe(constants.O_NOFOLLOW); expect(openMode).toBe(0o600); expect(events.slice(1)).toEqual([ "write", "file-sync", "file-close", "rename", "directory-sync", "directory-close", ]); }); it.each(["existing", "missing"] as const)( "rejects invalid %s artifacts before changing destination state", async (destinationState) => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); if (destinationState === "existing") { await writeFile(destination, "previous-bytes\n", "utf8"); } const entriesBefore = await readdir(directory); await expect( writeValidatedJsonArtifact({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "" }, }), ).rejects.toThrow(); expect(await readdir(directory)).toEqual(entriesBefore); if (destinationState === "existing") { await expect(readFile(destination, "utf8")).resolves.toBe( "previous-bytes\n", ); } else { await expect(readFile(destination, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); } }, ); it("publishes complete formatted bytes through a sibling rename", async () => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); await writeValidatedJsonArtifact({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "valid" }, }); await expect(readFile(destination, "utf8")).resolves.toBe( '{\n "schemaVersion": 1,\n "name": "valid"\n}\n', ); expect(await readdir(directory)).toEqual(["artifact.json"]); }); it("writes the schema's defaulted and transformed parsed output", async () => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); const parsedOutputSchema = z .object({ name: z.string().default("generated"), count: z.string().transform((value) => Number(value)), }) .strict(); await writeValidatedJsonArtifact({ path: destination, schema: parsedOutputSchema, value: { count: "3" }, }); expect(JSON.parse(await readFile(destination, "utf8"))).toEqual({ name: "generated", count: 3, }); }); it("reports a close-only failure and cleans its owned temp", async () => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); const ownedTemp = path.join(directory, ".artifact.json.close-only.tmp"); const closeError = new Error("injected close-only failure"); const writer = createValidatedJsonArtifactWriter({ createNonce: () => "close-only", fileSystem: { open: async (target, flags) => { const handle = await open(target, flags); return { writeFile: async (data, encoding) => handle.writeFile(data, encoding), sync: async () => handle.sync(), close: async () => { await handle.close(); throw closeError; }, }; }, openDirectory: async (target) => { const handle = await open(target, constants.O_RDONLY); return { sync: async () => handle.sync(), close: async () => handle.close(), }; }, rename, rm, }, }); await expect( writer({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "valid" }, }), ).rejects.toBe(closeError); await expect(readFile(destination, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); await expect(readFile(ownedTemp, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); }); it("preserves the write error when write and close both fail", async () => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); const ownedTemp = path.join(directory, ".artifact.json.double-failure.tmp"); const writeError = new Error("injected primary write failure"); const closeError = new Error("injected secondary close failure"); const writer = createValidatedJsonArtifactWriter({ createNonce: () => "double-failure", fileSystem: { open: async (target, flags) => { const handle = await open(target, flags); return { writeFile: async (data, encoding) => { await handle.writeFile(data, encoding); throw writeError; }, sync: async () => handle.sync(), close: async () => { await handle.close(); throw closeError; }, }; }, openDirectory: async (target) => { const handle = await open(target, constants.O_RDONLY); return { sync: async () => handle.sync(), close: async () => handle.close(), }; }, rename, rm, }, }); await expect( writer({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "valid" }, }), ).rejects.toBe(writeError); await expect(readFile(destination, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); await expect(readFile(ownedTemp, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); }); it("publishes one complete document when two valid writers race", async () => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); const nonces = ["first", "second"]; const writer = createValidatedJsonArtifactWriter({ createNonce: () => { const nonce = nonces.shift(); if (nonce === undefined) throw new Error("nonce fixture exhausted"); return nonce; }, }); await Promise.all([ writer({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "first" }, }), writer({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "second" }, }), ]); expect([ { schemaVersion: 1, name: "first" }, { schemaVersion: 1, name: "second" }, ]).toContainEqual(JSON.parse(await readFile(destination, "utf8"))); expect(await readdir(directory)).toEqual(["artifact.json"]); }); it.each(["write", "rename"] as const)( "cleans only its owned sibling temp when %s fails", async (failurePoint) => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); const unrelatedTemp = path.join(directory, ".artifact.json.unrelated.tmp"); const ownedTemp = path.join(directory, ".artifact.json.owned.tmp"); await writeFile(destination, "previous-bytes\n", "utf8"); await writeFile(unrelatedTemp, "unrelated\n", "utf8"); const writer = createValidatedJsonArtifactWriter({ createNonce: () => "owned", fileSystem: { open: async (target, flags) => { const handle = await open(target, flags); return { writeFile: async (data, encoding) => { await handle.writeFile(data, encoding); if (failurePoint === "write") { throw new Error("injected write failure"); } }, sync: async () => handle.sync(), close: async () => handle.close(), }; }, openDirectory: async (target) => { const handle = await open(target, constants.O_RDONLY); return { sync: async () => handle.sync(), close: async () => handle.close(), }; }, rename: async (source, target) => { if (failurePoint === "rename") { throw new Error("injected rename failure"); } await rename(source, target); }, rm, }, }); await expect( writer({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "valid" }, }), ).rejects.toThrow(`injected ${failurePoint} failure`); await expect(readFile(destination, "utf8")).resolves.toBe( "previous-bytes\n", ); await expect(readFile(unrelatedTemp, "utf8")).resolves.toBe( "unrelated\n", ); await expect(readFile(ownedTemp, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); }, ); it("does not delete a pre-existing colliding sibling temp", async () => { const directory = await temporaryDirectory(); const destination = path.join(directory, "artifact.json"); const collidingTemp = path.join(directory, ".artifact.json.collision.tmp"); await writeFile(collidingTemp, "another-writer\n", "utf8"); const writer = createValidatedJsonArtifactWriter({ createNonce: () => "collision", }); await expect( writer({ path: destination, schema: artifactSchema, value: { schemaVersion: 1, name: "valid" }, }), ).rejects.toMatchObject({ code: "EEXIST" }); await expect(readFile(collidingTemp, "utf8")).resolves.toBe( "another-writer\n", ); await expect(readFile(destination, "utf8")).rejects.toMatchObject({ code: "ENOENT", }); }); });