import { afterEach, describe, expect, it, vi } from "vitest"; import { buildApp } from "../src/app.js"; import { loadConfig } from "../src/config.js"; import { toCloudflareCron } from "../src/services/health/mailbox.js"; import { HEALTH_PROBE_SCRIPT_NAME } from "@cfdm/shared"; async function authHeaders(app: Awaited>) { const res = await app.inject({ method: "POST", url: "/api/v1/auth/login", payload: { username: "admin", password: "admin" }, }); expect(res.statusCode).toBe(200); const { token } = res.json() as { token: string }; return { authorization: `Bearer ${token}` }; } function jsonOk(result: unknown, status = 200): Response { return new Response(JSON.stringify({ success: true, result }), { status, headers: { "content-type": "application/json" }, }); } describe("health worker deploy", () => { afterEach(() => { vi.unstubAllGlobals(); }); it("maps 6-field toad cron to 5-field Cloudflare cron", () => { expect(toCloudflareCron("0 */2 * * * *")).toBe("*/2 * * * *"); expect(toCloudflareCron("*/5 * * * *")).toBe("*/5 * * * *"); }); it("POST ensure creates KV+script; 403 is not local fallback", async () => { const calls: string[] = []; vi.stubGlobal( "fetch", async (input: RequestInfo | URL, init?: RequestInit) => { const url = typeof input === "string" || input instanceof URL ? String(input) : input.url; const method = ( init?.method ?? (typeof Request !== "undefined" && input instanceof Request ? input.method : "GET") ).toUpperCase(); calls.push(`${method} ${url}`); if ( (url.includes("/accounts?") || /\/accounts$/.test(url.split("?")[0] ?? "")) && !url.includes("/storage/") && !url.includes("/workers/") ) { return jsonOk([{ id: "acc-1", name: "Test" }]); } if (url.includes("/storage/kv/namespaces") && method === "GET" && !url.includes("/values/")) { return jsonOk([]); } if (url.includes("/storage/kv/namespaces") && method === "POST") { return jsonOk({ id: "kv-1", title: "cfdm-health-probe" }); } if ( url.includes(`/workers/scripts/${HEALTH_PROBE_SCRIPT_NAME}`) && method === "PUT" && !url.includes("/schedules") ) { return jsonOk({ id: "script-1" }); } if (url.includes("/schedules") && method === "PUT") { return jsonOk([{ cron: "*/2 * * * *" }]); } if (url.includes("/subdomain") && method === "POST") { return jsonOk({ enabled: true }); } if (url.includes("/workers/subdomain") && method === "GET") { return jsonOk({ subdomain: "example" }); } if (url.includes("/values/") && method === "GET") { return new Response("null", { status: 404 }); } if (url.includes("/values/") && method === "PUT") { return jsonOk(null); } return jsonOk({}); }, ); const app = await buildApp({ config: { ...loadConfig(), staticDir: null, cloudflareApiToken: "cf-token" }, memory: true, }); const headers = await authHeaders(app); const res = await app.inject({ method: "POST", url: "/api/v1/settings/health/worker/ensure", headers, }); expect(res.statusCode).toBe(200); const body = res.json() as { healthWorkerStatus: string; healthWorkerUrl: string; healthWorkerKvNamespaceId: string; healthWorkerError: string | null; }; expect(body.healthWorkerStatus).toBe("ready"); expect(body.healthWorkerKvNamespaceId).toBe("kv-1"); expect(body.healthWorkerUrl).toContain("cfdm-health-probe.example.workers.dev"); expect(body.healthWorkerError).toBeNull(); expect(calls.some((c) => c.includes("/workers/scripts/"))).toBe(true); await app.close(); }, 20_000); it("POST ensure 403 stores error, does not probe as local", async () => { vi.stubGlobal( "fetch", async (input: RequestInfo | URL) => { const url = String(input); if (url.includes("/storage/kv/namespaces")) { return new Response( JSON.stringify({ success: false, errors: [{ code: 10000, message: "Authentication error" }], }), { status: 403, headers: { "content-type": "application/json" } }, ); } if (url.includes("/accounts")) { return jsonOk([{ id: "acc-1" }]); } return jsonOk({}); }, ); const app = await buildApp({ config: { ...loadConfig(), staticDir: null, cloudflareApiToken: "zone-only" }, memory: true, }); const headers = await authHeaders(app); const res = await app.inject({ method: "POST", url: "/api/v1/settings/health/worker/ensure", headers, }); expect(res.statusCode).toBe(401); const again = await app.inject({ method: "GET", url: "/api/v1/settings", headers, }); const body = again.json() as { healthWorkerStatus: string; healthWorkerError: string | null; }; expect(body.healthWorkerStatus).toBe("error"); expect(body.healthWorkerError).toMatch(/Workers Scripts Write|токен/i); await app.close(); }, 20_000); });