diff --git a/planetmint/backend/__init__.py b/planetmint/backend/__init__.py index b1c15e2..7a2d9ee 100644 --- a/planetmint/backend/__init__.py +++ b/planetmint/backend/__init__.py @@ -14,4 +14,4 @@ configuration or the ``PLANETMINT_DATABASE_BACKEND`` environment variable. # Include the backend interfaces from planetmint.backend import schema, query # noqa -from planetmint.backend.connection_tarantool import connect # noqa +from planetmint.backend.connection import Connection diff --git a/planetmint/backend/connection.py b/planetmint/backend/connection.py index 7e76b03..a1b5d26 100644 --- a/planetmint/backend/connection.py +++ b/planetmint/backend/connection.py @@ -3,168 +3,29 @@ # SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) # Code is Apache-2.0 and docs are CC-BY-4.0 +import sys import logging from importlib import import_module from itertools import repeat -import planetmint from planetmint.backend.exceptions import ConnectionError from planetmint.backend.utils import get_planetmint_config_value, get_planetmint_config_value_or_key_error from planetmint.common.exceptions import ConfigurationError BACKENDS = { # This is path to MongoDBClass - 'localmongodb': 'planetmint.backend.localmongodb.connection.LocalMongoDBConnection', + 'tarantool_db': 'planetmint.backend.tarantool.connection.TarantoolDB', + 'localmongodb': 'planetmint.backend.localmongodb.connection.LocalMongoDBConnection' } logger = logging.getLogger(__name__) -def connect(backend=None, host=None, port=None, name=None, max_tries=None, - connection_timeout=None, replicaset=None, ssl=None, login=None, password=None, - ca_cert=None, certfile=None, keyfile=None, keyfile_passphrase=None, - crlfile=None): - """Create a new connection to the database backend. - - All arguments default to the current configuration's values if not - given. - - Args: - backend (str): the name of the backend to use. - host (str): the host to connect to. - port (int): the port to connect to. - name (str): the name of the database to use. - replicaset (str): the name of the replica set (only relevant for - MongoDB connections). - - Returns: - An instance of :class:`~planetmint.backend.connection.Connection` - based on the given (or defaulted) :attr:`backend`. - - Raises: - :exc:`~ConnectionError`: If the connection to the database fails. - :exc:`~ConfigurationError`: If the given (or defaulted) :attr:`backend` - is not supported or could not be loaded. - :exc:`~AuthenticationError`: If there is a OperationFailure due to - Authentication failure after connecting to the database. - """ - - backend = backend or get_planetmint_config_value_or_key_error('backend') - host = host or get_planetmint_config_value_or_key_error('host') - port = port or get_planetmint_config_value_or_key_error('port') - dbname = name or get_planetmint_config_value_or_key_error('name') - # Not sure how to handle this here. This setting is only relevant for - # mongodb. - # I added **kwargs for both RethinkDBConnection and MongoDBConnection - # to handle these these additional args. In case of RethinkDBConnection - # it just does not do anything with it. - # - # UPD: RethinkDBConnection is not here anymore cause we no longer support RethinkDB. - # The problem described above might be reconsidered next time we introduce a backend, - # if it ever happens. - replicaset = replicaset or get_planetmint_config_value('replicaset') - ssl = ssl if ssl is not None else get_planetmint_config_value('ssl', False) - login = login or get_planetmint_config_value('login') - password = password or get_planetmint_config_value('password') - ca_cert = ca_cert or get_planetmint_config_value('ca_cert') - certfile = certfile or get_planetmint_config_value('certfile') - keyfile = keyfile or get_planetmint_config_value('keyfile') - keyfile_passphrase = keyfile_passphrase or get_planetmint_config_value('keyfile_passphrase', None) - crlfile = crlfile or get_planetmint_config_value('crlfile') - - try: # Here we get class using getattr function - module_name, _, class_name = BACKENDS[backend].rpartition('.') - Class = getattr(import_module(module_name), class_name) - except KeyError: - raise ConfigurationError('Backend `{}` is not supported. ' - 'Planetmint currently supports {}'.format(backend, BACKENDS.keys())) - except (ImportError, AttributeError) as exc: - raise ConfigurationError('Error loading backend `{}`'.format(backend)) from exc - - logger.debug('Connection: {}'.format(Class)) - return Class(host=host, port=port, dbname=dbname, - max_tries=max_tries, connection_timeout=connection_timeout, - replicaset=replicaset, ssl=ssl, login=login, password=password, - ca_cert=ca_cert, certfile=certfile, keyfile=keyfile, - keyfile_passphrase=keyfile_passphrase, crlfile=crlfile) +modulename = sys.modules[__name__] +backend = get_planetmint_config_value("backend") +current_backend = getattr(modulename, BACKENDS[backend]) -class Connection: - """Connection class interface. +class Connection(current_backend): + pass - All backend implementations should provide a connection class that inherits - from and implements this class. - """ - def __init__(self, host=None, port=None, dbname=None, - connection_timeout=None, max_tries=None, - **kwargs): - """Create a new :class:`~.Connection` instance. - - Args: - host (str): the host to connect to. - port (int): the port to connect to. - dbname (str): the name of the database to use. - connection_timeout (int, optional): the milliseconds to wait - until timing out the database connection attempt. - Defaults to 5000ms. - max_tries (int, optional): how many tries before giving up, - if 0 then try forever. Defaults to 3. - **kwargs: arbitrary keyword arguments provided by the - configuration's ``database`` settings - """ - - dbconf = planetmint.config['database'] - - self.host = host or dbconf['host'] - self.port = port or dbconf['port'] - self.dbname = dbname or dbconf['name'] - self.connection_timeout = connection_timeout if connection_timeout is not None \ - else dbconf['connection_timeout'] - self.max_tries = max_tries if max_tries is not None else dbconf['max_tries'] - self.max_tries_counter = range(self.max_tries) if self.max_tries != 0 else repeat(0) - self._conn = None - - @property - def conn(self): - if self._conn is None: - self.connect() - return self._conn - - def run(self, query): - """Run a query. - - Args: - query: the query to run - Raises: - :exc:`~DuplicateKeyError`: If the query fails because of a - duplicate key constraint. - :exc:`~OperationFailure`: If the query fails for any other - reason. - :exc:`~ConnectionError`: If the connection to the database - fails. - """ - - raise NotImplementedError() - - def connect(self): - """Try to connect to the database. - - Raises: - :exc:`~ConnectionError`: If the connection to the database - fails. - """ - - attempt = 0 - for i in self.max_tries_counter: - attempt += 1 - try: - self._conn = self._connect() - except ConnectionError as exc: - logger.warning('Attempt %s/%s. Connection to %s:%s failed after %sms.', - attempt, self.max_tries if self.max_tries != 0 else '∞', - self.host, self.port, self.connection_timeout) - if attempt == self.max_tries: - logger.critical('Cannot connect to the Database. Giving up.') - raise ConnectionError() from exc - else: - break diff --git a/planetmint/backend/connection_tarantool.py b/planetmint/backend/connection_tarantool.py deleted file mode 100644 index b502de9..0000000 --- a/planetmint/backend/connection_tarantool.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright © 2020 Interplanetary Database Association e.V., -# Planetmint and IPDB software contributors. -# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) -# Code is Apache-2.0 and docs are CC-BY-4.0 - -import logging -from importlib import import_module -from itertools import repeat - -import tarantool -import planetmint - -from planetmint.backend.exceptions import ConnectionError -from planetmint.backend.utils import get_planetmint_config_value, get_planetmint_config_value_or_key_error -from planetmint.common.exceptions import ConfigurationError - -BACKENDS = { # This is path to MongoDBClass - 'tarantool_db': 'planetmint.backend.connection_tarantool.TarantoolDB', -} - -logger = logging.getLogger(__name__) - - -class TarantoolDB: - def __init__(self, host: str, port: int, user: str, password: str, reset_database: bool = False): - self.db_connect = tarantool.connect(host=host, port=port, user=user, password=password) - if reset_database: - self.drop_database() - self.init_database() - - def get_connection(self, space_name: str = None): - return self.db_connect if space_name is None else self.db_connect.space(space_name) - - def __read_commands(self, file_path): - with open(file_path, "r") as cmd_file: - commands = [line.strip() for line in cmd_file.readlines() if len(str(line)) > 1] - cmd_file.close() - return commands - - def drop_database(self): - from planetmint.backend.tarantool.utils import run - config = get_planetmint_config_value_or_key_error("ctl_config") - drop_config = config["drop_config"] - f_path = "%s%s" % (drop_config["relative_path"], drop_config["drop_file"]) - commands = self.__read_commands(file_path=f_path) - run(commands=commands, config=config) - - def init_database(self): - from planetmint.backend.tarantool.utils import run - config = get_planetmint_config_value_or_key_error("ctl_config") - init_config = config["init_config"] - f_path = "%s%s" % (init_config["relative_path"], init_config["init_file"]) - commands = self.__read_commands(file_path=f_path) - run(commands=commands, config=config) - -def connect(host: str = None, port: int = None, username: str = "admin", password: str = "pass", - backend: str = None, reset_database: bool = False, name=None, max_tries=None, - connection_timeout=None, replicaset=None, ssl=None, login: str = "admin", ctl_config=None, - ca_cert=None, certfile=None, keyfile=None, keyfile_passphrase=None, reconnect_delay=None, - crlfile=None, connect_now=True, encoding=None): - backend = backend or get_planetmint_config_value_or_key_error('backend') # TODO Rewrite Configs - host = host or get_planetmint_config_value_or_key_error('host') - port = port or get_planetmint_config_value_or_key_error('port') - username = username or login or get_planetmint_config_value('login') - password = password or get_planetmint_config_value('password') - - try: # Here we get class using getattr function - module_name, _, class_name = BACKENDS[backend].rpartition('.') - Class = getattr(import_module(module_name), class_name) - except KeyError: - raise ConfigurationError('Backend `{}` is not supported. ' - 'Planetmint currently supports {}'.format(backend, BACKENDS.keys())) - except (ImportError, AttributeError) as exc: - raise ConfigurationError('Error loading backend `{}`'.format(backend)) from exc - - logger.debug('Connection: {}'.format(Class)) - return Class(host=host, port=port, user=username, password=password, reset_database=reset_database) - - -class Connection: - """Connection class interface. - - All backend implementations should provide a connection class that inherits - from and implements this class. - """ - - def __init__(self, host=None, port=None, connection_timeout=None, max_tries=None, - **kwargs): - """Create a new :class:`~.Connection` instance. - - Args: - host (str): the host to connect to. - port (int): the port to connect to. - dbname (str): the name of the database to use. - connection_timeout (int, optional): the milliseconds to wait - until timing out the database connection attempt. - Defaults to 5000ms. - max_tries (int, optional): how many tries before giving up, - if 0 then try forever. Defaults to 3. - **kwargs: arbitrary keyword arguments provided by the - configuration's ``database`` settings - """ - - dbconf = planetmint.config['database'] - - self.host = host or dbconf['host'] - self.port = port or dbconf['port'] - self.connection_timeout = connection_timeout if connection_timeout is not None \ - else dbconf['connection_timeout'] - self.max_tries = max_tries if max_tries is not None else dbconf['max_tries'] - self.max_tries_counter = range(self.max_tries) if self.max_tries != 0 else repeat(0) - self._conn = None - - @property - def conn(self): - pass - if self._conn is None: - self.connect() - return self._conn - - def run(self, query): - pass - """Run a query. - - Args: - query: the query to run - Raises: - :exc:`~DuplicateKeyError`: If the query fails because of a - duplicate key constraint. - :exc:`~OperationFailure`: If the query fails for any other - reason. - :exc:`~ConnectionError`: If the connection to the database - fails. - """ - - raise NotImplementedError() - - def connect(self): - pass - """Try to connect to the database. - - Raises: - :exc:`~ConnectionError`: If the connection to the database - fails. - """ - - attempt = 0 - for i in self.max_tries_counter: - attempt += 1 - try: - self._conn = self._connect() - except ConnectionError as exc: - logger.warning('Attempt %s/%s. Connection to %s:%s failed after %sms.', - attempt, self.max_tries if self.max_tries != 0 else '∞', - self.host, self.port, self.connection_timeout) - if attempt == self.max_tries: - logger.critical('Cannot connect to the Database. Giving up.') - raise ConnectionError() from exc - else: - break diff --git a/planetmint/backend/localmongodb/connection.py b/planetmint/backend/localmongodb/connection.py index 945a2ff..995bc04 100644 --- a/planetmint/backend/localmongodb/connection.py +++ b/planetmint/backend/localmongodb/connection.py @@ -4,11 +4,11 @@ # Code is Apache-2.0 and docs are CC-BY-4.0 import logging +import bigchaindb from ssl import CERT_REQUIRED import pymongo -from planetmint.backend.connection import Connection from planetmint.backend.exceptions import (DuplicateKeyError, OperationError, ConnectionError) @@ -19,6 +19,84 @@ from planetmint.utils import Lazy logger = logging.getLogger(__name__) +class Connection: + """Connection class interface. + All backend implementations should provide a connection class that inherits + from and implements this class. + """ + + def __init__(self, host=None, port=None, dbname=None, + connection_timeout=None, max_tries=None, + **kwargs): + """Create a new :class:`~.Connection` instance. + Args: + host (str): the host to connect to. + port (int): the port to connect to. + dbname (str): the name of the database to use. + connection_timeout (int, optional): the milliseconds to wait + until timing out the database connection attempt. + Defaults to 5000ms. + max_tries (int, optional): how many tries before giving up, + if 0 then try forever. Defaults to 3. + **kwargs: arbitrary keyword arguments provided by the + configuration's ``database`` settings + """ + + dbconf = bigchaindb.config['database'] + + self.host = host or dbconf['host'] + self.port = port or dbconf['port'] + self.dbname = dbname or dbconf['name'] + self.connection_timeout = connection_timeout if connection_timeout is not None \ + else dbconf['connection_timeout'] + self.max_tries = max_tries if max_tries is not None else dbconf['max_tries'] + self.max_tries_counter = range(self.max_tries) if self.max_tries != 0 else repeat(0) + self._conn = None + + @property + def conn(self): + if self._conn is None: + self.connect() + return self._conn + + def run(self, query): + """Run a query. + Args: + query: the query to run + Raises: + :exc:`~DuplicateKeyError`: If the query fails because of a + duplicate key constraint. + :exc:`~OperationFailure`: If the query fails for any other + reason. + :exc:`~ConnectionError`: If the connection to the database + fails. + """ + + raise NotImplementedError() + + def connect(self): + """Try to connect to the database. + Raises: + :exc:`~ConnectionError`: If the connection to the database + fails. + """ + + attempt = 0 + for i in self.max_tries_counter: + attempt += 1 + try: + self._conn = self._connect() + except ConnectionError as exc: + logger.warning('Attempt %s/%s. Connection to %s:%s failed after %sms.', + attempt, self.max_tries if self.max_tries != 0 else '∞', + self.host, self.port, self.connection_timeout) + if attempt == self.max_tries: + logger.critical('Cannot connect to the Database. Giving up.') + raise ConnectionError() from exc + else: + break + + class LocalMongoDBConnection(Connection): def __init__(self, replicaset=None, ssl=None, login=None, password=None, diff --git a/planetmint/backend/schema.py b/planetmint/backend/schema.py index 49586bf..118864f 100644 --- a/planetmint/backend/schema.py +++ b/planetmint/backend/schema.py @@ -9,8 +9,7 @@ from functools import singledispatch import logging import planetmint -from planetmint.backend.connection import connect as connect_mongo -from planetmint.backend.connection_tarantool import connect +from planetmint.backend.connection import Connection from planetmint.common.exceptions import ValidationError from planetmint.common.utils import validate_all_values_for_key_in_obj, validate_all_values_for_key_in_list @@ -64,7 +63,7 @@ def drop_database(connection, dbname): raise NotImplementedError -def init_database(connection=None, dbname=None): +def init_database(connection=None, dbname=None): # FIXME HERE IS INIT DATABASE """Initialize the configured backend for use with Planetmint. Creates a database with :attr:`dbname` with any required tables @@ -79,7 +78,7 @@ def init_database(connection=None, dbname=None): configuration. """ - connection = connection or connect() + connection = connection or Connection() dbname = dbname or planetmint.config['database']['name'] create_database(connection, dbname) diff --git a/planetmint/backend/tarantool/__init__.py b/planetmint/backend/tarantool/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/planetmint/backend/tarantool/connection.py b/planetmint/backend/tarantool/connection.py new file mode 100644 index 0000000..a6316ba --- /dev/null +++ b/planetmint/backend/tarantool/connection.py @@ -0,0 +1,55 @@ +# Copyright © 2020 Interplanetary Database Association e.V., +# Planetmint and IPDB software contributors. +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + +import logging +from importlib import import_module +from itertools import repeat + +import tarantool +import planetmint + +from planetmint.backend.exceptions import ConnectionError +from planetmint.backend.utils import get_planetmint_config_value, get_planetmint_config_value_or_key_error +from planetmint.common.exceptions import ConfigurationError + +BACKENDS = { # This is path to MongoDBClass + 'tarantool_db': 'planetmint.backend.connection_tarantool.TarantoolDB', + 'localmongodb': 'planetmint.backend.localmongodb.connection.LocalMongoDBConnection' +} + +logger = logging.getLogger(__name__) + + +class TarantoolDB: + def __init__(self, host: str, port: int, user: str, password: str, reset_database: bool = False): + self.db_connect = tarantool.connect(host=host, port=port, user=user, password=password) + if reset_database: + self.drop_database() + self.init_database() + + def get_connection(self, space_name: str = None): + return self.db_connect if space_name is None else self.db_connect.space(space_name) + + def __read_commands(self, file_path): + with open(file_path, "r") as cmd_file: + commands = [line.strip() for line in cmd_file.readlines() if len(str(line)) > 1] + cmd_file.close() + return commands + + def drop_database(self): + from planetmint.backend.tarantool.utils import run + config = get_planetmint_config_value_or_key_error("ctl_config") + drop_config = config["drop_config"] + f_path = "%s%s" % (drop_config["relative_path"], drop_config["drop_file"]) + commands = self.__read_commands(file_path=f_path) + run(commands=commands, config=config) + + def init_database(self): + from planetmint.backend.tarantool.utils import run + config = get_planetmint_config_value_or_key_error("ctl_config") + init_config = config["init_config"] + f_path = "%s%s" % (init_config["relative_path"], init_config["init_file"]) + commands = self.__read_commands(file_path=f_path) + run(commands=commands, config=config) diff --git a/planetmint/lib.py b/planetmint/lib.py index 179f23d..329ad21 100644 --- a/planetmint/lib.py +++ b/planetmint/lib.py @@ -76,7 +76,7 @@ class Planetmint(object): else: self.validation = BaseValidationRules - self.connection = connection if connection else backend.connection_tarantool.connect(**planetmint.config['database']) + self.connection = connection if connection else planetmint.backend.tarantool.connection_tarantool.connect(**planetmint.config['database']) def post_transaction(self, transaction, mode): """Submit a valid transaction to the mempool.""" diff --git a/tests/backend/localmongodb/test_queries.py b/tests/backend/localmongodb/test_queries.py index 57e11a4..2ccdd7b 100644 --- a/tests/backend/localmongodb/test_queries.py +++ b/tests/backend/localmongodb/test_queries.py @@ -180,7 +180,7 @@ def test_write_metadata(): # check that 3 assets were written to the database cursor = conn.db.metadata.find({}, projection={'_id': False})\ .sort('id', pymongo.ASCENDING) - +TarantoolDB assert cursor.collection.count_documents({}) == 3 assert list(cursor) == metadata diff --git a/tests/backend/tarantool/test_queries.py b/tests/backend/tarantool/test_queries.py index 7821b46..634bbec 100644 --- a/tests/backend/tarantool/test_queries.py +++ b/tests/backend/tarantool/test_queries.py @@ -9,16 +9,16 @@ import pytest # import pymongo -# from planetmint.backend import connect, query +# from planetmint.backend.connection import Connection, query pytestmark = pytest.mark.bdb def test_get_txids_filtered(signed_create_tx, signed_transfer_tx): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query from planetmint.models import Transaction - conn = connect(reset_database=True).get_connection() + conn = Connection(reset_database=True).get_connection() # create and insert two blocks, one for the create and one for the # transfer transaction create_tx_dict = signed_create_tx.to_dict() @@ -43,9 +43,9 @@ def test_get_txids_filtered(signed_create_tx, signed_transfer_tx): def test_write_assets(): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() assets = [ {'id': "1", 'data': '1'}, {'id': "2", 'data': '2'}, @@ -66,9 +66,9 @@ def test_write_assets(): def test_get_assets(): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() assets = [ {'id': "1", 'data': '1'}, @@ -167,9 +167,9 @@ def test_text_search(table): def test_write_metadata(): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() metadata = [ {'id': "1", 'data': '1'}, @@ -194,9 +194,9 @@ def test_write_metadata(): def test_get_metadata(): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() metadata = [ {'id': "dd86682db39e4b424df0eec1413cfad65488fd48712097c5d865ca8e8e059b64", 'metadata': None}, @@ -211,9 +211,9 @@ def test_get_metadata(): def test_get_owned_ids(signed_create_tx, user_pk): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() # insert a transaction query.store_transactions(connection=conn, signed_transactions=[signed_create_tx.to_dict()]) @@ -225,9 +225,9 @@ def test_get_owned_ids(signed_create_tx, user_pk): def test_get_spending_transactions(user_pk, user_sk): from planetmint.models import Transaction - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() out = [([user_pk], 1)] tx1 = Transaction.create([user_pk], out * 3) @@ -249,10 +249,10 @@ def test_get_spending_transactions(user_pk, user_sk): def test_get_spending_transactions_multiple_inputs(): from planetmint.models import Transaction from planetmint.common.crypto import generate_key_pair - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() (alice_sk, alice_pk) = generate_key_pair() (bob_sk, bob_pk) = generate_key_pair() @@ -294,10 +294,10 @@ def test_get_spending_transactions_multiple_inputs(): def test_store_block(): from planetmint.lib import Block - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() block = Block(app_hash='random_utxo', height=3, @@ -310,10 +310,10 @@ def test_store_block(): def test_get_block(): from planetmint.lib import Block - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() block = Block(app_hash='random_utxo', height=3, @@ -424,10 +424,10 @@ def test_get_block(): def test_store_pre_commit_state(db_context): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() state = dict(height=3, transactions=[]) @@ -440,10 +440,10 @@ def test_store_pre_commit_state(db_context): def test_get_pre_commit_state(db_context): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() space = conn.space("pre_commits") all_pre = space.select([]) for pre in all_pre.data: @@ -457,10 +457,10 @@ def test_get_pre_commit_state(db_context): def test_validator_update(): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() def gen_validator_update(height): return {'validators': [], 'height': height, 'election_id': f'election_id_at_height_{height}'} @@ -519,10 +519,10 @@ def test_validator_update(): ), ]) def test_store_abci_chain(description, stores, expected): - from planetmint.backend import connect + from planetmint.backend.connection import Connection from planetmint.backend.tarantool import query - conn = connect().get_connection() + conn = Connection().get_connection() for store in stores: query.store_abci_chain(conn, **store) diff --git a/tests/backend/tarantool/test_schema.py b/tests/backend/tarantool/test_schema.py new file mode 100644 index 0000000..0c5f02e --- /dev/null +++ b/tests/backend/tarantool/test_schema.py @@ -0,0 +1,76 @@ +# Copyright © 2020 Interplanetary Database Association e.V., +# Planetmint and IPDB software contributors. +# SPDX-License-Identifier: (Apache-2.0 AND CC-BY-4.0) +# Code is Apache-2.0 and docs are CC-BY-4.0 + + +def test_init_database_is_graceful_if_db_exists(): + import planetmint + from planetmint import backend + from planetmint.backend.schema import init_database + + conn = backend.connect() + dbname = planetmint.config['database']['name'] + + # The db is set up by the fixtures + assert dbname in conn.conn.list_database_names() + + init_database() + + +def test_create_tables(): + import planetmint + from planetmint import backend + from planetmint.backend import schema + + conn = backend.connect() + dbname = planetmint.config['database']['name'] + + # The db is set up by the fixtures so we need to remove it + conn.conn.drop_database(dbname) + schema.create_database(conn, dbname) + schema.create_tables(conn, dbname) + + collection_names = conn.conn[dbname].list_collection_names() + assert set(collection_names) == { + 'transactions', 'assets', 'metadata', 'blocks', 'utxos', 'validators', 'elections', + 'pre_commit', 'abci_chains', + } + + indexes = conn.conn[dbname]['assets'].index_information().keys() + assert set(indexes) == {'_id_', 'asset_id', 'text'} + + index_info = conn.conn[dbname]['transactions'].index_information() + indexes = index_info.keys() + assert set(indexes) == { + '_id_', 'transaction_id', 'asset_id', 'outputs', 'inputs'} + assert index_info['transaction_id']['unique'] + + index_info = conn.conn[dbname]['blocks'].index_information() + indexes = index_info.keys() + assert set(indexes) == {'_id_', 'height'} + assert index_info['height']['unique'] + + index_info = conn.conn[dbname]['utxos'].index_information() + assert set(index_info.keys()) == {'_id_', 'utxo'} + assert index_info['utxo']['unique'] + assert index_info['utxo']['key'] == [('transaction_id', 1), + ('output_index', 1)] + + indexes = conn.conn[dbname]['elections'].index_information() + assert set(indexes.keys()) == {'_id_', 'election_id_height'} + assert indexes['election_id_height']['unique'] + + indexes = conn.conn[dbname]['pre_commit'].index_information() + assert set(indexes.keys()) == {'_id_', 'height'} + assert indexes['height']['unique'] + + +def test_drop(dummy_db): + from planetmint import backend + from planetmint.backend import schema + + conn = backend.connect() + assert dummy_db in conn.conn.list_database_names() + schema.drop_database(conn, dummy_db) + assert dummy_db not in conn.conn.list_database_names()