Add CI config for Gitlab CI integration tests (bash and pytest)
This commit is contained in:
parent
503c35d5f5
commit
08c3209dbc
26
.gitlab-ci.yml
Normal file
26
.gitlab-ci.yml
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
image: "registry.gitlab.com/gnu-taler/docker-taler-ci:latest"
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- pg_ctlcluster 12 main start
|
||||||
|
|
||||||
|
integration_tests_legacy:
|
||||||
|
script:
|
||||||
|
- cd integrationtests
|
||||||
|
- ./test-base.sh
|
||||||
|
- ./test-double-link.sh
|
||||||
|
- ./test-double-spend.sh
|
||||||
|
- ./test-recoup.sh
|
||||||
|
- ./test-refund.sh
|
||||||
|
- ./test-retries.sh
|
||||||
|
- ./test-withdrawal.sh
|
||||||
|
allow_failure: true
|
||||||
|
|
||||||
|
integration_tests:
|
||||||
|
script:
|
||||||
|
- ./bootstrap
|
||||||
|
- ./configure
|
||||||
|
- make install
|
||||||
|
- pytest -rP tests/
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
- pg_ctlcluster 12 main stop
|
1
tests/.gitignore
vendored
Normal file
1
tests/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
14
tests/__init__.py
Normal file
14
tests/__init__.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from taler.util.amount import Amount
|
||||||
|
|
||||||
|
|
||||||
|
def check_single_balance(balances, available, pending_in="TESTKUDOS:0", pending_out="TESTKUDOS:0",
|
||||||
|
has_pending=False):
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
def json_to_amount(d):
|
||||||
|
return Amount(d["currency"], d["value"], d["fraction"])
|
0
tests/components/__init__.py
Normal file
0
tests/components/__init__.py
Normal file
43
tests/components/bank.py
Normal file
43
tests/components/bank.py
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
import os
|
||||||
|
from subprocess import run
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
|
||||||
|
from .taler_service import TalerService
|
||||||
|
|
||||||
|
|
||||||
|
class Bank(TalerService):
|
||||||
|
|
||||||
|
def __init__(self, config, watcher_getter, request):
|
||||||
|
super().__init__(config, watcher_getter, request)
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
db = "postgres:///%s" % self.config.db
|
||||||
|
log_path = os.path.join(self.config.tmpdir, "bank.log")
|
||||||
|
log_file = open(log_path, 'w')
|
||||||
|
self.watcher_getter(
|
||||||
|
name='taler-bank-manage-testing',
|
||||||
|
arguments=[self.config.conf, db, 'serve-http'],
|
||||||
|
checker=self.test_process,
|
||||||
|
kwargs=dict(stderr=log_file, stdout=log_file),
|
||||||
|
request=self.request, # Needed for the correct execution order of finalizers
|
||||||
|
)
|
||||||
|
|
||||||
|
def close_log():
|
||||||
|
log_file.close()
|
||||||
|
|
||||||
|
self.request.addfinalizer(close_log)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
def test_process():
|
||||||
|
for p in psutil.process_iter(['name', 'cmdline']):
|
||||||
|
if p.info["name"] == "uwsgi" and p.info["cmdline"][-1] == "talerbank.wsgi":
|
||||||
|
return True
|
||||||
|
return False
|
62
tests/components/config.py
Normal file
62
tests/components/config.py
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from shutil import copyfile
|
||||||
|
from subprocess import run
|
||||||
|
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
|
||||||
|
def __init__(self, request, tmpdir, worker_id):
|
||||||
|
self.tmpdir = tmpdir.strpath
|
||||||
|
self.data_home = os.path.join(self.tmpdir, 'data')
|
||||||
|
|
||||||
|
# workaround for https://github.com/pytest-dev/pytest-services/issues/37
|
||||||
|
logger = logging.getLogger(
|
||||||
|
'[{worker_id}] {name}'.format(name="pytest_services.log", worker_id=worker_id))
|
||||||
|
logger.handlers.clear()
|
||||||
|
|
||||||
|
# copy config file from template
|
||||||
|
self.conf = tmpdir.join("test.conf").strpath
|
||||||
|
template = os.path.join(os.path.dirname(__file__), 'template.ini')
|
||||||
|
copyfile(template, self.conf)
|
||||||
|
|
||||||
|
# set TALER_HOME base dir
|
||||||
|
config_cmd = ["taler-config", "-c", self.conf]
|
||||||
|
run(config_cmd + ["-s", "PATHS", "-o", "TALER_HOME", "-V", self.tmpdir], check=True)
|
||||||
|
|
||||||
|
# get path of exchange private key file and create key pair
|
||||||
|
config_cmd = ["taler-config", "-c", self.conf]
|
||||||
|
r = run(config_cmd + ["-f", "-s", "EXCHANGE", "-o", "MASTER_PRIV_FILE"],
|
||||||
|
capture_output=True, check=True, text=True)
|
||||||
|
master_priv_file = r.stdout.rstrip()
|
||||||
|
master_priv_dir = os.path.dirname(master_priv_file)
|
||||||
|
os.makedirs(master_priv_dir)
|
||||||
|
run(["gnunet-ecc", "-g1", master_priv_file], check=True, capture_output=True)
|
||||||
|
r = run(["gnunet-ecc", "-p", master_priv_file], check=True, capture_output=True, text=True)
|
||||||
|
self.master_pub = r.stdout.rstrip()
|
||||||
|
|
||||||
|
# write exchange public key into config
|
||||||
|
run(config_cmd + ["-s", "exchange",
|
||||||
|
"-o", "MASTER_PUBLIC_KEY",
|
||||||
|
"-V", self.master_pub], check=True)
|
||||||
|
run(config_cmd + ["-s", "merchant-exchange-default",
|
||||||
|
"-o", "MASTER_KEY",
|
||||||
|
"-V", self.master_pub], check=True)
|
||||||
|
|
||||||
|
# write DB name into config
|
||||||
|
self.db = "test-db"
|
||||||
|
db_uri = "postgres:///" + self.db
|
||||||
|
run(config_cmd + ["-s", "exchangedb-postgres", "-o", "CONFIG", "-V", db_uri], check=True)
|
||||||
|
run(config_cmd + ["-s", "auditordb-postgres", "-o", "CONFIG", "-V", db_uri], check=True)
|
||||||
|
run(config_cmd + ["-s", "merchantdb-postgres", "-o", "CONFIG", "-V", db_uri], check=True)
|
||||||
|
run(config_cmd + ["-s", "bank", "-o", "database", "-V", db_uri], check=True)
|
||||||
|
|
||||||
|
# create new DB
|
||||||
|
run(["dropdb", self.db], capture_output=True)
|
||||||
|
run(["createdb", self.db], check=True)
|
||||||
|
|
||||||
|
# drop DB when test ends
|
||||||
|
def finalize():
|
||||||
|
run(["dropdb", self.db], capture_output=True)
|
||||||
|
|
||||||
|
request.addfinalizer(finalize)
|
53
tests/components/exchange.py
Normal file
53
tests/components/exchange.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import os
|
||||||
|
from subprocess import run
|
||||||
|
|
||||||
|
from .taler_service import TalerService
|
||||||
|
|
||||||
|
|
||||||
|
class Exchange(TalerService):
|
||||||
|
|
||||||
|
def __init__(self, config, watcher_getter, request):
|
||||||
|
super().__init__(config, watcher_getter, request)
|
||||||
|
|
||||||
|
# get own URL from config
|
||||||
|
r = run(["taler-config", "-c", config.conf, "-s", "EXCHANGE", "-o", "BASE_URL"],
|
||||||
|
check=True, text=True, capture_output=True)
|
||||||
|
self.url = r.stdout.rstrip()
|
||||||
|
|
||||||
|
# get and create directory for terms of service
|
||||||
|
r = run(["taler-config", "-c", config.conf, "-s", "EXCHANGE", "-o", "TERMS_DIR"],
|
||||||
|
check=True, text=True, capture_output=True)
|
||||||
|
self.terms_dir = r.stdout.rstrip().replace("${TALER_DATA_HOME}", config.data_home)
|
||||||
|
terms_dir_en = os.path.join(self.terms_dir, 'en')
|
||||||
|
os.makedirs(terms_dir_en)
|
||||||
|
|
||||||
|
# get eTag and create ToS file for it
|
||||||
|
r = run(["taler-config", "-c", config.conf, "-s", "EXCHANGE", "-o", "TERMS_ETAG"],
|
||||||
|
check=True, text=True, capture_output=True)
|
||||||
|
self.terms_etag = r.stdout.rstrip()
|
||||||
|
self.tos = "ToS Foo Bar\n"
|
||||||
|
with open(os.path.join(terms_dir_en, "%s.txt" % self.terms_etag), 'w') as f:
|
||||||
|
f.write(self.tos)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
run(["taler-exchange-dbinit", "-c", self.config.conf], check=True)
|
||||||
|
run(["taler-exchange-wire", "-c", self.config.conf], check=True)
|
||||||
|
run(["taler-exchange-keyup", "-c", self.config.conf,
|
||||||
|
"-L", "INFO",
|
||||||
|
"-o", os.path.join(self.config.tmpdir, "e2a.dat")
|
||||||
|
], check=True, capture_output=True)
|
||||||
|
log_path = os.path.join(self.config.tmpdir, "exchange.log")
|
||||||
|
self.watcher_getter(
|
||||||
|
name='taler-exchange-httpd',
|
||||||
|
arguments=['-c', self.config.conf, '-l', log_path],
|
||||||
|
checker=self.test_url,
|
||||||
|
request=self.request, # Needed for the correct execution order of finalizers
|
||||||
|
)
|
||||||
|
# the wirewatch is needed for interaction with the bank
|
||||||
|
log_wirewatch_path = os.path.join(self.config.tmpdir, "exchange-wirewatch.log")
|
||||||
|
self.watcher_getter(
|
||||||
|
name='taler-exchange-wirewatch',
|
||||||
|
arguments=['-c', self.config.conf, '-l', log_wirewatch_path],
|
||||||
|
checker=lambda: True, # no need to wait for this to come up
|
||||||
|
request=self.request, # Needed for the correct execution order of finalizers
|
||||||
|
)
|
18
tests/components/taler_service.py
Normal file
18
tests/components/taler_service.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import requests
|
||||||
|
from requests.exceptions import ConnectionError
|
||||||
|
|
||||||
|
|
||||||
|
class TalerService:
|
||||||
|
|
||||||
|
def __init__(self, config, watcher_getter, request):
|
||||||
|
self.config = config
|
||||||
|
self.watcher_getter = watcher_getter
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def test_url(self):
|
||||||
|
try:
|
||||||
|
requests.get(self.url, timeout=3)
|
||||||
|
except ConnectionError as e:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
228
tests/components/template.ini
Normal file
228
tests/components/template.ini
Normal file
@ -0,0 +1,228 @@
|
|||||||
|
[exchange]
|
||||||
|
KEYDIR = ${TALER_DATA_HOME}/exchange/live-keys/
|
||||||
|
REVOCATION_DIR = ${TALER_DATA_HOME}/exchange/revocations/
|
||||||
|
MAX_KEYS_CACHING = forever
|
||||||
|
DB = postgres
|
||||||
|
MASTER_PRIV_FILE = ${TALER_DATA_HOME}/exchange/offline-keys/master.priv
|
||||||
|
SERVE = tcp
|
||||||
|
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
|
||||||
|
UNIXPATH_MODE = 660
|
||||||
|
PORT = 8081
|
||||||
|
BASE_URL = http://localhost:8081/
|
||||||
|
SIGNKEY_DURATION = 4 weeks
|
||||||
|
LEGAL_DURATION = 2 years
|
||||||
|
LOOKAHEAD_SIGN = 32 weeks 1 day
|
||||||
|
LOOKAHEAD_PROVIDE = 4 weeks 1 day
|
||||||
|
TERMS_ETAG = test_etag
|
||||||
|
TERMS_DIR = ${TALER_DATA_HOME}/exchange/terms
|
||||||
|
|
||||||
|
[merchant]
|
||||||
|
SERVE = tcp
|
||||||
|
PORT = 9966
|
||||||
|
UNIXPATH = ${TALER_RUNTIME_DIR}/merchant.http
|
||||||
|
UNIXPATH_MODE = 660
|
||||||
|
DEFAULT_WIRE_FEE_AMORTIZATION = 1
|
||||||
|
DB = postgres
|
||||||
|
WIREFORMAT = default
|
||||||
|
# Set very low, so we can be sure that the database generated
|
||||||
|
# will contain wire transfers "ready" for the aggregator.
|
||||||
|
WIRE_TRANSFER_DELAY = 1 minute
|
||||||
|
DEFAULT_PAY_DEADLINE = 1 day
|
||||||
|
DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1
|
||||||
|
KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv
|
||||||
|
DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10
|
||||||
|
|
||||||
|
# Ensure that merchant reports EVERY deposit confirmation to auditor
|
||||||
|
FORCE_AUDIT = YES
|
||||||
|
|
||||||
|
[instance-default]
|
||||||
|
KEYFILE = ${TALER_DATA_HOME}/merchant/default.priv
|
||||||
|
NAME = Merchant Inc.
|
||||||
|
TIP_EXCHANGE = http://localhost:8081/
|
||||||
|
# TODO necessary to specify a different key here?
|
||||||
|
TIP_RESERVE_PRIV_FILENAME = ${TALER_DATA_HOME}/merchant/default.priv
|
||||||
|
|
||||||
|
[auditor]
|
||||||
|
DB = postgres
|
||||||
|
AUDITOR_PRIV_FILE = ${TALER_DATA_HOME}/auditor/offline-keys/auditor.priv
|
||||||
|
SERVE = tcp
|
||||||
|
UNIXPATH = ${TALER_RUNTIME_DIR}/exchange.http
|
||||||
|
UNIXPATH_MODE = 660
|
||||||
|
PORT = 8083
|
||||||
|
AUDITOR_URL = http://localhost:8083/
|
||||||
|
TINY_AMOUNT = TESTKUDOS:0.01
|
||||||
|
|
||||||
|
[PATHS]
|
||||||
|
TALER_DATA_HOME = $TALER_HOME/data/
|
||||||
|
TALER_CONFIG_HOME = $TALER_HOME/config/
|
||||||
|
TALER_CACHE_HOME = $TALER_HOME/cache/
|
||||||
|
TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/taler-system-runtime/
|
||||||
|
|
||||||
|
[bank]
|
||||||
|
DATABASE = postgres:///taler-auditor-basedb
|
||||||
|
MAX_DEBT = TESTKUDOS:50.0
|
||||||
|
MAX_DEBT_BANK = TESTKUDOS:100000.0
|
||||||
|
HTTP_PORT = 8082
|
||||||
|
SUGGESTED_EXCHANGE = http://localhost:8081/
|
||||||
|
SUGGESTED_EXCHANGE_PAYTO = payto://x-taler-bank/localhost/2
|
||||||
|
ALLOW_REGISTRATIONS = YES
|
||||||
|
|
||||||
|
[exchangedb]
|
||||||
|
AUDITOR_BASE_DIR = ${TALER_DATA_HOME}/auditors/
|
||||||
|
WIREFEE_BASE_DIR = ${TALER_DATA_HOME}/exchange/wirefees/
|
||||||
|
IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
|
||||||
|
LEGAL_RESERVE_EXPIRATION_TIME = 7 years
|
||||||
|
|
||||||
|
[exchange_keys]
|
||||||
|
signkey_duration = 4 weeks
|
||||||
|
legal_duration = 2 years
|
||||||
|
lookahead_sign = 32 weeks 1 day
|
||||||
|
lookahead_provide = 4 weeks 1 day
|
||||||
|
|
||||||
|
[taler]
|
||||||
|
CURRENCY = TESTKUDOS
|
||||||
|
CURRENCY_ROUND_UNIT = TESTKUDOS:0.01
|
||||||
|
|
||||||
|
[exchange-account-1]
|
||||||
|
WIRE_RESPONSE = ${TALER_DATA_HOME}/exchange/account-1.json
|
||||||
|
PAYTO_URI = payto://x-taler-bank/localhost/Exchange
|
||||||
|
enable_debit = yes
|
||||||
|
enable_credit = yes
|
||||||
|
WIRE_GATEWAY_URL = "http://localhost:8082/taler-wire-gateway/Exchange/"
|
||||||
|
WIRE_GATEWAY_AUTH_METHOD = basic
|
||||||
|
USERNAME = Exchange
|
||||||
|
PASSWORD = x
|
||||||
|
|
||||||
|
[merchant-account-merchant]
|
||||||
|
PAYTO_URI = payto://x-taler-bank/localhost/42
|
||||||
|
WIRE_RESPONSE = ${TALER_CONFIG_HOME}/merchant/account-3.json
|
||||||
|
HONOR_default = YES
|
||||||
|
ACTIVE_default = YES
|
||||||
|
|
||||||
|
[fees-x-taler-bank]
|
||||||
|
wire-fee-2020 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2020 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2021 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2021 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2022 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2022 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2023 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2023 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2024 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2024 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2025 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2025 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2026 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2026 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2027 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2027 = TESTKUDOS:0.01
|
||||||
|
wire-fee-2028 = TESTKUDOS:0.01
|
||||||
|
closing-fee-2028 = TESTKUDOS:0.01
|
||||||
|
|
||||||
|
[merchant-instance-wireformat-default]
|
||||||
|
TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/merchant/wire/tutorial.json
|
||||||
|
|
||||||
|
[merchant-exchange-default]
|
||||||
|
EXCHANGE_BASE_URL = http://localhost:8081/
|
||||||
|
CURRENCY = TESTKUDOS
|
||||||
|
|
||||||
|
[payments-generator]
|
||||||
|
currency = TESTKUDOS
|
||||||
|
instance = default
|
||||||
|
bank = http://localhost:8082/
|
||||||
|
merchant = http://localhost:9966/
|
||||||
|
exchange_admin = http://localhost:18080/
|
||||||
|
exchange-admin = http://localhost:18080/
|
||||||
|
exchange = http://localhost:8081/
|
||||||
|
|
||||||
|
[coin_kudos_ct_1]
|
||||||
|
value = TESTKUDOS:0.01
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.01
|
||||||
|
fee_deposit = TESTKUDOS:0.01
|
||||||
|
fee_refresh = TESTKUDOS:0.01
|
||||||
|
fee_refund = TESTKUDOS:0.01
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_ct_10]
|
||||||
|
value = TESTKUDOS:0.10
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.01
|
||||||
|
fee_deposit = TESTKUDOS:0.01
|
||||||
|
fee_refresh = TESTKUDOS:0.03
|
||||||
|
fee_refund = TESTKUDOS:0.01
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_1]
|
||||||
|
value = TESTKUDOS:1
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.02
|
||||||
|
fee_deposit = TESTKUDOS:0.02
|
||||||
|
fee_refresh = TESTKUDOS:0.03
|
||||||
|
fee_refund = TESTKUDOS:0.01
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_2]
|
||||||
|
value = TESTKUDOS:2
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.03
|
||||||
|
fee_deposit = TESTKUDOS:0.03
|
||||||
|
fee_refresh = TESTKUDOS:0.04
|
||||||
|
fee_refund = TESTKUDOS:0.02
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_4]
|
||||||
|
value = TESTKUDOS:4
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.03
|
||||||
|
fee_deposit = TESTKUDOS:0.03
|
||||||
|
fee_refresh = TESTKUDOS:0.04
|
||||||
|
fee_refund = TESTKUDOS:0.02
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_5]
|
||||||
|
value = TESTKUDOS:5
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.01
|
||||||
|
fee_deposit = TESTKUDOS:0.01
|
||||||
|
fee_refresh = TESTKUDOS:0.03
|
||||||
|
fee_refund = TESTKUDOS:0.01
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_8]
|
||||||
|
value = TESTKUDOS:8
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.05
|
||||||
|
fee_deposit = TESTKUDOS:0.02
|
||||||
|
fee_refresh = TESTKUDOS:0.03
|
||||||
|
fee_refund = TESTKUDOS:0.04
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[coin_kudos_10]
|
||||||
|
value = TESTKUDOS:10
|
||||||
|
duration_withdraw = 7 days
|
||||||
|
duration_spend = 2 years
|
||||||
|
duration_legal = 3 years
|
||||||
|
fee_withdraw = TESTKUDOS:0.01
|
||||||
|
fee_deposit = TESTKUDOS:0.01
|
||||||
|
fee_refresh = TESTKUDOS:0.03
|
||||||
|
fee_refund = TESTKUDOS:0.01
|
||||||
|
rsa_keysize = 1024
|
||||||
|
|
||||||
|
[benchmark]
|
||||||
|
BANK_DETAILS = bank_details.json
|
||||||
|
MERCHANT_DETAILS = merchant_details.json
|
50
tests/components/wallet.py
Normal file
50
tests/components/wallet.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from subprocess import run
|
||||||
|
|
||||||
|
|
||||||
|
class Wallet:
|
||||||
|
|
||||||
|
def __init__(self, config):
|
||||||
|
self.db = os.path.join(config.tmpdir, "wallet-db.json")
|
||||||
|
self.arg_db = "--wallet-db=%s" % self.db
|
||||||
|
self.log_path = os.path.join(config.tmpdir, "wallet.log")
|
||||||
|
|
||||||
|
def cmd(self, command, request=None):
|
||||||
|
if request is None:
|
||||||
|
request = dict()
|
||||||
|
request = json.dumps(request)
|
||||||
|
r = run(["taler-wallet-cli", self.arg_db, "api", command, request],
|
||||||
|
timeout=10, text=True, capture_output=True)
|
||||||
|
self.write_to_log(r.stderr)
|
||||||
|
if r.returncode != 0:
|
||||||
|
print(r)
|
||||||
|
assert r.returncode == 0
|
||||||
|
json_r = json.loads(r.stdout)
|
||||||
|
if json_r["isError"]:
|
||||||
|
print(r)
|
||||||
|
assert not json_r["isError"]
|
||||||
|
if "result" not in json_r:
|
||||||
|
# TODO should there not always be a "result"?
|
||||||
|
return None
|
||||||
|
return json_r["result"]
|
||||||
|
|
||||||
|
def testing_withdraw(self, amount, exchange_url, bank_url):
|
||||||
|
r = run(["taler-wallet-cli", self.arg_db, "--no-throttle", "testing", "withdraw",
|
||||||
|
"-a", amount,
|
||||||
|
"-e", exchange_url,
|
||||||
|
"-b", bank_url
|
||||||
|
], 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)
|
||||||
|
self.write_to_log(r.stderr)
|
||||||
|
return r.stdout.rstrip()
|
||||||
|
|
||||||
|
def write_to_log(self, data):
|
||||||
|
with open(self.log_path, "a") as f:
|
||||||
|
f.write(data)
|
30
tests/conftest.py
Normal file
30
tests/conftest.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from tests.components.bank import Bank
|
||||||
|
from tests.components.config import Config
|
||||||
|
from tests.components.exchange import Exchange
|
||||||
|
from tests.components.wallet import Wallet
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def config(watcher_getter, request, tmpdir, worker_id):
|
||||||
|
return Config(request, tmpdir, worker_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def exchange(watcher_getter, request, config):
|
||||||
|
exchange = Exchange(config, watcher_getter, request)
|
||||||
|
exchange.start()
|
||||||
|
return exchange
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def bank(watcher_getter, request, config):
|
||||||
|
bank = Bank(config, watcher_getter, request)
|
||||||
|
bank.start()
|
||||||
|
return bank
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def wallet(watcher_getter, config):
|
||||||
|
return Wallet(config)
|
4
tests/requirements.txt
Normal file
4
tests/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pytest==5.4.*
|
||||||
|
pytest-services==2.1.*
|
||||||
|
taler-util==0.6.*
|
||||||
|
psutil==5.7.*
|
18
tests/test_exchange_management.py
Normal file
18
tests/test_exchange_management.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
|
||||||
|
def test_exchanges(exchange, wallet):
|
||||||
|
# list of exchanges is initially empty
|
||||||
|
result = wallet.cmd("listExchanges")
|
||||||
|
assert not result["exchanges"]
|
||||||
|
|
||||||
|
# adding an exchange works
|
||||||
|
result = wallet.cmd("addExchange", {"exchangeBaseUrl": exchange.url})
|
||||||
|
assert not result # result is empty
|
||||||
|
|
||||||
|
# list includes added exchange
|
||||||
|
result = wallet.cmd("listExchanges")
|
||||||
|
e = result["exchanges"][0]
|
||||||
|
assert e["exchangeBaseUrl"] == exchange.url
|
||||||
|
assert e["currency"] == "TESTKUDOS"
|
||||||
|
assert len(e["paytoUris"]) >= 1
|
120
tests/test_withdrawal.py
Normal file
120
tests/test_withdrawal.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from taler.util.amount import Amount
|
||||||
|
|
||||||
|
from tests import check_single_balance
|
||||||
|
|
||||||
|
|
||||||
|
def test_withdrawal(exchange, bank, wallet):
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# check that balance is correct
|
||||||
|
result = wallet.cmd("getBalances")
|
||||||
|
amount_effective = Amount("TESTKUDOS", 4, 84000000).stringify()
|
||||||
|
check_single_balance(result["balances"], amount_effective)
|
||||||
|
|
||||||
|
# assert that withdrawal shows up properly in transactions
|
||||||
|
result = wallet.cmd("getTransactions")
|
||||||
|
assert len(result["transactions"]) == 1
|
||||||
|
transaction = result["transactions"][0]
|
||||||
|
assert transaction["type"] == "withdrawal"
|
||||||
|
assert transaction["amountEffective"] == amount_effective
|
||||||
|
assert transaction["amountRaw"] == amount_raw
|
||||||
|
assert transaction["exchangeBaseUrl"] == exchange.url
|
||||||
|
assert not transaction["pending"]
|
||||||
|
withdrawal_details = transaction["withdrawalDetails"]
|
||||||
|
assert withdrawal_details["type"] == "manual-transfer"
|
||||||
|
payto_list = ["payto://x-taler-bank/localhost/Exchange"]
|
||||||
|
assert withdrawal_details["exchangePaytoUris"] == payto_list
|
||||||
|
|
||||||
|
# get a withdrawal URI
|
||||||
|
uri = wallet.gen_withdraw_uri(amount_raw, bank.url)
|
||||||
|
assert uri.startswith("taler+http://withdraw")
|
||||||
|
|
||||||
|
# get withdrawal details from URI
|
||||||
|
result = wallet.cmd("getWithdrawalDetailsForUri", {"talerWithdrawUri": uri})
|
||||||
|
assert result["amount"] == amount_raw
|
||||||
|
assert result["defaultExchangeBaseUrl"] == exchange.url
|
||||||
|
assert len(result["possibleExchanges"]) == 1
|
||||||
|
assert result["possibleExchanges"][0]["exchangeBaseUrl"] == exchange.url
|
||||||
|
assert result["possibleExchanges"][0]["currency"] == "TESTKUDOS"
|
||||||
|
assert result["possibleExchanges"][0]["paytoUris"] == payto_list
|
||||||
|
|
||||||
|
# check withdrawal details for amount
|
||||||
|
request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw}
|
||||||
|
result = wallet.cmd("getWithdrawalDetailsForAmount", request)
|
||||||
|
assert result["amountRaw"] == amount_raw
|
||||||
|
assert result["amountEffective"] == amount_effective
|
||||||
|
assert result["paytoUris"] == payto_list
|
||||||
|
assert not result["tosAccepted"]
|
||||||
|
|
||||||
|
# get ToS
|
||||||
|
result = wallet.cmd("getExchangeTos", {"exchangeBaseUrl": exchange.url})
|
||||||
|
assert result["currentEtag"] == exchange.terms_etag
|
||||||
|
assert result["tos"] == exchange.tos
|
||||||
|
|
||||||
|
# accept ToS
|
||||||
|
request = {"exchangeBaseUrl": exchange.url, "etag": exchange.terms_etag}
|
||||||
|
wallet.cmd("setExchangeTosAccepted", request)
|
||||||
|
|
||||||
|
# check that ToS are now shown as accepted
|
||||||
|
request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw}
|
||||||
|
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/")
|
||||||
|
confirm_url = result["confirmTransferUrl"]
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# 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 transaction["exchangeBaseUrl"] == exchange.url
|
||||||
|
assert transaction["pending"]
|
||||||
|
withdrawal_details = transaction["withdrawalDetails"]
|
||||||
|
assert withdrawal_details["type"] == "taler-bank-integration-api"
|
||||||
|
assert not withdrawal_details["confirmed"]
|
||||||
|
assert withdrawal_details["bankConfirmationUrl"] == confirm_url
|
||||||
|
|
||||||
|
# new withdrawal is newer than old one
|
||||||
|
timestamp0 = result["transactions"][0]["timestamp"]["t_ms"]
|
||||||
|
timestamp1 = result["transactions"][1]["timestamp"]["t_ms"]
|
||||||
|
assert timestamp0 > timestamp1
|
||||||
|
|
||||||
|
# one more manual withdrawal
|
||||||
|
request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw}
|
||||||
|
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)
|
||||||
|
|
||||||
|
# assert that 3nd withdrawal shows up properly in transactions
|
||||||
|
result = wallet.cmd("getTransactions")
|
||||||
|
# TODO where is the manual withdrawal!??
|
||||||
|
# assert len(result["transactions"]) == 3
|
||||||
|
for t in result["transactions"]:
|
||||||
|
print(t)
|
||||||
|
print()
|
Loading…
Reference in New Issue
Block a user