From 563ec973d229131b762592e04e28cf3a3dd5a38b Mon Sep 17 00:00:00 2001 From: julesGoullee Date: Mon, 28 Mar 2022 16:24:16 +0100 Subject: [PATCH 1/3] update checkIntegrityBalance and db tx admin endpoint --- .../api/controllers/admin/transaction.js | 101 ++++++++++-------- src/tools/checkIntegrityBalance.js | 36 ++++++- 2 files changed, 85 insertions(+), 52 deletions(-) diff --git a/src/services/api/controllers/admin/transaction.js b/src/services/api/controllers/admin/transaction.js index 7a1a2b716..271a55b2b 100644 --- a/src/services/api/controllers/admin/transaction.js +++ b/src/services/api/controllers/admin/transaction.js @@ -8,64 +8,71 @@ const { setImmediateAsync } = require('../../../../helpers/utils'); const WalletService = require('../../../wallet'); const PaymentProcessor = require('../../../wallet/paymentProcessor'); const Notification = require('../../../../modules/notification'); +const DB = require('../../../../modules/db'); const llo = logger.logMeta.bind(null, { service: 'api:controller:admin:transaction' }); const ControllerAdminTransaction = { setStatus: async (txId, status) => { - const tx = await Models.Transaction.findByIdWithWalletAndCoin(txId); - errors.assertExposable(tx && tx.Wallet, 'not_found', null, null, llo({ txId })); - - if (status === Models.Transaction.STATUS.APPROVED) { - const proofOfFunds = await tx.getProofOfFunds(); - const refundTransaction = await Models.Transaction.findTransactionByRefundTransactionId(tx.id); - errors.assertExposable(!refundTransaction, 'submitted_refund', null, null, llo({ dbTxId: tx.id })); - errors.assertExposable(tx.status === Models.Transaction.STATUS.BLOCKED, 'transaction_not_blocked', null, null, { - txId, - }); - errors.assertExposable( - tx.type !== Models.Transaction.TYPE.OUTGOING || + return DB.executeTxFn(async (tOpts) => { + + const tx = await Models.Transaction.findByIdWithWalletAndCoin(txId, tOpts); + errors.assertExposable(tx && tx.Wallet, 'not_found', null, null, llo({ txId })); + + if (status === Models.Transaction.STATUS.APPROVED) { + const proofOfFunds = await tx.getProofOfFunds(tOpts); + const refundTransaction = await Models.Transaction.findTransactionByRefundTransactionId(tx.id, tOpts); + errors.assertExposable(!refundTransaction, 'submitted_refund', null, null, llo({ dbTxId: tx.id })); + errors.assertExposable(tx.status === Models.Transaction.STATUS.BLOCKED, 'transaction_not_blocked', null, null, { + txId, + }); + errors.assertExposable( + tx.type !== Models.Transaction.TYPE.OUTGOING || Decimal(tx.Wallet.balance).gte(Decimal(tx.amount).add(tx.fees || tx.Wallet.Coin.networkFee)), - 'balance_not_enough', - null, - null, - { txId } - ); - errors.assertExposable( - (proofOfFunds.length === 0 || - proofOfFunds.find((proofOfFund) => proofOfFund.status === Models.ProofOfFund.STATUS.APPROVED)) && + 'balance_not_enough', + null, + null, + { txId } + ); + errors.assertExposable( + (proofOfFunds.length === 0 || + proofOfFunds.find((proofOfFund) => proofOfFund.status === Models.ProofOfFund.STATUS.APPROVED)) && !proofOfFunds.find((proofOfFund) => proofOfFund.status === Models.ProofOfFund.STATUS.UNDER_REVIEW), - 'proof_of_fund_not_reviewed', - null, - null, - { txId } - ); - - await tx.update({ status: Models.Transaction.STATUS.APPROVED }); + 'proof_of_fund_not_reviewed', + null, + null, + { txId } + ); - if (tx.type === Models.Transaction.TYPE.OUTGOING) { - await WalletService.queuePaymentRequest(tx.id); - } + await tx.update({ status: Models.Transaction.STATUS.APPROVED }, tOpts); + await tOpts.transaction.commit(); - logger.info('Approve transaction', llo({ txId })); - } else if (status === Models.Transaction.STATUS.CANCELED) { - errors.assertExposable( - [Models.Transaction.STATUS.BLOCKED, Models.Transaction.STATUS.PENDING].includes(tx.status), - 'transaction_not_blocked_or_pending', - null, - null, - { txId } - ); - errors.assertExposable(!tx.debited, 'transaction_not_debited', null, null, { txId }); - errors.assertExposable(tx.type === 'OUTGOING', 'transaction_not_outgoing', null, null, { txId }); + if (tx.type === Models.Transaction.TYPE.OUTGOING) { + await WalletService.queuePaymentRequest(tx.id); + } - await tx.update({ status: Models.Transaction.STATUS.CANCELED }); - logger.info('Cancel transaction', llo({ txId })); - } else { - errors.throwError('transaction_status_invalid', null, null, { txId, status }); - } + logger.info('Approve transaction', llo({ txId })); + } else if (status === Models.Transaction.STATUS.CANCELED) { + errors.assertExposable( + [Models.Transaction.STATUS.BLOCKED, Models.Transaction.STATUS.PENDING].includes(tx.status), + 'transaction_not_blocked_or_pending', + null, + null, + { txId } + ); + errors.assertExposable(!tx.debited, 'transaction_not_debited', null, null, { txId }); + errors.assertExposable(tx.type === 'OUTGOING', 'transaction_not_outgoing', null, null, { txId }); + + await tx.update({ status: Models.Transaction.STATUS.CANCELED }, tOpts); + await tOpts.transaction.commit(); + logger.info('Cancel transaction', llo({ txId })); + } else { + await tOpts.transaction.rollback(); + errors.throwError('transaction_status_invalid', null, null, { txId, status }); + } - return true; + return true; + }); }, reviewProofOfFund: async (proofOfFundId, status, level, publicMessage) => { diff --git a/src/tools/checkIntegrityBalance.js b/src/tools/checkIntegrityBalance.js index 9209cb385..76ed70fee 100644 --- a/src/tools/checkIntegrityBalance.js +++ b/src/tools/checkIntegrityBalance.js @@ -13,23 +13,32 @@ async function checkUser(user) { await Promise.all( user.Wallets.map(async (wallet) => { const sumTransactions = wallet.Transactions.concat(wallet.TransactionTo).reduce((acc, transaction) => { - if (transaction.status === Models.Transaction.STATUS.ERROR) { + if (transaction.status === Models.Transaction.STATUS.REFUNDED) { if (transaction.type === Models.Transaction.TYPE.OUTGOING) { acc = acc.sub(transaction.fees || 0); + } else if(transaction.type === Models.Transaction.TYPE.INCOMING){ + const refundTransaction = wallet.Transactions.find(tx => tx.refundTransactionId === transaction.id); + if(refundTransaction){ + acc = acc.add(transaction.amount); + } } - return acc; } + if(transaction.status === Models.Transaction.STATUS.CANCELED){ + return acc; + } if (transaction.type === Models.Transaction.TYPE.INCOMING) { - acc = acc.add(transaction.amount); + if(![Models.Transaction.STATUS.BLOCKED, Models.Transaction.STATUS.UNCONFIRMED].includes(transaction.status)){ + acc = acc.add(transaction.amount); + } } else if (transaction.type === Models.Transaction.TYPE.INTERNAL) { if (transaction.WalletId === wallet.id) { acc = acc.sub(transaction.amount); } else { acc = acc.add(transaction.amount); } - } else { + } else if(transaction.type === Models.Transaction.TYPE.OUTGOING){ acc = acc.sub(transaction.amount).sub(transaction.fees || 0); } @@ -60,7 +69,22 @@ async function checkUser(user) { return acc; }, Decimal(0)); - const rightBalance = sumTransactions.add(sumExchanges).add(sumInterests).sub(sumInvestments); + const cardOperations = await wallet.getCardOperations(); + const sumCardOperations = cardOperations.reduce( (acc, cardOperation) => { + if([Models.CardOperation.STATUS.PRE_AUTHORIZED, Models.CardOperation.STATUS.AUTHORIZED, Models.CardOperation.STATUS.CONFIRMED].includes(cardOperation.status)){ + acc = acc.add(cardOperation.amount); + } + return acc; + }, Decimal(0)); + + let cardTierBalance = Decimal(0); + if(wallet.CoinCode === 'AMN'){ + const cardTier = await Models.Tier.findActiveByUserId(user.id); + if(cardTier){ + cardTierBalance = Decimal(cardTier.balanceAMN); + } + } + const rightBalance = sumTransactions.add(sumExchanges).add(sumInterests).sub(sumInvestments).sub(sumCardOperations).sub(cardTierBalance); if (!rightBalance.eq(wallet.balance) || !sumInvestments.eq(wallet.investBalance)) { const diffBalance = rightBalance.sub(wallet.balance); @@ -79,6 +103,8 @@ async function checkUser(user) { sumExchanges: sumExchanges.toFixed(), sumInvestments: sumInvestments.toFixed(), sumInterests: sumInterests.toFixed(), + sumCardOperations: sumCardOperations.toFixed(), + cardTierBalance: cardTierBalance.toFixed(), rightBalance: rightBalance.toFixed(), diffBalance: diffBalance.toFixed(), diffInvestBalance: diffInvestBalance.toFixed(), From cc918f51c800f94433ccf6d23c57365b51ce0ad8 Mon Sep 17 00:00:00 2001 From: julesGoullee Date: Mon, 28 Mar 2022 18:01:40 +0100 Subject: [PATCH 2/3] test --- .../api/controllers/admin/transaction.js | 5 +- src/tools/checkIntegrityBalance.js | 35 +- test/unit/tools/checkIntegrityBalance.spec.js | 430 +++++++++++++++++- 3 files changed, 454 insertions(+), 16 deletions(-) diff --git a/src/services/api/controllers/admin/transaction.js b/src/services/api/controllers/admin/transaction.js index 271a55b2b..f661c3e65 100644 --- a/src/services/api/controllers/admin/transaction.js +++ b/src/services/api/controllers/admin/transaction.js @@ -15,7 +15,6 @@ const llo = logger.logMeta.bind(null, { service: 'api:controller:admin:transacti const ControllerAdminTransaction = { setStatus: async (txId, status) => { return DB.executeTxFn(async (tOpts) => { - const tx = await Models.Transaction.findByIdWithWalletAndCoin(txId, tOpts); errors.assertExposable(tx && tx.Wallet, 'not_found', null, null, llo({ txId })); @@ -28,7 +27,7 @@ const ControllerAdminTransaction = { }); errors.assertExposable( tx.type !== Models.Transaction.TYPE.OUTGOING || - Decimal(tx.Wallet.balance).gte(Decimal(tx.amount).add(tx.fees || tx.Wallet.Coin.networkFee)), + Decimal(tx.Wallet.balance).gte(Decimal(tx.amount).add(tx.fees || tx.Wallet.Coin.networkFee)), 'balance_not_enough', null, null, @@ -37,7 +36,7 @@ const ControllerAdminTransaction = { errors.assertExposable( (proofOfFunds.length === 0 || proofOfFunds.find((proofOfFund) => proofOfFund.status === Models.ProofOfFund.STATUS.APPROVED)) && - !proofOfFunds.find((proofOfFund) => proofOfFund.status === Models.ProofOfFund.STATUS.UNDER_REVIEW), + !proofOfFunds.find((proofOfFund) => proofOfFund.status === Models.ProofOfFund.STATUS.UNDER_REVIEW), 'proof_of_fund_not_reviewed', null, null, diff --git a/src/tools/checkIntegrityBalance.js b/src/tools/checkIntegrityBalance.js index 76ed70fee..16b1f63ca 100644 --- a/src/tools/checkIntegrityBalance.js +++ b/src/tools/checkIntegrityBalance.js @@ -16,20 +16,22 @@ async function checkUser(user) { if (transaction.status === Models.Transaction.STATUS.REFUNDED) { if (transaction.type === Models.Transaction.TYPE.OUTGOING) { acc = acc.sub(transaction.fees || 0); - } else if(transaction.type === Models.Transaction.TYPE.INCOMING){ - const refundTransaction = wallet.Transactions.find(tx => tx.refundTransactionId === transaction.id); - if(refundTransaction){ + } else { + const refundTransaction = wallet.Transactions.find((tx) => tx.refundTransactionId === transaction.id); + if (refundTransaction) { acc = acc.add(transaction.amount); } } return acc; } - if(transaction.status === Models.Transaction.STATUS.CANCELED){ + if (transaction.status === Models.Transaction.STATUS.CANCELED) { return acc; } if (transaction.type === Models.Transaction.TYPE.INCOMING) { - if(![Models.Transaction.STATUS.BLOCKED, Models.Transaction.STATUS.UNCONFIRMED].includes(transaction.status)){ + if ( + ![Models.Transaction.STATUS.BLOCKED, Models.Transaction.STATUS.UNCONFIRMED].includes(transaction.status) + ) { acc = acc.add(transaction.amount); } } else if (transaction.type === Models.Transaction.TYPE.INTERNAL) { @@ -38,7 +40,7 @@ async function checkUser(user) { } else { acc = acc.add(transaction.amount); } - } else if(transaction.type === Models.Transaction.TYPE.OUTGOING){ + } else { acc = acc.sub(transaction.amount).sub(transaction.fees || 0); } @@ -70,21 +72,32 @@ async function checkUser(user) { }, Decimal(0)); const cardOperations = await wallet.getCardOperations(); - const sumCardOperations = cardOperations.reduce( (acc, cardOperation) => { - if([Models.CardOperation.STATUS.PRE_AUTHORIZED, Models.CardOperation.STATUS.AUTHORIZED, Models.CardOperation.STATUS.CONFIRMED].includes(cardOperation.status)){ + const sumCardOperations = cardOperations.reduce((acc, cardOperation) => { + if ( + [ + Models.CardOperation.STATUS.PRE_AUTHORIZED, + Models.CardOperation.STATUS.AUTHORIZED, + Models.CardOperation.STATUS.CONFIRMED, + ].includes(cardOperation.status) + ) { acc = acc.add(cardOperation.amount); } return acc; }, Decimal(0)); let cardTierBalance = Decimal(0); - if(wallet.CoinCode === 'AMN'){ + if (wallet.CoinCode === 'AMN') { const cardTier = await Models.Tier.findActiveByUserId(user.id); - if(cardTier){ + if (cardTier) { cardTierBalance = Decimal(cardTier.balanceAMN); } } - const rightBalance = sumTransactions.add(sumExchanges).add(sumInterests).sub(sumInvestments).sub(sumCardOperations).sub(cardTierBalance); + const rightBalance = sumTransactions + .add(sumExchanges) + .add(sumInterests) + .sub(sumInvestments) + .sub(sumCardOperations) + .sub(cardTierBalance); if (!rightBalance.eq(wallet.balance) || !sumInvestments.eq(wallet.investBalance)) { const diffBalance = rightBalance.sub(wallet.balance); diff --git a/test/unit/tools/checkIntegrityBalance.spec.js b/test/unit/tools/checkIntegrityBalance.spec.js index a9bf24c17..8526d81dc 100644 --- a/test/unit/tools/checkIntegrityBalance.spec.js +++ b/test/unit/tools/checkIntegrityBalance.spec.js @@ -107,7 +107,7 @@ describe('Tools: check integrity balance', () => { }); }); - it('Should check ok transaction incoming and outgoing with outgoing error', async () => { + it('Should check ok transaction incoming and outgoing with outgoing refunded', async () => { await Promise.all( this.supportedCoins.map(async (coin) => { return Promise.all( @@ -155,7 +155,218 @@ describe('Tools: check integrity balance', () => { fromAddress: `fromAddress_${user.id}_${coin.code}3`, type: Models.Transaction.TYPE.OUTGOING, fees: coinFees, - status: Models.Transaction.STATUS.ERROR, + status: Models.Transaction.STATUS.REFUNDED, + WalletId: wallet.id, + }); + }) + ); + }) + ); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.callCount).to.be.eq(0); + + this.users.forEach((user) => { + expect( + this.stubLoggerVerbose.calledWith('User balance ok', { + userId: user.id, + service: 'check-integrity-balance', + }) + ).to.be.true; + }); + }); + + it('Should check ok transaction incoming and outgoing with incoming refunded', async () => { + await Promise.all( + this.supportedCoins.map(async (coin) => { + return Promise.all( + this.users.map(async (user) => { + const coinFees = await (await WalletProxy.getWalletConstructor(coin.code)).getFees(); + + const wallet = await Models.Wallet.create({ + CoinCode: coin.code, + UserId: user.id, + balance: '0', + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}0`, + amount: Decimal(0.5).add(Decimal(coinFees).mul(2)).toFixed(), + fromAddress: `fromAddress_${user.id}_${coin.code}0`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + const txRefunded = await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}1`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}1`, + type: Models.Transaction.TYPE.INCOMING, + fees: coinFees, + status: Models.Transaction.STATUS.REFUNDED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}2`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}2`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}3`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}3`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CONFIRMED, + refundTransactionId: txRefunded.id, + WalletId: wallet.id, + }); + }) + ); + }) + ); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.callCount).to.be.eq(0); + + this.users.forEach((user) => { + expect( + this.stubLoggerVerbose.calledWith('User balance ok', { + userId: user.id, + service: 'check-integrity-balance', + }) + ).to.be.true; + }); + }); + + it('Should check ok transaction incoming and outgoing with outgoing canceled', async () => { + await Promise.all( + this.supportedCoins.map(async (coin) => { + return Promise.all( + this.users.map(async (user) => { + const coinFees = await (await WalletProxy.getWalletConstructor(coin.code)).getFees(); + + const wallet = await Models.Wallet.create({ + CoinCode: coin.code, + UserId: user.id, + balance: '0', + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}0`, + amount: Decimal(1).add(Decimal(coinFees).mul(2)).toFixed(), + fromAddress: `fromAddress_${user.id}_${coin.code}0`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}1`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}1`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}2`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}2`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}3`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}3`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CANCELED, + WalletId: wallet.id, + }); + }) + ); + }) + ); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.callCount).to.be.eq(0); + + this.users.forEach((user) => { + expect( + this.stubLoggerVerbose.calledWith('User balance ok', { + userId: user.id, + service: 'check-integrity-balance', + }) + ).to.be.true; + }); + }); + + it('Should check ok transaction incoming and outgoing with incoming unconfirmed', async () => { + await Promise.all( + this.supportedCoins.map(async (coin) => { + return Promise.all( + this.users.map(async (user) => { + const coinFees = await (await WalletProxy.getWalletConstructor(coin.code)).getFees(); + + const wallet = await Models.Wallet.create({ + CoinCode: coin.code, + UserId: user.id, + balance: '0', + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}0`, + amount: Decimal(1).add(Decimal(coinFees).mul(2)).toFixed(), + fromAddress: `fromAddress_${user.id}_${coin.code}0`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}1`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}1`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}2`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}2`, + type: Models.Transaction.TYPE.OUTGOING, + fees: coinFees, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + + await Models.Transaction.create({ + txId: `txId_${user.id}_${coin.code}3`, + amount: '0.5', + fromAddress: `fromAddress_${user.id}_${coin.code}3`, + type: Models.Transaction.TYPE.INCOMING, + fees: coinFees, + status: Models.Transaction.STATUS.UNCONFIRMED, WalletId: wallet.id, }); }) @@ -331,6 +542,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0', sumInterests: '0', sumInvestments: '0', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: '0.6', diffBalance: '-0.4', diffInvestBalance: '0', @@ -469,6 +682,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0', sumInterests: '0', sumInvestments: '0', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: Decimal(0.5).sub(Decimal(coinFees).mul(2)).toFixed(), diffBalance: '-0.5', diffInvestBalance: '0', @@ -614,6 +829,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0.6', sumInterests: '0', sumInvestments: '0', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: '0.6', diffBalance: '-0.4', diffInvestBalance: '0', @@ -743,6 +960,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0.5', sumInterests: '0', sumInvestments: '0', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: '0.5', diffBalance: '-0.5', diffInvestBalance: '0', @@ -943,6 +1162,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0', sumInvestments: '0.6', sumInterests: '0', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: '1.4', diffBalance: '0.4', diffInvestBalance: '-0.4', @@ -1045,6 +1266,8 @@ describe('Tools: check integrity balance', () => { sumTransactions: '2', sumExchanges: '0', sumInterests: '0', + cardTierBalance: '0', + sumCardOperations: '0', sumInvestments: '0.5', rightBalance: '1.5', diffBalance: '0.5', @@ -1174,6 +1397,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0', sumInvestments: '0', sumInterests: '0.6', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: '0.6', diffBalance: '-0.4', diffInvestBalance: '0', @@ -1181,6 +1406,203 @@ describe('Tools: check integrity balance', () => { }); }); + describe('card Operation', () => { + it('Should check ok card operation only', async () => { + await Promise.all( + this.users.map(async (user) => { + const wallet = await Models.Wallet.create({ + CoinCode: 'EUR', + UserId: user.id, + balance: '0', + }); + const card = await Models.Card.create({ + UserId: user.id, + CoinCode: 'EUR', + design: '1', + deliveryStreetNumber: 'deliveryStreetNumber1', + deliveryStreet: 'deliveryStreet2', + deliveryTown: 'deliveryTown2', + deliveryPostCode: 'deliveryPostCode2', + deliveryCountry: 'IT', + status: Models.Card.STATUS.ACTIVATED, + tribeHolderId: `tribeHolderId2_${user.id}`, + tribeAccountId: `tribeAccountId2_${user.id}`, + tribeCardId: `tribeCardId2_${user.id}`, + }); + await Models.Transaction.create({ + txId: `txId_${user.id}_EUR_1`, + amount: '2', + fromAddress: `fromAddress_${user.id}_EUR_1`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + await Models.CardOperation.create({ + CardId: card.id, + WalletId: wallet.id, + status: Models.CardOperation.STATUS.AUTHORIZED, + transactionAmount: '2', + transactionCurrency: 'EUR', + country: 'FR', + amount: '2', + amountEUR: '2', + type: 'ATM', + entryMode: 'CHIP', + fees: '0', + internalFees: '0', + transLink: `transLink1_${card.id}`, + merchantId: 'merchantId1', + merchantName: 'merchantName1', + merchantCategoryCode: '1234', + }); + }) + ); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.callCount).to.be.eq(0); + + this.users.forEach((user) => { + expect( + this.stubLoggerVerbose.calledWith('User balance ok', { + userId: user.id, + service: 'check-integrity-balance', + }) + ).to.be.true; + }); + }); + + it('Should check wrong card operation only', async () => { + await Promise.all( + this.users.map(async (user) => { + const wallet = await Models.Wallet.create({ + CoinCode: 'EUR', + UserId: user.id, + balance: '1', + }); + const card = await Models.Card.create({ + UserId: user.id, + CoinCode: 'EUR', + design: '1', + deliveryStreetNumber: 'deliveryStreetNumber1', + deliveryStreet: 'deliveryStreet2', + deliveryTown: 'deliveryTown2', + deliveryPostCode: 'deliveryPostCode2', + deliveryCountry: 'IT', + status: Models.Card.STATUS.ACTIVATED, + tribeHolderId: `tribeHolderId2_${user.id}`, + tribeAccountId: `tribeAccountId2_${user.id}`, + tribeCardId: `tribeCardId2_${user.id}`, + }); + await Models.Transaction.create({ + txId: `txId_${user.id}_EUR_1`, + amount: '2', + fromAddress: `fromAddress_${user.id}_EUR_1`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + await Models.CardOperation.create({ + CardId: card.id, + WalletId: wallet.id, + status: Models.CardOperation.STATUS.AUTHORIZED, + transactionAmount: '2', + transactionCurrency: 'EUR', + country: 'FR', + amount: '2', + amountEUR: '2', + type: 'ATM', + entryMode: 'CHIP', + fees: '0', + internalFees: '0', + transLink: `transLink1_${card.id}`, + merchantId: 'merchantId1', + merchantName: 'merchantName1', + merchantCategoryCode: '1234', + }); + }) + ); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.calledWith('invalid_balance')).to.be.true; + expect(this.stubLoggerError.callCount).to.be.eq(this.users.length); + }); + }); + + describe('Card tier', () => { + + it('Should check card tier ok tier only', async () => { + this.users.map(async (user) => { + const wallet = await Models.Wallet.create({ + CoinCode: 'AMN', + UserId: user.id, + balance: '0', + }); + await Models.Transaction.create({ + txId: `txId_${user.id}_EUR_1`, + amount: '2', + fromAddress: `fromAddress_${user.id}_EUR_1`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + await Models.Tier.create({ + label: 'label', + balanceAMN: '2', + active: true, + UserId: user.id, + WalletId: wallet.id, + }) + }); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.callCount).to.be.eq(0); + + this.users.forEach((user) => { + expect( + this.stubLoggerVerbose.calledWith('User balance ok', { + userId: user.id, + service: 'check-integrity-balance', + }) + ).to.be.true; + }); + + }); + + it('Should check card tier wrong tier only', async () => { + this.users.map(async (user) => { + const wallet = await Models.Wallet.create({ + CoinCode: 'AMN', + UserId: user.id, + balance: '1', + }); + await Models.Transaction.create({ + txId: `txId_${user.id}_EUR_1`, + amount: '2', + fromAddress: `fromAddress_${user.id}_EUR_1`, + type: Models.Transaction.TYPE.INCOMING, + status: Models.Transaction.STATUS.CONFIRMED, + WalletId: wallet.id, + }); + await Models.Tier.create({ + label: 'label', + balanceAMN: '2', + active: true, + UserId: user.id, + WalletId: wallet.id, + }) + }); + + await CheckIntegrityBalance.run(); + + expect(this.stubLoggerError.calledWith('invalid_balance')).to.be.true; + expect(this.stubLoggerError.callCount).to.be.eq(this.users.length); + + }); + }); + it('Should check ok transaction, exchange, investment, interest', async () => { await Promise.all( this.supportedCoins.map(async (coin) => { @@ -1435,6 +1857,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0.5', sumInterests: '2.5', sumInvestments: '0.6', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: Decimal(2.9).sub(Decimal(coinFees).mul(2)).toFixed(), diffBalance: '0.9', diffInvestBalance: '-0.4', @@ -1489,6 +1913,8 @@ describe('Tools: check integrity balance', () => { sumExchanges: '0', sumInterests: '0', sumInvestments: '0', + cardTierBalance: '0', + sumCardOperations: '0', rightBalance: '1.1', diffBalance: '0.1', diffInvestBalance: '0', From 64330d8092057282ef5d67a9f6593d6426398e41 Mon Sep 17 00:00:00 2001 From: julesGoullee Date: Mon, 28 Mar 2022 18:39:23 +0100 Subject: [PATCH 3/3] format --- test/unit/tools/checkIntegrityBalance.spec.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/unit/tools/checkIntegrityBalance.spec.js b/test/unit/tools/checkIntegrityBalance.spec.js index 8526d81dc..032414f09 100644 --- a/test/unit/tools/checkIntegrityBalance.spec.js +++ b/test/unit/tools/checkIntegrityBalance.spec.js @@ -1531,7 +1531,6 @@ describe('Tools: check integrity balance', () => { }); describe('Card tier', () => { - it('Should check card tier ok tier only', async () => { this.users.map(async (user) => { const wallet = await Models.Wallet.create({ @@ -1553,7 +1552,7 @@ describe('Tools: check integrity balance', () => { active: true, UserId: user.id, WalletId: wallet.id, - }) + }); }); await CheckIntegrityBalance.run(); @@ -1568,7 +1567,6 @@ describe('Tools: check integrity balance', () => { }) ).to.be.true; }); - }); it('Should check card tier wrong tier only', async () => { @@ -1592,14 +1590,13 @@ describe('Tools: check integrity balance', () => { active: true, UserId: user.id, WalletId: wallet.id, - }) + }); }); await CheckIntegrityBalance.run(); expect(this.stubLoggerError.calledWith('invalid_balance')).to.be.true; expect(this.stubLoggerError.callCount).to.be.eq(this.users.length); - }); });