135 lines
3.6 KiB
JavaScript
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.');
|