Changeset View
Changeset View
Standalone View
Standalone View
swh/core/tests/test_cli.py
# Copyright (C) 2019 The Software Heritage developers | # Copyright (C) 2019 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
import logging | import logging | ||||
import pkg_resources | import pkg_resources | ||||
import textwrap | import textwrap | ||||
from unittest.mock import patch | from unittest.mock import patch | ||||
import click | import click | ||||
from click.testing import CliRunner | from click.testing import CliRunner | ||||
import pytest | import pytest | ||||
help_msg = '''Usage: swh [OPTIONS] COMMAND [ARGS]... | help_msg = """Usage: swh [OPTIONS] COMMAND [ARGS]... | ||||
Command line interface for Software Heritage. | Command line interface for Software Heritage. | ||||
Options: | Options: | ||||
-l, --log-level [NOTSET|DEBUG|INFO|WARNING|ERROR|CRITICAL] | -l, --log-level [NOTSET|DEBUG|INFO|WARNING|ERROR|CRITICAL] | ||||
Log level (defaults to INFO). | Log level (defaults to INFO). | ||||
--log-config FILENAME Python yaml logging configuration file. | --log-config FILENAME Python yaml logging configuration file. | ||||
--sentry-dsn TEXT DSN of the Sentry instance to report to | --sentry-dsn TEXT DSN of the Sentry instance to report to | ||||
-h, --help Show this message and exit. | -h, --help Show this message and exit. | ||||
Notes: | Notes: | ||||
If both options are present, --log-level will override the root logger | If both options are present, --log-level will override the root logger | ||||
configuration set in --log-config. | configuration set in --log-config. | ||||
The --log-config YAML must conform to the logging.config.dictConfig schema | The --log-config YAML must conform to the logging.config.dictConfig schema | ||||
documented at https://docs.python.org/3/library/logging.config.html. | documented at https://docs.python.org/3/library/logging.config.html. | ||||
''' | """ | ||||
def test_swh_help(swhmain): | def test_swh_help(swhmain): | ||||
runner = CliRunner() | runner = CliRunner() | ||||
result = runner.invoke(swhmain, ['-h']) | result = runner.invoke(swhmain, ["-h"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.startswith(help_msg) | assert result.output.startswith(help_msg) | ||||
result = runner.invoke(swhmain, ['--help']) | result = runner.invoke(swhmain, ["--help"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.startswith(help_msg) | assert result.output.startswith(help_msg) | ||||
def test_command(swhmain): | def test_command(swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
with patch('sentry_sdk.init') as sentry_sdk_init: | with patch("sentry_sdk.init") as sentry_sdk_init: | ||||
result = runner.invoke(swhmain, ['test']) | result = runner.invoke(swhmain, ["test"]) | ||||
sentry_sdk_init.assert_not_called() | sentry_sdk_init.assert_not_called() | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == 'Hello SWH!' | assert result.output.strip() == "Hello SWH!" | ||||
def test_loglevel_default(caplog, swhmain): | def test_loglevel_default(caplog, swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
assert logging.root.level == 20 | assert logging.root.level == 20 | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
result = runner.invoke(swhmain, ['test']) | result = runner.invoke(swhmain, ["test"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
print(result.output) | print(result.output) | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
def test_loglevel_error(caplog, swhmain): | def test_loglevel_error(caplog, swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
assert logging.root.level == 40 | assert logging.root.level == 40 | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
result = runner.invoke(swhmain, ['-l', 'ERROR', 'test']) | result = runner.invoke(swhmain, ["-l", "ERROR", "test"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
def test_loglevel_debug(caplog, swhmain): | def test_loglevel_debug(caplog, swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
assert logging.root.level == 10 | assert logging.root.level == 10 | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
result = runner.invoke(swhmain, ['-l', 'DEBUG', 'test']) | result = runner.invoke(swhmain, ["-l", "DEBUG", "test"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
def test_sentry(swhmain): | def test_sentry(swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
with patch('sentry_sdk.init') as sentry_sdk_init: | with patch("sentry_sdk.init") as sentry_sdk_init: | ||||
result = runner.invoke(swhmain, ['--sentry-dsn', 'test_dsn', 'test']) | result = runner.invoke(swhmain, ["--sentry-dsn", "test_dsn", "test"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
sentry_sdk_init.assert_called_once_with( | sentry_sdk_init.assert_called_once_with( | ||||
dsn='test_dsn', | dsn="test_dsn", debug=False, integrations=[], release=None, environment=None | ||||
debug=False, | |||||
integrations=[], | |||||
release=None, | |||||
environment=None, | |||||
) | ) | ||||
def test_sentry_debug(swhmain): | def test_sentry_debug(swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
with patch('sentry_sdk.init') as sentry_sdk_init: | with patch("sentry_sdk.init") as sentry_sdk_init: | ||||
result = runner.invoke( | result = runner.invoke( | ||||
swhmain, ['--sentry-dsn', 'test_dsn', '--sentry-debug', 'test']) | swhmain, ["--sentry-dsn", "test_dsn", "--sentry-debug", "test"] | ||||
) | |||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
sentry_sdk_init.assert_called_once_with( | sentry_sdk_init.assert_called_once_with( | ||||
dsn='test_dsn', | dsn="test_dsn", debug=True, integrations=[], release=None, environment=None | ||||
debug=True, | |||||
integrations=[], | |||||
release=None, | |||||
environment=None, | |||||
) | ) | ||||
def test_sentry_env(swhmain): | def test_sentry_env(swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
with patch('sentry_sdk.init') as sentry_sdk_init: | with patch("sentry_sdk.init") as sentry_sdk_init: | ||||
env = { | env = {"SWH_SENTRY_DSN": "test_dsn", "SWH_SENTRY_DEBUG": "1"} | ||||
'SWH_SENTRY_DSN': 'test_dsn', | result = runner.invoke(swhmain, ["test"], env=env, auto_envvar_prefix="SWH") | ||||
'SWH_SENTRY_DEBUG': '1', | |||||
} | |||||
result = runner.invoke( | |||||
swhmain, ['test'], env=env, auto_envvar_prefix='SWH') | |||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
sentry_sdk_init.assert_called_once_with( | sentry_sdk_init.assert_called_once_with( | ||||
dsn='test_dsn', | dsn="test_dsn", debug=True, integrations=[], release=None, environment=None | ||||
debug=True, | |||||
integrations=[], | |||||
release=None, | |||||
environment=None, | |||||
) | ) | ||||
def test_sentry_env_main_package(swhmain): | def test_sentry_env_main_package(swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
with patch('sentry_sdk.init') as sentry_sdk_init: | with patch("sentry_sdk.init") as sentry_sdk_init: | ||||
env = { | env = { | ||||
'SWH_SENTRY_DSN': 'test_dsn', | "SWH_SENTRY_DSN": "test_dsn", | ||||
'SWH_MAIN_PACKAGE': 'swh.core', | "SWH_MAIN_PACKAGE": "swh.core", | ||||
'SWH_SENTRY_ENVIRONMENT': 'tests', | "SWH_SENTRY_ENVIRONMENT": "tests", | ||||
} | } | ||||
result = runner.invoke( | result = runner.invoke(swhmain, ["test"], env=env, auto_envvar_prefix="SWH") | ||||
swhmain, ['test'], env=env, auto_envvar_prefix='SWH') | |||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
version = pkg_resources.get_distribution('swh.core').version | version = pkg_resources.get_distribution("swh.core").version | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
sentry_sdk_init.assert_called_once_with( | sentry_sdk_init.assert_called_once_with( | ||||
dsn='test_dsn', | dsn="test_dsn", | ||||
debug=False, | debug=False, | ||||
integrations=[], | integrations=[], | ||||
release='swh.core@' + version, | release="swh.core@" + version, | ||||
environment='tests', | environment="tests", | ||||
) | ) | ||||
@pytest.fixture | @pytest.fixture | ||||
def log_config_path(tmp_path): | def log_config_path(tmp_path): | ||||
log_config = textwrap.dedent('''\ | log_config = textwrap.dedent( | ||||
"""\ | |||||
--- | --- | ||||
version: 1 | version: 1 | ||||
formatters: | formatters: | ||||
formatter: | formatter: | ||||
format: 'custom format:%(name)s:%(levelname)s:%(message)s' | format: 'custom format:%(name)s:%(levelname)s:%(message)s' | ||||
handlers: | handlers: | ||||
console: | console: | ||||
class: logging.StreamHandler | class: logging.StreamHandler | ||||
stream: ext://sys.stdout | stream: ext://sys.stdout | ||||
formatter: formatter | formatter: formatter | ||||
level: DEBUG | level: DEBUG | ||||
root: | root: | ||||
level: DEBUG | level: DEBUG | ||||
handlers: | handlers: | ||||
- console | - console | ||||
loggers: | loggers: | ||||
dontshowdebug: | dontshowdebug: | ||||
level: INFO | level: INFO | ||||
''') | """ | ||||
) | |||||
(tmp_path / 'log_config.yml').write_text(log_config) | (tmp_path / "log_config.yml").write_text(log_config) | ||||
yield str(tmp_path / 'log_config.yml') | yield str(tmp_path / "log_config.yml") | ||||
def test_log_config(caplog, log_config_path, swhmain): | def test_log_config(caplog, log_config_path, swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
logging.debug('Root log debug') | logging.debug("Root log debug") | ||||
logging.info('Root log info') | logging.info("Root log info") | ||||
logging.getLogger('dontshowdebug').debug('Not shown') | logging.getLogger("dontshowdebug").debug("Not shown") | ||||
logging.getLogger('dontshowdebug').info('Shown') | logging.getLogger("dontshowdebug").info("Shown") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
result = runner.invoke( | result = runner.invoke(swhmain, ["--log-config", log_config_path, "test"]) | ||||
swhmain, [ | |||||
'--log-config', log_config_path, | |||||
'test', | |||||
], | |||||
) | |||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '\n'.join([ | assert result.output.strip() == "\n".join( | ||||
'custom format:root:DEBUG:Root log debug', | [ | ||||
'custom format:root:INFO:Root log info', | "custom format:root:DEBUG:Root log debug", | ||||
'custom format:dontshowdebug:INFO:Shown', | "custom format:root:INFO:Root log info", | ||||
]) | "custom format:dontshowdebug:INFO:Shown", | ||||
] | |||||
) | |||||
def test_log_config_log_level_interaction(caplog, log_config_path, swhmain): | def test_log_config_log_level_interaction(caplog, log_config_path, swhmain): | ||||
@swhmain.command(name='test') | @swhmain.command(name="test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
logging.debug('Root log debug') | logging.debug("Root log debug") | ||||
logging.info('Root log info') | logging.info("Root log info") | ||||
logging.getLogger('dontshowdebug').debug('Not shown') | logging.getLogger("dontshowdebug").debug("Not shown") | ||||
logging.getLogger('dontshowdebug').info('Shown') | logging.getLogger("dontshowdebug").info("Shown") | ||||
runner = CliRunner() | runner = CliRunner() | ||||
result = runner.invoke( | result = runner.invoke( | ||||
swhmain, [ | swhmain, ["--log-config", log_config_path, "--log-level", "INFO", "test"] | ||||
'--log-config', log_config_path, | |||||
'--log-level', 'INFO', | |||||
'test', | |||||
], | |||||
) | ) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '\n'.join([ | assert result.output.strip() == "\n".join( | ||||
'custom format:root:INFO:Root log info', | [ | ||||
'custom format:dontshowdebug:INFO:Shown', | "custom format:root:INFO:Root log info", | ||||
]) | "custom format:dontshowdebug:INFO:Shown", | ||||
] | |||||
) | |||||
def test_aliased_command(swhmain): | def test_aliased_command(swhmain): | ||||
@swhmain.command(name='canonical-test') | @swhmain.command(name="canonical-test") | ||||
@click.pass_context | @click.pass_context | ||||
def swhtest(ctx): | def swhtest(ctx): | ||||
'A test command.' | "A test command." | ||||
click.echo('Hello SWH!') | click.echo("Hello SWH!") | ||||
swhmain.add_alias(swhtest, 'othername') | |||||
swhmain.add_alias(swhtest, "othername") | |||||
runner = CliRunner() | runner = CliRunner() | ||||
# check we have only 'canonical-test' listed in the usage help msg | # check we have only 'canonical-test' listed in the usage help msg | ||||
result = runner.invoke(swhmain, ['-h']) | result = runner.invoke(swhmain, ["-h"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert 'canonical-test A test command.' in result.output | assert "canonical-test A test command." in result.output | ||||
assert 'othername' not in result.output | assert "othername" not in result.output | ||||
# check we can execute the cmd with 'canonical-test' | # check we can execute the cmd with 'canonical-test' | ||||
result = runner.invoke(swhmain, ['canonical-test']) | result = runner.invoke(swhmain, ["canonical-test"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" | ||||
# check we can also execute the cmd with the alias 'othername' | # check we can also execute the cmd with the alias 'othername' | ||||
result = runner.invoke(swhmain, ['othername']) | result = runner.invoke(swhmain, ["othername"]) | ||||
assert result.exit_code == 0 | assert result.exit_code == 0 | ||||
assert result.output.strip() == '''Hello SWH!''' | assert result.output.strip() == """Hello SWH!""" |