fix: 完善动态错误指引与发票日期校验(09-05-error-explainability-payment-readiness)

这个提交包含在:
2026-09-16 12:56:27 +08:00
父节点 b7d2285d8d
当前提交 3516d7ec04
共修改 28 个文件,包含 791 行新增和 616 行删除
@@ -204,6 +204,58 @@ async function buildApp(options = {}) {
}
describe("invoicePool verification routes", () => {
it("explains invalid verification limits without reading records", async () => {
const { app, tx } = await buildApp();
try {
const response = await app.inject({ method: "GET", url: `/api/invoice-pool/${INVOICE_ID}/verification?limit=51`, headers: { authorization: "Bearer test" } });
expect(response.statusCode).toBe(400);
expect(response.json().error.details.reasonCode).toBe("invoice_verification.list_query_invalid");
expect(tx.invoiceVerificationRecord.findMany).not.toHaveBeenCalled();
expect(app.boss.send).not.toHaveBeenCalled();
} finally {
await app.close();
}
});
it.each([
[null, "invoice_verification.record_missing"],
[buildVerificationRecord({ status: "pending" }), "invoice_verification.already_pending"],
])("explains blocked retry without writing or enqueueing: %s", async (latestRecord, reasonCode) => {
const { app, tx } = await buildApp({ latestRecord });
try {
const response = await app.inject({
method: "POST",
url: `/api/invoice-pool/${INVOICE_ID}/verification/retry`,
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(409);
expect(response.json().error.details.reasonCode).toBe(reasonCode);
expect(tx.invoiceVerificationRecord.create).not.toHaveBeenCalled();
expect(tx.auditLog.create).not.toHaveBeenCalled();
expect(app.boss.send).not.toHaveBeenCalled();
} finally {
await app.close();
}
});
it("explains invalid history identifiers before reading records", async () => {
const { app, tx } = await buildApp();
try {
const response = await app.inject({
method: "GET",
url: "/api/invoice-pool/invalid/verification",
headers: { authorization: "Bearer test" },
});
expect(response.statusCode).toBe(400);
expect(response.json().error.details).toEqual({ id: "invalid_uuid", reasonCode: "invoice_pool.id_invalid" });
expect(tx.invoicePool.findUnique).not.toHaveBeenCalled();
expect(tx.invoiceVerificationRecord.findMany).not.toHaveBeenCalled();
expect(app.boss.send).not.toHaveBeenCalled();
} finally {
await app.close();
}
});
beforeEach(() => {
vi.clearAllMocks();
});
@@ -268,7 +320,7 @@ describe("invoicePool verification routes", () => {
expect(response.statusCode).toBe(409);
expect(response.json().error).toMatchObject({
code: "VALID_001",
details: { verification: "disabled", featureFlag: "env_hard_off" },
details: { verification: "disabled", featureFlag: "env_hard_off", reasonCode: "invoice_verification.disabled" },
});
expect(tx.invoiceVerificationRecord.create).not.toHaveBeenCalled();
expect(app.boss.send).not.toHaveBeenCalled();
@@ -290,7 +342,7 @@ describe("invoicePool verification routes", () => {
});
expect(response.statusCode).toBe(409);
expect(response.json().error.details).toEqual({ verification: "already_pending" });
expect(response.json().error.details).toEqual({ verification: "already_pending", reasonCode: "invoice_verification.already_pending" });
expect(tx.invoiceVerificationRecord.create).not.toHaveBeenCalled();
} finally {
await app.close();