const { buildReadableReimbursementWhere, canReadReimbursementBusinessFact, } = require("../../../src/services/reimbursementVisibility"); const fs = require("node:fs"); const path = require("node:path"); const ACTOR_ID = "11111111-1111-4111-8111-111111111111"; const OTHER_ID = "22222222-2222-4222-8222-222222222222"; const REIMBURSEMENT_ID = "33333333-3333-4333-8333-333333333333"; describe("reimbursement visibility", () => { it.each([ ["global finance", ["finance"], {}], ["global finance manager", ["finance_mgr"], {}], ["global ceo", ["ceo"], {}], ["global hr", ["hr"], {}], ["global admin", ["admin"], {}], [ "manager team", ["manager"], { OR: [ { owner_id: ACTOR_ID }, { submitter_id: ACTOR_ID }, { owner: { direct_manager_id: ACTOR_ID } }, ], }, ], ])("keeps the existing %s scope while adding the business-fact filter", (_label, roleCodes, expectedScope) => { expect(buildReadableReimbursementWhere({ employee: { id: ACTOR_ID, roleCodes }, scope: "all", filters: { status: "draft" }, })).toEqual({ AND: [ expectedScope, { OR: [ { status: { not: "draft" } }, { owner_id: ACTOR_ID }, { submitter_id: ACTOR_ID }, { workflow_instances: { some: {} } }, ], }, { status: "draft" }, { deleted_at: null }, ], }); }); it("keeps own scope independent from workflow history", () => { expect(buildReadableReimbursementWhere({ employee: { id: ACTOR_ID, roleCodes: ["employee"] }, scope: "own", filters: { status: "draft" }, })).toEqual({ AND: [ { OR: [{ owner_id: ACTOR_ID }, { submitter_id: ACTOR_ID }] }, { status: "draft" }, { deleted_at: null }, ], }); }); it("keeps every reimbursement read surface on the shared visibility contract", () => { const routesDir = path.resolve(__dirname, "../../../src/routes"); const reimbursementsSource = fs.readFileSync(path.join(routesDir, "reimbursements.js"), "utf8"); const searchSource = fs.readFileSync(path.join(routesDir, "search.js"), "utf8"); const exportsSource = fs.readFileSync(path.join(routesDir, "exports.js"), "utf8"); const dashboardSource = fs.readFileSync(path.join(routesDir, "dashboard.js"), "utf8"); expect(reimbursementsSource).toContain("await ensureOwnOrCanViewAll(fastify, request, reimbursement)"); expect(reimbursementsSource.match(/await ensureOwnCanViewAllOrWorkflowReviewer\(fastify, request/g)).toHaveLength(4); expect(reimbursementsSource).toContain("buildReadableReimbursementWhere({"); expect(reimbursementsSource).toContain("EXISTS ("); expect(searchSource).toContain("buildReadableReimbursementWhere({"); expect(exportsSource.match(/buildReadableReimbursementWhere\(\{/g)).toHaveLength(2); expect(dashboardSource).toContain("buildReadableReimbursementWhere({"); }); it.each([ ["owner draft", { owner_id: ACTOR_ID, submitter_id: OTHER_ID, status: "draft" }, null, true], ["submitter draft", { owner_id: OTHER_ID, submitter_id: ACTOR_ID, status: "draft" }, null, true], ["other approved", { owner_id: OTHER_ID, submitter_id: OTHER_ID, status: "approved" }, null, true], ["other returned draft", { owner_id: OTHER_ID, submitter_id: OTHER_ID, status: "draft" }, { id: "returned" }, true], ["other withdrawn draft", { owner_id: OTHER_ID, submitter_id: OTHER_ID, status: "draft" }, { id: "cancelled" }, true], ["other never-submitted draft", { owner_id: OTHER_ID, submitter_id: OTHER_ID, status: "draft" }, null, false], ])("classifies %s", async (_label, record, workflowInstance, expected) => { const prisma = { reimbursementForm: { findUnique: vi.fn() }, workflowInstance: { findFirst: vi.fn().mockResolvedValue(workflowInstance) }, }; await expect(canReadReimbursementBusinessFact(prisma, { id: REIMBURSEMENT_ID, ...record, }, ACTOR_ID)).resolves.toBe(expected); if (record.status === "draft" && record.owner_id !== ACTOR_ID && record.submitter_id !== ACTOR_ID) { expect(prisma.workflowInstance.findFirst).toHaveBeenCalledWith({ where: { entity_type: "reimbursement", entity_id: REIMBURSEMENT_ID }, select: { id: true }, }); } else { expect(prisma.workflowInstance.findFirst).not.toHaveBeenCalled(); } }); });