ソースを参照

Get existing unit/integration tests passing (#303).

Dan Helfman 2 ヶ月 前
コミット
423627e67b

+ 29 - 23
borgmatic/commands/arguments.py

@@ -69,9 +69,9 @@ def get_subactions_for_actions(action_parsers):
 
 def omit_values_colliding_with_action_names(unparsed_arguments, parsed_arguments):
     '''
-    Given a sequence of string arguments and a dict from action name to parsed argparse.Namespace
-    arguments, return the string arguments with any values omitted that happen to be the same as
-    the name of a borgmatic action.
+    Given unparsed arguments as a sequence of strings and a dict from action name to parsed
+    argparse.Namespace arguments, return the string arguments with any values omitted that happen to
+    be the same as the name of a borgmatic action.
 
     This prevents, for instance, "check --only extract" from triggering the "extract" action.
     '''
@@ -320,15 +320,15 @@ def make_argument_description(schema, flag_name):
 
 
 def add_array_element_arguments_from_schema(arguments_group, schema, unparsed_arguments, flag_name):
-    '''
+    r'''
     Given an argparse._ArgumentGroup instance, a configuration schema dict, a sequence of unparsed
     argument strings, and a dotted flag name, convert the schema into corresponding command-line
     array element flags that correspond to the given unparsed arguments.
 
     Here's the background. We want to support flags that can have arbitrary indices like:
-    
+
       --foo.bar[1].baz
-    
+
     But argparse doesn't support that natively because the index can be an arbitrary number. We
     won't let that stop us though, will we?
 
@@ -336,22 +336,22 @@ def add_array_element_arguments_from_schema(arguments_group, schema, unparsed_ar
     pattern that would match the flag name regardless of the number that's in it. The idea is that
     we want to look for unparsed arguments that appear like the flag name, but instead of "[0]" they
     have, say, "[1]" or "[123]".
-    
+
     Next, we check each unparsed argument against that pattern. If one of them matches, add an
     argument flag for it to the argument parser group. Example:
-    
+
     Let's say flag_name is:
-    
+
         --foo.bar[0].baz
-    
+
     ... then the regular expression pattern will be:
-    
+
         ^--foo\.bar\[\d+\]\.baz
-    
+
     ... and, if that matches an unparsed argument of:
-    
+
         --foo.bar[1].baz
-    
+
     ... then an argument flag will get added equal to that unparsed argument. And the unparsed
     argument will match it when parsing is performed! In this manner, we're using the actual user
     CLI input to inform what exact flags we support!
@@ -359,7 +359,7 @@ def add_array_element_arguments_from_schema(arguments_group, schema, unparsed_ar
     if '[0]' not in flag_name or '--help' in unparsed_arguments:
         return
 
-    pattern = re.compile(f'^--{flag_name.replace("[0]", r"\[\d+\]").replace(".", r"\.")}$')
+    pattern = re.compile(fr'^--{flag_name.replace("[0]", r"\[\d+\]").replace(".", r"\.")}$')
     existing_flags = set(
         itertools.chain(
             *(group_action.option_strings for group_action in arguments_group._group_actions)
@@ -409,10 +409,15 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
 
     And if names are also passed in, they are considered to be the name components of an option
     (e.g. "foo" and "bar") and are used to construct a resulting flag.
+
+    Bail if the schema is not a dict.
     '''
     if names is None:
         names = ()
 
+    if not isinstance(schema, dict):
+        return
+
     schema_type = schema.get('type')
 
     # If this option has multiple types, just use the first one (that isn't "null").
@@ -449,13 +454,13 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
                 )
 
     flag_name = '.'.join(names)
-    metavar = names[-1].upper()
 
     # Certain options already have corresponding flags on individual actions (like "create
     # --progress"), so don't bother adding them to the global flags.
-    if flag_name in OMITTED_FLAG_NAMES:
+    if not flag_name or flag_name in OMITTED_FLAG_NAMES:
         return
 
+    metavar = names[-1].upper()
     description = make_argument_description(schema, flag_name)
     argument_type = borgmatic.config.schema.parse_type(schema_type)
     full_flag_name = f"--{flag_name.replace('_', '-')}"
@@ -482,10 +487,11 @@ def add_arguments_from_schema(arguments_group, schema, unparsed_arguments, names
 
 def make_parsers(schema, unparsed_arguments):
     '''
-    Given a configuration schema dict, build a global arguments parser, individual action parsers,
-    and a combined parser containing both. Return them as a tuple. The global parser is useful for
-    parsing just global arguments while ignoring actions, and the combined parser is handy for
-    displaying help that includes everything: global flags, a list of actions, etc.
+    Given a configuration schema dict and unparsed arguments as a sequence of strings, build a
+    global arguments parser, individual action parsers, and a combined parser containing both.
+    Return them as a tuple. The global parser is useful for parsing just global arguments while
+    ignoring actions, and the combined parser is handy for displaying help that includes everything:
+    global flags, a list of actions, etc.
     '''
     config_paths = collect.get_default_config_paths(expand_home=True)
     unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
@@ -1752,8 +1758,8 @@ def make_parsers(schema, unparsed_arguments):
 def parse_arguments(schema, *unparsed_arguments):
     '''
     Given a configuration schema dict and the command-line arguments with which this script was
-    invoked, parse the arguments and return them as a dict mapping from action name (or "global") to
-    an argparse.Namespace instance.
+    invoked and unparsed arguments as a sequence of strings, parse the arguments and return them as
+    a dict mapping from action name (or "global") to an argparse.Namespace instance.
 
     Raise ValueError if the arguments cannot be parsed.
     Raise SystemExit with an error code of 0 if "--help" was requested.

+ 3 - 1
borgmatic/commands/completion/bash.py

@@ -1,6 +1,7 @@
 import borgmatic.commands.arguments
 import borgmatic.commands.completion.actions
 import borgmatic.commands.completion.flag
+import borgmatic.config.validate
 
 
 def parser_flags(parser):
@@ -10,7 +11,8 @@ def parser_flags(parser):
     '''
     return ' '.join(
         flag_variant
-        for action in parser._actions for flag_name in action.option_strings
+        for action in parser._actions
+        for flag_name in action.option_strings
         for flag_variant in borgmatic.commands.completion.flag.variants(flag_name)
     )
 

+ 0 - 3
borgmatic/commands/completion/flag.py

@@ -11,6 +11,3 @@ def variants(flag_name):
         return
 
     yield flag_name
-
-
-

+ 3 - 1
borgmatic/config/generate.py

@@ -39,7 +39,9 @@ def schema_to_sample_configuration(schema, source_config=None, level=0, parent_i
     if borgmatic.config.schema.compare_types(schema_type, {'array'}):
         config = ruamel.yaml.comments.CommentedSeq(
             example
-            if borgmatic.config.schema.compare_types(schema['items'].get('type'), SCALAR_SCHEMA_TYPES)
+            if borgmatic.config.schema.compare_types(
+                schema['items'].get('type'), SCALAR_SCHEMA_TYPES
+            )
             else [
                 schema_to_sample_configuration(
                     schema['items'], source_config, level, parent_is_sequence=True

+ 1 - 3
borgmatic/config/schema.py

@@ -50,9 +50,7 @@ def compare_types(schema_type, target_types, match=any):
     compare elements.
     '''
     if isinstance(schema_type, list):
-        if match(
-            element_schema_type in target_types for element_schema_type in schema_type
-        ):
+        if match(element_schema_type in target_types for element_schema_type in schema_type):
             return True
 
         return False

+ 12 - 0
borgmatic/config/validate.py

@@ -21,6 +21,18 @@ def schema_filename():
         return schema_path
 
 
+def load_schema(schema_path):
+    '''
+    Given a schema filename path, load the schema and return it as a dict.
+
+    Raise Validation_error if the schema could not be parsed.
+    '''
+    try:
+        return load.load_configuration(schema_path)
+    except (ruamel.yaml.error.YAMLError, RecursionError) as error:
+        raise Validation_error(schema_path, (str(error),))
+
+
 def format_json_error_path_element(path_element):
     '''
     Given a path element into a JSON data structure, format it for display as a string.

+ 6 - 6
tests/integration/borg/test_commands.py

@@ -53,7 +53,7 @@ def fuzz_argument(arguments, argument_name):
 
 def test_transfer_archives_command_does_not_duplicate_flags_or_raise():
     arguments = borgmatic.commands.arguments.parse_arguments(
-        'transfer', '--source-repository', 'foo'
+        {}, 'transfer', '--source-repository', 'foo'
     )['transfer']
     flexmock(borgmatic.borg.transfer).should_receive('execute_command').replace_with(
         assert_command_does_not_duplicate_flags
@@ -74,7 +74,7 @@ def test_transfer_archives_command_does_not_duplicate_flags_or_raise():
 
 
 def test_prune_archives_command_does_not_duplicate_flags_or_raise():
-    arguments = borgmatic.commands.arguments.parse_arguments('prune')['prune']
+    arguments = borgmatic.commands.arguments.parse_arguments({}, 'prune')['prune']
     flexmock(borgmatic.borg.prune).should_receive('execute_command').replace_with(
         assert_command_does_not_duplicate_flags
     )
@@ -94,7 +94,7 @@ def test_prune_archives_command_does_not_duplicate_flags_or_raise():
 
 
 def test_mount_archive_command_does_not_duplicate_flags_or_raise():
-    arguments = borgmatic.commands.arguments.parse_arguments('mount', '--mount-point', 'tmp')[
+    arguments = borgmatic.commands.arguments.parse_arguments({}, 'mount', '--mount-point', 'tmp')[
         'mount'
     ]
     flexmock(borgmatic.borg.mount).should_receive('execute_command').replace_with(
@@ -116,7 +116,7 @@ def test_mount_archive_command_does_not_duplicate_flags_or_raise():
 
 
 def test_make_list_command_does_not_duplicate_flags_or_raise():
-    arguments = borgmatic.commands.arguments.parse_arguments('list')['list']
+    arguments = borgmatic.commands.arguments.parse_arguments({}, 'list')['list']
 
     for argument_name in dir(arguments):
         if argument_name.startswith('_'):
@@ -134,7 +134,7 @@ def test_make_list_command_does_not_duplicate_flags_or_raise():
 
 
 def test_make_repo_list_command_does_not_duplicate_flags_or_raise():
-    arguments = borgmatic.commands.arguments.parse_arguments('repo-list')['repo-list']
+    arguments = borgmatic.commands.arguments.parse_arguments({}, 'repo-list')['repo-list']
 
     for argument_name in dir(arguments):
         if argument_name.startswith('_'):
@@ -152,7 +152,7 @@ def test_make_repo_list_command_does_not_duplicate_flags_or_raise():
 
 
 def test_display_archives_info_command_does_not_duplicate_flags_or_raise():
-    arguments = borgmatic.commands.arguments.parse_arguments('info')['info']
+    arguments = borgmatic.commands.arguments.parse_arguments({}, 'info')['info']
     flexmock(borgmatic.borg.info).should_receive('execute_command_and_capture_output').replace_with(
         assert_command_does_not_duplicate_flags
     )

+ 9 - 2
tests/integration/commands/completion/test_actions.py

@@ -1,4 +1,5 @@
 import borgmatic.commands.arguments
+import borgmatic.config.validate
 from borgmatic.commands.completion import actions as module
 
 
@@ -7,7 +8,10 @@ def test_available_actions_uses_only_subactions_for_action_with_subactions():
         unused_global_parser,
         action_parsers,
         unused_combined_parser,
-    ) = borgmatic.commands.arguments.make_parsers()
+    ) = borgmatic.commands.arguments.make_parsers(
+        schema=borgmatic.config.validate.load_schema(borgmatic.config.validate.schema_filename()),
+        unparsed_arguments=(),
+    )
 
     actions = module.available_actions(action_parsers, 'config')
 
@@ -20,7 +24,10 @@ def test_available_actions_omits_subactions_for_action_without_subactions():
         unused_global_parser,
         action_parsers,
         unused_combined_parser,
-    ) = borgmatic.commands.arguments.make_parsers()
+    ) = borgmatic.commands.arguments.make_parsers(
+        schema=borgmatic.config.validate.load_schema(borgmatic.config.validate.schema_filename()),
+        unparsed_arguments=(),
+    )
 
     actions = module.available_actions(action_parsers, 'list')
 

+ 97 - 83
tests/integration/commands/test_arguments.py

@@ -8,7 +8,7 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
     config_paths = ['default']
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
 
-    arguments = module.parse_arguments()
+    arguments = module.parse_arguments({})
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == config_paths
@@ -21,7 +21,7 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
 def test_parse_arguments_with_multiple_config_flags_parses_as_list():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--config', 'myconfig', '--config', 'otherconfig')
+    arguments = module.parse_arguments({}, '--config', 'myconfig', '--config', 'otherconfig')
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == ['myconfig', 'otherconfig']
@@ -34,7 +34,7 @@ def test_parse_arguments_with_multiple_config_flags_parses_as_list():
 def test_parse_arguments_with_action_after_config_path_omits_action():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--config', 'myconfig', 'list', '--json')
+    arguments = module.parse_arguments({}, '--config', 'myconfig', 'list', '--json')
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == ['myconfig']
@@ -45,7 +45,9 @@ def test_parse_arguments_with_action_after_config_path_omits_action():
 def test_parse_arguments_with_action_after_config_path_omits_aliased_action():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey')
+    arguments = module.parse_arguments(
+        {}, '--config', 'myconfig', 'init', '--encryption', 'repokey'
+    )
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == ['myconfig']
@@ -56,7 +58,7 @@ def test_parse_arguments_with_action_after_config_path_omits_aliased_action():
 def test_parse_arguments_with_action_and_positional_arguments_after_config_path_omits_action_and_arguments():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--config', 'myconfig', 'borg', 'key', 'export')
+    arguments = module.parse_arguments({}, '--config', 'myconfig', 'borg', 'key', 'export')
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == ['myconfig']
@@ -68,7 +70,7 @@ def test_parse_arguments_with_verbosity_overrides_default():
     config_paths = ['default']
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
 
-    arguments = module.parse_arguments('--verbosity', '1')
+    arguments = module.parse_arguments({}, '--verbosity', '1')
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == config_paths
@@ -82,7 +84,7 @@ def test_parse_arguments_with_syslog_verbosity_overrides_default():
     config_paths = ['default']
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
 
-    arguments = module.parse_arguments('--syslog-verbosity', '2')
+    arguments = module.parse_arguments({}, '--syslog-verbosity', '2')
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == config_paths
@@ -96,7 +98,7 @@ def test_parse_arguments_with_log_file_verbosity_overrides_default():
     config_paths = ['default']
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(config_paths)
 
-    arguments = module.parse_arguments('--log-file-verbosity', '-1')
+    arguments = module.parse_arguments({}, '--log-file-verbosity', '-1')
 
     global_arguments = arguments['global']
     assert global_arguments.config_paths == config_paths
@@ -109,7 +111,7 @@ def test_parse_arguments_with_log_file_verbosity_overrides_default():
 def test_parse_arguments_with_single_override_parses():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--override', 'foo.bar=baz')
+    arguments = module.parse_arguments({}, '--override', 'foo.bar=baz')
 
     global_arguments = arguments['global']
     assert global_arguments.overrides == ['foo.bar=baz']
@@ -119,7 +121,7 @@ def test_parse_arguments_with_multiple_overrides_flags_parses():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     arguments = module.parse_arguments(
-        '--override', 'foo.bar=baz', '--override', 'foo.quux=7', '--override', 'this.that=8'
+        {}, '--override', 'foo.bar=baz', '--override', 'foo.quux=7', '--override', 'this.that=8'
     )
 
     global_arguments = arguments['global']
@@ -127,7 +129,7 @@ def test_parse_arguments_with_multiple_overrides_flags_parses():
 
 
 def test_parse_arguments_with_list_json_overrides_default():
-    arguments = module.parse_arguments('list', '--json')
+    arguments = module.parse_arguments({}, 'list', '--json')
 
     assert 'list' in arguments
     assert arguments['list'].json is True
@@ -136,7 +138,7 @@ def test_parse_arguments_with_list_json_overrides_default():
 def test_parse_arguments_with_no_actions_defaults_to_all_actions_enabled():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments()
+    arguments = module.parse_arguments({})
 
     assert 'prune' in arguments
     assert 'create' in arguments
@@ -146,7 +148,7 @@ def test_parse_arguments_with_no_actions_defaults_to_all_actions_enabled():
 def test_parse_arguments_with_no_actions_passes_argument_to_relevant_actions():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--stats', '--list')
+    arguments = module.parse_arguments({}, '--stats', '--list')
 
     assert 'prune' in arguments
     assert arguments['prune'].stats
@@ -161,7 +163,7 @@ def test_parse_arguments_with_help_and_no_actions_shows_global_help(capsys):
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit) as exit:
-        module.parse_arguments('--help')
+        module.parse_arguments({}, '--help')
 
     assert exit.value.code == 0
     captured = capsys.readouterr()
@@ -173,7 +175,7 @@ def test_parse_arguments_with_help_and_action_shows_action_help(capsys):
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit) as exit:
-        module.parse_arguments('create', '--help')
+        module.parse_arguments({}, 'create', '--help')
 
     assert exit.value.code == 0
     captured = capsys.readouterr()
@@ -185,7 +187,7 @@ def test_parse_arguments_with_help_and_action_shows_action_help(capsys):
 def test_parse_arguments_with_action_before_global_options_parses_options():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('prune', '--verbosity', '2')
+    arguments = module.parse_arguments({}, 'prune', '--verbosity', '2')
 
     assert 'prune' in arguments
     assert arguments['global'].verbosity == 2
@@ -194,7 +196,7 @@ def test_parse_arguments_with_action_before_global_options_parses_options():
 def test_parse_arguments_with_global_options_before_action_parses_options():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('--verbosity', '2', 'prune')
+    arguments = module.parse_arguments({}, '--verbosity', '2', 'prune')
 
     assert 'prune' in arguments
     assert arguments['global'].verbosity == 2
@@ -203,7 +205,7 @@ def test_parse_arguments_with_global_options_before_action_parses_options():
 def test_parse_arguments_with_prune_action_leaves_other_actions_disabled():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('prune')
+    arguments = module.parse_arguments({}, 'prune')
 
     assert 'prune' in arguments
     assert 'create' not in arguments
@@ -213,7 +215,7 @@ def test_parse_arguments_with_prune_action_leaves_other_actions_disabled():
 def test_parse_arguments_with_multiple_actions_leaves_other_action_disabled():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    arguments = module.parse_arguments('create', 'check')
+    arguments = module.parse_arguments({}, 'create', 'check')
 
     assert 'prune' not in arguments
     assert 'create' in arguments
@@ -224,60 +226,53 @@ def test_parse_arguments_disallows_invalid_argument():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--posix-me-harder')
+        module.parse_arguments({}, '--posix-me-harder')
 
 
 def test_parse_arguments_disallows_encryption_mode_without_init():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--config', 'myconfig', '--encryption', 'repokey')
+        module.parse_arguments({}, '--config', 'myconfig', '--encryption', 'repokey')
 
 
 def test_parse_arguments_allows_encryption_mode_with_init():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey')
-
-
-def test_parse_arguments_requires_encryption_mode_with_init():
-    flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
-
-    with pytest.raises(SystemExit):
-        module.parse_arguments('--config', 'myconfig', 'init')
+    module.parse_arguments({}, '--config', 'myconfig', 'init', '--encryption', 'repokey')
 
 
 def test_parse_arguments_disallows_append_only_without_init():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--config', 'myconfig', '--append-only')
+        module.parse_arguments({}, '--config', 'myconfig', '--append-only')
 
 
 def test_parse_arguments_disallows_storage_quota_without_init():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--config', 'myconfig', '--storage-quota', '5G')
+        module.parse_arguments({}, '--config', 'myconfig', '--storage-quota', '5G')
 
 
 def test_parse_arguments_allows_init_and_prune():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'prune')
+    module.parse_arguments({}, '--config', 'myconfig', 'init', '--encryption', 'repokey', 'prune')
 
 
 def test_parse_arguments_allows_init_and_create():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'init', '--encryption', 'repokey', 'create')
+    module.parse_arguments({}, '--config', 'myconfig', 'init', '--encryption', 'repokey', 'create')
 
 
 def test_parse_arguments_allows_repository_with_extract():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     module.parse_arguments(
-        '--config', 'myconfig', 'extract', '--repository', 'test.borg', '--archive', 'test'
+        {}, '--config', 'myconfig', 'extract', '--repository', 'test.borg', '--archive', 'test'
     )
 
 
@@ -285,6 +280,7 @@ def test_parse_arguments_allows_repository_with_mount():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     module.parse_arguments(
+        {},
         '--config',
         'myconfig',
         'mount',
@@ -300,187 +296,187 @@ def test_parse_arguments_allows_repository_with_mount():
 def test_parse_arguments_allows_repository_with_list():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'list', '--repository', 'test.borg')
+    module.parse_arguments({}, '--config', 'myconfig', 'list', '--repository', 'test.borg')
 
 
 def test_parse_arguments_disallows_archive_unless_action_consumes_it():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--config', 'myconfig', '--archive', 'test')
+        module.parse_arguments({}, '--config', 'myconfig', '--archive', 'test')
 
 
 def test_parse_arguments_disallows_paths_unless_action_consumes_it():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--config', 'myconfig', '--path', 'test')
+        module.parse_arguments({}, '--config', 'myconfig', '--path', 'test')
 
 
 def test_parse_arguments_disallows_other_actions_with_config_bootstrap():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('config', 'bootstrap', '--repository', 'test.borg', 'list')
+        module.parse_arguments({}, 'config', 'bootstrap', '--repository', 'test.borg', 'list')
 
 
 def test_parse_arguments_allows_archive_with_extract():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'extract', '--archive', 'test')
+    module.parse_arguments({}, '--config', 'myconfig', 'extract', '--archive', 'test')
 
 
 def test_parse_arguments_allows_archive_with_mount():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     module.parse_arguments(
-        '--config', 'myconfig', 'mount', '--archive', 'test', '--mount-point', '/mnt'
+        {}, '--config', 'myconfig', 'mount', '--archive', 'test', '--mount-point', '/mnt'
     )
 
 
 def test_parse_arguments_allows_archive_with_restore():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'restore', '--archive', 'test')
+    module.parse_arguments({}, '--config', 'myconfig', 'restore', '--archive', 'test')
 
 
 def test_parse_arguments_allows_archive_with_list():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--config', 'myconfig', 'list', '--archive', 'test')
+    module.parse_arguments({}, '--config', 'myconfig', 'list', '--archive', 'test')
 
 
 def test_parse_arguments_requires_archive_with_extract():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit):
-        module.parse_arguments('--config', 'myconfig', 'extract')
+        module.parse_arguments({}, '--config', 'myconfig', 'extract')
 
 
 def test_parse_arguments_requires_archive_with_restore():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit):
-        module.parse_arguments('--config', 'myconfig', 'restore')
+        module.parse_arguments({}, '--config', 'myconfig', 'restore')
 
 
 def test_parse_arguments_requires_mount_point_with_mount():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit):
-        module.parse_arguments('--config', 'myconfig', 'mount', '--archive', 'test')
+        module.parse_arguments({}, '--config', 'myconfig', 'mount', '--archive', 'test')
 
 
 def test_parse_arguments_requires_mount_point_with_umount():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit):
-        module.parse_arguments('--config', 'myconfig', 'umount')
+        module.parse_arguments({}, '--config', 'myconfig', 'umount')
 
 
 def test_parse_arguments_allows_progress_before_create():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--progress', 'create', 'list')
+    module.parse_arguments({}, '--progress', 'create', 'list')
 
 
 def test_parse_arguments_allows_progress_after_create():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('create', '--progress', 'list')
+    module.parse_arguments({}, 'create', '--progress', 'list')
 
 
 def test_parse_arguments_allows_progress_and_extract():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--progress', 'extract', '--archive', 'test', 'list')
+    module.parse_arguments({}, '--progress', 'extract', '--archive', 'test', 'list')
 
 
 def test_parse_arguments_disallows_progress_without_create():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--progress', 'list')
+        module.parse_arguments({}, '--progress', 'list')
 
 
 def test_parse_arguments_with_stats_and_create_flags_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--stats', 'create', 'list')
+    module.parse_arguments({}, '--stats', 'create', 'list')
 
 
 def test_parse_arguments_with_stats_and_prune_flags_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--stats', 'prune', 'list')
+    module.parse_arguments({}, '--stats', 'prune', 'list')
 
 
 def test_parse_arguments_with_stats_flag_but_no_create_or_prune_flag_raises_value_error():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--stats', 'list')
+        module.parse_arguments({}, '--stats', 'list')
 
 
 def test_parse_arguments_with_list_and_create_flags_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--list', 'create')
+    module.parse_arguments({}, '--list', 'create')
 
 
 def test_parse_arguments_with_list_and_prune_flags_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('--list', 'prune')
+    module.parse_arguments({}, '--list', 'prune')
 
 
 def test_parse_arguments_with_list_flag_but_no_relevant_action_raises_value_error():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    with pytest.raises(SystemExit):
-        module.parse_arguments('--list', 'repo-create')
+    with pytest.raises(ValueError):
+        module.parse_arguments({}, '--list', 'repo-create')
 
 
 def test_parse_arguments_disallows_list_with_progress_for_create_action():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('create', '--list', '--progress')
+        module.parse_arguments({}, 'create', '--list', '--progress')
 
 
 def test_parse_arguments_disallows_list_with_json_for_create_action():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('create', '--list', '--json')
+        module.parse_arguments({}, 'create', '--list', '--json')
 
 
 def test_parse_arguments_allows_json_with_list_or_info():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('list', '--json')
-    module.parse_arguments('info', '--json')
+    module.parse_arguments({}, 'list', '--json')
+    module.parse_arguments({}, 'info', '--json')
 
 
 def test_parse_arguments_disallows_json_with_both_list_and_info():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('list', 'info', '--json')
+        module.parse_arguments({}, 'list', 'info', '--json')
 
 
 def test_parse_arguments_disallows_json_with_both_list_and_repo_info():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('list', 'repo-info', '--json')
+        module.parse_arguments({}, 'list', 'repo-info', '--json')
 
 
 def test_parse_arguments_disallows_json_with_both_repo_info_and_info():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('repo-info', 'info', '--json')
+        module.parse_arguments({}, 'repo-info', 'info', '--json')
 
 
 def test_parse_arguments_disallows_transfer_with_both_archive_and_match_archives():
@@ -488,6 +484,7 @@ def test_parse_arguments_disallows_transfer_with_both_archive_and_match_archives
 
     with pytest.raises(ValueError):
         module.parse_arguments(
+            {},
             'transfer',
             '--source-repository',
             'source.borg',
@@ -502,74 +499,74 @@ def test_parse_arguments_disallows_list_with_both_prefix_and_match_archives():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('list', '--prefix', 'foo', '--match-archives', 'sh:*bar')
+        module.parse_arguments({}, 'list', '--prefix', 'foo', '--match-archives', 'sh:*bar')
 
 
 def test_parse_arguments_disallows_repo_list_with_both_prefix_and_match_archives():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('repo-list', '--prefix', 'foo', '--match-archives', 'sh:*bar')
+        module.parse_arguments({}, 'repo-list', '--prefix', 'foo', '--match-archives', 'sh:*bar')
 
 
 def test_parse_arguments_disallows_info_with_both_archive_and_match_archives():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('info', '--archive', 'foo', '--match-archives', 'sh:*bar')
+        module.parse_arguments({}, 'info', '--archive', 'foo', '--match-archives', 'sh:*bar')
 
 
 def test_parse_arguments_disallows_info_with_both_archive_and_prefix():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('info', '--archive', 'foo', '--prefix', 'bar')
+        module.parse_arguments({}, 'info', '--archive', 'foo', '--prefix', 'bar')
 
 
 def test_parse_arguments_disallows_info_with_both_prefix_and_match_archives():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('info', '--prefix', 'foo', '--match-archives', 'sh:*bar')
+        module.parse_arguments({}, 'info', '--prefix', 'foo', '--match-archives', 'sh:*bar')
 
 
 def test_parse_arguments_check_only_extract_does_not_raise_extract_subparser_error():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('check', '--only', 'extract')
+    module.parse_arguments({}, 'check', '--only', 'extract')
 
 
 def test_parse_arguments_extract_archive_check_does_not_raise_check_subparser_error():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('extract', '--archive', 'check')
+    module.parse_arguments({}, 'extract', '--archive', 'check')
 
 
 def test_parse_arguments_extract_with_check_only_extract_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('extract', '--archive', 'name', 'check', '--only', 'extract')
+    module.parse_arguments({}, 'extract', '--archive', 'name', 'check', '--only', 'extract')
 
 
 def test_parse_arguments_bootstrap_without_config_errors():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('bootstrap')
+        module.parse_arguments({}, 'bootstrap')
 
 
 def test_parse_arguments_config_with_no_subaction_errors():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('config')
+        module.parse_arguments({}, 'config')
 
 
 def test_parse_arguments_config_with_help_shows_config_help(capsys):
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit) as exit:
-        module.parse_arguments('config', '--help')
+        module.parse_arguments({}, 'config', '--help')
 
     assert exit.value.code == 0
     captured = capsys.readouterr()
@@ -582,7 +579,7 @@ def test_parse_arguments_config_with_subaction_but_missing_flags_errors():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit) as exit:
-        module.parse_arguments('config', 'bootstrap')
+        module.parse_arguments({}, 'config', 'bootstrap')
 
     assert exit.value.code == 2
 
@@ -591,7 +588,7 @@ def test_parse_arguments_config_with_subaction_and_help_shows_subaction_help(cap
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(SystemExit) as exit:
-        module.parse_arguments('config', 'bootstrap', '--help')
+        module.parse_arguments({}, 'config', 'bootstrap', '--help')
 
     assert exit.value.code == 0
     captured = capsys.readouterr()
@@ -601,26 +598,30 @@ def test_parse_arguments_config_with_subaction_and_help_shows_subaction_help(cap
 def test_parse_arguments_config_with_subaction_and_required_flags_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('config', 'bootstrap', '--repository', 'repo.borg')
+    module.parse_arguments({}, 'config', 'bootstrap', '--repository', 'repo.borg')
 
 
 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('--verbosity', '1', 'config', 'bootstrap', '--repository', 'repo.borg')
+    module.parse_arguments(
+        {}, '--verbosity', '1', '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', '--verbosity', '1')
+    module.parse_arguments(
+        {}, 'config', 'bootstrap', '--repository', 'repo.borg', '--verbosity', '1'
+    )
 
 
 def test_parse_arguments_config_with_subaction_and_explicit_config_file_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     module.parse_arguments(
-        'config', 'bootstrap', '--repository', 'repo.borg', '--config', 'test.yaml'
+        {}, 'config', 'bootstrap', '--repository', 'repo.borg', '--config', 'test.yaml'
     )
 
 
@@ -628,10 +629,23 @@ def test_parse_arguments_with_borg_action_and_dry_run_raises():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
     with pytest.raises(ValueError):
-        module.parse_arguments('--dry-run', 'borg', 'list')
+        module.parse_arguments({}, '--dry-run', 'borg', 'list')
 
 
 def test_parse_arguments_with_borg_action_and_no_dry_run_does_not_raise():
     flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
 
-    module.parse_arguments('borg', 'list')
+    module.parse_arguments({}, 'borg', 'list')
+
+
+def test_parse_arguments_with_argument_from_schema_does_not_raise():
+    flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
+
+    module.parse_arguments(
+        {
+            'type': 'object',
+            'properties': {'foo': {'type': 'object', 'properties': {'bar': {'type': 'integer'}}}},
+        },
+        '--foo.bar',
+        '3',
+    )

+ 12 - 12
tests/integration/config/test_generate.py

@@ -21,9 +21,9 @@ def test_schema_to_sample_configuration_comments_out_non_default_options():
         'type': 'object',
         'properties': dict(
             [
-                ('field1', {'example': 'Example 1'}),
-                ('field2', {'example': 'Example 2'}),
-                ('source_directories', {'example': 'Example 3'}),
+                ('field1', {'type': 'string', 'example': 'Example 1'}),
+                ('field2', {'type': 'string', 'example': 'Example 2'}),
+                ('source_directories', {'type': 'string', 'example': 'Example 3'}),
             ]
         ),
     }
@@ -47,9 +47,9 @@ def test_schema_to_sample_configuration_comments_out_non_source_config_options()
         'type': 'object',
         'properties': dict(
             [
-                ('field1', {'example': 'Example 1'}),
-                ('field2', {'example': 'Example 2'}),
-                ('field3', {'example': 'Example 3'}),
+                ('field1', {'type': 'string', 'example': 'Example 1'}),
+                ('field2', {'type': 'string', 'example': 'Example 2'}),
+                ('field3', {'type': 'string', 'example': 'Example 3'}),
             ]
         ),
     }
@@ -76,9 +76,9 @@ def test_schema_to_sample_configuration_comments_out_non_default_options_in_sequ
             'type': 'object',
             'properties': dict(
                 [
-                    ('field1', {'example': 'Example 1'}),
-                    ('field2', {'example': 'Example 2'}),
-                    ('source_directories', {'example': 'Example 3'}),
+                    ('field1', {'type': 'string', 'example': 'Example 1'}),
+                    ('field2', {'type': 'string', 'example': 'Example 2'}),
+                    ('source_directories', {'type': 'string', 'example': 'Example 3'}),
                 ]
             ),
         },
@@ -105,9 +105,9 @@ def test_schema_to_sample_configuration_comments_out_non_source_config_options_i
             'type': 'object',
             'properties': dict(
                 [
-                    ('field1', {'example': 'Example 1'}),
-                    ('field2', {'example': 'Example 2'}),
-                    ('field3', {'example': 'Example 3'}),
+                    ('field1', {'type': 'string', 'example': 'Example 1'}),
+                    ('field2', {'type': 'string', 'example': 'Example 2'}),
+                    ('field3', {'type': 'string', 'example': 'Example 3'}),
                 ]
             ),
         },

+ 32 - 11
tests/integration/config/test_validate.py

@@ -58,7 +58,9 @@ def test_parse_configuration_transforms_file_into_mapping():
         '''
     )
 
-    config, config_paths, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+    config, config_paths, logs = module.parse_configuration(
+        '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+    )
 
     assert config == {
         'source_directories': ['/home', '/etc'],
@@ -86,7 +88,9 @@ def test_parse_configuration_passes_through_quoted_punctuation():
         '''
     )
 
-    config, config_paths, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+    config, config_paths, logs = module.parse_configuration(
+        '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+    )
 
     assert config == {
         'source_directories': [f'/home/{string.punctuation}'],
@@ -119,7 +123,7 @@ def test_parse_configuration_with_schema_lacking_examples_does_not_raise():
         ''',
     )
 
-    module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+    module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock())
 
 
 def test_parse_configuration_inlines_include_inside_deprecated_section():
@@ -145,7 +149,9 @@ def test_parse_configuration_inlines_include_inside_deprecated_section():
     include_file.name = 'include.yaml'
     builtins.should_receive('open').with_args('/tmp/include.yaml').and_return(include_file)
 
-    config, config_paths, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+    config, config_paths, logs = module.parse_configuration(
+        '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+    )
 
     assert config == {
         'source_directories': ['/home'],
@@ -181,7 +187,9 @@ def test_parse_configuration_merges_include():
     include_file.name = 'include.yaml'
     builtins.should_receive('open').with_args('/tmp/include.yaml').and_return(include_file)
 
-    config, config_paths, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+    config, config_paths, logs = module.parse_configuration(
+        '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+    )
 
     assert config == {
         'source_directories': ['/home'],
@@ -196,7 +204,9 @@ def test_parse_configuration_merges_include():
 
 def test_parse_configuration_raises_for_missing_config_file():
     with pytest.raises(FileNotFoundError):
-        module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+        module.parse_configuration(
+            '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+        )
 
 
 def test_parse_configuration_raises_for_missing_schema_file():
@@ -208,14 +218,18 @@ def test_parse_configuration_raises_for_missing_schema_file():
     builtins.should_receive('open').with_args('/tmp/schema.yaml').and_raise(FileNotFoundError)
 
     with pytest.raises(FileNotFoundError):
-        module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+        module.parse_configuration(
+            '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+        )
 
 
 def test_parse_configuration_raises_for_syntax_error():
     mock_config_and_schema('foo:\nbar')
 
     with pytest.raises(ValueError):
-        module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+        module.parse_configuration(
+            '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+        )
 
 
 def test_parse_configuration_raises_for_validation_error():
@@ -228,7 +242,9 @@ def test_parse_configuration_raises_for_validation_error():
     )
 
     with pytest.raises(module.Validation_error):
-        module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+        module.parse_configuration(
+            '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+        )
 
 
 def test_parse_configuration_applies_overrides():
@@ -245,7 +261,10 @@ def test_parse_configuration_applies_overrides():
     )
 
     config, config_paths, logs = module.parse_configuration(
-        '/tmp/config.yaml', '/tmp/schema.yaml', overrides=['local_path=borg2']
+        '/tmp/config.yaml',
+        '/tmp/schema.yaml',
+        global_arguments=flexmock(),
+        overrides=['local_path=borg2'],
     )
 
     assert config == {
@@ -273,7 +292,9 @@ def test_parse_configuration_applies_normalization_after_environment_variable_in
     )
     flexmock(os).should_receive('getenv').replace_with(lambda variable_name, default: default)
 
-    config, config_paths, logs = module.parse_configuration('/tmp/config.yaml', '/tmp/schema.yaml')
+    config, config_paths, logs = module.parse_configuration(
+        '/tmp/config.yaml', '/tmp/schema.yaml', global_arguments=flexmock()
+    )
 
     assert config == {
         'source_directories': ['/home'],

+ 7 - 2
tests/unit/commands/test_borgmatic.py

@@ -1537,6 +1537,7 @@ def test_load_configurations_collects_parsed_configurations_and_logs(resolve_env
     configs, config_paths, logs = tuple(
         module.load_configurations(
             ('test.yaml', 'other.yaml'),
+            global_arguments=flexmock(),
             resolve_env=resolve_env,
         )
     )
@@ -1549,7 +1550,9 @@ def test_load_configurations_collects_parsed_configurations_and_logs(resolve_env
 def test_load_configurations_logs_warning_for_permission_error():
     flexmock(module.validate).should_receive('parse_configuration').and_raise(PermissionError)
 
-    configs, config_paths, logs = tuple(module.load_configurations(('test.yaml',)))
+    configs, config_paths, logs = tuple(
+        module.load_configurations(('test.yaml',), global_arguments=flexmock())
+    )
 
     assert configs == {}
     assert config_paths == []
@@ -1559,7 +1562,9 @@ def test_load_configurations_logs_warning_for_permission_error():
 def test_load_configurations_logs_critical_for_parse_error():
     flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
 
-    configs, config_paths, logs = tuple(module.load_configurations(('test.yaml',)))
+    configs, config_paths, logs = tuple(
+        module.load_configurations(('test.yaml',), global_arguments=flexmock())
+    )
 
     assert configs == {}
     assert config_paths == []

+ 41 - 13
tests/unit/config/test_generate.py

@@ -17,9 +17,15 @@ def test_schema_to_sample_configuration_generates_config_map_with_examples():
         ),
     }
     flexmock(module.borgmatic.config.schema).should_receive('compare_types').and_return(False)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('object', {'object'}).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('string', module.SCALAR_SCHEMA_TYPES, match=all).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('get_properties').and_return(schema['properties'])
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'object', {'object'}
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'string', module.SCALAR_SCHEMA_TYPES, match=all
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('get_properties').and_return(
+        schema['properties']
+    )
     flexmock(module.ruamel.yaml.comments).should_receive('CommentedMap').replace_with(dict)
     flexmock(module).should_receive('add_comments_to_configuration_object')
 
@@ -50,15 +56,26 @@ def test_schema_to_sample_configuration_generates_config_sequence_of_maps_with_e
         'items': {
             'type': 'object',
             'properties': dict(
-                [('field1', {'type': 'string', 'example': 'Example 1'}), ('field2', {'type': 'string', 'example': 'Example 2'})]
+                [
+                    ('field1', {'type': 'string', 'example': 'Example 1'}),
+                    ('field2', {'type': 'string', 'example': 'Example 2'}),
+                ]
             ),
         },
     }
     flexmock(module.borgmatic.config.schema).should_receive('compare_types').and_return(False)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('array', {'array'}).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('object', {'object'}).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('string', module.SCALAR_SCHEMA_TYPES, match=all).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('get_properties').and_return(schema['items']['properties'])
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'array', {'array'}
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'object', {'object'}
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'string', module.SCALAR_SCHEMA_TYPES, match=all
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('get_properties').and_return(
+        schema['items']['properties']
+    )
     flexmock(module.ruamel.yaml.comments).should_receive('CommentedSeq').replace_with(list)
     flexmock(module).should_receive('add_comments_to_configuration_sequence')
     flexmock(module).should_receive('add_comments_to_configuration_object')
@@ -74,15 +91,26 @@ def test_schema_to_sample_configuration_generates_config_sequence_of_maps_with_m
         'items': {
             'type': ['object', 'null'],
             'properties': dict(
-                [('field1', {'type': 'string', 'example': 'Example 1'}), ('field2', {'type': 'string', 'example': 'Example 2'})]
+                [
+                    ('field1', {'type': 'string', 'example': 'Example 1'}),
+                    ('field2', {'type': 'string', 'example': 'Example 2'}),
+                ]
             ),
         },
     }
     flexmock(module.borgmatic.config.schema).should_receive('compare_types').and_return(False)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('array', {'array'}).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(['object', 'null'], {'object'}).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args('string', module.SCALAR_SCHEMA_TYPES, match=all).and_return(True)
-    flexmock(module.borgmatic.config.schema).should_receive('get_properties').and_return(schema['items']['properties'])
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'array', {'array'}
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        ['object', 'null'], {'object'}
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('compare_types').with_args(
+        'string', module.SCALAR_SCHEMA_TYPES, match=all
+    ).and_return(True)
+    flexmock(module.borgmatic.config.schema).should_receive('get_properties').and_return(
+        schema['items']['properties']
+    )
     flexmock(module.ruamel.yaml.comments).should_receive('CommentedSeq').replace_with(list)
     flexmock(module).should_receive('add_comments_to_configuration_sequence')
     flexmock(module).should_receive('add_comments_to_configuration_object')