const fs = require("node:fs/promises"); const os = require("node:os"); const path = require("node:path"); const Fastify = require("fastify"); const multipart = require("@fastify/multipart"); const { AppError } = require("../../../src/utils/AppError"); const reimbursementRoutes = require("../../../src/routes/reimbursements"); const { signPayload, verifyToken } = require("../../../src/utils/signedUrl"); const ACTOR_ID = "cccccccc-cccc-4ccc-8ccc-cccccccccccc"; const JWT_SECRET = "synthetic-reimbursement-preview-secret"; function buildMultipartRequest({ files, fields = {} }) { const boundary = "----codex-reimbursement-attachment-boundary"; const segments = []; for (const [name, value] of Object.entries(fields)) { segments.push( Buffer.from( `--${boundary}\r\nContent-Disposition: form-data; name="${name}"\r\n\r\n${value}\r\n`, ), ); } for (const file of files) { segments.push( Buffer.from( `--${boundary}\r\nContent-Disposition: form-data; name="files"; filename="${file.filename}"\r\nContent-Type: ${file.contentType}\r\n\r\n`, ), Buffer.isBuffer(file.content) ? file.content : Buffer.from(file.content, "utf8"), Buffer.from("\r\n"), ); } segments.push(Buffer.from(`--${boundary}--\r\n`)); return { payload: Buffer.concat(segments), headers: { "content-type": `multipart/form-data; boundary=${boundary}`, }, }; } async function buildApp({ reimbursementStatus = "draft", materialPreviewStore = null } = {}) { const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "hyb-oa-r111-reimbursement-attachments-")); const reimbursementId = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"; const createdAttachments = []; const prisma = { reimbursementForm: { findUnique: vi.fn().mockImplementation(async ({ select }) => ({ id: reimbursementId, owner_id: ACTOR_ID, submitter_id: ACTOR_ID, status: reimbursementStatus, ...(select?.invoice_pool_items ? { invoice_pool_items: [], attachments: createdAttachments } : {}), })), }, reimbursementAttachment: { create: vi.fn().mockImplementation(async ({ data, select }) => { const created = { id: `bbbbbbbb-bbbb-4bbb-8bbb-${String(createdAttachments.length + 1).padStart(12, "0")}`, reimbursement_id: data.reimbursement_id, original_name: data.original_name, mime_type: data.mime_type, file_size: data.file_size, file_sha256: data.file_sha256, governance_status: data.governance_status, sensitivity_level: data.sensitivity_level, attachment_type: data.attachment_type, reviewed_by: data.reviewed_by ?? null, reviewed_at: data.reviewed_at ?? null, review_note: data.review_note ?? null, archived_by: data.archived_by ?? null, archived_at: data.archived_at ?? null, archive_note: data.archive_note ?? null, download_policy: data.download_policy, storage_path: data.storage_path, created_at: new Date("2026-05-08T08:00:00.000Z"), uploader: { id: ACTOR_ID, employee_no: "E001", name: "测试用户", }, }; createdAttachments.push(created); if (!select) { return created; } return created; }), deleteMany: vi.fn().mockImplementation(async ({ where }) => { const index = createdAttachments.findIndex( (item) => item.id === where.id && item.reimbursement_id === where.reimbursement_id, ); if (index < 0) { return { count: 0 }; } createdAttachments.splice(index, 1); return { count: 1 }; }), findFirst: vi.fn().mockImplementation(async ({ where }) => { const item = createdAttachments.find( (candidate) => candidate.id === where.id && candidate.reimbursement_id === where.reimbursement_id, ); if (!item) { return null; } return { id: item.id, storage_path: item.storage_path, original_name: item.original_name, mime_type: item.mime_type, file_size: item.file_size, governance_status: item.governance_status, sensitivity_level: item.sensitivity_level, attachment_type: item.attachment_type, download_policy: item.download_policy, reimbursement_form: { owner_id: ACTOR_ID, submitter_id: ACTOR_ID, }, }; }), }, auditLog: { create: vi.fn().mockResolvedValue({ id: "audit-1" }), }, $queryRaw: vi.fn().mockResolvedValue([ { id: reimbursementId, owner_id: ACTOR_ID, submitter_id: ACTOR_ID, status: reimbursementStatus, }, ]), $transaction: vi.fn().mockImplementation(async (callback) => callback(prisma)), }; const app = Fastify(); app.decorate("prisma", prisma); if (materialPreviewStore) { app.decorate("reimbursementMaterialPreviewStore", materialPreviewStore); } app.decorate("config", { appTimezone: "Asia/Shanghai", storageDir: tempDir, maxUploadSizeBytes: 5 * 1024 * 1024, jwtSecret: JWT_SECRET, appOrigin: "https://oa.hyinda.com", signedUrlTtlSeconds: 600, }); app.decorate("authenticate", async (request) => { if (!request.headers.authorization) { throw new AppError({ code: "AUTH_001", message: "未登录", statusCode: 401 }); } request.employee = { id: ACTOR_ID, roleCodes: ["employee"], permissionCodes: ["expense.edit_own", "expense.view_own"], }; }); app.decorate("hasPermission", (employee, permissionCode) => { return (employee?.permissionCodes ?? []).includes("*") || (employee?.permissionCodes ?? []).includes(permissionCode); }); app.decorate("requirePermission", (permissionCode) => async (request) => { await app.authenticate(request); if (!app.hasPermission(request.employee, permissionCode)) { throw new AppError({ code: "PERM_001", message: "无权限", statusCode: 403 }); } }); app.setErrorHandler((error, request, reply) => { if (error instanceof AppError) { return reply.status(error.statusCode).send({ requestId: request.id, error: { code: error.code, message: error.message, details: error.details ?? {}, }, }); } return reply.status(500).send({ requestId: request.id, error: { code: "SYS_001", message: "服务器内部错误", details: {} }, }); }); await app.register(multipart, { attachFieldsToBody: false, limits: { files: 1, fileSize: 5 * 1024 * 1024 }, }); await app.register(reimbursementRoutes, { prefix: "/api" }); return { app, prisma, tempDir, reimbursementId, createdAttachments }; } describe("reimbursement attachment routes", () => { it("uploads five attachment materials with the production global one-file baseline", async () => { const { app, prisma, tempDir, reimbursementId, createdAttachments } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [ { filename: "contract.pdf", contentType: "application/pdf", content: "%PDF contract", }, { filename: "payment.png", contentType: "image/png", content: "png-content", }, { filename: "order.jpg", contentType: "image/jpeg", content: "jpg-content", }, { filename: "note.webp", contentType: "image/webp", content: "webp-content", }, { filename: "extra.pdf", contentType: "application/pdf", content: "%PDF extra", }, ], }); const response = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers, }, payload: requestPayload.payload, }); expect(response.statusCode).toBe(200); expect(response.json().data.items).toHaveLength(5); expect(response.json().data.items[0]).toEqual( expect.objectContaining({ originalName: "contract.pdf", mimeType: "application/pdf", governanceStatus: "approved", sensitivityLevel: "internal", attachmentType: "supporting_material", downloadPolicy: "standard", uploadedBy: expect.objectContaining({ name: "测试用户" }), }), ); expect(prisma.reimbursementAttachment.create).toHaveBeenCalledTimes(5); expect(prisma.auditLog.create).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ action: "REIMBURSEMENT_ATTACHMENTS_UPLOADED", target_id: reimbursementId, extra: expect.objectContaining({ attachmentType: "supporting_material", sensitivityLevel: "internal", }), }), }), ); for (const attachment of createdAttachments) { const storedBuffer = await fs.readFile(path.join(tempDir, attachment.storage_path)); expect(storedBuffer.length).toBeGreaterThan(0); } } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("returns a stable validation error when a request contains six files", async () => { const { app, prisma, tempDir, reimbursementId } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: Array.from({ length: 6 }, (_, index) => ({ filename: `attachment-${index + 1}.pdf`, contentType: "application/pdf", content: `%PDF attachment ${index + 1}`, })), }); const response = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers, }, payload: requestPayload.payload, }); expect(response.statusCode).toBe(400); expect(response.json().error).toMatchObject({ code: "VALID_001", details: { file: "too_many_files", maxFiles: 5 }, }); expect(prisma.reimbursementAttachment.create).not.toHaveBeenCalled(); expect(prisma.auditLog.create).not.toHaveBeenCalled(); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("rejects attachment upload after reimbursement leaves editable status", async () => { const { app, prisma, tempDir, reimbursementId } = await buildApp({ reimbursementStatus: "reviewing" }); try { const requestPayload = buildMultipartRequest({ files: [ { filename: "contract.pdf", contentType: "application/pdf", content: "%PDF contract", }, ], }); const response = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers, }, payload: requestPayload.payload, }); expect(response.statusCode).toBe(409); expect(response.json().error.details).toEqual({ status: "invalid_attachment_edit_status" }); expect(prisma.reimbursementAttachment.create).not.toHaveBeenCalled(); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("downloads and deletes draft attachment materials", async () => { const { app, prisma, tempDir, reimbursementId, createdAttachments } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [ { filename: "payment.png", contentType: "image/png", content: "png-content", }, ], }); const uploadResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers, }, payload: requestPayload.payload, }); expect(uploadResponse.statusCode).toBe(200); const attachmentId = uploadResponse.json().data.items[0].id; const downloadResponse = await app.inject({ method: "GET", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/file`, headers: { authorization: "Bearer test" }, }); expect(downloadResponse.statusCode).toBe(200); expect(downloadResponse.headers["content-type"]).toContain("image/png"); expect(downloadResponse.headers["content-disposition"]).toBe( "inline; filename=\"payment.png\"; filename*=UTF-8''payment.png", ); expect(downloadResponse.body).toBe("png-content"); const deleteResponse = await app.inject({ method: "DELETE", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}`, headers: { authorization: "Bearer test", "content-type": "application/json" }, payload: { reason: "重复上传" }, }); expect(deleteResponse.statusCode).toBe(200); expect(deleteResponse.json().data).toEqual({ attachmentId, outcome: "deleted", }); expect(prisma.reimbursementAttachment.deleteMany).toHaveBeenCalledWith({ where: { id: attachmentId, reimbursement_id: reimbursementId, }, }); expect(prisma.auditLog.create).toHaveBeenLastCalledWith( expect.objectContaining({ data: expect.objectContaining({ action: "REIMBURSEMENT_ATTACHMENT_DELETED", target_id: attachmentId, extra: expect.objectContaining({ reimbursementId, reason: "重复上传" }), }), }), ); expect(createdAttachments).toHaveLength(0); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("returns Chinese attachment names through a safe inline Content-Disposition", async () => { const { app, tempDir, reimbursementId, createdAttachments } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [{ filename: "payment.pdf", contentType: "application/pdf", content: "%PDF payment" }], }); const uploadResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers }, payload: requestPayload.payload, }); const attachmentId = uploadResponse.json().data.items[0].id; createdAttachments[0].original_name = "支付截图.pdf"; const response = await app.inject({ method: "GET", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/file`, headers: { authorization: "Bearer test" }, }); expect(response.statusCode).toBe(200); expect(response.headers["content-disposition"]).toBe( "inline; filename=\"attachment.pdf\"; filename*=UTF-8''%E6%94%AF%E4%BB%98%E6%88%AA%E5%9B%BE.pdf", ); expect(response.headers["content-disposition"]).not.toContain("支付截图"); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("rejects attachment deletion without a meaningful reason before mutation", async () => { const { app, prisma, tempDir, reimbursementId } = await buildApp(); try { const response = await app.inject({ method: "DELETE", url: `/api/reimbursements/${reimbursementId}/attachments/bbbbbbbb-bbbb-4bbb-8bbb-000000000001`, headers: { authorization: "Bearer test", "content-type": "application/json" }, payload: { reason: " " }, }); expect(response.statusCode).toBe(400); expect(response.json().error.code).toBe("VALID_001"); expect(prisma.reimbursementAttachment.deleteMany).not.toHaveBeenCalled(); expect(prisma.auditLog.create).not.toHaveBeenCalled(); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("issues and consumes a short-lived object-bound WeCom preview URL", async () => { const { app, prisma, tempDir, reimbursementId } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [{ filename: "evidence.pdf", contentType: "application/pdf", content: "%PDF preview" }], }); const uploadResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers }, payload: requestPayload.payload, }); const attachment = uploadResponse.json().data.items[0]; const issuedBefore = Date.now(); const previewResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments/${attachment.id}/preview-url`, headers: { authorization: "Bearer test", "content-type": "application/json" }, payload: {}, }); expect(previewResponse.statusCode).toBe(200); expect(previewResponse.json().data).toEqual(expect.objectContaining({ signedUrl: expect.stringMatching(/^https:\/\/oa\.hyinda\.com\/api\/reimbursements\//), originalName: "evidence.pdf", mimeType: "application/pdf", fileSize: Buffer.byteLength("%PDF preview"), })); const signedUrl = new URL(previewResponse.json().data.signedUrl); const verified = verifyToken(signedUrl.searchParams.get("previewToken"), JWT_SECRET); expect(verified).toEqual(expect.objectContaining({ ok: true, payload: expect.objectContaining({ purpose: "wecom_preview", reimbursementId, attachmentId: attachment.id, actorId: ACTOR_ID, }), })); expect(verified.payload.exp).toBeGreaterThanOrEqual(issuedBefore + 119_000); expect(verified.payload.exp).toBeLessThanOrEqual(Date.now() + 120_000); expect(prisma.auditLog.create).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ action: "REIMBURSEMENT_ATTACHMENT_PREVIEW_URL_ISSUED" }), })); const fileResponse = await app.inject({ method: "GET", url: `${signedUrl.pathname}${signedUrl.search}`, }); expect(fileResponse.statusCode).toBe(200); expect(fileResponse.body).toBe("%PDF preview"); expect(fileResponse.headers["cache-control"]).toBe("private, no-store"); expect(fileResponse.headers["referrer-policy"]).toBe("no-referrer"); expect(prisma.auditLog.create).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ action: "REIMBURSEMENT_ATTACHMENT_DOWNLOADED", actor_id: ACTOR_ID, extra: expect.objectContaining({ reason: "企业微信在线预览" }), }), })); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("rejects tampered, expired, and object-mismatched preview grants", async () => { const { app, tempDir, reimbursementId } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [{ filename: "evidence.pdf", contentType: "application/pdf", content: "%PDF preview" }], }); const uploadResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers }, payload: requestPayload.payload, }); const attachmentId = uploadResponse.json().data.items[0].id; const basePayload = { purpose: "wecom_preview", reimbursementId, attachmentId, actorId: ACTOR_ID, }; const validToken = signPayload({ ...basePayload, exp: Date.now() + 60_000 }, JWT_SECRET); const tamperedToken = `${validToken.slice(0, -1)}${validToken.endsWith("a") ? "b" : "a"}`; const expiredToken = signPayload({ ...basePayload, exp: Date.now() - 1 }, JWT_SECRET); const mismatchedToken = signPayload({ ...basePayload, reimbursementId: "dddddddd-dddd-4ddd-8ddd-dddddddddddd", exp: Date.now() + 60_000, }, JWT_SECRET); for (const token of [tamperedToken, expiredToken, mismatchedToken]) { const response = await app.inject({ method: "GET", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/file?previewToken=${encodeURIComponent(token)}`, }); expect(response.statusCode).toBe(403); expect(response.json().error.code).toBe("PERM_001"); } } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("requires login and any configured download reason before issuing a preview URL", async () => { const { app, prisma, tempDir, reimbursementId, createdAttachments } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [{ filename: "evidence.pdf", contentType: "application/pdf", content: "%PDF preview" }], }); const uploadResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers }, payload: requestPayload.payload, }); const attachmentId = uploadResponse.json().data.items[0].id; createdAttachments[0].download_policy = "reason_required"; prisma.auditLog.create.mockClear(); const unauthenticated = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/preview-url`, payload: {}, }); const missingReason = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/preview-url`, headers: { authorization: "Bearer test", "content-type": "application/json" }, payload: {}, }); const missingBundleReason = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/materials/preview-bundle`, headers: { authorization: "Bearer test", "content-type": "application/json" }, payload: {}, }); expect(unauthenticated.statusCode).toBe(401); expect(missingReason.statusCode).toBe(400); expect(missingReason.json().error.details).toEqual({ reason: "required" }); expect(missingBundleReason.statusCode).toBe(400); expect(missingBundleReason.json().error.details).toEqual({ reason: "required" }); expect(prisma.auditLog.create).not.toHaveBeenCalled(); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("fails closed before preview bytes when the access audit cannot be written", async () => { const { app, prisma, tempDir, reimbursementId } = await buildApp(); try { const requestPayload = buildMultipartRequest({ files: [{ filename: "evidence.pdf", contentType: "application/pdf", content: "%PDF preview" }], }); const uploadResponse = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers }, payload: requestPayload.payload, }); const attachmentId = uploadResponse.json().data.items[0].id; const previewToken = signPayload({ purpose: "wecom_preview", reimbursementId, attachmentId, actorId: ACTOR_ID, exp: Date.now() + 60_000, }, JWT_SECRET); prisma.auditLog.create.mockRejectedValueOnce(new Error("synthetic audit outage")); const response = await app.inject({ method: "GET", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/file?previewToken=${encodeURIComponent(previewToken)}`, }); expect(response.statusCode).toBe(500); expect(response.json().error.code).toBe("SYS_001"); expect(response.body).not.toContain("%PDF preview"); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("rejects missing or invalid authorization before looking up the attachment", async () => { const { app, prisma, tempDir, reimbursementId } = await buildApp(); const attachmentId = "bbbbbbbb-bbbb-4bbb-8bbb-000000000001"; try { const unauthenticated = await app.inject({ method: "GET", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/file`, }); const invalidGrant = await app.inject({ method: "GET", url: `/api/reimbursements/${reimbursementId}/attachments/${attachmentId}/file?previewToken=invalid`, }); expect(unauthenticated.statusCode).toBe(401); expect(invalidGrant.statusCode).toBe(403); expect(prisma.reimbursementAttachment.findFirst).not.toHaveBeenCalled(); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); it("issues one token-bound PDF bundle with exact size and audits access", async () => { const bundleId = "eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee"; const previewBuffer = Buffer.from("%PDF synthetic merged preview"); const materialPreviewStore = { createBundle: vi.fn().mockImplementation(async ({ actorId, reimbursementId, materials, expiresAt }) => ({ bundleId, bundle: { actorId, reimbursementId, expiresAt, pageCount: 1, }, fileSize: previewBuffer.length, })), getBundle: vi.fn(), }; const { app, prisma, tempDir, reimbursementId } = await buildApp({ materialPreviewStore }); try { const requestPayload = buildMultipartRequest({ files: [{ filename: "payment.png", contentType: "image/png", content: "png-content" }], }); await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/attachments`, headers: { authorization: "Bearer test", ...requestPayload.headers }, payload: requestPayload.payload, }); const issued = await app.inject({ method: "POST", url: `/api/reimbursements/${reimbursementId}/materials/preview-bundle`, headers: { authorization: "Bearer test", "content-type": "application/json" }, payload: {}, }); expect(issued.statusCode).toBe(200); expect(issued.json().data).toEqual(expect.objectContaining({ mimeType: "application/pdf", fileSize: previewBuffer.length, pageCount: 1, })); const previewUrl = new URL(issued.json().data.signedUrl); const verified = verifyToken(previewUrl.searchParams.get("previewToken"), JWT_SECRET); expect(verified).toEqual(expect.objectContaining({ ok: true, payload: expect.objectContaining({ purpose: "reimbursement_material_preview", reimbursementId, bundleId, actorId: ACTOR_ID, }), })); const issuedToken = previewUrl.searchParams.get("previewToken"); const tamperedToken = `${issuedToken.slice(0, -1)}${issuedToken.endsWith("a") ? "b" : "a"}`; const tamperedResponse = await app.inject({ method: "GET", url: `${previewUrl.pathname}?previewToken=${encodeURIComponent(tamperedToken)}`, }); expect(tamperedResponse.statusCode).toBe(403); expect(materialPreviewStore.getBundle).not.toHaveBeenCalled(); const missingBundleResponse = await app.inject({ method: "GET", url: `${previewUrl.pathname}${previewUrl.search}`, }); expect(missingBundleResponse.statusCode).toBe(403); materialPreviewStore.getBundle.mockReturnValue({ bundle: { actorId: ACTOR_ID, reimbursementId, expiresAt: verified.payload.exp, }, artifact: { buffer: previewBuffer }, }); const previewResponse = await app.inject({ method: "GET", url: `${previewUrl.pathname}${previewUrl.search}` }); expect(previewResponse.statusCode).toBe(200); expect(previewResponse.rawPayload).toEqual(previewBuffer); expect(previewResponse.headers["content-type"]).toContain("application/pdf"); expect(Number(previewResponse.headers["content-length"])).toBe(previewBuffer.length); expect(previewResponse.headers["cache-control"]).toBe("private, no-store"); expect(prisma.auditLog.create).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ action: "REIMBURSEMENT_MATERIAL_PREVIEWED", actor_id: ACTOR_ID }), })); prisma.auditLog.create.mockRejectedValueOnce(new Error("synthetic audit outage")); const auditFailureResponse = await app.inject({ method: "GET", url: `${previewUrl.pathname}${previewUrl.search}` }); expect(auditFailureResponse.statusCode).toBe(500); expect(auditFailureResponse.body).not.toContain(previewBuffer.toString()); } finally { await app.close(); await fs.rm(tempDir, { recursive: true, force: true }); } }); });