import * as Effect from "effect/Effect"; import assert from "node:assert/strict"; import { createHash } from "node:crypto"; import { readFile, readdir } from "node:fs/promises"; import { test } from "node:test"; import { unstable_dev } from "wrangler"; import { parse } from "jsonc-parser"; import { customerBindings } from "./fixtures/config.mjs"; import { loadArtifact, verifyUpgradeIdentity, } from "../control-plane/artifact.ts"; const readJson = async (path) => JSON.parse(await readFile(path, "utf8")); const digest = (bytes) => createHash("sha256").update(bytes).digest("hex"); const artifactStore = (files) => ({ get: async (key) => { const path = key.replace(/^releases\/[a-f0-9]{64}\//, ""); const bytes = files[path]; return bytes ? { arrayBuffer: async () => bytes } : null; }, }); test("release contains intact Worker, assets and a consistent SQLite lifecycle", async () => { const manifestBytes = await readFile("dist/release/manifest.json"); assert.equal( digest(manifestBytes), (await readFile("dist/release/manifest.sha256", "utf8")).trim(), ); const manifest = JSON.parse(manifestBytes); const config = await readJson("dist/release/deployment.json"); const source = parse(await readFile("wrangler.jsonc", "utf8"), [], { allowTrailingComma: true, }); assert.equal(manifest.schemaVersion, 1); assert.equal(manifest.release, (await readJson("package.json")).version); assert.equal( manifest.lockfileSha256, digest(await readFile("pnpm-lock.yaml")), ); assert.equal(config.no_bundle, true); assert.equal(source.minify, true); assert.equal(source.keep_names, true); assert.deepEqual(config.compatibility_flags, [ "nodejs_compat", "global_fetch_strictly_public", ]); assert.equal(config.name, undefined); assert.equal(config.account_id, undefined); assert.equal(config.vars, undefined); assert.equal(config.env, undefined); assert.equal(config.migrations, undefined); assert.deepEqual(config.exports, { PersonalAgent: { type: "durable-object", storage: "sqlite" }, Sandbox: { type: "durable-object", storage: "sqlite" }, }); assert.deepEqual(config.durable_objects, source.durable_objects); assert.deepEqual(config.containers, source.containers); assert.equal(config.containers[0].image, manifest.shell.image); assert.match(manifest.shell.image, /0\.12\.9@sha256:[0-9a-f]{64}$/); assert.equal(config.containers[0].class_name, manifest.shell.className); assert.equal(config.containers[0].max_instances, manifest.shell.maxInstances); assert.equal(config.containers[0].instance_type, manifest.shell.instanceType); assert.equal( config.durable_objects.bindings[0].class_name, manifest.identity.durableObjectClass, ); assert.equal( config.durable_objects.bindings[0].name, manifest.identity.durableObjectBinding, ); assert.deepEqual(config.ai, { binding: "AI" }); assert.equal(config.browser.binding, "BROWSER"); assert.deepEqual(config.worker_loaders, [{ binding: "LOADER" }]); assert.ok( manifest.files.some( ({ path }) => path === config.main.replace(/^\.\//, ""), ), ); assert.ok( manifest.files.some( ({ path }) => path.startsWith("assets/") && path.endsWith(".js"), ), ); assert.ok(!manifest.files.some(({ path }) => path.endsWith(".map"))); assert.deepEqual( (await readdir("dist/release/worker")).filter((path) => path.endsWith(".map"), ), [], ); assert.doesNotMatch( await readFile("dist/release/worker/index.js", "utf8"), /sourceMappingURL=/, ); for (const file of manifest.files) { assert.match(file.path, /^(worker\/|assets\/|deployment\.json$)/); assert.ok(!file.path.split("/").includes("..")); const bytes = await readFile(`dist/release/${file.path}`); assert.equal(bytes.length, file.size, file.path); assert.equal(digest(bytes), file.sha256, file.path); assert.ok( !bytes.includes(customerBindings.FLAREBOT_SESSION_SECRET), file.path, ); if (file.path.startsWith("assets/")) { assert.ok( !bytes.includes("FLAREBOT_SESSION_SECRET"), "server configuration must not enter browser assets", ); } if (file.path.startsWith("worker/")) { assert.ok( !bytes.includes("__golden__") && !bytes.includes("TEST_RUNNER_PORT"), "golden-path fixture routes and boundaries must not enter the release", ); assert.ok( !bytes.includes("fixtureEcho"), "test tools must not enter the release", ); assert.ok( !bytes.includes("MockLanguageModel"), "test model must not enter the release", ); assert.ok( !bytes.includes("react.production"), "React runtime must not enter the Worker", ); assert.ok( !bytes.includes("FLAREBOT_OAUTH_CLIENT_SECRET"), "control-plane secret loader must not enter customer Worker", ); } } }); test("public fetch release retains legacy artifacts and requires an explicit forward edge", async () => { const source = parse(await readFile("wrangler.jsonc", "utf8")); const publisher = parse( await readFile("wrangler.control-plane.jsonc", "utf8"), ); const contract = await readJson("deployment/manifest.json"); assert.deepEqual(source.compatibility_flags, [ "nodejs_compat", "global_fetch_strictly_public", ]); assert.deepEqual(publisher.compatibility_flags, source.compatibility_flags); assert.equal(source.minify, true); assert.equal(publisher.minify, true); assert.equal(publisher.keep_names, true); assert.equal((await readJson("package.json")).version, "0.1.0-dev.30"); assert.ok( contract.compatibility.fromArtifacts.includes( "2fae16205095d79235a7a7de3feb37dad1e26c5076ea944dd1eb3a26bd13e559", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "fa06ea743f734b05016a0f63fe121565269aa094e72d54692983464e0d6c62cb", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "3d41d7f0274d186589e74749a28a4ecacd7887c72a2a56669de93d44e4ed71d5", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "228ba2535b9cf4fc2ab6953fe67d8fc1b41eb1af3a2e15d2d5fafe62580a18af", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "8ca6c35fcdfa9acac7df37a75b9a4aae8fd8f62764b31f223b6b0445a85e9f7f", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "4a57fe8b30dee3b4e3ce9eed571f6a2db022c630af4d2487c6948ff74daf4d84", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "514817aee0492898f391cb4bcd7d2bb6fdc131870d02057c7ee3f110f728a912", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "5a769faed06bc294ce6fdaa4835f20c9ca3edf7e4783084ec2355f7a4d61f303", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "3d4c2fe8987a0860aff853e539556777eec62056e0c7da9db61b8d080acbee4f", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "726defbcb2d839d90975984cb37eba63bde916e61f3ea0c949dbbd21efc196b4", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "fcb03b878db306f0c65963ac44b9020903a9448cd240b1e3b79c61cf14e4ce9e", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "5f53ae6df76bc08ae3ae319ad519ca4784d53ac342c08394da96c410bcffb12d", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "d6f29eeef087a45920ccc1c7f2ba07dcd9a7873aadbb102bc5c0e7f69e4e9ba4", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "cd703c946a3865211efe98e6e68e7d4b875ac6f825db8ad03711e5adf7c52843", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "fe4c23a4371d331dd7fe2c9c15a6f236bef06324cf999a998b27e832f12b1a31", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "ac05648bb6b8fe93d46018d1aa2b4fb08b0034005f9f10ed92314cadfe6d022d", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "a4e69efa2b85b88d02f51ebbaa47a0ea4c211afcbffada4b49354896f5a91a9d", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "2306652b8f72ad772243e3a24ba1164a59e966539082c9eccdac4346cd74ec5d", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "cb19b72d84999e8e0a71f165c94715bb2a62a9254ce064ee0b291d123253e020", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "f978451c3c8e3ef1a62d0c558059426f78a09e4b608b5e1df25d9c6c7b8b2a4f", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "7b52b11b9b2ddb492309607664ca229433dfb28968391634b32cdced1f87b65c", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "d4e44b5430b871ced18c624901e09ea19d0ee67215aeed9f8c1beed25d860bc2", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "ecf047e0c3cec1fc92429c7589801c7a7d5660d9ff65e9884667c29fda2e0281", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "4c0e2ed811f0f254890af50c92c73bd6ea9e224ae77cbca066e285f29ddf92ca", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "0f54bfd186c7577b8cfb69f88ff9f2e8540abd344a09d198c97a7619c76da208", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "ca98843d197f6a4fbaf8e1039beede3db706336ca4610bd20e1f8a8c6feb6c73", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "b48bc5126a2b2a7ba34f94c73ed0b524d52fb1d11ee62bf5a995424028ac3366", ), ); assert.ok( contract.compatibility.fromArtifacts.includes( "362dcc03f0a80013b96a4972f9a7192469738716d92ee90f12041865b1520068", ), ); async function entry(flags, version, fromArtifacts = []) { const deployment = await readJson("dist/release/deployment.json"); deployment.compatibility_flags = flags; const bytes = Object.fromEntries( Object.entries({ "deployment.json": JSON.stringify(deployment), "worker/index.js": "export default {};", "assets/index.html": "