fix: 完善动态错误指引与发票日期校验(09-05-error-explainability-payment-readiness)
这个提交包含在:
@@ -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();
|
||||
|
||||
在新工单中引用
屏蔽一个用户