diff --git a/bigchaindb/commands/bigchaindb.py b/bigchaindb/commands/bigchaindb.py index 68cb930c..fa5bd1a7 100644 --- a/bigchaindb/commands/bigchaindb.py +++ b/bigchaindb/commands/bigchaindb.py @@ -9,23 +9,13 @@ import copy import json import sys -from bigchaindb.common import crypto -from bigchaindb.common.exceptions import (StartupError, - DatabaseAlreadyExists, - KeypairNotFoundException, +from bigchaindb.common.exceptions import (DatabaseAlreadyExists, DatabaseDoesNotExist) import bigchaindb from bigchaindb import backend from bigchaindb.backend import schema from bigchaindb.backend import query -from bigchaindb.backend.admin import (set_replicas, set_shards, add_replicas, - remove_replicas) -from bigchaindb.backend.exceptions import OperationError from bigchaindb.commands import utils -from bigchaindb.commands.messages import ( - CANNOT_START_KEYPAIR_NOT_FOUND, - RETHINKDB_STARTUP_ERROR, -) from bigchaindb.commands.utils import ( configure_bigchaindb, start_logging_process, input_on_stderr) @@ -53,12 +43,9 @@ def run_show_config(args): print(json.dumps(config, indent=4, sort_keys=True)) -def run_configure(args, skip_if_exists=False): - """Run a script to configure the current node. - - Args: - skip_if_exists (bool): skip the function if a config file already exists - """ +@configure_bigchaindb +def run_configure(args): + """Run a script to configure the current node.""" config_path = args.config or bigchaindb.config_utils.CONFIG_DEFAULT_PATH config_file_exists = False @@ -66,9 +53,6 @@ def run_configure(args, skip_if_exists=False): if config_path != '-': config_file_exists = os.path.exists(config_path) - if config_file_exists and skip_if_exists: - return - if config_file_exists and not args.yes: want = input_on_stderr('Config file `{}` exists, do you want to ' 'override it? (cannot be undone) [y/N]: '.format(config_path)) @@ -77,15 +61,6 @@ def run_configure(args, skip_if_exists=False): conf = copy.deepcopy(bigchaindb.config) - # Patch the default configuration with the new values - conf = bigchaindb.config_utils.update( - conf, - bigchaindb.config_utils.env_config(bigchaindb.config)) - - print('Generating keypair', file=sys.stderr) - conf['keypair']['private'], conf['keypair']['public'] = \ - crypto.generate_key_pair() - # select the correct config defaults based on the backend print('Generating default configuration for backend {}' .format(args.backend), file=sys.stderr) @@ -117,23 +92,7 @@ def run_configure(args, skip_if_exists=False): print('Ready to go!', file=sys.stderr) -@configure_bigchaindb -def run_export_my_pubkey(args): - """Export this node's public key to standard output - """ - pubkey = bigchaindb.config['keypair']['public'] - if pubkey is not None: - print(pubkey) - else: - sys.exit("This node's public key wasn't set anywhere " - "so it can't be exported") - # raises SystemExit exception - # message is sent to stderr - # exits with exit code 1 (signals tha an error happened) - - def _run_init(): - # Try to access the keypair, throws an exception if it does not exist b = bigchaindb.Bigchain() schema.init_database(connection=b.connection) @@ -194,82 +153,18 @@ def run_start(args): # run_recover(BigchainDB()) - if args.allow_temp_keypair: - if not (bigchaindb.config['keypair']['private'] or - bigchaindb.config['keypair']['public']): - - private_key, public_key = crypto.generate_key_pair() - bigchaindb.config['keypair']['private'] = private_key - bigchaindb.config['keypair']['public'] = public_key - else: - logger.warning('Keypair found, no need to create one on the fly.') - - if args.start_rethinkdb: - try: - proc = utils.start_rethinkdb() - except StartupError as e: - sys.exit(RETHINKDB_STARTUP_ERROR.format(e)) - logger.info('RethinkDB started with PID %s' % proc.pid) - try: if not args.skip_initialize_database: logger.info('Initializing database') _run_init() except DatabaseAlreadyExists: pass - except KeypairNotFoundException: - sys.exit(CANNOT_START_KEYPAIR_NOT_FOUND) - logger.info('Starting BigchainDB main process with public key %s', - bigchaindb.config['keypair']['public']) + logger.info('Starting BigchainDB main process.') from bigchaindb.tendermint.commands import start start() -@configure_bigchaindb -def run_set_shards(args): - conn = backend.connect() - try: - set_shards(conn, shards=args.num_shards) - except OperationError as e: - sys.exit(str(e)) - - -@configure_bigchaindb -def run_set_replicas(args): - conn = backend.connect() - try: - set_replicas(conn, replicas=args.num_replicas) - except OperationError as e: - sys.exit(str(e)) - - -@configure_bigchaindb -def run_add_replicas(args): - # Note: This command is specific to MongoDB - conn = backend.connect() - - try: - add_replicas(conn, args.replicas) - except (OperationError, NotImplementedError) as e: - sys.exit(str(e)) - else: - print('Added {} to the replicaset.'.format(args.replicas)) - - -@configure_bigchaindb -def run_remove_replicas(args): - # Note: This command is specific to MongoDB - conn = backend.connect() - - try: - remove_replicas(conn, args.replicas) - except (OperationError, NotImplementedError) as e: - sys.exit(str(e)) - else: - print('Removed {} from the replicaset.'.format(args.replicas)) - - def create_parser(): parser = argparse.ArgumentParser( description='Control your BigchainDB node.', @@ -284,23 +179,19 @@ def create_parser(): # parser for writing a config file config_parser = subparsers.add_parser('configure', - help='Prepare the config file ' - 'and create the node keypair') + help='Prepare the config file.') config_parser.add_argument('backend', choices=['localmongodb'], default='localmongodb', const='localmongodb', nargs='?', - help='The backend to use. It can be either ' - 'rethinkdb or mongodb.') + help='The backend to use. It can only be ' + '"localmongodb", currently.') # parsers for showing/exporting config values subparsers.add_parser('show-config', help='Show the current configuration') - subparsers.add_parser('export-my-pubkey', - help="Export this node's public key") - # parser for database-level commands subparsers.add_parser('init', help='Init the database') @@ -312,63 +203,12 @@ def create_parser(): start_parser = subparsers.add_parser('start', help='Start BigchainDB') - start_parser.add_argument('--dev-allow-temp-keypair', - dest='allow_temp_keypair', - action='store_true', - help='Generate a random keypair on start') - - start_parser.add_argument('--dev-start-rethinkdb', - dest='start_rethinkdb', - action='store_true', - help='Run RethinkDB on start') - start_parser.add_argument('--no-init', dest='skip_initialize_database', default=False, action='store_true', help='Skip database initialization') - # parser for configuring the number of shards - sharding_parser = subparsers.add_parser('set-shards', - help='Configure number of shards') - - sharding_parser.add_argument('num_shards', metavar='num_shards', - type=int, default=1, - help='Number of shards') - - # parser for configuring the number of replicas - replicas_parser = subparsers.add_parser('set-replicas', - help='Configure number of replicas') - - replicas_parser.add_argument('num_replicas', metavar='num_replicas', - type=int, default=1, - help='Number of replicas (i.e. the replication factor)') - - # parser for adding nodes to the replica set - add_replicas_parser = subparsers.add_parser('add-replicas', - help='Add a set of nodes to the ' - 'replica set. This command ' - 'is specific to the MongoDB' - ' backend.') - - add_replicas_parser.add_argument('replicas', nargs='+', - type=utils.mongodb_host, - help='A list of space separated hosts to ' - 'add to the replicaset. Each host ' - 'should be in the form `host:port`.') - - # parser for removing nodes from the replica set - rm_replicas_parser = subparsers.add_parser('remove-replicas', - help='Remove a set of nodes from the ' - 'replica set. This command ' - 'is specific to the MongoDB' - ' backend.') - - rm_replicas_parser.add_argument('replicas', nargs='+', - type=utils.mongodb_host, - help='A list of space separated hosts to ' - 'remove from the replicaset. Each host ' - 'should be in the form `host:port`.') return parser diff --git a/bigchaindb/commands/messages.py b/bigchaindb/commands/messages.py deleted file mode 100644 index c65fe973..00000000 --- a/bigchaindb/commands/messages.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Module to store messages used in commands, such as error messages, -warnings, prompts, etc. - -""" -CANNOT_START_KEYPAIR_NOT_FOUND = ( - "Can't start BigchainDB, no keypair found. " - 'Did you run `bigchaindb configure`?' -) - -RETHINKDB_STARTUP_ERROR = 'Error starting RethinkDB, reason is: {}' diff --git a/bigchaindb/commands/utils.py b/bigchaindb/commands/utils.py index 11d97395..ae8217d4 100644 --- a/bigchaindb/commands/utils.py +++ b/bigchaindb/commands/utils.py @@ -6,16 +6,10 @@ import argparse import builtins import functools import multiprocessing as mp -import subprocess import sys -import rethinkdb as r -from pymongo import uri_parser - import bigchaindb import bigchaindb.config_utils -from bigchaindb import backend -from bigchaindb.common.exceptions import StartupError from bigchaindb.log.setup import setup_logging from bigchaindb.version import __version__ @@ -125,46 +119,6 @@ def input_on_stderr(prompt='', default=None, convert=None): return _convert(value, default, convert) -def start_rethinkdb(): - """Start RethinkDB as a child process and wait for it to be - available. - - Raises: - :class:`~bigchaindb.common.exceptions.StartupError` if - RethinkDB cannot be started. - """ - - proc = subprocess.Popen(['rethinkdb', '--bind', 'all'], - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - universal_newlines=True) - - dbname = bigchaindb.config['database']['name'] - line = '' - - for line in proc.stdout: - if line.startswith('Server ready'): - - # FIXME: seems like tables are not ready when the server is ready, - # that's why we need to query RethinkDB to know the state - # of the database. This code assumes the tables are ready - # when the database is ready. This seems a valid assumption. - try: - conn = backend.connect() - # Before checking if the db is ready, we need to query - # the server to check if it contains that db - if conn.run(r.db_list().contains(dbname)): - conn.run(r.db(dbname).wait()) - except (r.ReqlOpFailedError, r.ReqlDriverError) as exc: - raise StartupError('Error waiting for the database `{}` ' - 'to be ready'.format(dbname)) from exc - return proc - - # We are here when we exhaust the stdout of the process. - # The last `line` contains info about the error. - raise StartupError(line) - - def start(parser, argv, scope): """Utility function to execute a subcommand. @@ -206,34 +160,6 @@ def start(parser, argv, scope): return func(args) -def mongodb_host(host): - """Utility function that works as a type for mongodb ``host`` args. - - This function validates the ``host`` args provided by to the - ``add-replicas`` and ``remove-replicas`` commands and checks if each arg - is in the form "host:port" - - Args: - host (str): A string containing hostname and port (e.g. "host:port") - - Raises: - ArgumentTypeError: if it fails to parse the argument - """ - # check if mongodb can parse the host - try: - hostname, port = uri_parser.parse_host(host, default_port=None) - except ValueError as exc: - raise argparse.ArgumentTypeError(exc.args[0]) - - # we do require the port to be provided. - if port is None or hostname == '': - raise argparse.ArgumentTypeError('expected host in the form ' - '`host:port`. Got `{}` instead.' - .format(host)) - - return host - - base_parser = argparse.ArgumentParser(add_help=False, prog='bigchaindb') base_parser.add_argument('-c', '--config', diff --git a/bigchaindb/core.py b/bigchaindb/core.py index dab88045..55993449 100644 --- a/bigchaindb/core.py +++ b/bigchaindb/core.py @@ -68,8 +68,8 @@ class Bigchain(object): self.consensus = BaseConsensusRules self.connection = connection if connection else backend.connect(**bigchaindb.config['database']) - if not self.me or not self.me_private: - raise exceptions.KeypairNotFoundException() + # if not self.me: + # raise exceptions.KeypairNotFoundException() federation = property(lambda self: set(self.nodes_except_me + [self.me])) """ Set of federation member public keys """ diff --git a/tests/commands/conftest.py b/tests/commands/conftest.py index 46f8a8f6..d073eb78 100644 --- a/tests/commands/conftest.py +++ b/tests/commands/conftest.py @@ -47,8 +47,6 @@ def run_start_args(request): param = getattr(request, 'param', {}) return Namespace( config=param.get('config'), - start_rethinkdb=param.get('start_rethinkdb', False), - allow_temp_keypair=param.get('allow_temp_keypair', False), skip_initialize_database=param.get('skip_initialize_database', False), ) diff --git a/tests/commands/rethinkdb/__init__.py b/tests/commands/rethinkdb/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/commands/rethinkdb/test_commands.py b/tests/commands/rethinkdb/test_commands.py deleted file mode 100644 index c8990582..00000000 --- a/tests/commands/rethinkdb/test_commands.py +++ /dev/null @@ -1,125 +0,0 @@ -import pytest -import rethinkdb - -from unittest.mock import Mock, patch -from argparse import Namespace - - -@patch('bigchaindb.commands.utils.start_rethinkdb', return_value=Mock()) -def test_bigchain_run_start_with_rethinkdb(mock_start_rethinkdb, - mock_run_configure, - mock_processes_start, - mock_db_init_with_existing_db, - mocked_setup_logging): - from bigchaindb import config - from bigchaindb.commands.bigchaindb import run_start - args = Namespace(start_rethinkdb=True, allow_temp_keypair=False, config=None, yes=True, - skip_initialize_database=False) - run_start(args) - - mock_start_rethinkdb.assert_called_with() - mocked_setup_logging.assert_called_once_with(user_log_config=config['log']) - - -@patch('subprocess.Popen') -def test_start_rethinkdb_returns_a_process_when_successful(mock_popen): - from bigchaindb.commands import utils - mock_popen.return_value = Mock(stdout=[ - 'Listening for client driver 1234', - 'Server ready']) - assert utils.start_rethinkdb() is mock_popen.return_value - - -@patch('subprocess.Popen') -def test_start_rethinkdb_exits_when_cannot_start(mock_popen): - from bigchaindb.common import exceptions - from bigchaindb.commands import utils - mock_popen.return_value = Mock(stdout=['Nopety nope']) - with pytest.raises(exceptions.StartupError): - utils.start_rethinkdb() - - -@patch('rethinkdb.ast.Table.reconfigure') -def test_set_shards(mock_reconfigure, monkeypatch, b): - from bigchaindb.commands.bigchaindb import run_set_shards - - # this will mock the call to retrieve the database config - # we will set it to return one replica - def mockreturn_one_replica(self, conn): - return {'shards': [{'replicas': [1]}]} - - monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_one_replica) - args = Namespace(num_shards=3, config=None) - run_set_shards(args) - mock_reconfigure.assert_called_with(replicas=1, shards=3, dry_run=False) - - # this will mock the call to retrieve the database config - # we will set it to return three replica - def mockreturn_three_replicas(self, conn): - return {'shards': [{'replicas': [1, 2, 3]}]} - - monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_three_replicas) - run_set_shards(args) - mock_reconfigure.assert_called_with(replicas=3, shards=3, dry_run=False) - - -def test_set_shards_raises_exception(monkeypatch, b): - from bigchaindb.commands.bigchaindb import run_set_shards - - # test that we are correctly catching the exception - def mock_raise(*args, **kwargs): - raise rethinkdb.ReqlOpFailedError('') - - def mockreturn_one_replica(self, conn): - return {'shards': [{'replicas': [1]}]} - - monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_one_replica) - monkeypatch.setattr(rethinkdb.ast.Table, 'reconfigure', mock_raise) - - args = Namespace(num_shards=3, config=None) - with pytest.raises(SystemExit) as exc: - run_set_shards(args) - assert exc.value.args == ('Failed to reconfigure tables.',) - - -@patch('rethinkdb.ast.Table.reconfigure') -def test_set_replicas(mock_reconfigure, monkeypatch, b): - from bigchaindb.commands.bigchaindb import run_set_replicas - - # this will mock the call to retrieve the database config - # we will set it to return two shards - def mockreturn_two_shards(self, conn): - return {'shards': [1, 2]} - - monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_two_shards) - args = Namespace(num_replicas=2, config=None) - run_set_replicas(args) - mock_reconfigure.assert_called_with(replicas=2, shards=2, dry_run=False) - - # this will mock the call to retrieve the database config - # we will set it to return three shards - def mockreturn_three_shards(self, conn): - return {'shards': [1, 2, 3]} - - monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_three_shards) - run_set_replicas(args) - mock_reconfigure.assert_called_with(replicas=2, shards=3, dry_run=False) - - -def test_set_replicas_raises_exception(monkeypatch, b): - from bigchaindb.commands.bigchaindb import run_set_replicas - - # test that we are correctly catching the exception - def mock_raise(*args, **kwargs): - raise rethinkdb.ReqlOpFailedError('') - - def mockreturn_two_shards(self, conn): - return {'shards': [1, 2]} - - monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_two_shards) - monkeypatch.setattr(rethinkdb.ast.Table, 'reconfigure', mock_raise) - - args = Namespace(num_replicas=2, config=None) - with pytest.raises(SystemExit) as exc: - run_set_replicas(args) - assert exc.value.args == ('Failed to reconfigure tables.',) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 4c963cb4..73aaa8fe 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -1,7 +1,6 @@ import json from unittest.mock import Mock, patch from argparse import Namespace -import copy import pytest @@ -13,19 +12,14 @@ def test_make_sure_we_dont_remove_any_command(): parser = create_parser() - assert parser.parse_args(['configure', 'localmongodb']).command assert parser.parse_args(['configure', 'localmongodb']).command assert parser.parse_args(['show-config']).command - assert parser.parse_args(['export-my-pubkey']).command assert parser.parse_args(['init']).command assert parser.parse_args(['drop']).command assert parser.parse_args(['start']).command - assert parser.parse_args(['set-shards', '1']).command - assert parser.parse_args(['set-replicas', '1']).command - assert parser.parse_args(['add-replicas', 'localhost:27017']).command - assert parser.parse_args(['remove-replicas', 'localhost:27017']).command +@pytest.mark.tendermint @patch('bigchaindb.commands.utils.start') def test_main_entrypoint(mock_start): from bigchaindb.commands.bigchaindb import main @@ -40,95 +34,38 @@ def test_bigchain_run_start(mock_run_configure, mocked_setup_logging): from bigchaindb import config from bigchaindb.commands.bigchaindb import run_start - args = Namespace(start_rethinkdb=False, allow_temp_keypair=False, config=None, yes=True, + args = Namespace(config=None, yes=True, skip_initialize_database=False) run_start(args) mocked_setup_logging.assert_called_once_with(user_log_config=config['log']) -@pytest.mark.skipif(reason="BigchainDB doesn't support the automatic creation of a config file anymore") -def test_bigchain_run_start_assume_yes_create_default_config( - monkeypatch, mock_processes_start, mock_generate_key_pair, - mock_db_init_with_existing_db, mocked_setup_logging): - import bigchaindb - from bigchaindb.commands.bigchaindb import run_start - from bigchaindb import config_utils - - value = {} - expected_config = copy.deepcopy(bigchaindb._config) - expected_config['keypair']['public'] = 'pubkey' - expected_config['keypair']['private'] = 'privkey' - - def mock_write_config(newconfig, filename=None): - value['return'] = newconfig - - monkeypatch.setattr(config_utils, 'write_config', mock_write_config) - monkeypatch.setattr(config_utils, 'file_config', lambda x: config_utils.set_config(expected_config)) - monkeypatch.setattr('os.path.exists', lambda path: False) - - args = Namespace(config=None, yes=True) - run_start(args) - - mocked_setup_logging.assert_called_once_with() - assert value['return'] == expected_config - - # TODO Please beware, that if debugging, the "-s" switch for pytest will # interfere with capsys. # See related issue: https://github.com/pytest-dev/pytest/issues/128 +@pytest.mark.tendermint @pytest.mark.usefixtures('ignore_local_config_file') def test_bigchain_show_config(capsys): - from bigchaindb import config from bigchaindb.commands.bigchaindb import run_show_config args = Namespace(config=None) _, _ = capsys.readouterr() run_show_config(args) output_config = json.loads(capsys.readouterr()[0]) + # Note: This test passed previously because we were always + # using the default configuration parameters, but since we + # are running with docker-compose now and expose parameters like + # BIGCHAINDB_SERVER_BIND, BIGCHAINDB_WSSERVER_HOST, BIGCHAINDB_WSSERVER_ADVERTISED_HOST + # the default comparison fails i.e. when config is imported at the beginning the + # dict returned is different that what is expected after run_show_config + # and run_show_config updates the bigchaindb.config + from bigchaindb import config del config['CONFIGURED'] config['keypair']['private'] = 'x' * 45 assert output_config == config -@pytest.mark.usefixtures('ignore_local_config_file') -def test_bigchain_export_my_pubkey_when_pubkey_set(capsys, monkeypatch): - from bigchaindb import config - from bigchaindb.commands.bigchaindb import run_export_my_pubkey - - args = Namespace(config=None) - # so in run_export_my_pubkey(args) below, - # filename=args.config='dummy' is passed to autoconfigure(). - # We just assume autoconfigure() works and sets - # config['keypair']['public'] correctly (tested elsewhere). - # We force-set config['keypair']['public'] using monkeypatch. - monkeypatch.setitem(config['keypair'], 'public', 'Charlie_Bucket') - _, _ = capsys.readouterr() # has the effect of clearing capsys - run_export_my_pubkey(args) - out, _ = capsys.readouterr() - lines = out.splitlines() - assert config['keypair']['public'] in lines - assert 'Charlie_Bucket' in lines - - -@pytest.mark.usefixtures('ignore_local_config_file') -def test_bigchain_export_my_pubkey_when_pubkey_not_set(monkeypatch): - from bigchaindb import config - from bigchaindb.commands.bigchaindb import run_export_my_pubkey - - args = Namespace(config=None) - monkeypatch.setitem(config['keypair'], 'public', None) - # assert that run_export_my_pubkey(args) raises SystemExit: - with pytest.raises(SystemExit) as exc_info: - run_export_my_pubkey(args) - # exc_info is an object of class ExceptionInfo - # https://pytest.org/latest/builtin.html#_pytest._code.ExceptionInfo - assert exc_info.type == SystemExit - # exc_info.value is an object of class SystemExit - # https://docs.python.org/3/library/exceptions.html#SystemExit - assert exc_info.value.code == \ - "This node's public key wasn't set anywhere so it can't be exported" - - +@pytest.mark.tendermint def test_bigchain_run_init_when_db_exists(mocker, capsys): from bigchaindb.commands.bigchaindb import run_init from bigchaindb.common.exceptions import DatabaseAlreadyExists @@ -148,6 +85,7 @@ def test_bigchain_run_init_when_db_exists(mocker, capsys): ) +@pytest.mark.tendermint def test__run_init(mocker): from bigchaindb.commands.bigchaindb import _run_init bigchain_mock = mocker.patch( @@ -164,6 +102,7 @@ def test__run_init(mocker): bigchain_mock.return_value.create_genesis_block.assert_called_once_with() +@pytest.mark.tendermint @patch('bigchaindb.backend.schema.drop_database') def test_drop_db_when_assumed_yes(mock_db_drop): from bigchaindb.commands.bigchaindb import run_drop @@ -173,16 +112,19 @@ def test_drop_db_when_assumed_yes(mock_db_drop): assert mock_db_drop.called +@pytest.mark.tendermint @patch('bigchaindb.backend.schema.drop_database') def test_drop_db_when_interactive_yes(mock_db_drop, monkeypatch): from bigchaindb.commands.bigchaindb import run_drop args = Namespace(config=None, yes=False) - monkeypatch.setattr('bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'y') + monkeypatch.setattr( + 'bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'y') run_drop(args) assert mock_db_drop.called +@pytest.mark.tendermint @patch('bigchaindb.backend.schema.drop_database') def test_drop_db_when_db_does_not_exist(mock_db_drop, capsys): from bigchaindb import config @@ -197,27 +139,22 @@ def test_drop_db_when_db_does_not_exist(mock_db_drop, capsys): name=config['database']['name']) +@pytest.mark.tendermint @patch('bigchaindb.backend.schema.drop_database') def test_drop_db_does_not_drop_when_interactive_no(mock_db_drop, monkeypatch): from bigchaindb.commands.bigchaindb import run_drop args = Namespace(config=None, yes=False) - monkeypatch.setattr('bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'n') + monkeypatch.setattr( + 'bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'n') run_drop(args) assert not mock_db_drop.called -def test_run_configure_when_config_exists_and_skipping(monkeypatch): - from bigchaindb.commands.bigchaindb import run_configure - monkeypatch.setattr('os.path.exists', lambda path: True) - args = Namespace(config='foo', yes=True) - return_value = run_configure(args, skip_if_exists=True) - assert return_value is None - - # TODO Beware if you are putting breakpoints in there, and using the '-s' # switch with pytest. It will just hang. Seems related to the monkeypatching of # input_on_stderr. +@pytest.mark.tendermint def test_run_configure_when_config_does_not_exist(monkeypatch, mock_write_config, mock_generate_key_pair, @@ -225,11 +162,12 @@ def test_run_configure_when_config_does_not_exist(monkeypatch, from bigchaindb.commands.bigchaindb import run_configure monkeypatch.setattr('os.path.exists', lambda path: False) monkeypatch.setattr('builtins.input', lambda: '\n') - args = Namespace(config='foo', backend='rethinkdb', yes=True) + args = Namespace(config=None, backend='localmongodb', yes=True) return_value = run_configure(args) assert return_value is None +@pytest.mark.tendermint def test_run_configure_when_config_does_exist(monkeypatch, mock_write_config, mock_generate_key_pair, @@ -242,16 +180,17 @@ def test_run_configure_when_config_does_exist(monkeypatch, from bigchaindb.commands.bigchaindb import run_configure monkeypatch.setattr('os.path.exists', lambda path: True) monkeypatch.setattr('builtins.input', lambda: '\n') - monkeypatch.setattr('bigchaindb.config_utils.write_config', mock_write_config) + monkeypatch.setattr( + 'bigchaindb.config_utils.write_config', mock_write_config) - args = Namespace(config='foo', yes=None) + args = Namespace(config=None, yes=None) run_configure(args) assert value == {} +@pytest.mark.tendermint @pytest.mark.parametrize('backend', ( - 'rethinkdb', - 'mongodb', + 'localmongodb', )) def test_run_configure_with_backend(backend, monkeypatch, mock_write_config): import bigchaindb @@ -267,7 +206,7 @@ def test_run_configure_with_backend(backend, monkeypatch, mock_write_config): monkeypatch.setattr('bigchaindb.config_utils.write_config', mock_write_config) - args = Namespace(config='foo', backend=backend, yes=True) + args = Namespace(config=None, backend=backend, yes=True) expected_config = bigchaindb.config run_configure(args) @@ -279,54 +218,6 @@ def test_run_configure_with_backend(backend, monkeypatch, mock_write_config): assert value['return'] == expected_config -@patch('bigchaindb.common.crypto.generate_key_pair', - return_value=('private_key', 'public_key')) -@pytest.mark.usefixtures('ignore_local_config_file') -def test_allow_temp_keypair_generates_one_on_the_fly( - mock_gen_keypair, mock_processes_start, - mock_db_init_with_existing_db, mocked_setup_logging): - import bigchaindb - from bigchaindb.commands.bigchaindb import run_start - - bigchaindb.config['keypair'] = {'private': None, 'public': None} - - args = Namespace(allow_temp_keypair=True, start_rethinkdb=False, config=None, yes=True, - skip_initialize_database=False) - run_start(args) - - mocked_setup_logging.assert_called_once_with( - user_log_config=bigchaindb.config['log']) - assert bigchaindb.config['keypair']['private'] == 'private_key' - assert bigchaindb.config['keypair']['public'] == 'public_key' - - -@patch('bigchaindb.common.crypto.generate_key_pair', - return_value=('private_key', 'public_key')) -@pytest.mark.usefixtures('ignore_local_config_file') -def test_allow_temp_keypair_doesnt_override_if_keypair_found(mock_gen_keypair, - mock_processes_start, - mock_db_init_with_existing_db, - mocked_setup_logging): - import bigchaindb - from bigchaindb.commands.bigchaindb import run_start - - # Preconditions for the test - original_private_key = bigchaindb.config['keypair']['private'] - original_public_key = bigchaindb.config['keypair']['public'] - - assert isinstance(original_public_key, str) - assert isinstance(original_private_key, str) - - args = Namespace(allow_temp_keypair=True, start_rethinkdb=False, config=None, yes=True, - skip_initialize_database=False) - run_start(args) - - mocked_setup_logging.assert_called_once_with( - user_log_config=bigchaindb.config['log']) - assert bigchaindb.config['keypair']['private'] == original_private_key - assert bigchaindb.config['keypair']['public'] == original_public_key - - def test_run_start_when_db_already_exists(mocker, monkeypatch, run_start_args, @@ -338,7 +229,7 @@ def test_run_start_when_db_already_exists(mocker, def mock_run_init(): raise DatabaseAlreadyExists() - + monkeypatch.setattr('builtins.input', lambda: '\x03') monkeypatch.setattr( 'bigchaindb.commands.bigchaindb._run_init', mock_run_init) run_start(run_start_args) @@ -346,58 +237,7 @@ def test_run_start_when_db_already_exists(mocker, assert mocked_start.called -def test_run_start_when_keypair_not_found(mocker, - monkeypatch, - run_start_args, - mocked_setup_logging): - from bigchaindb import config - from bigchaindb.commands.bigchaindb import run_start - from bigchaindb.commands.messages import CANNOT_START_KEYPAIR_NOT_FOUND - from bigchaindb.common.exceptions import KeypairNotFoundException - mocked_start = mocker.patch('bigchaindb.processes.start') - - def mock_run_init(): - raise KeypairNotFoundException() - - monkeypatch.setattr( - 'bigchaindb.commands.bigchaindb._run_init', mock_run_init) - - with pytest.raises(SystemExit) as exc: - run_start(run_start_args) - - mocked_setup_logging.assert_called_once_with(user_log_config=config['log']) - assert len(exc.value.args) == 1 - assert exc.value.args[0] == CANNOT_START_KEYPAIR_NOT_FOUND - assert not mocked_start.called - - -def test_run_start_when_start_rethinkdb_fails(mocker, - monkeypatch, - run_start_args, - mocked_setup_logging): - from bigchaindb import config - from bigchaindb.commands.bigchaindb import run_start - from bigchaindb.commands.messages import RETHINKDB_STARTUP_ERROR - from bigchaindb.common.exceptions import StartupError - run_start_args.start_rethinkdb = True - mocked_start = mocker.patch('bigchaindb.processes.start') - err_msg = 'Error starting rethinkdb.' - - def mock_start_rethinkdb(): - raise StartupError(err_msg) - - monkeypatch.setattr( - 'bigchaindb.commands.utils.start_rethinkdb', mock_start_rethinkdb) - - with pytest.raises(SystemExit) as exc: - run_start(run_start_args) - - mocked_setup_logging.assert_called_once_with(user_log_config=config['log']) - assert len(exc.value.args) == 1 - assert exc.value.args[0] == RETHINKDB_STARTUP_ERROR.format(err_msg) - assert not mocked_start.called - - +@pytest.mark.tendermint @patch('argparse.ArgumentParser.parse_args') @patch('bigchaindb.commands.utils.base_parser') @patch('bigchaindb.commands.utils.start') @@ -419,107 +259,18 @@ def test_calling_main(start_mock, base_parser_mock, parse_args_mock, parser.add_subparsers.assert_called_with(title='Commands', dest='command') subparsers.add_parser.assert_any_call('configure', - help='Prepare the config file ' - 'and create the node keypair') + help='Prepare the config file.') subparsers.add_parser.assert_any_call('show-config', help='Show the current ' 'configuration') - subparsers.add_parserassert_any_call('export-my-pubkey', - help="Export this node's public " - 'key') subparsers.add_parser.assert_any_call('init', help='Init the database') subparsers.add_parser.assert_any_call('drop', help='Drop the database') subparsers.add_parser.assert_any_call('start', help='Start BigchainDB') - subsubparsers.add_argument.assert_any_call('--dev-start-rethinkdb', - dest='start_rethinkdb', - action='store_true', - help='Run RethinkDB on start') - subsubparsers.add_argument.assert_any_call('--dev-allow-temp-keypair', - dest='allow_temp_keypair', - action='store_true', - help='Generate a random keypair on start') - - subparsers.add_parser.assert_any_call('set-shards', - help='Configure number of shards') - subsubparsers.add_argument.assert_any_call('num_shards', - metavar='num_shards', - type=int, default=1, - help='Number of shards') - - subparsers.add_parser.assert_any_call('set-replicas', - help='Configure number of replicas') - subsubparsers.add_argument.assert_any_call('num_replicas', - metavar='num_replicas', - type=int, default=1, - help='Number of replicas (i.e. ' - 'the replication factor)') assert start_mock.called is True -@pytest.mark.usefixtures('ignore_local_config_file') -@patch('bigchaindb.commands.bigchaindb.add_replicas') -def test_run_add_replicas(mock_add_replicas): - from bigchaindb.commands.bigchaindb import run_add_replicas - from bigchaindb.backend.exceptions import OperationError - - args = Namespace(config=None, replicas=['localhost:27017']) - - # test add_replicas no raises - mock_add_replicas.return_value = None - assert run_add_replicas(args) is None - assert mock_add_replicas.call_count == 1 - mock_add_replicas.reset_mock() - - # test add_replicas with `OperationError` - mock_add_replicas.side_effect = OperationError('err') - with pytest.raises(SystemExit) as exc: - run_add_replicas(args) - assert exc.value.args == ('err',) - assert mock_add_replicas.call_count == 1 - mock_add_replicas.reset_mock() - - # test add_replicas with `NotImplementedError` - mock_add_replicas.side_effect = NotImplementedError('err') - with pytest.raises(SystemExit) as exc: - run_add_replicas(args) - assert exc.value.args == ('err',) - assert mock_add_replicas.call_count == 1 - mock_add_replicas.reset_mock() - - -@pytest.mark.usefixtures('ignore_local_config_file') -@patch('bigchaindb.commands.bigchaindb.remove_replicas') -def test_run_remove_replicas(mock_remove_replicas): - from bigchaindb.commands.bigchaindb import run_remove_replicas - from bigchaindb.backend.exceptions import OperationError - - args = Namespace(config=None, replicas=['localhost:27017']) - - # test add_replicas no raises - mock_remove_replicas.return_value = None - assert run_remove_replicas(args) is None - assert mock_remove_replicas.call_count == 1 - mock_remove_replicas.reset_mock() - - # test add_replicas with `OperationError` - mock_remove_replicas.side_effect = OperationError('err') - with pytest.raises(SystemExit) as exc: - run_remove_replicas(args) - assert exc.value.args == ('err',) - assert mock_remove_replicas.call_count == 1 - mock_remove_replicas.reset_mock() - - # test add_replicas with `NotImplementedError` - mock_remove_replicas.side_effect = NotImplementedError('err') - with pytest.raises(SystemExit) as exc: - run_remove_replicas(args) - assert exc.value.args == ('err',) - assert mock_remove_replicas.call_count == 1 - mock_remove_replicas.reset_mock() - - @pytest.mark.tendermint @pytest.mark.bdb def test_recover_db_from_zombie_txn(b, monkeypatch): diff --git a/tests/commands/test_utils.py b/tests/commands/test_utils.py index 0ddec6ef..e72b4df1 100644 --- a/tests/commands/test_utils.py +++ b/tests/commands/test_utils.py @@ -1,5 +1,5 @@ import argparse -from argparse import ArgumentTypeError, Namespace +from argparse import Namespace import logging import pytest @@ -120,19 +120,3 @@ def test_start_sets_multiprocess_var_based_on_cli_args(mock_cpu_count): scope = {'run_mp_arg_test': run_mp_arg_test} assert utils.start(parser, ['mp_arg_test'], scope).multiprocess == 1 assert utils.start(parser, ['mp_arg_test', '--multiprocess'], scope).multiprocess == 42 - - -def test_mongodb_host_type(): - from bigchaindb.commands.utils import mongodb_host - - # bad port provided - with pytest.raises(ArgumentTypeError): - mongodb_host('localhost:11111111111') - - # no port information provided - with pytest.raises(ArgumentTypeError): - mongodb_host('localhost') - - # bad host provided - with pytest.raises(ArgumentTypeError): - mongodb_host(':27017')