Files
HiveGrid-Wireframes/03 Quellen und Referenzen/hivegrid-cloud/apps/web/app/utils/integrations/hrRequestService.m05.test.ts
T

180 lines
5.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
const mocks = vi.hoisted(() => ({
tx: {
hrRequest: { create: vi.fn(), update: vi.fn() },
hrRequestTemplate: { findFirst: vi.fn() },
form: { findFirst: vi.fn(), findMany: vi.fn(), upsert: vi.fn() },
formSubmission: { create: vi.fn() },
activity: { create: vi.fn() },
},
}));
vi.mock("~/utils/db/.server/tenantRls.server", () => ({
withTenantRLS: async (_tenantId: string, callback: (tx: typeof mocks.tx) => Promise<unknown>) => callback(mocks.tx),
}));
vi.mock("@hivegrid/db/audit-events.server", () => ({ createAuditEvent: vi.fn().mockResolvedValue(undefined) }));
vi.mock("@hivegrid/db/notifications.server", () => ({ createNotificationDelivery: vi.fn().mockResolvedValue(undefined) }));
import { listLinkableHrRequestForms, submitRequest } from "./hrRequestService.server";
import { buildHrRequestFormDefinition } from "./hrM05Forms";
const createdRequest = {
id: "request-1",
tenantId: "tenant-a",
employeeUserId: "user-employee",
templateId: "template-1",
requestKind: "hardware",
title: "Neues Notebook",
details: '{"budget":"1200"}',
formSubmissionId: null,
status: "submitted",
assignedApproverUserId: null,
};
describe("HR request M05 form reuse (AK-P16c)", () => {
beforeEach(() => {
vi.clearAllMocks();
mocks.tx.hrRequest.create.mockResolvedValue({ ...createdRequest });
mocks.tx.hrRequest.update.mockImplementation(async ({ data }: { data: Record<string, unknown> }) => ({ ...createdRequest, ...data }));
mocks.tx.hrRequestTemplate.findFirst.mockResolvedValue({
id: "template-1",
title: "Hardware",
requestKind: "hardware",
defaultApproverUserId: null,
linkedFormId: "form-1",
});
mocks.tx.form.findFirst.mockResolvedValue({
id: "form-1",
definition: buildHrRequestFormDefinition(),
});
mocks.tx.form.upsert.mockResolvedValue({ id: "system-form-1" });
mocks.tx.formSubmission.create.mockResolvedValue({ id: "submission-1" });
mocks.tx.activity.create.mockResolvedValue({ id: "activity-1" });
});
it("persists a FormSubmission and wires formSubmissionId when the template links a form", async () => {
const row = await submitRequest({
tenantId: "tenant-a",
employeeUserId: "user-employee",
templateId: "template-1",
requestKind: "hardware",
title: "Neues Notebook",
details: '{"budget":"1200"}',
formValues: {
requestKind: "hardware",
title: "Neues Notebook",
details: '{"budget":"1200"}',
},
});
expect(mocks.tx.formSubmission.create).toHaveBeenCalledWith({
data: {
formId: "form-1",
tenantId: "tenant-a",
createdByUserId: "user-employee",
payload: { values: { title: "Neues Notebook", details: '{"budget":"1200"}', requestKind: "hardware" } },
status: "received",
linkedEntityType: "hr_request",
linkedEntityId: "request-1",
},
select: { id: true },
});
expect(mocks.tx.hrRequest.update).toHaveBeenCalledWith({ where: { id: "request-1" }, data: { formSubmissionId: "submission-1" } });
expect(row.formSubmissionId).toBe("submission-1");
});
it("fails closed when a selected template has no linked form", async () => {
mocks.tx.hrRequestTemplate.findFirst
.mockResolvedValueOnce({
id: "template-1",
title: "Hardware",
requestKind: "hardware",
defaultApproverUserId: null,
})
.mockResolvedValueOnce({ linkedFormId: null });
await expect(
submitRequest({
tenantId: "tenant-a",
employeeUserId: "user-employee",
templateId: "template-1",
requestKind: "hardware",
title: "Neues Notebook",
formValues: {
requestKind: "hardware",
title: "Neues Notebook",
details: "",
},
}),
).rejects.toThrow("hr.m05.templateFormRequired");
expect(mocks.tx.formSubmission.create).not.toHaveBeenCalled();
expect(mocks.tx.hrRequest.update).not.toHaveBeenCalled();
});
it("fails closed when the linked form no longer exists", async () => {
mocks.tx.form.findFirst.mockResolvedValue(null);
await expect(
submitRequest({
tenantId: "tenant-a",
employeeUserId: "user-employee",
templateId: "template-1",
requestKind: "hardware",
title: "Neues Notebook",
formValues: {
requestKind: "hardware",
title: "Neues Notebook",
details: "",
},
}),
).rejects.toThrow("hr.m05.templateFormUnavailable");
expect(mocks.tx.formSubmission.create).not.toHaveBeenCalled();
expect(mocks.tx.hrRequest.update).not.toHaveBeenCalled();
});
it("uses the system M05 form for a request without a template", async () => {
mocks.tx.hrRequest.create.mockResolvedValue({
...createdRequest,
templateId: null,
});
await submitRequest({
tenantId: "tenant-a",
employeeUserId: "user-employee",
requestKind: "hardware",
title: "Neues Notebook",
formValues: {
requestKind: "hardware",
title: "Neues Notebook",
details: "",
},
});
expect(mocks.tx.form.upsert).toHaveBeenCalled();
expect(mocks.tx.formSubmission.create).toHaveBeenCalledWith({
data: expect.objectContaining({
formId: "system-form-1",
linkedEntityType: "hr_request",
linkedEntityId: "request-1",
}),
select: { id: true },
});
});
it("lists only published hr_request forms as linkable", async () => {
mocks.tx.form.findMany.mockResolvedValue([{ id: "form-1", name: "Spesenantrag" }]);
const forms = await listLinkableHrRequestForms("tenant-a");
expect(mocks.tx.form.findMany).toHaveBeenCalledWith({
where: { tenantId: "tenant-a", subtype: "hr_request", status: "published" },
select: { id: true, name: true },
orderBy: { name: "asc" },
});
expect(forms).toEqual([{ id: "form-1", name: "Spesenantrag" }]);
});
});