chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,409 @@
|
||||
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<string> {
|
||||
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<string, unknown>;
|
||||
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",
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user