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,
tx: TransactionHandle,
): Promise<BalancesResponse> {
const balanceStore: Record<string, WalletBalance> = {};
/**
@ -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;
}

View File

@ -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;
}

View File

@ -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):

View File

@ -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

View File

@ -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()

View File

@ -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()