portail-associations/server/materialReturnWorkflow.test.ts

143 lines
4.5 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("./db", () => ({
getMaterialReturnFollowupByRequestId: vi.fn(),
upsertMaterialReturnFollowup: vi.fn(),
}));
vi.mock("./mailer", () => ({
sendOperationalEmail: vi.fn(),
}));
vi.mock("./materialReturnEmail", () => ({
generateMaterialReturnReminderEmail: vi.fn(),
}));
vi.mock("./materialReturnPdf", () => ({
generateMaterialReturnStatementPdf: vi.fn(),
}));
vi.mock("./logisticsSettings", () => ({
getLogisticsSettings: vi.fn(),
}));
import * as db from "./db";
import { getLogisticsSettings } from "./logisticsSettings";
import { scheduleMaterialReturnFollowup } from "./materialReturnWorkflow";
describe("scheduleMaterialReturnFollowup", () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(getLogisticsSettings).mockResolvedValue({
materialReturnLeadDays: 4,
materialReturnGraceDays: 3,
inventory: {
tente3x3: 12,
chapiteau5x5: 10,
podium: 1,
autres: null,
},
replacementValues: {
tente3x3: 0,
chapiteau5x5: 0,
podium: 0,
autres: null,
},
});
});
it("keeps the standard expiry window for a fresh follow-up", async () => {
vi.mocked(db.getMaterialReturnFollowupByRequestId).mockResolvedValue(undefined);
vi.mocked(db.upsertMaterialReturnFollowup).mockResolvedValue(1);
const request = {
id: 12,
associationId: 3,
formData: JSON.stringify({ dateRestitution: "2026-06-15" }),
} as any;
await scheduleMaterialReturnFollowup({
request,
serviceLabel: "Service technique",
recipientEmails: ["agent@example.com"],
});
expect(db.upsertMaterialReturnFollowup).toHaveBeenCalledTimes(1);
const payload = vi.mocked(db.upsertMaterialReturnFollowup).mock.calls[0]?.[1];
expect(payload.status).toBe("planifie");
expect(new Date(payload.uploadTokenExpiresAt).toISOString().startsWith("2026-06-18")).toBe(true);
});
it("reactivates an expired upload window when the follow-up is reassigned", async () => {
const expiredDate = new Date();
expiredDate.setDate(expiredDate.getDate() - 2);
expiredDate.setHours(23, 59, 59, 999);
vi.mocked(db.getMaterialReturnFollowupByRequestId).mockResolvedValue({
id: 4,
requestId: 12,
associationId: 3,
status: "en_cours",
uploadToken: "existing-token",
uploadTokenExpiresAt: expiredDate,
} as any);
vi.mocked(db.upsertMaterialReturnFollowup).mockResolvedValue(4);
const request = {
id: 12,
associationId: 3,
formData: JSON.stringify({ dateRestitution: "2026-05-01" }),
} as any;
await scheduleMaterialReturnFollowup({
request,
serviceLabel: "Service technique",
recipientEmails: ["agent@example.com"],
});
const payload = vi.mocked(db.upsertMaterialReturnFollowup).mock.calls[0]?.[1];
expect(payload.uploadToken).toBe("existing-token");
expect(new Date(payload.uploadTokenExpiresAt).getTime()).toBeGreaterThan(Date.now());
expect(payload.status).toBe("planifie");
});
it("generates a new token and a fresh upload window when reactivation is explicit", async () => {
const futureExpiry = new Date("2026-06-18T23:59:59.999Z");
vi.mocked(db.getMaterialReturnFollowupByRequestId).mockResolvedValue({
id: 4,
requestId: 12,
associationId: 3,
status: "en_attente",
uploadToken: "existing-token",
uploadTokenExpiresAt: futureExpiry,
} as any);
vi.mocked(db.upsertMaterialReturnFollowup).mockResolvedValue(4);
const request = {
id: 12,
associationId: 3,
formData: JSON.stringify({ dateRestitution: "2026-06-15" }),
} as any;
const reactivatedAt = new Date("2026-06-20T10:30:00.000Z");
await scheduleMaterialReturnFollowup({
request,
serviceLabel: "Service technique",
recipientEmails: ["agent@example.com"],
}, {
forceNewToken: true,
forceImmediateSendWindow: true,
reactivatedByUserId: 9,
reactivatedAt,
reactivationReason: "Suivi rouvert",
});
const payload = vi.mocked(db.upsertMaterialReturnFollowup).mock.calls[0]?.[1];
expect(payload.uploadToken).not.toBe("existing-token");
expect(new Date(payload.uploadTokenExpiresAt).getTime()).toBeGreaterThan(futureExpiry.getTime());
expect(new Date(payload.uploadTokenExpiresAt).getTime()).toBeGreaterThan(reactivatedAt.getTime());
expect(payload.reactivatedByUserId).toBe(9);
expect(payload.reactivationReason).toBe("Suivi rouvert");
});
});