-
-
Notifications
You must be signed in to change notification settings - Fork 0
test(pages): pin-staleness tripwire, chunk-closure check, deploy smoke #128
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -187,6 +187,98 @@ test("every jsDelivr-pinned landing asset exists in landing/assets", () => { | |
| } | ||
| }); | ||
|
|
||
| test("pinned landing chunks form a complete closure (no dangling imports)", () => { | ||
| // A pin can name an entry chunk that exists while one of its static imports does not — | ||
| // the shell then loads, the SPA 404s a chunk, and the site dies with a green build. | ||
| // Walk the import graph from the pinned entry (and the pinned CSS) through | ||
| // landing/assets and require every referenced file to exist on disk. History-free: | ||
| // works in shallow CI checkouts where git ancestry is unavailable. | ||
| const pins = [ | ||
| ...landing.matchAll( | ||
| /cdn\.jsdelivr\.net\/gh\/CodeWithJuber\/forgekit@[0-9a-f]{40}\/landing\/assets\/([^"']+)/g, | ||
| ), | ||
| ].map((m) => m[1]); | ||
| const entry = pins.find((f) => /^index-.*\.js$/.test(f)); | ||
| assert.ok(entry, "landing pins exactly one entry chunk"); | ||
| const seen = new Set(); | ||
| const queue = [entry]; | ||
| while (queue.length > 0) { | ||
| const file = queue.pop(); | ||
| if (seen.has(file)) continue; | ||
| seen.add(file); | ||
| const path = fileURLToPath(new URL(`../landing/assets/${file}`, import.meta.url)); | ||
| assert.ok(existsSync(path), `landing/assets/${file} is pinned/imported but missing`); | ||
| if (!file.endsWith(".js")) continue; | ||
| const src = readFileSync(path, "utf8"); | ||
| for (const m of src.matchAll(/(?:from|import)\s*["']\.\/([^"']+)["']/g)) queue.push(m[1]); | ||
| for (const m of src.matchAll(/import\(\s*["']\.\/([^"']+)["']\s*\)/g)) queue.push(m[1]); | ||
| } | ||
| }); | ||
|
|
||
| test("jsDelivr pin is never older than the newest landing/assets commit", async (t) => { | ||
| // The pin is only re-cut when a chunk actually changes — so the newest commit touching | ||
| // landing/assets/ must be the pinned commit itself or one of its ancestors. If someone | ||
| // commits rebuilt chunks without re-cutting the pin, the deployed site silently serves | ||
| // the old build with a green deploy, and only this check notices. Requires history; | ||
| // the quality gate checks out with fetch-depth: 0, which is where this bites. | ||
| const { execFileSync } = await import("node:child_process"); | ||
| const repoRoot = fileURLToPath(new URL("..", import.meta.url)); | ||
| const git = (args) => execFileSync("git", args, { cwd: repoRoot, encoding: "utf8" }).trim(); | ||
| if (git(["rev-parse", "--is-shallow-repository"]) === "true") { | ||
| t.skip("shallow checkout — pin-vs-assets ancestry needs fetch-depth: 0"); | ||
| return; | ||
| } | ||
| const pinSha = landing.match( | ||
| /cdn\.jsdelivr\.net\/gh\/CodeWithJuber\/forgekit@([0-9a-f]{40})\//, | ||
| )?.[1]; | ||
|
Comment on lines
+231
to
+233
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This extracts only the first jsDelivr SHA, which is currently the script pin, although the page has three independently editable pinned URLs. If rebuilt assets are committed and the script URL is updated while either CSS URL accidentally retains its old SHA, Useful? React with 👍 / 👎. |
||
| assert.ok(pinSha, "landing pins at least one asset to a full commit SHA"); | ||
| const newestAssets = git(["log", "-1", "--format=%H", "--", "landing/assets"]); | ||
| assert.ok(newestAssets, "landing/assets has at least one commit"); | ||
| try { | ||
| execFileSync("git", ["merge-base", "--is-ancestor", newestAssets, pinSha], { | ||
| cwd: repoRoot, | ||
| }); | ||
| } catch { | ||
| assert.fail( | ||
| `landing/assets changed in ${newestAssets.slice(0, 8)} after the pin was cut at ` + | ||
| `${pinSha.slice(0, 8)} — re-cut the jsDelivr pin in landing/index.html to the ` + | ||
| `newest chunk commit (the deployed site is serving stale chunks)`, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| test("deployed site serves the same chunks the repo pins", async (t) => { | ||
| if (process.env.RUN_INTEGRATION !== "1") { | ||
| t.skip("set RUN_INTEGRATION=1 to hit the deployed site"); | ||
| return; | ||
| } | ||
| // The end-to-end smoke: what Pages serves must equal what the repo pins. Catches a | ||
| // failed/partial deploy that every in-repo check is blind to. Retried like the | ||
| // build-time fetch in scripts/build-pages.mjs — a transient network blip must not | ||
| // masquerade as a deploy failure. | ||
| let res; | ||
| let lastErr; | ||
| for (let i = 0; i < 3 && !res; i++) { | ||
| try { | ||
| res = await fetch("https://codewithjuber.github.io/forgekit/"); | ||
| } catch (e) { | ||
|
Comment on lines
+261
to
+264
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
A resolved HTTP 429 or 5xx response assigns Useful? React with 👍 / 👎. |
||
| lastErr = e; | ||
| await new Promise((r) => setTimeout(r, 200 * 2 ** i)); | ||
| } | ||
| } | ||
| assert.ok(res, `deployed site unreachable after 3 attempts: ${lastErr}`); | ||
| assert.ok(res.ok, `deployed site returned HTTP ${res.status}`); | ||
| const deployed = await res.text(); | ||
| const repoPins = [ | ||
| ...landing.matchAll( | ||
| /cdn\.jsdelivr\.net\/gh\/CodeWithJuber\/forgekit@[0-9a-f]{40}\/landing\/assets\/[^"']+/g, | ||
| ), | ||
| ]; | ||
| assert.ok(repoPins.length > 0, "repo pins at least one asset"); | ||
| for (const [pin] of repoPins) | ||
| assert.ok(deployed.includes(pin), `deployed site is missing pinned asset ${pin}`); | ||
| }); | ||
|
|
||
| test("the generated status page is not shipped in the npm tarball", () => { | ||
| const { files } = JSON.parse(repo("package.json")); | ||
| assert.ok( | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test says it walks from the entry and pinned CSS, but the queue contains only the JavaScript entry and CSS files are never parsed. If a future pinned stylesheet adds a relative
@importorurl(...)dependency that is absent fromlanding/assets, this check remains green while browsers receive a 404 for that dependency. Seed the walk with all pinned assets and inspect relative CSS dependencies as well.Useful? React with 👍 / 👎.