quality / changes (push) Successful in 9s
quality / commitlint (push) Skipped
quality / docker-check (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / web (push) Successful in 1m4s
quality / api (push) Successful in 54s
CD / quality (push) Successful in 2m17s
CD / publish (push) Successful in 2m21s
Worker сам ходит на origin по Cron Trigger; CFDM кладёт цели в KV и забирает результаты без POST /probe. Co-authored-by: Cursor <[email protected]>
161 lines
5.3 KiB
TypeScript
161 lines
5.3 KiB
TypeScript
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<ReturnType<typeof buildApp>>) {
|
|
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);
|
|
});
|