From 7e34b6699a77620b148b167e6aa12d50cc7456e5 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Wed, 29 Jul 2020 14:33:59 +0530 Subject: test cases --- src/operations/balance.ts | 36 +++++++++++++++--------- src/operations/transactions.ts | 10 ------- tests/__init__.py | 15 ++++++---- tests/components/bank.py | 47 +++++++++++++++++++++++++++++++- tests/components/wallet.py | 14 ++++++---- tests/test_withdrawal.py | 62 ++++++++++++++++++++++++++++-------------- 6 files changed, 128 insertions(+), 56 deletions(-) diff --git a/src/operations/balance.ts b/src/operations/balance.ts index b503b7546..26f0aaeee 100644 --- a/src/operations/balance.ts +++ b/src/operations/balance.ts @@ -40,7 +40,6 @@ export async function getBalancesInsideTransaction( ws: InternalWalletState, tx: TransactionHandle, ): Promise { - const balanceStore: Record = {}; /** @@ -57,11 +56,17 @@ export async function getBalancesInsideTransaction( }; } return balanceStore[currency]; - } + }; // Initialize balance to zero, even if we didn't start withdrawing yet. await tx.iter(Stores.reserves).forEach((r) => { - initBalance(r.currency); + const b = initBalance(r.currency); + if (!r.initialWithdrawalStarted) { + b.pendingIncoming = Amounts.add( + b.pendingIncoming, + r.initialDenomSel.totalCoinValue, + ).amount; + } }); await tx.iter(Stores.coins).forEach((c) => { @@ -95,23 +100,28 @@ export async function getBalancesInsideTransaction( return; } const b = initBalance(wds.denomsSel.totalWithdrawCost.currency); - b.pendingIncoming = Amounts.add(b.pendingIncoming, wds.denomsSel.totalCoinValue).amount; + b.pendingIncoming = Amounts.add( + b.pendingIncoming, + wds.denomsSel.totalCoinValue, + ).amount; }); const balancesResponse: BalancesResponse = { balances: [], }; - Object.keys(balanceStore).sort().forEach((c) => { - const v = balanceStore[c]; - balancesResponse.balances.push({ - available: Amounts.stringify(v.available), - pendingIncoming: Amounts.stringify(v.pendingIncoming), - pendingOutgoing: Amounts.stringify(v.pendingOutgoing), - hasPendingTransactions: false, - requiresUserInput: false, + Object.keys(balanceStore) + .sort() + .forEach((c) => { + const v = balanceStore[c]; + balancesResponse.balances.push({ + available: Amounts.stringify(v.available), + pendingIncoming: Amounts.stringify(v.pendingIncoming), + pendingOutgoing: Amounts.stringify(v.pendingOutgoing), + hasPendingTransactions: false, + requiresUserInput: false, + }); }); - }) return balancesResponse; } diff --git a/src/operations/transactions.ts b/src/operations/transactions.ts index fb0629660..647949d22 100644 --- a/src/operations/transactions.ts +++ b/src/operations/transactions.ts @@ -20,8 +20,6 @@ import { InternalWalletState } from "./state"; import { Stores, - ReserveRecordStatus, - PurchaseRecord, WithdrawalSourceType, } from "../types/dbTypes"; import { Amounts, AmountJson } from "../util/amounts"; @@ -182,14 +180,6 @@ export async function getTransactions( if (shouldSkipSearch(transactionsRequest, [])) { return; } - switch (r.reserveStatus) { - case ReserveRecordStatus.WAIT_CONFIRM_BANK: - break; - case ReserveRecordStatus.WITHDRAWING: - break; - default: - return; - } if (r.initialWithdrawalStarted) { return; } diff --git a/tests/__init__.py b/tests/__init__.py index a01ed07c2..9f0dc11da 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1,13 +1,16 @@ from taler.util.amount import Amount -def check_single_balance(balances, available, pending_in="TESTKUDOS:0", pending_out="TESTKUDOS:0", - has_pending=False): +def check_single_balance( + balances, + available, + pending_in=Amount.parse("TESTKUDOS:0"), + pending_out=Amount.parse("TESTKUDOS:0"), +): assert len(balances) == 1 - assert balances[0]["available"] == available - assert balances[0]["pendingIncoming"] == pending_in - assert balances[0]["pendingOutgoing"] == pending_out - assert balances[0]["hasPendingTransactions"] == has_pending + assert Amount.parse(balances[0]["available"]) == available + assert Amount.parse(balances[0]["pendingIncoming"]) == pending_in + assert Amount.parse(balances[0]["pendingOutgoing"]) == pending_out def json_to_amount(d): diff --git a/tests/components/bank.py b/tests/components/bank.py index 707edbfc4..ee2d6e923 100644 --- a/tests/components/bank.py +++ b/tests/components/bank.py @@ -3,8 +3,24 @@ from subprocess import run import psutil +import requests + +import secrets + from .taler_service import TalerService +from dataclasses import dataclass + +@dataclass +class BankUser: + username: str + password: str + +@dataclass +class WithdrawUriResponse: + taler_withdraw_uri: str + withdrawal_id: str + class Bank(TalerService): @@ -14,7 +30,7 @@ class Bank(TalerService): # get localhost port and store bank URL r = run(["taler-config", "-c", config.conf, "-s", "BANK", "-o", "HTTP_PORT"], check=True, text=True, capture_output=True) - self.url = "http://localhost:%s" % r.stdout.rstrip() + self.url = "http://localhost:%s/" % r.stdout.rstrip() def start(self): db = "postgres:///%s" % self.config.db @@ -33,6 +49,35 @@ class Bank(TalerService): self.request.addfinalizer(close_log) + def register_random_user(self): + username = f"testuser-{secrets.token_hex(10)}" + password = f"testpw-{secrets.token_hex(10)}" + requests.post( + f"{self.url}testing/register", + json=dict(username=username, password=password) + ) + return BankUser(username, password) + + def generate_withdraw_uri(self, bankuser, amount): + auth = (bankuser.username, bankuser.password) + resp = requests.post( + f"{self.url}accounts/{bankuser.username}/withdrawals", + json=dict(amount=amount), + auth=auth + ) + rj = resp.json() + return WithdrawUriResponse( + taler_withdraw_uri=rj["taler_withdraw_uri"], + withdrawal_id=rj["withdrawal_id"], + ) + + def confirm_withdrawal(self, bankuser, withdrawal_id): + auth = (bankuser.username, bankuser.password) + resp = requests.post( + f"{self.url}accounts/{bankuser.username}/withdrawals/{withdrawal_id}/confirm", + auth=auth + ) + # Alternative way to check if the bank came up. # Testing the URL has the issue that on the CI, django keeps closing the connection. @staticmethod diff --git a/tests/components/wallet.py b/tests/components/wallet.py index 77099ab88..cedae84e1 100644 --- a/tests/components/wallet.py +++ b/tests/components/wallet.py @@ -37,11 +37,15 @@ class Wallet: ], timeout=10, check=True, text=True, capture_output=True) self.write_to_log(r.stderr) - def gen_withdraw_uri(self, amount, bank_url): - r = run(["taler-wallet-cli", self.arg_db, "testing", "gen-withdraw-uri", - "-a", amount, - "-b", bank_url - ], timeout=10, check=True, text=True, capture_output=True) + def run_pending(self): + r = run(["taler-wallet-cli", self.arg_db, "run-pending"], + timeout=10, check=True, text=True, capture_output=True) + self.write_to_log(r.stderr) + return r.stdout.rstrip() + + def run_until_done(self): + r = run(["taler-wallet-cli", self.arg_db, "run-until-done"], + timeout=10, check=True, text=True, capture_output=True) self.write_to_log(r.stderr) return r.stdout.rstrip() diff --git a/tests/test_withdrawal.py b/tests/test_withdrawal.py index 8a68807b6..0f5c8435b 100644 --- a/tests/test_withdrawal.py +++ b/tests/test_withdrawal.py @@ -6,17 +6,19 @@ from tests import check_single_balance def test_withdrawal(exchange, bank, wallet): + bank_user = bank.register_random_user() + # assert that we start with no transactions result = wallet.cmd("getTransactions") assert not result["transactions"] # test withdrawal - amount_raw = "TESTKUDOS:5" - wallet.testing_withdraw(amount_raw, exchange.url, bank.url) + amount_raw = Amount.parse("TESTKUDOS:5") + wallet.testing_withdraw(amount_raw.stringify(), exchange.url, bank.url) # check that balance is correct result = wallet.cmd("getBalances") - amount_effective = Amount("TESTKUDOS", 4, 84000000).stringify() + amount_effective = Amount("TESTKUDOS", 4, 84000000) check_single_balance(result["balances"], amount_effective) # assert that withdrawal shows up properly in transactions @@ -24,8 +26,8 @@ def test_withdrawal(exchange, bank, wallet): assert len(result["transactions"]) == 1 transaction = result["transactions"][0] assert transaction["type"] == "withdrawal" - assert transaction["amountEffective"] == amount_effective - assert transaction["amountRaw"] == amount_raw + assert Amount.parse(transaction["amountEffective"]) == amount_effective + assert Amount.parse(transaction["amountRaw"]) == amount_raw assert transaction["exchangeBaseUrl"] == exchange.url assert not transaction["pending"] withdrawal_details = transaction["withdrawalDetails"] @@ -34,12 +36,13 @@ def test_withdrawal(exchange, bank, wallet): assert withdrawal_details["exchangePaytoUris"] == payto_list # get a withdrawal URI - uri = wallet.gen_withdraw_uri(amount_raw, bank.url) + bank_uri_resp = bank.generate_withdraw_uri(bank_user, "TESTKUDOS:5") + uri = bank_uri_resp.taler_withdraw_uri assert uri.startswith("taler+http://withdraw") # get withdrawal details from URI result = wallet.cmd("getWithdrawalDetailsForUri", {"talerWithdrawUri": uri}) - assert result["amount"] == amount_raw + assert Amount.parse(result["amount"]) == amount_raw assert result["defaultExchangeBaseUrl"] == exchange.url assert len(result["possibleExchanges"]) == 1 assert result["possibleExchanges"][0]["exchangeBaseUrl"] == exchange.url @@ -47,10 +50,10 @@ def test_withdrawal(exchange, bank, wallet): assert result["possibleExchanges"][0]["paytoUris"] == payto_list # check withdrawal details for amount - request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw} + request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()} result = wallet.cmd("getWithdrawalDetailsForAmount", request) - assert result["amountRaw"] == amount_raw - assert result["amountEffective"] == amount_effective + assert Amount.parse(result["amountRaw"]) == amount_raw + assert Amount.parse(result["amountEffective"]) == amount_effective assert result["paytoUris"] == payto_list assert not result["tosAccepted"] @@ -64,29 +67,33 @@ def test_withdrawal(exchange, bank, wallet): wallet.cmd("setExchangeTosAccepted", request) # check that ToS are now shown as accepted - request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw} + request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()} result = wallet.cmd("getWithdrawalDetailsForAmount", request) assert result["tosAccepted"] # accept withdrawal request = {"exchangeBaseUrl": exchange.url, "talerWithdrawUri": uri} result = wallet.cmd("acceptBankIntegratedWithdrawal", request) - assert result["confirmTransferUrl"].startswith(bank.url + "/confirm-withdrawal/") + assert result["confirmTransferUrl"].startswith(bank.url + "confirm-withdrawal/") confirm_url = result["confirmTransferUrl"] + # Let the wallet do its work. At this point, the bank-integrated + # withdrawal won't have succeeded yet, as it's not confirmed at the bank + # side. + wallet.run_pending() + # check that balance is correct result = wallet.cmd("getBalances") - # TODO pendingIncoming and hasPendingTransactions are wrong, right? print(result) - # check_single_balance(result["balances"], amount_effective, amount_effective, has_pending=True) + check_single_balance(result["balances"], amount_effective, amount_effective) # assert that 2nd withdrawal shows up properly in transactions result = wallet.cmd("getTransactions") assert len(result["transactions"]) == 2 transaction = result["transactions"][0] assert transaction["type"] == "withdrawal" - assert transaction["amountEffective"] == amount_effective - assert transaction["amountRaw"] == amount_raw + assert Amount.parse(transaction["amountEffective"]) == amount_effective + assert Amount.parse(transaction["amountRaw"]) == amount_raw assert transaction["exchangeBaseUrl"] == exchange.url assert transaction["pending"] withdrawal_details = transaction["withdrawalDetails"] @@ -99,22 +106,35 @@ def test_withdrawal(exchange, bank, wallet): timestamp1 = result["transactions"][1]["timestamp"]["t_ms"] assert timestamp0 > timestamp1 + # now we actually confirm the withdrawal + bank.confirm_withdrawal(bank_user, bank_uri_resp.withdrawal_id) + # It might take some time until the exchange knows about the reserve, + # so we'll try until it works. + wallet.run_until_done() + + # check that balance is correct + result = wallet.cmd("getBalances") + print(result) + check_single_balance( + result["balances"], Amount.parse("TESTKUDOS:9.68"), Amount.parse("TESTKUDOS:0"), + ) + # one more manual withdrawal - request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw} + request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()} result = wallet.cmd("acceptManualWithdrawal", request) assert len(result["exchangePaytoUris"]) == 1 result["exchangePaytoUris"][0].startswith(payto_list[0]) # check that balance is correct result = wallet.cmd("getBalances") - # TODO pendingIncoming and hasPendingTransactions are wrong, right? print(result) - # check_single_balance(result["balances"], amount_effective, TODO, has_pending=True) + check_single_balance( + result["balances"], amount_effective + amount_effective, amount_effective + ) # assert that 3nd withdrawal shows up properly in transactions result = wallet.cmd("getTransactions") - # TODO where is the manual withdrawal!?? - # assert len(result["transactions"]) == 3 + assert len(result["transactions"]) == 3 for t in result["transactions"]: print(t) print() -- cgit v1.2.3