feat : limit commis
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const requireCommissionUser = vi.fn();
|
||||
const limitRequest = vi.fn();
|
||||
const validateCommissionCatalog = vi.fn();
|
||||
const promptPayConfig = vi.fn();
|
||||
const transaction = vi.fn();
|
||||
const forUpdate = vi.fn();
|
||||
const returning = vi.fn();
|
||||
const insert = vi.fn();
|
||||
|
||||
vi.mock("@/lib/commission/server", () => ({ requireCommissionUser }));
|
||||
vi.mock("@/lib/security/rate-limit", () => ({ limitRequest }));
|
||||
vi.mock("@/lib/commission/catalog", () => ({ validateCommissionCatalog }));
|
||||
vi.mock("@/lib/commission/payment", () => ({ promptPayConfig }));
|
||||
vi.mock("@/db", () => ({ getDb: () => ({ transaction }) }));
|
||||
|
||||
const { POST } = await import("./route");
|
||||
|
||||
function request() {
|
||||
return new Request("https://guide.sudloh.com/api/commission/checkouts", {
|
||||
method: "POST", headers: { Origin: "https://guide.sudloh.com", "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ teams: [], weapons: [{ characterKey: "c", weaponKeys: ["w1", "w2"] }],
|
||||
constellations: [] }),
|
||||
});
|
||||
}
|
||||
|
||||
describe("commission checkout availability", () => {
|
||||
beforeEach(() => {
|
||||
process.env.BETTER_AUTH_URL = "https://guide.sudloh.com";
|
||||
vi.clearAllMocks();
|
||||
requireCommissionUser.mockResolvedValue({ id: "customer-1" });
|
||||
limitRequest.mockResolvedValue(undefined);
|
||||
validateCommissionCatalog.mockResolvedValue(true);
|
||||
promptPayConfig.mockReturnValue({ identifier: "test" });
|
||||
transaction.mockImplementation((callback) => callback({
|
||||
select: () => ({ from: () => ({ where: () => ({ for: forUpdate }) }) }),
|
||||
insert,
|
||||
}));
|
||||
insert.mockReturnValue({ values: () => ({ returning }) });
|
||||
returning.mockResolvedValue([{ id: "checkout-1" }]);
|
||||
});
|
||||
|
||||
it("rejects a stale form submission after closure", async () => {
|
||||
forUpdate.mockResolvedValue([{ isOpen: false }]);
|
||||
const response = await POST(request());
|
||||
expect(response.status).toBe(409);
|
||||
expect(await response.json()).toEqual({ error: "commission-closed" });
|
||||
expect(insert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("creates a checkout while open", async () => {
|
||||
forUpdate.mockResolvedValue([{ isOpen: true }]);
|
||||
const response = await POST(request());
|
||||
expect(response.status).toBe(201);
|
||||
expect(await response.json()).toEqual({ checkoutId: "checkout-1" });
|
||||
expect(insert).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getDb } from "@/db";
|
||||
import { commissionCheckouts } from "@/db/schema";
|
||||
import { commissionCheckouts, commissionSettings } from "@/db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { validateCommissionCatalog } from "@/lib/commission/catalog";
|
||||
import { promptPayConfig } from "@/lib/commission/payment";
|
||||
import { commissionPrice, commissionRequestSchema } from "@/lib/commission/request";
|
||||
@@ -16,9 +17,16 @@ export async function POST(request: Request) {
|
||||
if (!parsed.success || !(await validateCommissionCatalog(parsed.data)))
|
||||
throw new HttpError(400, "invalid-commission-request");
|
||||
promptPayConfig();
|
||||
const [checkout] = await getDb().insert(commissionCheckouts).values({
|
||||
userId: user.id, request: parsed.data, amountBaht: commissionPrice(parsed.data),
|
||||
}).returning({ id: commissionCheckouts.id });
|
||||
const checkout = await getDb().transaction(async (tx) => {
|
||||
const [settings] = await tx.select({ isOpen: commissionSettings.isOpen }).from(commissionSettings)
|
||||
.where(eq(commissionSettings.id, 1)).for("update");
|
||||
if (!settings) throw new Error("Commission settings are missing. Apply the database migration.");
|
||||
if (!settings.isOpen) throw new HttpError(409, "commission-closed");
|
||||
const [created] = await tx.insert(commissionCheckouts).values({
|
||||
userId: user.id, request: parsed.data, amountBaht: commissionPrice(parsed.data),
|
||||
}).returning({ id: commissionCheckouts.id });
|
||||
return created;
|
||||
});
|
||||
return Response.json({ checkoutId: checkout.id }, { status: 201, headers: { "Cache-Control": "no-store" } });
|
||||
} catch (cause) { return errorResponse(cause); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user