From c0adc4f9df2c0768ba49bf51dfbfe4f0ee6b1254 Mon Sep 17 00:00:00 2001 From: Dan Helfman Date: Fri, 4 Apr 2025 17:19:14 -0700 Subject: [PATCH] Add test coverage for new code (#793). --- NEWS | 2 +- borgmatic/commands/borgmatic.py | 3 +- borgmatic/verbosity.py | 5 +- tests/integration/commands/test_arguments.py | 8 +-- tests/unit/borg/test_change_passphrase.py | 4 +- tests/unit/commands/test_borgmatic.py | 58 ++++++++++++++++++++ tests/unit/test_verbosity.py | 46 ++++++++++++++++ 7 files changed, 116 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 7465009a..943d77fa 100644 --- a/NEWS +++ b/NEWS @@ -24,7 +24,7 @@ * #790: BREAKING: Run all command hooks (both new and deprecated) respecting the "working_directory" option if configured, meaning that hook commands are run in that directory. * #793: Add configuration options for all verbosity and logging flags, so you don't have to set - verbosity on the command-line. + them on the command-line. * #836: Add a custom command option for the SQLite hook. * #837: Add custom command options for the MongoDB hook. * #1010: When using Borg 2, don't pass the "--stats" flag to "borg prune". diff --git a/borgmatic/commands/borgmatic.py b/borgmatic/commands/borgmatic.py index 0bd6beea..bcd3df92 100644 --- a/borgmatic/commands/borgmatic.py +++ b/borgmatic/commands/borgmatic.py @@ -987,8 +987,9 @@ def get_singular_option_value(configs, option_name): if len(distinct_values) > 1: configure_logging(logging.CRITICAL) + joined_values = ', '.join(str(value) for value in distinct_values) logger.critical( - f'The {option_name} option has conflicting values across configuration files: {", ".join(distinct_values)}' + f'The {option_name} option has conflicting values across configuration files: {joined_values}' ) exit_with_help_link() diff --git a/borgmatic/verbosity.py b/borgmatic/verbosity.py index 7556df15..d1000cb6 100644 --- a/borgmatic/verbosity.py +++ b/borgmatic/verbosity.py @@ -40,7 +40,10 @@ def get_verbosity(configs, option_name): ''' try: return max( - config.get(option_name, DEFAULT_VERBOSITIES[option_name]) for config in configs.values() + verbosity + for config in configs.values() + for verbosity in (config.get(option_name, DEFAULT_VERBOSITIES[option_name]),) + if verbosity ) except ValueError: return DEFAULT_VERBOSITIES[option_name] diff --git a/tests/integration/commands/test_arguments.py b/tests/integration/commands/test_arguments.py index 203db05e..f6d10ca3 100644 --- a/tests/integration/commands/test_arguments.py +++ b/tests/integration/commands/test_arguments.py @@ -657,17 +657,13 @@ def test_parse_arguments_config_with_subaction_and_required_flags_does_not_raise def test_parse_arguments_config_with_subaction_and_global_flags_at_start_does_not_raise(): flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) - module.parse_arguments( - {}, '--dry-run', 'config', 'bootstrap', '--repository', 'repo.borg' - ) + module.parse_arguments({}, '--dry-run', 'config', 'bootstrap', '--repository', 'repo.borg') def test_parse_arguments_config_with_subaction_and_global_flags_at_end_does_not_raise(): flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) - module.parse_arguments( - {}, 'config', 'bootstrap', '--repository', 'repo.borg', '--dry-run' - ) + module.parse_arguments({}, 'config', 'bootstrap', '--repository', 'repo.borg', '--dry-run') def test_parse_arguments_config_with_subaction_and_explicit_config_file_does_not_raise(): diff --git a/tests/unit/borg/test_change_passphrase.py b/tests/unit/borg/test_change_passphrase.py index bb75343d..f5b3b790 100644 --- a/tests/unit/borg/test_change_passphrase.py +++ b/tests/unit/borg/test_change_passphrase.py @@ -111,7 +111,9 @@ def test_change_passphrase_calls_borg_with_umask_flags(): def test_change_passphrase_calls_borg_with_log_json_flags(): flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) config = {'log_json': True} - insert_execute_command_mock(('borg', 'key', 'change-passphrase', '--log-json', 'repo'), config=config) + insert_execute_command_mock( + ('borg', 'key', 'change-passphrase', '--log-json', 'repo'), config=config + ) module.change_passphrase( repository_path='repo', diff --git a/tests/unit/commands/test_borgmatic.py b/tests/unit/commands/test_borgmatic.py index 7cfe31a8..000e93f4 100644 --- a/tests/unit/commands/test_borgmatic.py +++ b/tests/unit/commands/test_borgmatic.py @@ -2217,3 +2217,61 @@ def test_check_and_show_help_on_no_args_with_conflicting_configs(): flexmock(module.sys).should_receive('exit').never() module.check_and_show_help_on_no_args(configs) + + +def test_get_singular_option_value_with_conflicting_values_exits(): + flexmock(module).should_receive('configure_logging') + flexmock(module).should_receive('exit_with_help_link').once() + + module.get_singular_option_value( + configs={ + 'test1.yaml': {'foo': 1, 'bar': 'baz'}, + 'test2.yaml': {'foo': 2, 'bar': 'baz'}, + }, + option_name='foo', + ) + + +def test_get_singular_option_value_with_same_value_returns_it(): + flexmock(module).should_receive('configure_logging').never() + flexmock(module).should_receive('exit_with_help_link').never() + + assert ( + module.get_singular_option_value( + configs={ + 'test1.yaml': {'foo': 1, 'bar': 'baz'}, + 'test2.yaml': {'foo': 1, 'bar': 'baz'}, + }, + option_name='foo', + ) + == 1 + ) + + +def test_get_singular_option_value_with_no_values_returns_none(): + flexmock(module).should_receive('configure_logging').never() + flexmock(module).should_receive('exit_with_help_link').never() + + assert ( + module.get_singular_option_value( + configs={ + 'test1.yaml': {'bar': 'baz'}, + 'test2.yaml': {'bar': 'baz'}, + }, + option_name='foo', + ) + is None + ) + + +def test_get_singular_option_value_with_no_config_returns_none(): + flexmock(module).should_receive('configure_logging').never() + flexmock(module).should_receive('exit_with_help_link').never() + + assert ( + module.get_singular_option_value( + configs={}, + option_name='foo', + ) + is None + ) diff --git a/tests/unit/test_verbosity.py b/tests/unit/test_verbosity.py index a53e4edb..cd5d92d1 100644 --- a/tests/unit/test_verbosity.py +++ b/tests/unit/test_verbosity.py @@ -1,5 +1,6 @@ import logging +import pytest from flexmock import flexmock from borgmatic import verbosity as module @@ -31,3 +32,48 @@ def test_verbosity_to_log_level_maps_unknown_verbosity_to_warning_level(): flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER assert module.verbosity_to_log_level('my pants') == logging.WARNING + + +@pytest.mark.parametrize( + 'option_name', + ( + 'verbosity', + 'syslog_verbosity', + 'log_file_verbosity', + 'monitoring_verbosity', + ), +) +def test_get_verbosity_gets_maximum_verbosity_across_configurations(option_name): + assert ( + module.get_verbosity( + configs={ + 'test1.yaml': {option_name: -1}, + 'test2.yaml': {option_name: 2}, + 'test3.yaml': {option_name: None}, + }, + option_name=option_name, + ) + == 2 + ) + + +@pytest.mark.parametrize( + 'option_name', + ( + 'verbosity', + 'syslog_verbosity', + 'log_file_verbosity', + 'monitoring_verbosity', + ), +) +def test_get_verbosity_with_nothing_set_gets_default_verbosity(option_name): + assert ( + module.get_verbosity( + configs={ + 'test1.yaml': {}, + 'test2.yaml': {'other': 'thing'}, + }, + option_name=option_name, + ) + == module.DEFAULT_VERBOSITIES[option_name] + )