89 lines
3.2 KiB
TypeScript
89 lines
3.2 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const mocks = vi.hoisted(() => ({
|
|
audit: vi.fn(),
|
|
crypto: {
|
|
encrypt: vi.fn(async (_tenantId: string, value: string) => `cipher:${value}`),
|
|
decrypt: vi.fn(async (_tenantId: string, value: string) => value.replace("cipher:", "")),
|
|
},
|
|
tx: {
|
|
employeeSickLeaveReport: {
|
|
findFirst: vi.fn(),
|
|
update: vi.fn(),
|
|
},
|
|
file: { findFirst: vi.fn() },
|
|
fileEntityLink: { upsert: vi.fn() },
|
|
},
|
|
}));
|
|
|
|
vi.mock("~/utils/db/.server/tenantRls.server", () => ({
|
|
withTenantRLS: (_tenantId: string, callback: (tx: typeof mocks.tx) => unknown) => callback(mocks.tx),
|
|
}));
|
|
vi.mock("@hivegrid/db/audit-events.server", () => ({ createAuditEvent: mocks.audit }));
|
|
vi.mock("./hrFieldCrypto.server", () => ({ getHrFieldCrypto: () => mocks.crypto }));
|
|
|
|
import {
|
|
attachCertificate,
|
|
cancelSickLeave,
|
|
closeSickLeave,
|
|
revealCertificateFileId,
|
|
} from "./hrSickLeaveService.server";
|
|
|
|
const report = {
|
|
id: "report-1",
|
|
tenantId: "tenant-1",
|
|
employeeUserId: "employee-1",
|
|
reportedByUserId: "employee-1",
|
|
startDate: new Date("2026-07-20"),
|
|
expectedReturnDate: null,
|
|
returnDate: null,
|
|
certificateRefCiphertext: "cipher:file-1",
|
|
status: "reported",
|
|
note: null,
|
|
calendarEventId: null,
|
|
createdAt: new Date("2026-07-20"),
|
|
updatedAt: new Date("2026-07-20"),
|
|
};
|
|
|
|
describe("sick-leave object access", () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mocks.tx.employeeSickLeaveReport.findFirst.mockResolvedValue(report);
|
|
mocks.tx.employeeSickLeaveReport.update.mockResolvedValue(report);
|
|
mocks.tx.file.findFirst.mockResolvedValue({ id: "file-2" });
|
|
mocks.tx.fileEntityLink.upsert.mockResolvedValue({ id: "link-1" });
|
|
mocks.audit.mockResolvedValue({ id: "audit-1" });
|
|
});
|
|
|
|
it.each([
|
|
["attach", () => attachCertificate({ tenantId: "tenant-1", reportId: "report-1", actorUserId: "other-user", certificateFileId: "file-2", canManageAny: false })],
|
|
["reveal", () => revealCertificateFileId({ tenantId: "tenant-1", reportId: "report-1", revealerUserId: "other-user", canManageAny: false })],
|
|
["close", () => closeSickLeave({ tenantId: "tenant-1", reportId: "report-1", actorUserId: "other-user", returnDate: new Date("2026-07-22"), canManageAny: false })],
|
|
["cancel", () => cancelSickLeave({ tenantId: "tenant-1", reportId: "report-1", actorUserId: "other-user", canManageAny: false })],
|
|
])("returns not-found semantics for a foreign report on %s", async (_name, action) => {
|
|
await expect(action()).rejects.toThrow();
|
|
expect(mocks.tx.employeeSickLeaveReport.update).not.toHaveBeenCalled();
|
|
expect(mocks.audit).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("allows the owner and an explicitly authorised HR writer", async () => {
|
|
await expect(
|
|
revealCertificateFileId({
|
|
tenantId: "tenant-1",
|
|
reportId: "report-1",
|
|
revealerUserId: "employee-1",
|
|
canManageAny: false,
|
|
}),
|
|
).resolves.toBe("file-1");
|
|
await expect(
|
|
closeSickLeave({
|
|
tenantId: "tenant-1",
|
|
reportId: "report-1",
|
|
actorUserId: "hr-user",
|
|
returnDate: new Date("2026-07-22"),
|
|
canManageAny: true,
|
|
}),
|
|
).resolves.toBeUndefined();
|
|
});
|
|
});
|