const { FEATURE_FLAG_KEYS } = require("../../../src/services/featureFlags"); const { runVerificationForRecord } = require("../../../src/jobs/verificationJob"); const RECORD_ID = "11111111-1111-4111-8111-111111111111"; const INVOICE_ID = "22222222-2222-4222-8222-222222222222"; const DEPARTMENT_ID = "dept-finance"; function buildFlagRecord({ overrideMode = "force_on", rolloutType = "global", departmentIds = [] } = {}) { const now = new Date("2026-05-28T00:00:00.000Z"); return { id: "flag-invoice-verification", flag_key: FEATURE_FLAG_KEYS.invoiceVerification, override_mode: overrideMode, rollout_type: rolloutType, department_ids: departmentIds, note: "unit-test", version: 1, created_at: now, created_by: null, updated_at: now, updated_by: null, }; } function buildInvoice(overrides = {}) { return { id: INVOICE_ID, invoice_code: "0440026", invoice_number: "00012890", total_amount: "1060.00", remarks: "verify-ok", status: "available", owner_employee: { department_id: DEPARTMENT_ID }, linked_reimbursement: null, ...overrides, }; } function buildRecord(overrides = {}) { return { id: RECORD_ID, status: "pending", invoice: buildInvoice(), ...overrides, }; } function buildPrisma({ lockRows = [{ id: RECORD_ID }], record = buildRecord(), flagRecord = buildFlagRecord() } = {}) { const tx = { $queryRaw: vi.fn(async () => lockRows), featureFlag: { findUnique: vi.fn(async () => flagRecord), }, invoiceVerificationRecord: { findUnique: vi.fn(async () => record), update: vi.fn(async ({ data }) => ({ id: RECORD_ID, ...data })), }, }; const prisma = { ...tx, $transaction: vi.fn(async (callback) => callback(tx)), }; return { prisma, tx }; } describe("runVerificationForRecord", () => { beforeEach(() => { vi.clearAllMocks(); }); it("skips when verification record cannot be locked", async () => { const { prisma, tx } = buildPrisma({ lockRows: [] }); const result = await runVerificationForRecord(prisma, { invoiceVerificationEnabled: true }, RECORD_ID); expect(result).toEqual({ skipped: true, reason: "record_not_found" }); expect(tx.invoiceVerificationRecord.findUnique).not.toHaveBeenCalled(); expect(tx.invoiceVerificationRecord.update).not.toHaveBeenCalled(); }); it("marks record as error when linked invoice is missing", async () => { const { prisma, tx } = buildPrisma({ record: { id: RECORD_ID, status: "pending", invoice: null } }); const result = await runVerificationForRecord(prisma, { invoiceVerificationEnabled: true }, RECORD_ID); expect(result).toEqual({ skipped: true, reason: "invoice_not_found" }); expect(tx.invoiceVerificationRecord.update).toHaveBeenCalledWith({ where: { id: RECORD_ID }, data: expect.objectContaining({ status: "error", error_code: "invoice_not_found", error_message: "invoice not found", completed_at: expect.any(Date), }), }); }); it("skips records that are no longer pending", async () => { const { prisma, tx } = buildPrisma({ record: buildRecord({ status: "passed" }) }); const result = await runVerificationForRecord(prisma, { invoiceVerificationEnabled: true }, RECORD_ID); expect(result).toEqual({ skipped: true, reason: "status_not_pending" }); expect(tx.invoiceVerificationRecord.update).not.toHaveBeenCalled(); }); it("records feature-flag shutdown as a controlled verification error", async () => { const { prisma, tx } = buildPrisma(); const result = await runVerificationForRecord(prisma, { invoiceVerificationEnabled: false }, RECORD_ID); expect(result).toEqual({ skipped: false, status: "error", durationMs: 0, resultCode: "verification_disabled_by_flag", }); expect(tx.invoiceVerificationRecord.update).toHaveBeenCalledWith({ where: { id: RECORD_ID }, data: expect.objectContaining({ status: "error", error_code: "verification_disabled_by_flag", error_message: "env_hard_off", completed_at: expect.any(Date), }), }); }); it("persists provider error result without leaking raw invoice data", async () => { const record = buildRecord({ invoice: buildInvoice({ remarks: "verify-error" }), }); const { prisma, tx } = buildPrisma({ record }); const result = await runVerificationForRecord( prisma, { invoiceVerificationEnabled: true, verificationProvider: "mock" }, RECORD_ID, ); expect(result).toMatchObject({ skipped: false, status: "error", resultCode: "mock_transport_error", }); expect(tx.invoiceVerificationRecord.update).toHaveBeenCalledWith({ where: { id: RECORD_ID }, data: expect.objectContaining({ status: "error", result_code: "mock_transport_error", error_code: "mock_transport_error", error_message: "mock provider transport error", result_payload: expect.objectContaining({ provider: "mock" }), duration_ms: expect.any(Number), completed_at: expect.any(Date), }), }); }); });