120 lines
3.2 KiB
TypeScript
120 lines
3.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { appRouter } from "./routers";
|
|
import * as db from "./db";
|
|
import type { TrpcContext } from "./_core/context";
|
|
|
|
vi.mock("./db", () => ({
|
|
findInspectionTaskByModelAndName: vi.fn(async () => undefined),
|
|
createInspectionTask: vi.fn(async (data) => ({
|
|
id: 1,
|
|
modelId: data.modelId,
|
|
name: data.name,
|
|
inspector: data.inspector ?? null,
|
|
notes: data.notes ?? null,
|
|
status: data.status,
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
completedAt: null,
|
|
})),
|
|
}));
|
|
|
|
type AuthenticatedUser = NonNullable<TrpcContext["user"]>;
|
|
|
|
function createAuthContext(): TrpcContext {
|
|
const user: AuthenticatedUser = {
|
|
id: 1,
|
|
openId: "test-user",
|
|
email: "test@example.com",
|
|
name: "Test User",
|
|
loginMethod: "manus",
|
|
role: "user",
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
lastSignedIn: new Date(),
|
|
};
|
|
|
|
return {
|
|
user,
|
|
req: {
|
|
protocol: "https",
|
|
headers: {},
|
|
} as TrpcContext["req"],
|
|
res: {
|
|
clearCookie: vi.fn(),
|
|
} as unknown as TrpcContext["res"],
|
|
};
|
|
}
|
|
|
|
describe("inspection task router", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
vi.mocked(db.findInspectionTaskByModelAndName).mockResolvedValue(undefined);
|
|
});
|
|
|
|
it("trims name and creates a task in created status", async () => {
|
|
const ctx = createAuthContext();
|
|
const caller = appRouter.createCaller(ctx);
|
|
|
|
const result = await caller.inspectionTask.create({
|
|
modelId: 1,
|
|
name: " 首检任务 ",
|
|
});
|
|
|
|
expect(result.status).toBe("created");
|
|
expect(db.findInspectionTaskByModelAndName).toHaveBeenCalledWith(1, "首检任务");
|
|
expect(db.createInspectionTask).toHaveBeenCalledWith({
|
|
modelId: 1,
|
|
name: "首检任务",
|
|
inspector: null,
|
|
notes: null,
|
|
status: "created",
|
|
});
|
|
});
|
|
|
|
it("returns CONFLICT when the same model already has the task name", async () => {
|
|
vi.mocked(db.findInspectionTaskByModelAndName).mockResolvedValueOnce({
|
|
id: 1,
|
|
modelId: 1,
|
|
name: "首检任务",
|
|
inspector: null,
|
|
notes: null,
|
|
status: "created",
|
|
createdAt: new Date(),
|
|
updatedAt: new Date(),
|
|
completedAt: null,
|
|
});
|
|
|
|
const ctx = createAuthContext();
|
|
const caller = appRouter.createCaller(ctx);
|
|
|
|
await expect(caller.inspectionTask.create({
|
|
modelId: 1,
|
|
name: "首检任务",
|
|
})).rejects.toMatchObject({
|
|
code: "CONFLICT",
|
|
message: "该车型下已存在同名检测任务。",
|
|
});
|
|
|
|
expect(db.createInspectionTask).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("returns CONFLICT when the database unique key rejects a concurrent duplicate", async () => {
|
|
vi.mocked(db.createInspectionTask).mockRejectedValueOnce({
|
|
code: "ER_DUP_ENTRY",
|
|
errno: 1062,
|
|
message: "Duplicate entry '1-首检任务' for key 'uk_inspection_tasks_model_name'",
|
|
});
|
|
|
|
const ctx = createAuthContext();
|
|
const caller = appRouter.createCaller(ctx);
|
|
|
|
await expect(caller.inspectionTask.create({
|
|
modelId: 1,
|
|
name: "首检任务",
|
|
})).rejects.toMatchObject({
|
|
code: "CONFLICT",
|
|
message: "该车型下已存在同名检测任务。",
|
|
});
|
|
});
|
|
});
|