mirror of
https://github.com/Termix-SSH/Termix.git
synced 2026-10-28 06:05:24 +00:00
103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import { describe, expect, it, vi } from "vitest";
|
|
import type { PluginFetchInit } from "@termix/plugin-sdk/backend";
|
|
import { createFetchAdapter } from "../../src/backend/acme.js";
|
|
import { createTxtRecord } from "../../src/backend/cloudflare.js";
|
|
|
|
describe("createFetchAdapter", () => {
|
|
it("sends acme-client requests through ctx.fetch", async () => {
|
|
const fetch = vi.fn(
|
|
async (_url: string, _init?: PluginFetchInit) =>
|
|
new Response('{"ok":true}', {
|
|
status: 201,
|
|
headers: { "replay-nonce": "n1", location: "https://ca/acct/1" },
|
|
}),
|
|
);
|
|
const adapter = createFetchAdapter(fetch, ["ca.internal"]);
|
|
const result = await adapter({
|
|
url: "https://ca.internal/new-acct",
|
|
method: "post",
|
|
headers: { "Content-Type": "application/jose+json" },
|
|
data: '{"payload":"x"}',
|
|
validateStatus: null,
|
|
});
|
|
|
|
expect(fetch).toHaveBeenCalledWith(
|
|
"https://ca.internal/new-acct",
|
|
expect.objectContaining({
|
|
method: "POST",
|
|
body: '{"payload":"x"}',
|
|
allowPrivateHosts: ["ca.internal"],
|
|
headers: { "Content-Type": "application/jose+json" },
|
|
}),
|
|
);
|
|
expect(result.status).toBe(201);
|
|
expect(result.data).toBe('{"ok":true}');
|
|
expect(result.headers["replay-nonce"]).toBe("n1");
|
|
});
|
|
|
|
it("rejects with the response attached when validateStatus says no", async () => {
|
|
const adapter = createFetchAdapter(
|
|
async () => new Response("slow down", { status: 429 }),
|
|
);
|
|
await expect(
|
|
adapter({ url: "https://ca/x", validateStatus: () => false }),
|
|
).rejects.toMatchObject({
|
|
code: "ERR_BAD_REQUEST",
|
|
response: { status: 429, data: "slow down" },
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("createTxtRecord", () => {
|
|
it("finds the zone by parent domain, creates the record and deletes it", async () => {
|
|
const calls: Array<{ url: string; method: string; body?: string }> = [];
|
|
const fetch = vi.fn(async (url: string, init?: PluginFetchInit) => {
|
|
calls.push({ url, method: init?.method ?? "GET", body: init?.body });
|
|
if (url.includes("/zones?name=example.com")) {
|
|
return Response.json({
|
|
success: true,
|
|
result: [{ id: "zone-1", name: "example.com" }],
|
|
});
|
|
}
|
|
if (url.includes("/zones?name=")) {
|
|
return Response.json({ success: true, result: [] });
|
|
}
|
|
if (init?.method === "POST") {
|
|
return Response.json({ success: true, result: { id: "rec-1" } });
|
|
}
|
|
return Response.json({ success: true, result: {} });
|
|
});
|
|
|
|
const remove = await createTxtRecord(
|
|
fetch,
|
|
"token",
|
|
"_acme-challenge.termix.example.com",
|
|
"digest",
|
|
);
|
|
const post = calls.find((call) => call.method === "POST")!;
|
|
expect(post.url).toContain("/zones/zone-1/dns_records");
|
|
expect(JSON.parse(post.body!)).toMatchObject({
|
|
type: "TXT",
|
|
name: "_acme-challenge.termix.example.com",
|
|
content: "digest",
|
|
});
|
|
|
|
await remove();
|
|
expect(calls.at(-1)).toMatchObject({
|
|
method: "DELETE",
|
|
url: expect.stringContaining("/zones/zone-1/dns_records/rec-1"),
|
|
});
|
|
});
|
|
|
|
it("surfaces the Cloudflare error message", async () => {
|
|
const fetch = async () =>
|
|
Response.json(
|
|
{ success: false, errors: [{ message: "Invalid token" }] },
|
|
{ status: 403 },
|
|
);
|
|
await expect(
|
|
createTxtRecord(fetch, "bad", "_acme-challenge.a.example", "d"),
|
|
).rejects.toThrow(/Invalid token/);
|
|
});
|
|
});
|