fix: 完善动态错误指引与发票日期校验(09-05-error-explainability-payment-readiness)
这个提交包含在:
@@ -34,6 +34,29 @@ describe("client business error projection", () => {
|
||||
.toBe("报销人尚未维护所属公司,请联系管理员补充后再安排付款。");
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{ reasonCode: "invoice_pool.ocr_fields_missing", ocr: "missing_required_fields", missingFields: ["invoiceCode", "sellerName"] }, "票据仍缺少关键字段:发票代码、销售方,补齐后才能标记为可用。"],
|
||||
[{ reasonCode: "workflow.remind_cooldown", workflow: "remind_too_frequent", nextAllowedAt: "2026-09-16T08:30:00.000Z" }, "24 小时内已催办过,请在 2026-09-16 08:30:00 后再试。"],
|
||||
[{ reasonCode: "auth.password_policy_failed", password: ["密码长度必须 ≥ 12", "密码必须包含数字"] }, "新密码不符合要求:密码长度必须 ≥ 12;密码必须包含数字。"],
|
||||
])("preserves scoped dynamic guidance alongside reasonCode: %j", (details, expected) => {
|
||||
expect(resolveErrorMessage(new ApiError({ status: 400, code: "VALID_001", message: "private payload", details }))).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ reasonCode: "invoice_pool.ocr_fields_missing", missingFields: ["/private/secret", "__proto__"], password: ["密码必须包含数字"] },
|
||||
{ reasonCode: "workflow.remind_cooldown", nextAllowedAt: "/private/secret", password: ["密码必须包含数字"] },
|
||||
{ reasonCode: "auth.password_policy_failed", password: ["/private/secret"], ocr: "missing_required_fields", missingFields: ["invoiceCode"] },
|
||||
])("falls back safely for malformed or cross-domain details: %j", (details) => {
|
||||
expect(resolveErrorMessage(new ApiError({ status: 400, code: "VALID_001", message: "private payload", details })))
|
||||
.toBe(expectedMessages[details.reasonCode]);
|
||||
});
|
||||
|
||||
it("does not expose unknown fields mixed with known OCR labels", () => {
|
||||
const details = { reasonCode: "invoice_pool.ocr_fields_missing", missingFields: ["invoiceCode", "/private/secret"], password: ["private payload"] };
|
||||
expect(resolveErrorMessage(new ApiError({ status: 409, code: "VALID_001", message: "private payload", details })))
|
||||
.toBe("票据仍缺少关键字段:发票代码,补齐后才能标记为可用。");
|
||||
});
|
||||
|
||||
it.each([undefined, "unknown.reason", "__proto__", "constructor"])("fails safely for unknown reason %s", (reasonCode) => {
|
||||
expect(resolveErrorMessage(new ApiError({ status: 400, code: "VALID_001", message: "private payload", details: { reasonCode, internal: "/private/secret" } }))).toBe(fallback);
|
||||
});
|
||||
|
||||
在新工单中引用
屏蔽一个用户