test cases

This commit is contained in:
Florian Dold 2020-07-29 14:33:59 +05:30
parent 08c3209dbc
commit 7e34b6699a
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
6 changed files with 128 additions and 56 deletions

View File

@ -40,7 +40,6 @@ export async function getBalancesInsideTransaction(
ws: InternalWalletState, ws: InternalWalletState,
tx: TransactionHandle, tx: TransactionHandle,
): Promise<BalancesResponse> { ): Promise<BalancesResponse> {
const balanceStore: Record<string, WalletBalance> = {}; const balanceStore: Record<string, WalletBalance> = {};
/** /**
@ -57,11 +56,17 @@ export async function getBalancesInsideTransaction(
}; };
} }
return balanceStore[currency]; return balanceStore[currency];
} };
// Initialize balance to zero, even if we didn't start withdrawing yet. // Initialize balance to zero, even if we didn't start withdrawing yet.
await tx.iter(Stores.reserves).forEach((r) => { 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) => { await tx.iter(Stores.coins).forEach((c) => {
@ -95,14 +100,19 @@ export async function getBalancesInsideTransaction(
return; return;
} }
const b = initBalance(wds.denomsSel.totalWithdrawCost.currency); 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 = { const balancesResponse: BalancesResponse = {
balances: [], balances: [],
}; };
Object.keys(balanceStore).sort().forEach((c) => { Object.keys(balanceStore)
.sort()
.forEach((c) => {
const v = balanceStore[c]; const v = balanceStore[c];
balancesResponse.balances.push({ balancesResponse.balances.push({
available: Amounts.stringify(v.available), available: Amounts.stringify(v.available),
@ -111,7 +121,7 @@ export async function getBalancesInsideTransaction(
hasPendingTransactions: false, hasPendingTransactions: false,
requiresUserInput: false, requiresUserInput: false,
}); });
}) });
return balancesResponse; return balancesResponse;
} }

View File

@ -20,8 +20,6 @@
import { InternalWalletState } from "./state"; import { InternalWalletState } from "./state";
import { import {
Stores, Stores,
ReserveRecordStatus,
PurchaseRecord,
WithdrawalSourceType, WithdrawalSourceType,
} from "../types/dbTypes"; } from "../types/dbTypes";
import { Amounts, AmountJson } from "../util/amounts"; import { Amounts, AmountJson } from "../util/amounts";
@ -182,14 +180,6 @@ export async function getTransactions(
if (shouldSkipSearch(transactionsRequest, [])) { if (shouldSkipSearch(transactionsRequest, [])) {
return; return;
} }
switch (r.reserveStatus) {
case ReserveRecordStatus.WAIT_CONFIRM_BANK:
break;
case ReserveRecordStatus.WITHDRAWING:
break;
default:
return;
}
if (r.initialWithdrawalStarted) { if (r.initialWithdrawalStarted) {
return; return;
} }

View File

@ -1,13 +1,16 @@
from taler.util.amount import Amount from taler.util.amount import Amount
def check_single_balance(balances, available, pending_in="TESTKUDOS:0", pending_out="TESTKUDOS:0", def check_single_balance(
has_pending=False): balances,
available,
pending_in=Amount.parse("TESTKUDOS:0"),
pending_out=Amount.parse("TESTKUDOS:0"),
):
assert len(balances) == 1 assert len(balances) == 1
assert balances[0]["available"] == available assert Amount.parse(balances[0]["available"]) == available
assert balances[0]["pendingIncoming"] == pending_in assert Amount.parse(balances[0]["pendingIncoming"]) == pending_in
assert balances[0]["pendingOutgoing"] == pending_out assert Amount.parse(balances[0]["pendingOutgoing"]) == pending_out
assert balances[0]["hasPendingTransactions"] == has_pending
def json_to_amount(d): def json_to_amount(d):

View File

@ -3,8 +3,24 @@ from subprocess import run
import psutil import psutil
import requests
import secrets
from .taler_service import TalerService 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): class Bank(TalerService):
@ -14,7 +30,7 @@ class Bank(TalerService):
# get localhost port and store bank URL # get localhost port and store bank URL
r = run(["taler-config", "-c", config.conf, "-s", "BANK", "-o", "HTTP_PORT"], r = run(["taler-config", "-c", config.conf, "-s", "BANK", "-o", "HTTP_PORT"],
check=True, text=True, capture_output=True) 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): def start(self):
db = "postgres:///%s" % self.config.db db = "postgres:///%s" % self.config.db
@ -33,6 +49,35 @@ class Bank(TalerService):
self.request.addfinalizer(close_log) 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. # Alternative way to check if the bank came up.
# Testing the URL has the issue that on the CI, django keeps closing the connection. # Testing the URL has the issue that on the CI, django keeps closing the connection.
@staticmethod @staticmethod

View File

@ -37,11 +37,15 @@ class Wallet:
], timeout=10, check=True, text=True, capture_output=True) ], timeout=10, check=True, text=True, capture_output=True)
self.write_to_log(r.stderr) self.write_to_log(r.stderr)
def gen_withdraw_uri(self, amount, bank_url): def run_pending(self):
r = run(["taler-wallet-cli", self.arg_db, "testing", "gen-withdraw-uri", r = run(["taler-wallet-cli", self.arg_db, "run-pending"],
"-a", amount, timeout=10, check=True, text=True, capture_output=True)
"-b", bank_url self.write_to_log(r.stderr)
], timeout=10, check=True, text=True, capture_output=True) 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) self.write_to_log(r.stderr)
return r.stdout.rstrip() return r.stdout.rstrip()

View File

@ -6,17 +6,19 @@ from tests import check_single_balance
def test_withdrawal(exchange, bank, wallet): def test_withdrawal(exchange, bank, wallet):
bank_user = bank.register_random_user()
# assert that we start with no transactions # assert that we start with no transactions
result = wallet.cmd("getTransactions") result = wallet.cmd("getTransactions")
assert not result["transactions"] assert not result["transactions"]
# test withdrawal # test withdrawal
amount_raw = "TESTKUDOS:5" amount_raw = Amount.parse("TESTKUDOS:5")
wallet.testing_withdraw(amount_raw, exchange.url, bank.url) wallet.testing_withdraw(amount_raw.stringify(), exchange.url, bank.url)
# check that balance is correct # check that balance is correct
result = wallet.cmd("getBalances") result = wallet.cmd("getBalances")
amount_effective = Amount("TESTKUDOS", 4, 84000000).stringify() amount_effective = Amount("TESTKUDOS", 4, 84000000)
check_single_balance(result["balances"], amount_effective) check_single_balance(result["balances"], amount_effective)
# assert that withdrawal shows up properly in transactions # assert that withdrawal shows up properly in transactions
@ -24,8 +26,8 @@ def test_withdrawal(exchange, bank, wallet):
assert len(result["transactions"]) == 1 assert len(result["transactions"]) == 1
transaction = result["transactions"][0] transaction = result["transactions"][0]
assert transaction["type"] == "withdrawal" assert transaction["type"] == "withdrawal"
assert transaction["amountEffective"] == amount_effective assert Amount.parse(transaction["amountEffective"]) == amount_effective
assert transaction["amountRaw"] == amount_raw assert Amount.parse(transaction["amountRaw"]) == amount_raw
assert transaction["exchangeBaseUrl"] == exchange.url assert transaction["exchangeBaseUrl"] == exchange.url
assert not transaction["pending"] assert not transaction["pending"]
withdrawal_details = transaction["withdrawalDetails"] withdrawal_details = transaction["withdrawalDetails"]
@ -34,12 +36,13 @@ def test_withdrawal(exchange, bank, wallet):
assert withdrawal_details["exchangePaytoUris"] == payto_list assert withdrawal_details["exchangePaytoUris"] == payto_list
# get a withdrawal URI # 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") assert uri.startswith("taler+http://withdraw")
# get withdrawal details from URI # get withdrawal details from URI
result = wallet.cmd("getWithdrawalDetailsForUri", {"talerWithdrawUri": 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 result["defaultExchangeBaseUrl"] == exchange.url
assert len(result["possibleExchanges"]) == 1 assert len(result["possibleExchanges"]) == 1
assert result["possibleExchanges"][0]["exchangeBaseUrl"] == exchange.url assert result["possibleExchanges"][0]["exchangeBaseUrl"] == exchange.url
@ -47,10 +50,10 @@ def test_withdrawal(exchange, bank, wallet):
assert result["possibleExchanges"][0]["paytoUris"] == payto_list assert result["possibleExchanges"][0]["paytoUris"] == payto_list
# check withdrawal details for amount # 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) result = wallet.cmd("getWithdrawalDetailsForAmount", request)
assert result["amountRaw"] == amount_raw assert Amount.parse(result["amountRaw"]) == amount_raw
assert result["amountEffective"] == amount_effective assert Amount.parse(result["amountEffective"]) == amount_effective
assert result["paytoUris"] == payto_list assert result["paytoUris"] == payto_list
assert not result["tosAccepted"] assert not result["tosAccepted"]
@ -64,29 +67,33 @@ def test_withdrawal(exchange, bank, wallet):
wallet.cmd("setExchangeTosAccepted", request) wallet.cmd("setExchangeTosAccepted", request)
# check that ToS are now shown as accepted # 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) result = wallet.cmd("getWithdrawalDetailsForAmount", request)
assert result["tosAccepted"] assert result["tosAccepted"]
# accept withdrawal # accept withdrawal
request = {"exchangeBaseUrl": exchange.url, "talerWithdrawUri": uri} request = {"exchangeBaseUrl": exchange.url, "talerWithdrawUri": uri}
result = wallet.cmd("acceptBankIntegratedWithdrawal", request) 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"] 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 # check that balance is correct
result = wallet.cmd("getBalances") result = wallet.cmd("getBalances")
# TODO pendingIncoming and hasPendingTransactions are wrong, right?
print(result) 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 # assert that 2nd withdrawal shows up properly in transactions
result = wallet.cmd("getTransactions") result = wallet.cmd("getTransactions")
assert len(result["transactions"]) == 2 assert len(result["transactions"]) == 2
transaction = result["transactions"][0] transaction = result["transactions"][0]
assert transaction["type"] == "withdrawal" assert transaction["type"] == "withdrawal"
assert transaction["amountEffective"] == amount_effective assert Amount.parse(transaction["amountEffective"]) == amount_effective
assert transaction["amountRaw"] == amount_raw assert Amount.parse(transaction["amountRaw"]) == amount_raw
assert transaction["exchangeBaseUrl"] == exchange.url assert transaction["exchangeBaseUrl"] == exchange.url
assert transaction["pending"] assert transaction["pending"]
withdrawal_details = transaction["withdrawalDetails"] withdrawal_details = transaction["withdrawalDetails"]
@ -99,22 +106,35 @@ def test_withdrawal(exchange, bank, wallet):
timestamp1 = result["transactions"][1]["timestamp"]["t_ms"] timestamp1 = result["transactions"][1]["timestamp"]["t_ms"]
assert timestamp0 > timestamp1 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 # one more manual withdrawal
request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw} request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()}
result = wallet.cmd("acceptManualWithdrawal", request) result = wallet.cmd("acceptManualWithdrawal", request)
assert len(result["exchangePaytoUris"]) == 1 assert len(result["exchangePaytoUris"]) == 1
result["exchangePaytoUris"][0].startswith(payto_list[0]) result["exchangePaytoUris"][0].startswith(payto_list[0])
# check that balance is correct # check that balance is correct
result = wallet.cmd("getBalances") result = wallet.cmd("getBalances")
# TODO pendingIncoming and hasPendingTransactions are wrong, right?
print(result) 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 # assert that 3nd withdrawal shows up properly in transactions
result = wallet.cmd("getTransactions") 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"]: for t in result["transactions"]:
print(t) print(t)
print() print()