Files
qipai/backend/tests/wallet-ledger.test.mjs
2026-06-24 15:30:33 +08:00

135 lines
3.6 KiB
JavaScript

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.');