fix: harden Base manifest recovery
This commit is contained in:
@@ -106,7 +106,8 @@ import { pathToFileURL } from "node:url";
|
||||
|
||||
const repositoryRoot = process.argv[2];
|
||||
const { archiveManifest, selectManifest } = await import(pathToFileURL(join(repositoryRoot, "tools/select-manifest.mjs")));
|
||||
const { publishManifest } = await import(pathToFileURL(join(repositoryRoot, "tools/publish-web-manifest.mjs")));
|
||||
const { validateManifest } = await import(pathToFileURL(join(repositoryRoot, "tools/finalize-manifest.mjs")));
|
||||
const { publishManifest, validatePublicManifest } = await import(pathToFileURL(join(repositoryRoot, "tools/publish-web-manifest.mjs")));
|
||||
const root = await mkdtemp(join(tmpdir(), "uups-base-config-"));
|
||||
const deployments = join(root, "deployments");
|
||||
const webManifest = join(root, "web/public/deployment.json");
|
||||
@@ -157,14 +158,29 @@ try {
|
||||
assert.equal(published.rpcUrl, "https://public.invalid/rpc");
|
||||
assert.equal(published.explorerBaseUrl, "https://sepolia.basescan.org");
|
||||
assert.equal((await readFile(webManifest, "utf8")).includes("terminal.invalid"), false);
|
||||
for (const invalid of [
|
||||
{ ...base, rpcUrl: "http://public.invalid/rpc" },
|
||||
{ ...base, explorerBaseUrl: "https://example.invalid" },
|
||||
]) {
|
||||
const without = (field) => {
|
||||
const manifest = { ...base };
|
||||
delete manifest[field];
|
||||
return manifest;
|
||||
};
|
||||
const invalidCases = [
|
||||
["missing rpcUrl", without("rpcUrl"), /rpcUrl/],
|
||||
["missing explorerBaseUrl", without("explorerBaseUrl"), /explorerBaseUrl/],
|
||||
["wrong actor count", { ...base, actors: [base.actors[0]] }, /exactly Presenter and Recipient|actor configuration/],
|
||||
["wrong actor labels", { ...base, actors: [{ ...base.actors[0], label: "Owner" }, base.actors[1]] }, /Presenter and Recipient|actor configuration/],
|
||||
["owner is not Presenter", { ...base, owner: recipient }, /owner.*Presenter|first actor/],
|
||||
["HTTP public RPC", { ...base, rpcUrl: "http://public.invalid/rpc" }, /HTTPS/],
|
||||
["credential-bearing public RPC", { ...base, rpcUrl: "https://user@public.invalid/rpc" }, /credentials|prohibited secret/],
|
||||
["wrong BaseScan root", { ...base, explorerBaseUrl: "https://example.invalid" }, /BaseScan/],
|
||||
];
|
||||
for (const [label, invalid, rejection] of invalidCases) {
|
||||
assert.throws(() => validateManifest(invalid), rejection, `confirmation accepted ${label}`);
|
||||
assert.throws(() => validatePublicManifest(invalid), rejection, `publication validation accepted ${label}`);
|
||||
await writeFile(join(deployments, "active.json"), `${JSON.stringify(invalid)}\n`);
|
||||
await assert.rejects(
|
||||
() => publishManifest({ activePath: join(deployments, "active.json"), outputPath: webManifest }),
|
||||
/HTTPS|BaseScan/,
|
||||
rejection,
|
||||
`publication accepted ${label}`,
|
||||
);
|
||||
}
|
||||
await writeFile(join(deployments, "active.json"), activeBefore);
|
||||
@@ -179,6 +195,42 @@ try {
|
||||
await assert.rejects(() => access(join(deployments, "base-sepolia.json")));
|
||||
assert.deepEqual(await readFile(join(deployments, "active.json")), activeBefore);
|
||||
assert.deepEqual(await readFile(join(deployments, "anvil.json")), anvilBefore);
|
||||
|
||||
const canonicalPath = join(deployments, "base-sepolia.json");
|
||||
const canonicalBytes = Buffer.from(`${JSON.stringify(base)}\n`);
|
||||
const existingBytes = Buffer.from("existing archive bytes\n");
|
||||
const collisionDate = new Date("2026-08-21T12:35:56.789Z");
|
||||
const collisionPath = join(deployments, "base-sepolia.20260821T123556789Z.json");
|
||||
await writeFile(canonicalPath, canonicalBytes);
|
||||
await writeFile(collisionPath, existingBytes);
|
||||
await assert.rejects(
|
||||
() => archiveManifest({ root, network: "baseSepolia", now: collisionDate }),
|
||||
/existing Base Sepolia archive/,
|
||||
);
|
||||
assert.deepEqual(await readFile(canonicalPath), canonicalBytes);
|
||||
assert.deepEqual(await readFile(collisionPath), existingBytes);
|
||||
|
||||
const boundaryDate = new Date("2026-08-21T12:36:56.789Z");
|
||||
const boundaryPath = join(deployments, "base-sepolia.20260821T123656789Z.json");
|
||||
const boundaryBytes = Buffer.from("archive created by racing process\n");
|
||||
await assert.rejects(
|
||||
() => archiveManifest({
|
||||
root,
|
||||
network: "baseSepolia",
|
||||
now: boundaryDate,
|
||||
io: {
|
||||
link: async (_source, target) => {
|
||||
await writeFile(target, boundaryBytes);
|
||||
const error = new Error("collision at link boundary");
|
||||
error.code = "EEXIST";
|
||||
throw error;
|
||||
},
|
||||
},
|
||||
}),
|
||||
/existing Base Sepolia archive/,
|
||||
);
|
||||
assert.deepEqual(await readFile(canonicalPath), canonicalBytes);
|
||||
assert.deepEqual(await readFile(boundaryPath), boundaryBytes);
|
||||
await assert.rejects(() => archiveManifest({ root, network: "anvil" }), /baseSepolia/);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
|
||||
Reference in New Issue
Block a user