| 1 | // Exercise the built Worker in workerd before it can be deployed. Storage is |
| 2 | // temporary and local; no service bindings or Cloudflare credentials are used. |
| 3 | import assert from "node:assert/strict"; |
| 4 | import { readFileSync, readdirSync } from "node:fs"; |
| 5 | import { join, resolve } from "node:path"; |
| 6 | import { Miniflare, convertV4MiniflareOptions } from "miniflare"; |
| 7 | |
| 8 | const root = resolve("build/server"); |
| 9 | const config = JSON.parse(readFileSync(join(root, "wrangler.json"), "utf8")); |
| 10 | const source = readFileSync(join(root, "index.js"), "utf8"); |
| 11 | const build = source.match(/searchParams\.set\("_g1t_build", "([^"]+)"\)/)?.[1]; |
| 12 | assert.ok(build, "The page cache must be separated by build"); |
| 13 | const css = "body{color:red}"; |
| 14 | // Seed R2 and the page cache through a test-only entry, keeping all R2 I/O |
| 15 | // inside workerd. The application entry itself is the unmodified build. |
| 16 | const seed = `import app from './index.js'; |
| 17 | import { WorkerEntrypoint } from 'cloudflare:workers'; |
| 18 | export class ScreenshotStub extends WorkerEntrypoint { |
| 19 | async image(input) { |
| 20 | if (input.host !== 'public-acme.g1t.page' || input.commit !== 'abcdef1234567') throw new Error('Unexpected screenshot target'); |
| 21 | return {body:new TextEncoder().encode('production-preview').buffer,contentType:'image/jpeg',commit:input.commit,capturedAt:'2026-10-15T02:00:00Z'}; |
| 22 | } |
| 23 | } |
| 24 | export default { async fetch(request, env, ctx) { |
| 25 | if (new URL(request.url).pathname === '/__seed') { |
| 26 | await env.DOWNLOADS.put('web-assets/assets/app-old12345.css', ${JSON.stringify(css)}, {httpMetadata:{contentType:'text/css'}}); |
| 27 | const cache = new URL('/pricing.data', request.url); |
| 28 | cache.searchParams.set('_g1t_build', ${JSON.stringify(build)}); |
| 29 | cache.searchParams.set('_g1t_representation', 'html'); |
| 30 | await caches.default.put(new Request(cache), new Response('wrong-build-cache', {headers:{'cache-control':'public, max-age=300','x-g1t-kept-at':String(Date.now())}})); |
| 31 | return new Response('seeded'); |
| 32 | } |
| 33 | return app.fetch(request, env, ctx); |
| 34 | }};`; |
| 35 | const files = [join(root, "index.js"), ...readdirSync(join(root, "assets")).filter((name) => name.endsWith(".js")).map((name) => join(root, "assets", name))]; |
| 36 | const runtime = new Miniflare(convertV4MiniflareOptions({ |
| 37 | name: "web-deploy-check", cf: false, modulesRoot: root, |
| 38 | modules: [{ type: "ESModule", path: join(root, "deploy-check.mjs"), contents: seed }, ...files.map((path) => ({ type: "ESModule", path }))], |
| 39 | compatibilityDate: config.compatibility_date, |
| 40 | compatibilityFlags: config.compatibility_flags, |
| 41 | r2Buckets: ["DOWNLOADS"], |
| 42 | serviceBindings: { |
| 43 | IDENTITY: async () => Response.json(null), |
| 44 | PROJECTS: async (request) => { |
| 45 | const method = new URL(request.url).pathname.split("/").pop(); |
| 46 | const args = await request.json(); |
| 47 | if (method !== "get") return Response.json({ ok: true, value: [] }); |
| 48 | if (args.slug !== "public") return Response.json({ ok: false, error: { code: "not_found", message: "Not found" } }); |
| 49 | return Response.json({ ok: true, value: { source: { kind: "hosted", repo: { namespace: "acme", name: "public" } } } }); |
| 50 | }, |
| 51 | DEPLOYMENTS: async (request) => { |
| 52 | const method = new URL(request.url).pathname.split("/").pop(); |
| 53 | const args = await request.json(); |
| 54 | assert.equal(method, "list_environments", "Public previews must not read member-only deployment lists"); |
| 55 | assert.equal(args.viewer, null); |
| 56 | assert.equal(args.repo.name, "public"); |
| 57 | const current = { id: "dpl_live", source: "g1t_page", state: "success", sha: "abcdef1234567", environment_url: "https://public-acme.g1t.page", updated_at: "2026-10-15T01:00:00Z" }; |
| 58 | return Response.json({ ok: true, value: { environments: [{ name: "production", production_environment: true, updated_at: current.updated_at, current, latest: { ...current, id: "dpl_next", sha: "bbbbbbb", state: "in_progress" } }] } }); |
| 59 | }, |
| 60 | SCREENSHOTS: { name: "web-deploy-check", entrypoint: "ScreenshotStub" }, |
| 61 | }, |
| 62 | })); |
| 63 | try { |
| 64 | const origin = await runtime.ready; |
| 65 | const request = (path, init) => fetch(new URL(path, origin), { ...init, signal: AbortSignal.timeout(15_000) }); |
| 66 | assert.equal((await request("/__seed")).status, 200); |
| 67 | const screenshot = await request("/acme/public/production.jpg?v=abcdef1234567-" + Date.parse("2026-10-15T01:00:00Z")); |
| 68 | assert.equal(screenshot.status, 200, "Signed-out readers can see public production screenshots"); |
| 69 | assert.equal(screenshot.headers.get("content-type"), "image/jpeg"); |
| 70 | assert.equal(screenshot.headers.get("cache-control"), "private, max-age=31536000, immutable"); |
| 71 | assert.equal(await screenshot.text(), "production-preview"); |
| 72 | assert.equal((await request("/acme/private/production.jpg")).status, 404, "Unreadable projects never expose screenshots"); |
| 73 | console.log("Public production screenshots: anonymous access, private-project denial, and serving commit selection verified."); |
| 74 | for (const [path, destination] of [["/developers", "https://docs.g1t.sh/reference/developers/"], ["/openapi.json", "https://api.g1t.sh/openapi.json"], ["/mcp", "https://mcp.g1t.sh"]]) { |
| 75 | const answer = await request(path, { redirect: "manual" }); |
| 76 | assert.equal(answer.status, 308); |
| 77 | assert.equal(answer.headers.get("location"), new URL(destination).href); |
| 78 | } |
| 79 | // Run Markdown first, then HTML at the same URL: the cache must not mix them. |
| 80 | for (const path of ["/", "/__agent-readiness-missing"]) { |
| 81 | for (const accept of ["text/markdown", "text/html", "text/markdown"]) { |
| 82 | const answer = await request(path, { headers: { accept } }); |
| 83 | assert.equal(answer.status, path === "/" ? 200 : 404); |
| 84 | assert.ok(answer.headers.get("vary")?.toLowerCase().split(",").map((value) => value.trim()).includes("accept")); |
| 85 | assert.ok(answer.headers.get("content-type")?.startsWith(accept)); |
| 86 | const body = await answer.text(); |
| 87 | if (accept === "text/markdown") { assert.match(body, /\[.*\]\(.*\/llms.txt\)/); assert.ok(body.length > 20); } |
| 88 | else { assert.match(body, /<html/); if (path === "/") assert.match(body, /rel="canonical"/); } |
| 89 | } |
| 90 | } |
| 91 | assert.equal((await request("/", { headers: { accept: "application/json" } })).status, 406); |
| 92 | for (const accept of ["text/markdown", "text/html"]) { |
| 93 | const answer = await request("/", { method: "HEAD", headers: { accept } }); |
| 94 | assert.equal(answer.status, 200); |
| 95 | assert.ok(answer.headers.get("content-type")?.startsWith(accept)); |
| 96 | assert.equal(await answer.text(), ""); |
| 97 | } |
| 98 | const features = await request("/features", { headers: { accept: "text/html" } }); |
| 99 | assert.equal(features.status, 200); |
| 100 | const featureHtml = await features.text(); |
| 101 | assert.match(featureHtml, /<h1[^>]*>From the first conversation to shipped code[.]<\/h1>/); |
| 102 | assert.match(featureHtml, /<title>g1t features/); |
| 103 | assert.match(featureHtml, /href="https:\/\/g1t.sh\/features"/); |
| 104 | assert.match(featureHtml, /href="\/features"[^>]*>Features/); |
| 105 | assert.match(featureHtml, /href="\/developers"/); |
| 106 | console.log("Features: public HTML, branded title, canonical URL and navigation verified."); |
| 107 | const old = await request("/assets/app-old12345.css"); |
| 108 | assert.equal(old.status, 200); |
| 109 | assert.equal(old.headers.get("content-type"), "text/css"); |
| 110 | assert.equal(await old.text(), css); |
| 111 | const missing = await request("/assets/app-missing0.css"); |
| 112 | assert.equal(missing.status, 404); |
| 113 | assert.equal(missing.headers.get("cache-control"), "no-store"); |
| 114 | assert.doesNotMatch(missing.headers.get("content-type") ?? "", /html/); |
| 115 | for (const path of ["/pricing.data", "/flagon-io/-/home.data?tab=one&_routes=root"]) { |
| 116 | const answer = await request(path, { headers: { "x-g1t-build": "previous-build" } }); |
| 117 | assert.equal(answer.status, 202); |
| 118 | const body = await answer.text(); |
| 119 | assert.match(body, /reload/); |
| 120 | assert.ok(body.includes(path.split(".data")[0])); |
| 121 | assert.doesNotMatch(body, /_routes|wrong-build-cache/); |
| 122 | } |
| 123 | console.log("Deployment safety: old CSS survives, missing assets stay isolated, old navigations bypass cache and loaders."); |
| 124 | console.log("Agent pages: Markdown/HTML negotiation, isolated caches, 404s, HEAD, canonical metadata and discovery redirects verified."); |
| 125 | } finally { |
| 126 | await runtime.dispose(); |
| 127 | } |