test cases
This commit is contained in:
parent
08c3209dbc
commit
7e34b6699a
@ -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,23 +100,28 @@ 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)
|
||||||
const v = balanceStore[c];
|
.sort()
|
||||||
balancesResponse.balances.push({
|
.forEach((c) => {
|
||||||
available: Amounts.stringify(v.available),
|
const v = balanceStore[c];
|
||||||
pendingIncoming: Amounts.stringify(v.pendingIncoming),
|
balancesResponse.balances.push({
|
||||||
pendingOutgoing: Amounts.stringify(v.pendingOutgoing),
|
available: Amounts.stringify(v.available),
|
||||||
hasPendingTransactions: false,
|
pendingIncoming: Amounts.stringify(v.pendingIncoming),
|
||||||
requiresUserInput: false,
|
pendingOutgoing: Amounts.stringify(v.pendingOutgoing),
|
||||||
|
hasPendingTransactions: false,
|
||||||
|
requiresUserInput: false,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
|
||||||
|
|
||||||
return balancesResponse;
|
return balancesResponse;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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):
|
||||||
|
@ -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
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user