bigchaindb/tests/conftest.py

618 lines
20 KiB
Python

"""Fixtures and setup / teardown functions
Tasks:
1. setup test database before starting the tests
2. delete test database after running the tests
"""
import os
import copy
import random
from collections import namedtuple
from logging import getLogger
from logging.config import dictConfig
import pytest
from pymongo import MongoClient
from bigchaindb.common import crypto
from bigchaindb.log import setup_logging
from bigchaindb.tendermint.lib import Block
TEST_DB_NAME = 'bigchain_test'
USER2_SK, USER2_PK = crypto.generate_key_pair()
# Test user. inputs will be created for this user. Cryptography Keys
USER_PRIVATE_KEY = '8eJ8q9ZQpReWyQT5aFCiwtZ5wDZC4eDnCen88p3tQ6ie'
USER_PUBLIC_KEY = 'JEAkEJqLbbgDRAtMm8YAjGp759Aq2qTn9eaEHUj2XePE'
def pytest_runtest_setup(item):
if isinstance(item, item.Function):
backend = item.session.config.getoption('--database-backend')
if (item.get_marker('localmongodb') and backend != 'localmongodb'):
pytest.skip('Skip tendermint specific tests if not using localmongodb')
def pytest_addoption(parser):
from bigchaindb.backend.connection import BACKENDS
backends = ', '.join(BACKENDS.keys())
parser.addoption(
'--database-backend',
action='store',
default=os.environ.get('BIGCHAINDB_DATABASE_BACKEND', 'localmongodb'),
help='Defines the backend to use (available: {})'.format(backends),
)
def pytest_ignore_collect(path, config):
from bigchaindb.backend.connection import BACKENDS
path = str(path)
supported_backends = BACKENDS.keys()
if os.path.isdir(path):
dirname = os.path.split(path)[1]
if dirname in supported_backends and dirname != config.getoption('--database-backend'):
print('Ignoring unrequested backend test dir: ', path)
return True
def pytest_configure(config):
config.addinivalue_line(
'markers',
'bdb(): Mark the test as needing BigchainDB.'
'BigchainDB will be configured such that the database and tables are available for an '
'entire test session.'
'You need to run a backend (e.g. MongoDB) '
'prior to running tests with this marker. You should not need to restart the backend '
'in between tests runs since the test infrastructure flushes the backend upon session end.'
)
config.addinivalue_line(
'markers',
'abci(): Mark the test as needing a running ABCI server in place. Use this marker'
'for tests that require a running Tendermint instance. Note that the test infrastructure'
'has no way to reset Tendermint data upon session end - you need to do it manually.'
'Setup performed by this marker includes the steps performed by the bdb marker.'
)
@pytest.fixture(autouse=True)
def _bdb_marker(request):
if request.keywords.get('bdb', None):
request.getfixturevalue('_bdb')
@pytest.fixture(autouse=True)
def _restore_config(_configure_bigchaindb):
from bigchaindb import config, config_utils
config_before_test = copy.deepcopy(config)
yield
config_utils.set_config(config_before_test)
@pytest.fixture(scope='session')
def _configure_bigchaindb(request):
import bigchaindb
from bigchaindb import config_utils
test_db_name = TEST_DB_NAME
# Put a suffix like _gw0, _gw1 etc on xdist processes
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid')
if xdist_suffix:
test_db_name = '{}_{}'.format(TEST_DB_NAME, xdist_suffix)
backend = request.config.getoption('--database-backend')
config = {
'database': bigchaindb._database_map[backend],
'tendermint': {
'host': 'localhost',
'port': 26657,
}
}
config['database']['name'] = test_db_name
config = config_utils.env_config(config)
config_utils.set_config(config)
# NOTE: since we use a custom log level
# for benchmark logging we need to setup logging
setup_logging()
@pytest.fixture(scope='session')
def _setup_database(_configure_bigchaindb):
from bigchaindb import config
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import DatabaseDoesNotExist
print('Initializing test db')
dbname = config['database']['name']
conn = connect()
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
schema.init_database(conn)
print('Finishing init database')
yield
print('Deleting `{}` database'.format(dbname))
conn = connect()
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
print('Finished deleting `{}`'.format(dbname))
@pytest.fixture
def _bdb(_setup_database, _configure_bigchaindb):
from bigchaindb import config
from bigchaindb.backend import connect
from .utils import flush_db
conn = connect()
yield
dbname = config['database']['name']
flush_db(conn, dbname)
# We need this function to avoid loading an existing
# conf file located in the home of the user running
# the tests. If it's too aggressive we can change it
# later.
@pytest.fixture
def ignore_local_config_file(monkeypatch):
def mock_file_config(filename=None):
return {}
monkeypatch.setattr('bigchaindb.config_utils.file_config',
mock_file_config)
@pytest.fixture
def reset_logging_config():
# root_logger_level = getLogger().level
root_logger_level = 'DEBUG'
dictConfig({'version': 1, 'root': {'level': 'NOTSET'}})
yield
getLogger().setLevel(root_logger_level)
@pytest.fixture
def user_sk():
return USER_PRIVATE_KEY
@pytest.fixture
def user_pk():
return USER_PUBLIC_KEY
@pytest.fixture
def user2_sk():
return USER2_SK
@pytest.fixture
def user2_pk():
return USER2_PK
@pytest.fixture
def alice():
from bigchaindb.common.crypto import generate_key_pair
return generate_key_pair()
@pytest.fixture
def alice_privkey(alice):
return alice.private_key
@pytest.fixture
def alice_pubkey(alice):
return alice.public_key
@pytest.fixture
def bob():
from bigchaindb.common.crypto import generate_key_pair
return generate_key_pair()
@pytest.fixture
def bob_privkey(bob):
return bob.private_key
@pytest.fixture
def bob_pubkey(carol):
return bob.public_key
@pytest.fixture
def carol():
from bigchaindb.common.crypto import generate_key_pair
return generate_key_pair()
@pytest.fixture
def carol_privkey(carol):
return carol.private_key
@pytest.fixture
def carol_pubkey(carol):
return carol.public_key
@pytest.fixture
def merlin():
from bigchaindb.common.crypto import generate_key_pair
return generate_key_pair()
@pytest.fixture
def merlin_privkey(merlin):
return merlin.private_key
@pytest.fixture
def merlin_pubkey(merlin):
return merlin.public_key
@pytest.fixture
def b():
from bigchaindb.tendermint import BigchainDB
return BigchainDB()
@pytest.fixture
def tb():
from bigchaindb.tendermint import BigchainDB
return BigchainDB()
@pytest.fixture
def create_tx(alice, user_pk):
from bigchaindb.models import Transaction
name = f'I am created by the create_tx fixture. My random identifier is {random.random()}.'
return Transaction.create([alice.public_key], [([user_pk], 1)], asset={'name': name})
@pytest.fixture
def signed_create_tx(alice, create_tx):
return create_tx.sign([alice.private_key])
@pytest.mark.abci
@pytest.fixture
def posted_create_tx(b, signed_create_tx):
res = b.post_transaction(signed_create_tx, 'broadcast_tx_commit')
assert res.status_code == 200
return signed_create_tx
@pytest.fixture
def signed_transfer_tx(signed_create_tx, user_pk, user_sk):
from bigchaindb.models import Transaction
inputs = signed_create_tx.to_inputs()
tx = Transaction.transfer(inputs, [([user_pk], 1)], asset_id=signed_create_tx.id)
return tx.sign([user_sk])
@pytest.fixture
def double_spend_tx(signed_create_tx, carol_pubkey, user_sk):
from bigchaindb.models import Transaction
inputs = signed_create_tx.to_inputs()
tx = Transaction.transfer(
inputs, [([carol_pubkey], 1)], asset_id=signed_create_tx.id)
return tx.sign([user_sk])
def _get_height(b):
maybe_block = b.get_latest_block()
return 0 if maybe_block is None else maybe_block['height']
@pytest.fixture
def inputs(user_pk, b, alice):
from bigchaindb.models import Transaction
# create blocks with transactions for `USER` to spend
for block in range(4):
transactions = [
Transaction.create(
[alice_pubkey(alice)],
[([user_pk], 1)],
metadata={'msg': random.random()},
).sign([alice_privkey(alice)]).to_dict()
for _ in range(10)
]
block = Block(app_hash='', height=_get_height(b), transactions=transactions)
b.store_block(block._asdict())
@pytest.fixture
def inputs_shared(user_pk, user2_pk, alice):
from bigchaindb.models import Transaction
# create blocks with transactions for `USER` to spend
for block in range(4):
transactions = [
Transaction.create(
[alice.public_key],
[user_pk, user2_pk],
metadata={'msg': random.random()},
).sign([alice.private_key]).to_dict()
for _ in range(10)
]
block = Block(app_hash='', height=_get_height(b), transaction=transactions)
b.store_block(block._asdict())
@pytest.fixture
def dummy_db(request):
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import (DatabaseDoesNotExist,
DatabaseAlreadyExists)
conn = connect()
dbname = request.fixturename
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid')
if xdist_suffix:
dbname = '{}_{}'.format(dbname, xdist_suffix)
try:
schema.init_database(conn, dbname)
except DatabaseAlreadyExists:
schema.drop_database(conn, dbname)
schema.init_database(conn, dbname)
yield dbname
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
@pytest.fixture
def not_yet_created_db(request):
from bigchaindb.backend import connect, schema
from bigchaindb.common.exceptions import DatabaseDoesNotExist
conn = connect()
dbname = request.fixturename
xdist_suffix = getattr(request.config, 'slaveinput', {}).get('slaveid')
if xdist_suffix:
dbname = '{}_{}'.format(dbname, xdist_suffix)
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
yield dbname
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
pass
@pytest.fixture
def db_config():
from bigchaindb import config
return config['database']
@pytest.fixture
def db_host(db_config):
return db_config['host']
@pytest.fixture
def db_port(db_config):
return db_config['port']
@pytest.fixture
def db_name(db_config):
return db_config['name']
@pytest.fixture
def db_conn():
from bigchaindb.backend import connect
return connect()
@pytest.fixture
def db_context(db_config, db_host, db_port, db_name, db_conn):
DBContext = namedtuple(
'DBContext', ('config', 'host', 'port', 'name', 'conn'))
return DBContext(
config=db_config,
host=db_host,
port=db_port,
name=db_name,
conn=db_conn,
)
@pytest.fixture
def tendermint_host():
return os.getenv('BIGCHAINDB_TENDERMINT_HOST', 'localhost')
@pytest.fixture
def tendermint_port():
return int(os.getenv('BIGCHAINDB_TENDERMINT_PORT', 26657))
@pytest.fixture
def tendermint_ws_url(tendermint_host, tendermint_port):
return 'ws://{}:{}/websocket'.format(tendermint_host, tendermint_port)
@pytest.fixture
def tendermint_context(tendermint_host, tendermint_port, tendermint_ws_url):
TendermintContext = namedtuple(
'TendermintContext', ('host', 'port', 'ws_url'))
return TendermintContext(
host=tendermint_host,
port=tendermint_port,
ws_url=tendermint_ws_url,
)
@pytest.fixture
def mocked_setup_pub_logger(mocker):
return mocker.patch(
'bigchaindb.log.setup.setup_pub_logger', autospec=True, spec_set=True)
@pytest.fixture
def mocked_setup_sub_logger(mocker):
return mocker.patch(
'bigchaindb.log.setup.setup_sub_logger', autospec=True, spec_set=True)
@pytest.fixture(autouse=True)
def _abci_http(request):
if request.keywords.get('abci', None):
request.getfixturevalue('abci_http')
@pytest.fixture
def abci_http(_setup_database, _configure_bigchaindb, abci_server,
tendermint_host, tendermint_port):
import requests
import time
for i in range(300):
try:
uri = 'http://{}:{}/abci_info'.format(tendermint_host, tendermint_port)
requests.get(uri)
return True
except requests.exceptions.RequestException as e:
pass
time.sleep(1)
return False
@pytest.yield_fixture(scope='session')
def event_loop(request):
import asyncio
loop = asyncio.get_event_loop_policy().new_event_loop()
yield loop
loop.close()
@pytest.mark.bdb
@pytest.fixture(scope='session')
def abci_server():
from abci import ABCIServer
from bigchaindb.tendermint.core import App
from bigchaindb.utils import Process
app = ABCIServer(app=App())
abci_proxy = Process(name='ABCI', target=app.run)
yield abci_proxy.start()
abci_proxy.terminate()
@pytest.fixture
def wsserver_config():
from bigchaindb import config
return config['wsserver']
@pytest.fixture
def wsserver_scheme(wsserver_config):
return wsserver_config['advertised_scheme']
@pytest.fixture
def wsserver_host(wsserver_config):
return wsserver_config['advertised_host']
@pytest.fixture
def wsserver_port(wsserver_config):
return wsserver_config['advertised_port']
@pytest.fixture
def wsserver_base_url(wsserver_scheme, wsserver_host, wsserver_port):
return '{}://{}:{}'.format(wsserver_scheme, wsserver_host, wsserver_port)
@pytest.fixture
def unspent_output_0():
return {
'amount': 1,
'asset_id': 'e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d',
'condition_uri': 'ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072',
'fulfillment_message': '{"asset":{"data":{"hash":"06e47bcf9084f7ecfd2a2a2ad275444a"}},"id":"e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d","inputs":[{"fulfillment":"pGSAIIQT0Jm6LDlcSs9coJK4Q4W-SNtsO2EtMtQJ04EUjBMJgUAXKIqeaippbF-IClhhZNNaP6EIZ_OgrVQYU4mH6b-Vc3Tg-k6p-rJOlLGUUo_w8C5QgPHNRYFOqUk2f1q0Cs4G","fulfills":null,"owners_before":["9taLkHkaBXeSF8vrhDGFTAmcZuCEPqjQrKadfYGs4gHv"]}],"metadata":null,"operation":"CREATE","outputs":[{"amount":"1","condition":{"details":{"public_key":"6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz","type":"ed25519-sha-256"},"uri":"ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072"},"public_keys":["6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz"]},{"amount":"2","condition":{"details":{"public_key":"AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT","type":"ed25519-sha-256"},"uri":"ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072"},"public_keys":["AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT"]},{"amount":"3","condition":{"details":{"public_key":"HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB","type":"ed25519-sha-256"},"uri":"ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072"},"public_keys":["HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB"]}],"version":"1.0"}', # noqa
'output_index': 0,
'transaction_id': 'e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d'
}
@pytest.fixture
def unspent_output_1():
return {
'amount': 2,
'asset_id': 'e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d',
'condition_uri': 'ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072',
'fulfillment_message': '{"asset":{"data":{"hash":"06e47bcf9084f7ecfd2a2a2ad275444a"}},"id":"e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d","inputs":[{"fulfillment":"pGSAIIQT0Jm6LDlcSs9coJK4Q4W-SNtsO2EtMtQJ04EUjBMJgUAXKIqeaippbF-IClhhZNNaP6EIZ_OgrVQYU4mH6b-Vc3Tg-k6p-rJOlLGUUo_w8C5QgPHNRYFOqUk2f1q0Cs4G","fulfills":null,"owners_before":["9taLkHkaBXeSF8vrhDGFTAmcZuCEPqjQrKadfYGs4gHv"]}],"metadata":null,"operation":"CREATE","outputs":[{"amount":"1","condition":{"details":{"public_key":"6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz","type":"ed25519-sha-256"},"uri":"ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072"},"public_keys":["6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz"]},{"amount":"2","condition":{"details":{"public_key":"AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT","type":"ed25519-sha-256"},"uri":"ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072"},"public_keys":["AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT"]},{"amount":"3","condition":{"details":{"public_key":"HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB","type":"ed25519-sha-256"},"uri":"ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072"},"public_keys":["HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB"]}],"version":"1.0"}', # noqa
'output_index': 1,
'transaction_id': 'e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d',
}
@pytest.fixture
def unspent_output_2():
return {
'amount': 3,
'asset_id': 'e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d',
'condition_uri': 'ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072',
'fulfillment_message': '{"asset":{"data":{"hash":"06e47bcf9084f7ecfd2a2a2ad275444a"}},"id":"e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d","inputs":[{"fulfillment":"pGSAIIQT0Jm6LDlcSs9coJK4Q4W-SNtsO2EtMtQJ04EUjBMJgUAXKIqeaippbF-IClhhZNNaP6EIZ_OgrVQYU4mH6b-Vc3Tg-k6p-rJOlLGUUo_w8C5QgPHNRYFOqUk2f1q0Cs4G","fulfills":null,"owners_before":["9taLkHkaBXeSF8vrhDGFTAmcZuCEPqjQrKadfYGs4gHv"]}],"metadata":null,"operation":"CREATE","outputs":[{"amount":"1","condition":{"details":{"public_key":"6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz","type":"ed25519-sha-256"},"uri":"ni:///sha-256;RmovleG60-7K0CX60jjfUunV3lBpUOkiQOAnBzghm0w?fpt=ed25519-sha-256&cost=131072"},"public_keys":["6FDGsHrR9RZqNaEm7kBvqtxRkrvuWogBW2Uy7BkWc5Tz"]},{"amount":"2","condition":{"details":{"public_key":"AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT","type":"ed25519-sha-256"},"uri":"ni:///sha-256;-HlYmgwwl-vXwE52IaADhvYxaL1TbjqfJ-LGn5a1PFc?fpt=ed25519-sha-256&cost=131072"},"public_keys":["AH9D7xgmhyLmVE944zvHvuvYWuj5DfbMBJhnDM4A5FdT"]},{"amount":"3","condition":{"details":{"public_key":"HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB","type":"ed25519-sha-256"},"uri":"ni:///sha-256;xfn8pvQkTCPtvR0trpHy2pqkkNTmMBCjWMMOHtk3WO4?fpt=ed25519-sha-256&cost=131072"},"public_keys":["HpmSVrojHvfCXQbmoAs4v6Aq1oZiZsZDnjr68KiVtPbB"]}],"version":"1.0"}', # noqa
'output_index': 2,
'transaction_id': 'e897c7a0426461a02b4fca8ed73bc0debed7570cf3b40fb4f49c963434225a4d',
}
@pytest.fixture
def unspent_outputs(unspent_output_0, unspent_output_1, unspent_output_2):
return unspent_output_0, unspent_output_1, unspent_output_2
@pytest.fixture
def mongo_client(db_context):
return MongoClient(host=db_context.host, port=db_context.port)
@pytest.fixture
def utxo_collection(db_context, mongo_client):
return mongo_client[db_context.name].utxos
@pytest.fixture
def dummy_unspent_outputs():
return [
{'transaction_id': 'a', 'output_index': 0},
{'transaction_id': 'a', 'output_index': 1},
{'transaction_id': 'b', 'output_index': 0},
]
@pytest.fixture
def utxoset(dummy_unspent_outputs, utxo_collection):
res = utxo_collection.insert_many(copy.deepcopy(dummy_unspent_outputs))
assert res.acknowledged
assert len(res.inserted_ids) == 3
return dummy_unspent_outputs, utxo_collection