feat : big update
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { HttpError } from "@/lib/security/http";
|
||||
|
||||
const requireAdmin = vi.fn();
|
||||
const update = vi.fn();
|
||||
const set = vi.fn();
|
||||
const where = vi.fn();
|
||||
const returning = vi.fn();
|
||||
const notifyCommission = vi.fn();
|
||||
|
||||
vi.mock("@/lib/auth/server", () => ({ requireAdmin }));
|
||||
vi.mock("@/db", () => ({ getDb: () => ({ update }) }));
|
||||
vi.mock("@/lib/commission/server", () => ({ notifyCommission }));
|
||||
|
||||
const { PATCH } = await import("./route");
|
||||
const ticketId = "11111111-1111-4111-8111-111111111111";
|
||||
const userId = "customer-1";
|
||||
|
||||
function request(title: unknown, origin = "https://guide.sudloh.com") {
|
||||
return new Request(`https://guide.sudloh.com/api/commission/tickets/${ticketId}/title`, {
|
||||
method: "PATCH", headers: { Origin: origin, "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ title }),
|
||||
});
|
||||
}
|
||||
|
||||
function context() {
|
||||
return { params: Promise.resolve({ id: ticketId }) } as RouteContext<"/api/commission/tickets/[id]/title">;
|
||||
}
|
||||
|
||||
describe("commission ticket rename", () => {
|
||||
beforeEach(() => {
|
||||
process.env.BETTER_AUTH_URL = "https://guide.sudloh.com";
|
||||
vi.clearAllMocks();
|
||||
requireAdmin.mockResolvedValue({ user: { id: "admin-1" } });
|
||||
update.mockReturnValue({ set });
|
||||
set.mockReturnValue({ where });
|
||||
where.mockReturnValue({ returning });
|
||||
returning.mockResolvedValue([{ title: "New name", userId }]);
|
||||
notifyCommission.mockResolvedValue(undefined);
|
||||
});
|
||||
|
||||
it("rejects cross-origin and non-admin requests", async () => {
|
||||
expect((await PATCH(request("New name", "https://example.com"), context())).status).toBe(403);
|
||||
expect(requireAdmin).not.toHaveBeenCalled();
|
||||
requireAdmin.mockRejectedValueOnce(new HttpError(401, "unauthorized"));
|
||||
expect((await PATCH(request("New name"), context())).status).toBe(401);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("normalizes and saves a name, then notifies ticket viewers", async () => {
|
||||
const response = await PATCH(request(" New name "), context());
|
||||
expect(response.status).toBe(200);
|
||||
expect(set).toHaveBeenCalledWith({ title: "New name", updatedAt: expect.any(Date) });
|
||||
expect(notifyCommission).toHaveBeenCalledWith(ticketId, userId);
|
||||
});
|
||||
|
||||
it("allows clearing the name and rejects names over 80 characters", async () => {
|
||||
returning.mockResolvedValueOnce([{ title: null, userId }]);
|
||||
expect((await PATCH(request(" "), context())).status).toBe(200);
|
||||
expect(set).toHaveBeenCalledWith({ title: null, updatedAt: expect.any(Date) });
|
||||
vi.clearAllMocks();
|
||||
expect((await PATCH(request("x".repeat(81)), context())).status).toBe(400);
|
||||
expect(update).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { eq } from "drizzle-orm";
|
||||
import * as z from "zod";
|
||||
import { getDb } from "@/db";
|
||||
import { commissionTickets } from "@/db/schema";
|
||||
import { requireAdmin } from "@/lib/auth/server";
|
||||
import { notifyCommission } from "@/lib/commission/server";
|
||||
import { errorResponse, HttpError, readJson, requireSameOrigin } from "@/lib/security/http";
|
||||
|
||||
const inputSchema = z.object({ title: z.string().trim().max(80) });
|
||||
|
||||
export async function PATCH(request: Request, context: RouteContext<"/api/commission/tickets/[id]/title">) {
|
||||
try {
|
||||
requireSameOrigin(request);
|
||||
await requireAdmin();
|
||||
const { id } = await context.params;
|
||||
const parsed = inputSchema.safeParse(await readJson(request, 512));
|
||||
if (!parsed.success) throw new HttpError(400, "invalid-ticket-title");
|
||||
const title = parsed.data.title.replace(/\s+/gu, " ") || null;
|
||||
const [ticket] = await getDb().update(commissionTickets).set({ title, updatedAt: new Date() })
|
||||
.where(eq(commissionTickets.id, id)).returning({ title: commissionTickets.title, userId: commissionTickets.userId });
|
||||
if (!ticket) throw new HttpError(404, "ticket-not-found");
|
||||
await notifyCommission(id, ticket.userId);
|
||||
return Response.json({ title: ticket.title });
|
||||
} catch (cause) { return errorResponse(cause); }
|
||||
}
|
||||
Reference in New Issue
Block a user