const workflowRoutes = require("../../../src/routes/workflow"); describe("Workflow Route Permissions", () => { const { buildWorkflowEntityVisibilityWhere, canViewWorkflowEntityType, employeeHasBusinessApproveRole, listAllowedActionsForTask, parseTaskListQuery, } = workflowRoutes.__testables; it.each(["admin", "system_admin"])( "does not treat %s as a business approval role even with broad runtime permissions", (roleCode) => { expect(employeeHasBusinessApproveRole({ roleCodes: [roleCode], permissionCodes: ["*", "expense.approve"], }, "reimbursement")).toBe(false); }, ); it("allows leave approvers to act on leave tasks", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["leave.approve"] } }, { status: "pending", workflow_instance: { entity_type: "leave" }, }, true, ); expect(actions).toEqual(["approve", "reject", "return"]); }); it("exposes return instead of reject for reimbursement tasks", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["expense.approve"] } }, { status: "pending", workflow_instance: { entity_type: "reimbursement" }, }, true, ); expect(actions).toEqual(["approve", "return"]); }); it("does not allow expense approvers to act on leave tasks without leave.approve", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["expense.approve"] } }, { status: "pending", workflow_instance: { entity_type: "leave" }, }, true, ); expect(actions).toEqual([]); }); it("allows procurement approvers to act on purchase tasks", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["procurement.approve"] } }, { status: "pending", workflow_instance: { entity_type: "purchase_request" }, }, true, ); expect(actions).toEqual(["approve", "reject", "return"]); }); it("allows travel approvers to act on travel tasks", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["travel.approve"] } }, { status: "pending", workflow_instance: { entity_type: "travel_request" }, }, true, ); expect(actions).toEqual(["approve", "reject", "return"]); }); it("allows attendance approvers to approve/reject attendance corrections without return action", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["attendance.approve"] } }, { status: "pending", workflow_instance: { entity_type: "attendance_correction" }, }, true, ); expect(actions).toEqual(["approve", "reject"]); }); it("allows transfer on approval tasks when actor also has expense.transfer", () => { const reimbursementActions = listAllowedActionsForTask( { employee: { permissionCodes: ["expense.approve", "expense.transfer"] } }, { status: "pending", workflow_instance: { entity_type: "reimbursement" }, }, true, ); const leaveActions = listAllowedActionsForTask( { employee: { permissionCodes: ["leave.approve", "expense.transfer"] } }, { status: "pending", workflow_instance: { entity_type: "leave" }, }, true, ); const purchaseActions = listAllowedActionsForTask( { employee: { permissionCodes: ["procurement.approve", "expense.transfer"] } }, { status: "pending", workflow_instance: { entity_type: "purchase_request" }, }, true, ); const travelActions = listAllowedActionsForTask( { employee: { permissionCodes: ["travel.approve", "expense.transfer"] } }, { status: "pending", workflow_instance: { entity_type: "travel_request" }, }, true, ); const attendanceActions = listAllowedActionsForTask( { employee: { permissionCodes: ["attendance.approve", "expense.transfer"] } }, { status: "pending", workflow_instance: { entity_type: "attendance_correction" }, }, true, ); expect(reimbursementActions).toEqual(["approve", "return", "transfer"]); expect(leaveActions).toEqual(["approve", "reject", "return", "transfer"]); expect(purchaseActions).toEqual(["approve", "reject", "return", "transfer"]); expect(travelActions).toEqual(["approve", "reject", "return", "transfer"]); expect(attendanceActions).toEqual(["approve", "reject", "transfer"]); }); it("does not allow transfer without matching approve permission", () => { const actions = listAllowedActionsForTask( { employee: { permissionCodes: ["expense.transfer"] } }, { status: "pending", workflow_instance: { entity_type: "leave" }, }, true, ); expect(actions).toEqual([]); }); it("parses optional workflow entityType filter for PC approval center", () => { expect(parseTaskListQuery({ status: "pending", entityType: "attendance_correction", page: "2" })).toMatchObject({ status: "pending", entityType: "attendance_correction", page: 2, pageSize: 20, }); }); it("rejects unsupported workflow entityType filters", () => { expect(() => parseTaskListQuery({ entityType: "invoice_pool" })).toThrow("参数校验失败"); }); it("hides travel workflow entities when travel is frozen for the actor", () => { const request = { employee: { permissionCodes: [] } }; expect(canViewWorkflowEntityType(request, "travel_request")).toBe(false); expect(buildWorkflowEntityVisibilityWhere(request)).toEqual({ entity_type: { notIn: ["travel_request"] }, }); expect(buildWorkflowEntityVisibilityWhere(request, "travel_request")).toEqual({ entity_type: "__forbidden__", }); }); it("keeps travel workflow entities visible only for system settings holders", () => { const request = { employee: { permissionCodes: ["system.settings"] } }; expect(canViewWorkflowEntityType(request, "travel_request")).toBe(true); expect(buildWorkflowEntityVisibilityWhere(request)).toEqual({}); expect(buildWorkflowEntityVisibilityWhere(request, "travel_request")).toEqual({ entity_type: "travel_request", }); }); });