157 lines
5.9 KiB
TypeScript
157 lines
5.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import {
|
|
buildSchemaVerificationSql,
|
|
normalizeDeploymentUrl,
|
|
parseCapturedDeploymentTargets,
|
|
parseCapturedDeploymentUrls,
|
|
parseD1Rows,
|
|
parseJsonOutput,
|
|
parseSecretNames,
|
|
REQUIRED_TABLES,
|
|
resolveDeploymentUrl,
|
|
verifyCloudflareWorkerHealth,
|
|
} from "../scripts/verify-deployment.mjs";
|
|
import {
|
|
parseWranglerDeploymentUrls,
|
|
parseWranglerDeploymentVersionId,
|
|
shouldCaptureDeploymentTargets,
|
|
} from "../scripts/wrangler-runner.mjs";
|
|
|
|
describe("deployment verification", () => {
|
|
test("checks the remote schema tables required by current application code", () => {
|
|
const sql = buildSchemaVerificationSql();
|
|
for (const table of REQUIRED_TABLES) {
|
|
expect(sql).toContain(`'${table}'`);
|
|
}
|
|
});
|
|
|
|
test("parses Wrangler D1 JSON output", () => {
|
|
expect(parseD1Rows(JSON.stringify([{ results: [{ name: "users" }] }]))).toEqual([{ name: "users" }]);
|
|
});
|
|
|
|
test("reports empty Wrangler JSON output clearly", () => {
|
|
expect(() => parseJsonOutput("", "the remote D1 schema")).toThrow(
|
|
"Wrangler returned no JSON while checking the remote D1 schema.",
|
|
);
|
|
});
|
|
|
|
test("treats an empty D1 result as zero rows", () => {
|
|
expect(parseD1Rows("")).toEqual([]);
|
|
});
|
|
|
|
test("parses current and wrapped Wrangler secret output", () => {
|
|
expect(parseSecretNames(JSON.stringify([{ name: "EDGE_EVER_AUTH_PASSWORD" }]))).toContain(
|
|
"EDGE_EVER_AUTH_PASSWORD",
|
|
);
|
|
expect(parseSecretNames(JSON.stringify({ result: [{ name: "EDGE_EVER_AUTH_PASSWORD_HASH" }] }))).toContain(
|
|
"EDGE_EVER_AUTH_PASSWORD_HASH",
|
|
);
|
|
});
|
|
|
|
test("captures public Workers and custom-domain URLs from Wrangler deploy output", () => {
|
|
expect(parseWranglerDeploymentUrls([
|
|
"Uploaded edgeever (2.1 sec)",
|
|
"Deployed edgeever triggers (0.4 sec)",
|
|
" https://edgeever.example.workers.dev",
|
|
" notes.example.com (custom domain)",
|
|
"Current Version ID: version-1",
|
|
].join("\n"))).toEqual([
|
|
"https://edgeever.example.workers.dev",
|
|
"https://notes.example.com",
|
|
]);
|
|
expect(parseWranglerDeploymentUrls("No targets deployed for edgeever")).toEqual([]);
|
|
expect(parseWranglerDeploymentVersionId([
|
|
"Uploaded edgeever (2.1 sec)",
|
|
"Current Version ID: version-1",
|
|
].join("\n"))).toBe("version-1");
|
|
});
|
|
|
|
test("captures deployment targets only in CI environments", () => {
|
|
expect(shouldCaptureDeploymentTargets({ WORKERS_CI: "1" })).toBe(true);
|
|
expect(shouldCaptureDeploymentTargets({ CI: " TRUE " })).toBe(true);
|
|
expect(shouldCaptureDeploymentTargets({})).toBe(false);
|
|
});
|
|
|
|
test("resolves an explicit or captured deployment URL without credentials or paths", () => {
|
|
expect(normalizeDeploymentUrl("notes.example.com/path")).toBe("https://notes.example.com");
|
|
expect(parseCapturedDeploymentUrls(JSON.stringify({
|
|
urls: ["https://edgeever.example.workers.dev"],
|
|
}))).toEqual(["https://edgeever.example.workers.dev"]);
|
|
expect(parseCapturedDeploymentTargets(JSON.stringify({
|
|
urls: ["https://edgeever.example.workers.dev"],
|
|
versionId: "version-1",
|
|
}))).toEqual({
|
|
urls: ["https://edgeever.example.workers.dev"],
|
|
versionId: "version-1",
|
|
});
|
|
expect(resolveDeploymentUrl({
|
|
env: { EDGE_EVER_DEPLOYMENT_URL: "https://notes.example.com/app" },
|
|
capturedUrls: ["https://edgeever.example.workers.dev"],
|
|
})).toBe("https://notes.example.com");
|
|
expect(resolveDeploymentUrl({
|
|
env: { EDGE_EVER_CUSTOM_DOMAIN: "notes.example.com" },
|
|
capturedUrls: [],
|
|
})).toBe("https://notes.example.com");
|
|
expect(() => normalizeDeploymentUrl("file:///tmp/edgeever")).toThrow("must use HTTP(S)");
|
|
});
|
|
|
|
test("verifies the live Worker health response", async () => {
|
|
let requestedUrl = "";
|
|
const result = await verifyCloudflareWorkerHealth({
|
|
deploymentUrl: "https://notes.example.com",
|
|
attempts: 1,
|
|
fetchImpl: async (url) => {
|
|
requestedUrl = String(url);
|
|
return Response.json({ ok: true, authMode: "required" });
|
|
},
|
|
});
|
|
|
|
expect(requestedUrl).toBe("https://notes.example.com/api/health");
|
|
expect(result.payload).toMatchObject({ ok: true });
|
|
});
|
|
|
|
test("requires a URL before requesting the live Worker", async () => {
|
|
expect(verifyCloudflareWorkerHealth({ deploymentUrl: "" })).rejects.toThrow(
|
|
"A deployment URL is required",
|
|
);
|
|
});
|
|
|
|
test("diagnoses a live Worker bound to a different unprepared D1 database", async () => {
|
|
expect(verifyCloudflareWorkerHealth({
|
|
deploymentUrl: "https://notes.example.com",
|
|
attempts: 1,
|
|
fetchImpl: async () => Response.json({
|
|
error: { code: "database_not_ready" },
|
|
}, { status: 503 }),
|
|
})).rejects.toThrow("may differ from the DB binding used by the live Worker");
|
|
});
|
|
|
|
test("diagnoses a live Worker without its R2 binding", async () => {
|
|
expect(verifyCloudflareWorkerHealth({
|
|
deploymentUrl: "https://notes.example.com",
|
|
attempts: 1,
|
|
fetchImpl: async () => Response.json({
|
|
error: { code: "object_storage_not_ready" },
|
|
}, { status: 503 }),
|
|
})).rejects.toThrow("RESOURCES binding points to the configured R2 bucket");
|
|
});
|
|
|
|
test("preserves Cloudflare runtime diagnostics for an unknown health failure", async () => {
|
|
const verification = verifyCloudflareWorkerHealth({
|
|
deploymentUrl: "https://notes.example.com",
|
|
deploymentVersionId: "version-1101",
|
|
attempts: 1,
|
|
fetchImpl: async () => new Response("error code: 1101", {
|
|
status: 500,
|
|
statusText: "Internal Server Error",
|
|
headers: { "CF-Ray": "ray-id-SJC" },
|
|
}),
|
|
});
|
|
|
|
await expect(verification).rejects.toThrow("response body: error code: 1101");
|
|
await expect(verification).rejects.toThrow("CF-Ray: ray-id-SJC");
|
|
await expect(verification).rejects.toThrow("Worker Version ID: version-1101");
|
|
await expect(verification).rejects.toThrow("Logs > Live");
|
|
});
|
|
});
|