feat: add extensive test suite

This commit is contained in:
2026-08-30 11:33:53 +07:00 Unverified
parent 061cce00dc
commit 8359c7272e
131 changed files with 8717 additions and 2200 deletions
+69
View File
@@ -0,0 +1,69 @@
import { describe, expect, mock, test } from "bun:test";
import { createDonateHeartbeatHandler } from "./handler";
function dependencies() {
return {
authorize: mock(() => {}),
resumeDonation: mock(async (): Promise<unknown | undefined> => undefined),
publishHeartbeat: mock(() => {}),
queue: mock((task: () => void) => task()),
};
}
describe("donation heartbeat handler", () => {
test("authorizes before heartbeat side effects", async () => {
const deps = dependencies();
deps.authorize.mockImplementation(() => {
throw new Error("Unauthorized");
});
const response = await createDonateHeartbeatHandler(deps)(
new Request("https://buzz.test/api/donate/hb?tag=42&key=wrong"),
);
expect(response.status).toBe(401);
expect(await response.json()).toEqual({ error: "Unauthorized" });
expect(deps.authorize).toHaveBeenCalledWith("wrong");
expect(deps.publishHeartbeat).not.toHaveBeenCalled();
expect(deps.queue).not.toHaveBeenCalled();
});
test("rejects an invalid tag without side effects", async () => {
const deps = dependencies();
const response = await createDonateHeartbeatHandler(deps)(
new Request("https://buzz.test/api/donate/hb?tag=nope"),
);
expect(response.status).toBe(400);
expect(deps.publishHeartbeat).not.toHaveBeenCalled();
expect(deps.queue).not.toHaveBeenCalled();
});
test("publishes and queues a resume check for a normal heartbeat", async () => {
const deps = dependencies();
const response = await createDonateHeartbeatHandler(deps)(
new Request("https://buzz.test/api/donate/hb?tag=42"),
);
expect(await response.json()).toEqual({ success: true });
expect(deps.publishHeartbeat).toHaveBeenCalledWith(42);
expect(deps.queue).toHaveBeenCalledTimes(1);
expect(deps.resumeDonation).toHaveBeenCalledTimes(1);
});
test("returns a resumed donation with the existing redirect status", async () => {
const deps = dependencies();
deps.resumeDonation.mockResolvedValue({ id: "donation-1", amount: 50 });
const response = await createDonateHeartbeatHandler(deps)(
new Request("https://buzz.test/api/donate/hb?tag=7&resume=true"),
);
expect(response.status).toBe(302);
expect(await response.json()).toEqual({ id: "donation-1", amount: 50 });
expect(deps.queue).not.toHaveBeenCalled();
});
test("returns success when an explicit resume finds nothing", async () => {
const deps = dependencies();
const response = await createDonateHeartbeatHandler(deps)(
new Request("https://buzz.test/api/donate/hb?tag=1&resume=true"),
);
expect(response.status).toBe(200);
expect(await response.json()).toEqual({ success: true });
});
});
+35
View File
@@ -0,0 +1,35 @@
type HeartbeatDependencies = {
authorize: (credential: string | null) => void;
resumeDonation: () => Promise<unknown | undefined>;
publishHeartbeat: (tag: number) => unknown;
queue: (task: () => void) => void;
};
export function createDonateHeartbeatHandler(
dependencies: HeartbeatDependencies,
) {
return async function PATCH(req: Request) {
const params = new URL(req.url).searchParams;
try {
dependencies.authorize(params.get("key"));
} catch {
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
const tag = Number(params.get("tag") ?? "abc");
const resume = params.get("resume") === "true";
if (Number.isNaN(tag))
return Response.json({ error: "Invalid Tag" }, { status: 400 });
if (!resume)
dependencies.queue(() => {
void dependencies.resumeDonation();
});
dependencies.publishHeartbeat(tag);
if (resume) {
const donation = await dependencies.resumeDonation();
if (donation) return Response.json(donation, { status: 302 });
}
return Response.json({ success: true });
};
}
+11 -13
View File
@@ -1,10 +1,12 @@
import { and, asc, not, sql } from "drizzle-orm";
import type { NextRequest } from "next/server";
import { requireWidgetCredential } from "@/app/(ui)/donate/service";
import { db } from "@/lib/db";
import { donations } from "@/lib/db/schema";
import { sse } from "@/lib/db/sse-endpoints";
import { getPostHogClient } from "@/lib/posthog-server";
import { fileToDataUrl } from "@/lib/utils";
import { createDonateHeartbeatHandler } from "./handler";
async function runResume() {
const [havent] = await db
@@ -35,18 +37,14 @@ async function runResume() {
return result;
}
export async function PATCH(req: NextRequest) {
const tag = Number(req.nextUrl.searchParams.get("tag") ?? "abc");
const resume = req.nextUrl.searchParams.get("resume") === "true";
if (Number.isNaN(tag))
return Response.json({ error: "Invalid Tag" }, { status: 400 });
const handler = createDonateHeartbeatHandler({
authorize: (credential) =>
requireWidgetCredential(credential, process.env.DONATE_WIDGET_KEY),
resumeDonation: runResume,
publishHeartbeat: (tag) => sse.donate.pub("heartbeat", tag),
queue: queueMicrotask,
});
if (!resume) queueMicrotask(runResume);
sse.donate.pub("heartbeat", tag);
if (resume) {
const res = await runResume();
if (res) return Response.json(res, { status: 302 });
}
return Response.json({ success: true });
export function PATCH(req: NextRequest) {
return handler(req);
}