import assert from 'node:assert/strict'; import { WalletLedgerError, WalletLedgerService } from '../dist/wallets/wallet-ledger-service.js'; const state = { account: null, ledger: [], nextLedgerId: 100 }; const pool = { async getConnection() { return connection; } }; const connection = { async beginTransaction() {}, async commit() {}, async rollback() {}, release() {}, async execute(sql, params) { if (sql.includes('INSERT IGNORE INTO qipai_wallet_accounts')) { if (!state.account) { state.account = { id: '51', tenantId: String(params[0]), userId: String(params[1]), scopeType: params[2], storeId: params[3] === null ? null : String(params[3]), cashBalanceCents: 0, giftBalanceCents: 0, status: 'ACTIVE' }; } return [{ affectedRows: 1 }, []]; } if (sql.includes('FROM qipai_wallet_accounts')) { return [[state.account], []]; } if (sql.includes('FROM qipai_wallet_ledger_entries')) { const duplicate = state.ledger.find((item) => item.businessType === params[2] && item.businessId === params[3] ); return [duplicate ? [duplicate] : [], []]; } if (sql.includes('UPDATE qipai_wallet_accounts')) { state.account.cashBalanceCents = params[0]; state.account.giftBalanceCents = params[1]; return [{ affectedRows: 1 }, []]; } if (sql.includes('INSERT INTO qipai_wallet_ledger_entries')) { const item = { id: String(state.nextLedgerId++), businessType: params[4], businessId: params[5], cashDeltaCents: params[7], giftDeltaCents: params[8], cashBalanceAfterCents: params[9], giftBalanceAfterCents: params[10] }; state.ledger.push(item); return [{ insertId: item.id, affectedRows: 1 }, []]; } throw new Error(`Unexpected SQL: ${sql}`); } }; const service = new WalletLedgerService(pool); const base = { tenantId: '7', userId: '21', scopeType: 'STORE', storeId: '11', operatorId: '22', traceId: 'wallet-test' }; const credited = await service.credit({ ...base, businessType: 'RECHARGE', businessId: 'pay-001', entryType: 'RECHARGE', cashDeltaCents: 1000, giftDeltaCents: 300 }); assert.equal(credited.cashBalanceCents, 1000); assert.equal(credited.giftBalanceCents, 300); const duplicate = await service.credit({ ...base, businessType: 'RECHARGE', businessId: 'pay-001', entryType: 'RECHARGE', cashDeltaCents: 1000, giftDeltaCents: 300 }); assert.equal(duplicate.idempotent, true); assert.equal(state.ledger.length, 1); const debited = await service.debit({ ...base, businessType: 'ORDER', businessId: 'order-001', entryType: 'CONSUME', amountCents: 500 }); assert.equal(debited.giftDeltaCents, -300); assert.equal(debited.cashDeltaCents, -200); assert.equal(debited.cashBalanceCents, 800); assert.equal(debited.giftBalanceCents, 0); await assert.rejects( () => service.debit({ ...base, businessType: 'ORDER', businessId: 'order-002', entryType: 'CONSUME', amountCents: 9999 }), (error) => error instanceof WalletLedgerError && error.code === 'WALLET_BALANCE_INSUFFICIENT' ); const adjusted = await service.adjust({ ...base, businessType: 'ADMIN_ADJUST', businessId: 'adjust-001', entryType: 'ADJUST', cashDeltaCents: -100, giftDeltaCents: 50, note: 'sanitized adjustment' }); assert.equal(adjusted.cashBalanceCents, 700); assert.equal(adjusted.giftBalanceCents, 50); console.log('PASS: M07-A wallet ledger credits, gift-first debit, idempotency and adjustments work.');