fix: 完善动态错误指引与发票日期校验(09-05-error-explainability-payment-readiness)
这个提交包含在:
@@ -65,10 +65,45 @@ async function buildApp(permissionCodes) {
|
||||
});
|
||||
app.setErrorHandler((error, request, reply) => reply.status(error.statusCode ?? 500).send({ requestId: request.id, error: { code: error.code ?? "SYS_001", message: error.message, details: error.details ?? {} } }));
|
||||
await app.register(paymentAccountsRoutes, { prefix: "/api" });
|
||||
return { app, auditCreate, notificationCreateMany, paymentAccountCreate };
|
||||
return { app, tx, auditCreate, notificationCreateMany, paymentAccountCreate };
|
||||
}
|
||||
|
||||
describe("payment account routes", () => {
|
||||
const validBody = { legalEntityId: LEGAL_ENTITY_ID, bankInstitutionId: BANK_ID, accountName: "合成测试公司", accountNumber: SYNTHETIC_ACCOUNT };
|
||||
it.each([
|
||||
["account_input_invalid", "/payment-accounts", { ...validBody, accountNumber: "invalid" }, 400],
|
||||
["bank_input_invalid", "/bank-institutions", { code: "!", name: "" }, 400],
|
||||
["reveal_reason_required", `/payment-accounts/${ACCOUNT_ID}/reveal`, { reason: "" }, 400],
|
||||
["id_invalid", "/payment-accounts/invalid/reveal", { reason: "核对资料" }, 400],
|
||||
["legal_entity_unavailable", "/payment-accounts", validBody, 409],
|
||||
["bank_unavailable", "/payment-accounts", validBody, 409],
|
||||
["legal_entity_change_forbidden", `/payment-accounts/${ACCOUNT_ID}/replace`, { ...validBody, legalEntityId: ACTOR_ID }, 400],
|
||||
["bank_code_exists", "/bank-institutions", { code: "CCB", name: "合成银行" }, 409],
|
||||
["bank_in_use", `/bank-institutions/${BANK_ID}/disable`, {}, 409],
|
||||
])("explains %s without successful mutation or sensitive output", async (reason, url, payload, status) => {
|
||||
const testApp = await buildApp(["*"]);
|
||||
try {
|
||||
if (reason === "legal_entity_unavailable") testApp.tx.legalEntity.findFirst.mockResolvedValue(null);
|
||||
if (reason === "bank_unavailable") testApp.tx.bankInstitution.findFirst.mockResolvedValue(null);
|
||||
if (reason === "bank_code_exists") testApp.tx.bankInstitution.create = vi.fn().mockRejectedValue({ code: "P2002" });
|
||||
if (reason === "bank_in_use") {
|
||||
testApp.tx.bankInstitution.findUnique = vi.fn().mockResolvedValue({ id: BANK_ID });
|
||||
testApp.tx.paymentAccount.count = vi.fn().mockResolvedValue(1);
|
||||
}
|
||||
const response = await testApp.app.inject({ method: "POST", url: `/api${url}`, payload });
|
||||
expect(response.statusCode).toBe(status);
|
||||
expect(response.json().error.code).toBe("VALID_001");
|
||||
expect(response.json().error.details.reasonCode).toBe(`payment_account.${reason}`);
|
||||
const { getErrorReason } = require("../../../src/constants/errorReasons");
|
||||
const expected = require("../fixtures/error-reason-messages.json");
|
||||
expect(getErrorReason(`payment_account.${reason}`).defaultMessage).toBe(expected[`payment_account.${reason}`]);
|
||||
expect(JSON.stringify(response.json())).not.toContain(SYNTHETIC_ACCOUNT);
|
||||
expect(testApp.paymentAccountCreate).not.toHaveBeenCalled();
|
||||
expect(testApp.tx.paymentAccount.update).not.toHaveBeenCalled();
|
||||
expect(testApp.auditCreate).not.toHaveBeenCalled();
|
||||
expect(testApp.notificationCreateMany).not.toHaveBeenCalled();
|
||||
} finally { await testApp.app.close(); }
|
||||
});
|
||||
it("returns only a masked account on normal reads", async () => {
|
||||
const testApp = await buildApp(["payment.account.read"]);
|
||||
const response = await testApp.app.inject({ method: "GET", url: "/api/payment-accounts" });
|
||||
|
||||
在新工单中引用
屏蔽一个用户