修正CEO报销财务兜底与部署护栏(07-28-ceo-finance-fallback)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-07-28 13:42:48 +08:00
父节点 37b625c5dc
当前提交 35948729cf
共修改 31 个文件,包含 980 行新增和 74 行删除
@@ -14,7 +14,7 @@ function buildCreatePayload(roleCode) {
level1Role: roleCode,
level2Role: null,
level3Role: null,
ownerSelfFallbackRole: "finance_mgr",
ownerSelfFallbackRole: "finance",
enabled: true,
priority: 1,
};
@@ -82,6 +82,74 @@ async function buildApp() {
}
describe("reimbursement approval-rule role whitelist", () => {
it.each(["finance", "finance_mgr"])(
"allows owner self fallback role %s",
async (fallbackRole) => {
const testApp = await buildApp();
try {
const response = await testApp.app.inject({
method: "POST",
url: "/api/approval-rules",
payload: {
...buildCreatePayload("manager"),
ownerSelfFallbackRole: fallbackRole,
},
});
expect(response.statusCode).toBe(200);
expect(response.json().data.ownerSelfFallbackRole).toBe(fallbackRole);
} finally {
await testApp.app.close();
}
},
);
it("defaults owner self fallback to finance when omitted", async () => {
const testApp = await buildApp();
try {
const payload = buildCreatePayload("manager");
delete payload.ownerSelfFallbackRole;
const response = await testApp.app.inject({
method: "POST",
url: "/api/approval-rules",
payload,
});
expect(response.statusCode).toBe(200);
expect(response.json().data.ownerSelfFallbackRole).toBe("finance");
expect(testApp.approvalRuleCreate).toHaveBeenCalledWith(
expect.objectContaining({
data: expect.objectContaining({ owner_self_fallback_role: "finance" }),
}),
);
} finally {
await testApp.app.close();
}
});
it.each(["admin", "system_admin", "hr"])(
"rejects owner self fallback role %s",
async (fallbackRole) => {
const testApp = await buildApp();
try {
const response = await testApp.app.inject({
method: "POST",
url: "/api/approval-rules",
payload: {
...buildCreatePayload("manager"),
ownerSelfFallbackRole: fallbackRole,
},
});
expect(response.statusCode).toBe(400);
expect(response.json().error.code).toBe("VALID_001");
expect(testApp.transaction).not.toHaveBeenCalled();
} finally {
await testApp.app.close();
}
},
);
it.each(["manager", "ceo", "finance", "finance_mgr"])(
"allows role %s because it carries expense.approve",
async (roleCode) => {