feat : big update
CI / Verify (push) Successful in 3m14s
CI / Build immutable images and deploy (push) Successful in 4m0s

This commit is contained in:
2026-10-01 18:37:15 +07:00 Unverified
parent c5037154cb
commit 0022625edb
76 changed files with 18671 additions and 29 deletions
@@ -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();
});
});