浏览代码

Adding some missing tests and fixing related flag vs. config logic (#303).

Dan Helfman 2 月之前
父节点
当前提交
668f767bfc

+ 10 - 2
borgmatic/actions/compact.py

@@ -37,9 +37,17 @@ def run_compact(
             global_arguments,
             local_path=local_path,
             remote_path=remote_path,
-            progress=compact_arguments.progress or config.get('progress'),
+            progress=(
+                config.get('progress')
+                if compact_arguments.progress is None
+                else compact_arguments.progress
+            ),
             cleanup_commits=compact_arguments.cleanup_commits,
-            threshold=compact_arguments.threshold or config.get('compact_threshold'),
+            threshold=(
+                config.get('compact_threshold')
+                if compact_arguments.threshold is None
+                else compact_arguments.threshold
+            ),
         )
     else:  # pragma: nocover
         logger.info('Skipping compact (only available/needed in Borg 1.2+)')

+ 15 - 3
borgmatic/actions/create.py

@@ -327,10 +327,22 @@ def run_create(
             borgmatic_runtime_directory,
             local_path=local_path,
             remote_path=remote_path,
-            progress=create_arguments.progress or config.get('progress'),
-            stats=create_arguments.stats or config.get('stats'),
+            progress=(
+                config.get('progress')
+                if create_arguments.progress is None
+                else create_arguments.progress
+            ),
+            stats=(
+                config.get('statistics')
+                if create_arguments.stats is None
+                else create_arguments.stats
+            ),
             json=create_arguments.json,
-            list_files=create_arguments.list_files or config.get('list'),
+            list_files=(
+                config.get('list_details')
+                if create_arguments.list_files is None
+                else create_arguments.list_files
+            ),
             stream_processes=stream_processes,
         )
 

+ 5 - 1
borgmatic/actions/export_tar.py

@@ -43,6 +43,10 @@ def run_export_tar(
             local_path=local_path,
             remote_path=remote_path,
             tar_filter=export_tar_arguments.tar_filter,
-            list_files=export_tar_arguments.list_files or config.get('list'),
+            list_files=(
+                config.get('list_details')
+                if export_tar_arguments.list_files is None
+                else export_tar_arguments.list_files
+            ),
             strip_components=export_tar_arguments.strip_components,
         )

+ 5 - 1
borgmatic/actions/extract.py

@@ -45,5 +45,9 @@ def run_extract(
             remote_path=remote_path,
             destination_path=extract_arguments.destination,
             strip_components=extract_arguments.strip_components,
-            progress=extract_arguments.progress or config.get('progress'),
+            progress=(
+                config.get('progress')
+                if extract_arguments.progress is None
+                else extract_arguments.progress
+            ),
         )

+ 15 - 3
borgmatic/actions/repo_create.py

@@ -41,9 +41,21 @@ def run_repo_create(
         encryption_mode,
         repo_create_arguments.source_repository,
         repo_create_arguments.copy_crypt_key,
-        repo_create_arguments.append_only or repository.get('append_only'),
-        repo_create_arguments.storage_quota or repository.get('storage_quota'),
-        repo_create_arguments.make_parent_dirs or repository.get('make_parent_dirs'),
+        (
+            repository.get('append_only')
+            if repo_create_arguments.append_only is None
+            else repo_create_arguments.append_only
+        ),
+        (
+            repository.get('storage_quota')
+            if repo_create_arguments.storage_quota is None
+            else repo_create_arguments.storage_quota
+        ),
+        (
+            repository.get('make_parent_dirs')
+            if repo_create_arguments.make_parent_dirs is None
+            else repo_create_arguments.make_parent_dirs
+        ),
         local_path=local_path,
         remote_path=remote_path,
     )

+ 3 - 1
borgmatic/borg/check.py

@@ -143,7 +143,9 @@ def check_archives(
     umask = config.get('umask')
     borg_exit_codes = config.get('borg_exit_codes')
     working_directory = borgmatic.config.paths.get_working_directory(config)
-    progress = check_arguments.progress or config.get('progress')
+    progress = (
+        config.get('progress') if check_arguments.progress is None else check_arguments.progress
+    )
 
     if 'data' in checks:
         checks.add('archives')

+ 6 - 1
borgmatic/borg/delete.py

@@ -35,7 +35,12 @@ def make_delete_command(
         + borgmatic.borg.flags.make_flags('log-json', global_arguments.log_json)
         + borgmatic.borg.flags.make_flags('lock-wait', config.get('lock_wait'))
         + borgmatic.borg.flags.make_flags(
-            'list', delete_arguments.list_archives or config.get('list')
+            'list',
+            (
+                config.get('list_details')
+                if delete_arguments.list_archives is None
+                else delete_arguments.list_archives
+            ),
         )
         + (
             (('--force',) + (('--force',) if delete_arguments.force >= 2 else ()))

+ 8 - 3
borgmatic/borg/prune.py

@@ -66,7 +66,12 @@ def prune_archives(
     borgmatic.logger.add_custom_log_levels()
     umask = config.get('umask', None)
     lock_wait = config.get('lock_wait', None)
-    stats = prune_arguments.stats or config.get('stats')
+    stats = config.get('statistics') if prune_arguments.stats is None else prune_arguments.stats
+    list_archives = (
+        config.get('list_details')
+        if prune_arguments.list_archives is None
+        else prune_arguments.list_archives
+    )
     extra_borg_options = config.get('extra_borg_options', {}).get('prune', '')
 
     full_command = (
@@ -88,14 +93,14 @@ def prune_archives(
             prune_arguments,
             excludes=('repository', 'match_archives', 'stats', 'list_archives'),
         )
-        + (('--list',) if prune_arguments.list_archives or config.get('list') else ())
+        + (('--list',) if list_archives else ())
         + (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
         + (('--dry-run',) if dry_run else ())
         + (tuple(extra_borg_options.split(' ')) if extra_borg_options else ())
         + flags.make_repository_flags(repository_path, local_borg_version)
     )
 
-    if stats or prune_arguments.list_archives:
+    if stats or list_archives:
         output_log_level = logging.ANSWER
     else:
         output_log_level = logging.INFO

+ 6 - 3
borgmatic/borg/transfer.py

@@ -52,13 +52,16 @@ def transfer_archives(
         + flags.make_flags('other-repo', transfer_arguments.source_repository)
         + flags.make_flags('dry-run', dry_run)
     )
+    progress = (
+        config.get('progress')
+        if transfer_arguments.progress is None
+        else transfer_arguments.progress
+    )
 
     return execute_command(
         full_command,
         output_log_level=logging.ANSWER,
-        output_file=(
-            DO_NOT_CAPTURE if (transfer_arguments.progress or config.get('progress')) else None
-        ),
+        output_file=DO_NOT_CAPTURE if progress else None,
         environment=environment.make_environment(config),
         working_directory=borgmatic.config.paths.get_working_directory(config),
         borg_local_path=local_path,

+ 4 - 4
borgmatic/commands/arguments.py

@@ -287,7 +287,7 @@ def parse_arguments_for_actions(unparsed_arguments, action_parsers, global_parse
     )
 
 
-OMITTED_FLAG_NAMES = {'match_archives', 'progress', 'stats', 'list'}
+OMITTED_FLAG_NAMES = {'match_archives', 'progress', 'statistics', 'list_files'}
 
 
 def make_argument_description(schema, flag_name):
@@ -520,9 +520,9 @@ def make_parsers(schema, unparsed_arguments):
     config_paths = collect.get_default_config_paths(expand_home=True)
     unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
 
-    # allow_abbrev=False prevents the global parser from erroring about "ambiguous" options like
-    # --encryption. Such options are intended for an action parser rather than the global parser,
-    # and so we don't want to error on them here.
+    # Using allow_abbrev=False here prevents the global parser from erroring about "ambiguous"
+    # options like --encryption. Such options are intended for an action parser rather than the
+    # global parser, and so we don't want to error on them here.
     global_parser = ArgumentParser(allow_abbrev=False, add_help=False)
     global_group = global_parser.add_argument_group('global arguments')
 

+ 2 - 2
borgmatic/config/schema.yaml

@@ -823,7 +823,7 @@ properties:
             actions. Defaults to false.
         default: false
         example: true
-    stats:
+    statistics:
         type: boolean
         description: |
             Display statistics for an archive when running supported actions.
@@ -831,7 +831,7 @@ properties:
             false.
         default: false
         example: true
-    list:
+    list_details:
         type: boolean
         description: |
             Display details for each file or archive as it is processed when

+ 6 - 2
tests/end-to-end/test_config_flag.py

@@ -40,11 +40,15 @@ def test_config_flags_do_not_error():
         generate_configuration(config_path)
 
         subprocess.check_call(
-            shlex.split(f'borgmatic -v 2 --config {config_path} --repositories "[{{path: {repository_path}, label: repo}}]" repo-create --encryption repokey')
+            shlex.split(
+                f'borgmatic -v 2 --config {config_path} --repositories "[{{path: {repository_path}, label: repo}}]" repo-create --encryption repokey'
+            )
         )
 
         subprocess.check_call(
-            shlex.split(f'borgmatic create --config {config_path} --repositories[0].path "{repository_path}"')
+            shlex.split(
+                f'borgmatic create --config {config_path} --repositories[0].path "{repository_path}"'
+            )
         )
     finally:
         os.chdir(original_working_directory)

+ 78 - 0
tests/unit/actions/test_compact.py

@@ -51,6 +51,84 @@ def test_compact_runs_with_selected_repository():
     )
 
 
+def test_compact_favors_flags_over_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive(
+        'repositories_match'
+    ).once().and_return(True)
+    flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
+    flexmock(module.borgmatic.borg.compact).should_receive('compact_segments').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        progress=False,
+        cleanup_commits=object,
+        threshold=15,
+    ).once()
+    compact_arguments = flexmock(
+        repository=flexmock(),
+        progress=False,
+        cleanup_commits=flexmock(),
+        threshold=15,
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    module.run_compact(
+        config_filename='test.yaml',
+        repository={'path': 'repo'},
+        config={'progress': True, 'compact_threshold': 20},
+        local_borg_version=None,
+        compact_arguments=compact_arguments,
+        global_arguments=global_arguments,
+        dry_run_label='',
+        local_path=None,
+        remote_path=None,
+    )
+
+
+def test_compact_favors_defaults_to_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive(
+        'repositories_match'
+    ).once().and_return(True)
+    flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
+    flexmock(module.borgmatic.borg.compact).should_receive('compact_segments').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        progress=True,
+        cleanup_commits=object,
+        threshold=20,
+    ).once()
+    compact_arguments = flexmock(
+        repository=flexmock(),
+        progress=None,
+        cleanup_commits=flexmock(),
+        threshold=None,
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    module.run_compact(
+        config_filename='test.yaml',
+        repository={'path': 'repo'},
+        config={'progress': True, 'compact_threshold': 20},
+        local_borg_version=None,
+        compact_arguments=compact_arguments,
+        global_arguments=global_arguments,
+        dry_run_label='',
+        local_path=None,
+        remote_path=None,
+    )
+
+
 def test_compact_bails_if_repository_does_not_match():
     flexmock(module.logger).answer = lambda message: None
     flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)

+ 112 - 0
tests/unit/actions/test_create.py

@@ -506,6 +506,118 @@ def test_run_create_runs_with_selected_repository():
     )
 
 
+def test_run_create_favors_flags_over_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive(
+        'repositories_match'
+    ).once().and_return(True)
+    flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
+        flexmock()
+    )
+    flexmock(module.borgmatic.borg.create).should_receive('create_archive').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        progress=False,
+        stats=False,
+        json=object,
+        list_files=False,
+        stream_processes=object,
+    ).once()
+    flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return({})
+    flexmock(module.borgmatic.hooks.dispatch).should_receive(
+        'call_hooks_even_if_unconfigured'
+    ).and_return({})
+    flexmock(module).should_receive('collect_patterns').and_return(())
+    flexmock(module).should_receive('process_patterns').and_return([])
+    flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
+    create_arguments = flexmock(
+        repository=flexmock(),
+        progress=False,
+        stats=False,
+        json=False,
+        list_files=False,
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    list(
+        module.run_create(
+            config_filename='test.yaml',
+            repository={'path': 'repo'},
+            config={'progress': True, 'statistics': True, 'list_details': True},
+            config_paths=['/tmp/test.yaml'],
+            local_borg_version=None,
+            create_arguments=create_arguments,
+            global_arguments=global_arguments,
+            dry_run_label='',
+            local_path=None,
+            remote_path=None,
+        )
+    )
+
+
+def test_run_create_defaults_to_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive(
+        'repositories_match'
+    ).once().and_return(True)
+    flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
+        flexmock()
+    )
+    flexmock(module.borgmatic.borg.create).should_receive('create_archive').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        progress=True,
+        stats=True,
+        json=object,
+        list_files=True,
+        stream_processes=object,
+    ).once()
+    flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return({})
+    flexmock(module.borgmatic.hooks.dispatch).should_receive(
+        'call_hooks_even_if_unconfigured'
+    ).and_return({})
+    flexmock(module).should_receive('collect_patterns').and_return(())
+    flexmock(module).should_receive('process_patterns').and_return([])
+    flexmock(module.os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
+    create_arguments = flexmock(
+        repository=flexmock(),
+        progress=True,
+        stats=True,
+        json=False,
+        list_files=True,
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    list(
+        module.run_create(
+            config_filename='test.yaml',
+            repository={'path': 'repo'},
+            config={'progress': True, 'statistics': True, 'list_details': True},
+            config_paths=['/tmp/test.yaml'],
+            local_borg_version=None,
+            create_arguments=create_arguments,
+            global_arguments=global_arguments,
+            dry_run_label='',
+            local_path=None,
+            remote_path=None,
+        )
+    )
+
+
 def test_run_create_bails_if_repository_does_not_match():
     flexmock(module.logger).answer = lambda message: None
     flexmock(module.borgmatic.config.validate).should_receive(

+ 80 - 0
tests/unit/actions/test_export_tar.py

@@ -27,3 +27,83 @@ def test_run_export_tar_does_not_raise():
         local_path=None,
         remote_path=None,
     )
+
+
+def test_run_export_tar_favors_flags_over_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
+    flexmock(module.borgmatic.borg.export_tar).should_receive('export_tar_archive').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        tar_filter=object,
+        list_files=False,
+        strip_components=object,
+    ).once()
+    export_tar_arguments = flexmock(
+        repository=flexmock(),
+        archive=flexmock(),
+        paths=flexmock(),
+        destination=flexmock(),
+        tar_filter=flexmock(),
+        list_files=False,
+        strip_components=flexmock(),
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    module.run_export_tar(
+        repository={'path': 'repo'},
+        config={'list_details': True},
+        local_borg_version=None,
+        export_tar_arguments=export_tar_arguments,
+        global_arguments=global_arguments,
+        local_path=None,
+        remote_path=None,
+    )
+
+
+def test_run_export_tar_defaults_to_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
+    flexmock(module.borgmatic.borg.export_tar).should_receive('export_tar_archive').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        tar_filter=object,
+        list_files=True,
+        strip_components=object,
+    ).once()
+    export_tar_arguments = flexmock(
+        repository=flexmock(),
+        archive=flexmock(),
+        paths=flexmock(),
+        destination=flexmock(),
+        tar_filter=flexmock(),
+        list_files=None,
+        strip_components=flexmock(),
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    module.run_export_tar(
+        repository={'path': 'repo'},
+        config={'list_details': True},
+        local_borg_version=None,
+        export_tar_arguments=export_tar_arguments,
+        global_arguments=global_arguments,
+        local_path=None,
+        remote_path=None,
+    )

+ 78 - 0
tests/unit/actions/test_extract.py

@@ -27,3 +27,81 @@ def test_run_extract_calls_hooks():
         local_path=None,
         remote_path=None,
     )
+
+
+def test_run_extract_favors_flags_over_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
+    flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        destination_path=object,
+        strip_components=object,
+        progress=False,
+    ).once()
+    extract_arguments = flexmock(
+        paths=flexmock(),
+        progress=False,
+        destination=flexmock(),
+        strip_components=flexmock(),
+        archive=flexmock(),
+        repository='repo',
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    module.run_extract(
+        config_filename='test.yaml',
+        repository={'path': 'repo'},
+        config={'repositories': ['repo'], 'progress': True},
+        local_borg_version=None,
+        extract_arguments=extract_arguments,
+        global_arguments=global_arguments,
+        local_path=None,
+        remote_path=None,
+    )
+
+
+def test_run_extract_defaults_to_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
+    flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        local_path=object,
+        remote_path=object,
+        destination_path=object,
+        strip_components=object,
+        progress=True,
+    ).once()
+    extract_arguments = flexmock(
+        paths=flexmock(),
+        progress=None,
+        destination=flexmock(),
+        strip_components=flexmock(),
+        archive=flexmock(),
+        repository='repo',
+    )
+    global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
+
+    module.run_extract(
+        config_filename='test.yaml',
+        repository={'path': 'repo'},
+        config={'repositories': ['repo'], 'progress': True},
+        local_borg_version=None,
+        extract_arguments=extract_arguments,
+        global_arguments=global_arguments,
+        local_path=None,
+        remote_path=None,
+    )

+ 88 - 0
tests/unit/actions/test_repo_create.py

@@ -105,3 +105,91 @@ def test_run_repo_create_bails_if_repository_does_not_match():
         local_path=None,
         remote_path=None,
     )
+
+
+def test_run_repo_create_favors_flags_over_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
+    flexmock(module.borgmatic.borg.repo_create).should_receive('create_repository').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        append_only=False,
+        storage_quota=0,
+        make_parent_dirs=False,
+        local_path=object,
+        remote_path=object,
+    ).once()
+    arguments = flexmock(
+        encryption_mode=flexmock(),
+        source_repository=flexmock(),
+        repository=flexmock(),
+        copy_crypt_key=flexmock(),
+        append_only=False,
+        storage_quota=0,
+        make_parent_dirs=False,
+    )
+
+    module.run_repo_create(
+        repository={
+            'path': 'repo',
+            'append_only': True,
+            'storage_quota': '10G',
+            'make_parent_dirs': True,
+        },
+        config={},
+        local_borg_version=None,
+        repo_create_arguments=arguments,
+        global_arguments=flexmock(dry_run=False),
+        local_path=None,
+        remote_path=None,
+    )
+
+
+def test_run_repo_create_defaults_to_config():
+    flexmock(module.logger).answer = lambda message: None
+    flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
+    flexmock(module.borgmatic.borg.repo_create).should_receive('create_repository').with_args(
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        object,
+        append_only=True,
+        storage_quota='10G',
+        make_parent_dirs=True,
+        local_path=object,
+        remote_path=object,
+    ).once()
+    arguments = flexmock(
+        encryption_mode=flexmock(),
+        source_repository=flexmock(),
+        repository=flexmock(),
+        copy_crypt_key=flexmock(),
+        append_only=None,
+        storage_quota=None,
+        make_parent_dirs=None,
+    )
+
+    module.run_repo_create(
+        repository={
+            'path': 'repo',
+            'append_only': True,
+            'storage_quota': '10G',
+            'make_parent_dirs': True,
+        },
+        config={},
+        local_borg_version=None,
+        repo_create_arguments=arguments,
+        global_arguments=flexmock(dry_run=False),
+        local_path=None,
+        remote_path=None,
+    )

+ 37 - 2
tests/unit/borg/test_check.py

@@ -331,8 +331,8 @@ def test_get_repository_id_with_missing_json_keys_raises():
         )
 
 
-def test_check_archives_with_progress_passes_through_to_borg():
-    config = {}
+def test_check_archives_favors_progress_flag_over_config():
+    config = {'progress': False}
     flexmock(module).should_receive('make_check_name_flags').with_args(
         {'repository'}, ()
     ).and_return(())
@@ -366,6 +366,41 @@ def test_check_archives_with_progress_passes_through_to_borg():
     )
 
 
+def test_check_archives_defaults_to_progress_config():
+    config = {'progress': True}
+    flexmock(module).should_receive('make_check_name_flags').with_args(
+        {'repository'}, ()
+    ).and_return(())
+    flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
+    flexmock(module.environment).should_receive('make_environment')
+    flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'check', '--progress', 'repo'),
+        output_file=module.DO_NOT_CAPTURE,
+        environment=None,
+        working_directory=None,
+        borg_local_path='borg',
+        borg_exit_codes=None,
+    ).once()
+
+    module.check_archives(
+        repository_path='repo',
+        config=config,
+        local_borg_version='1.2.3',
+        check_arguments=flexmock(
+            progress=None,
+            repair=None,
+            only_checks=None,
+            force=None,
+            match_archives=None,
+            max_duration=None,
+        ),
+        global_arguments=flexmock(log_json=False),
+        checks={'repository'},
+        archive_filter_flags=(),
+    )
+
+
 def test_check_archives_with_repair_passes_through_to_borg():
     config = {}
     flexmock(module).should_receive('make_check_name_flags').with_args(

+ 34 - 2
tests/unit/borg/test_delete.py

@@ -171,7 +171,7 @@ def test_make_delete_command_includes_lock_wait():
     assert command == ('borg', 'delete', '--lock-wait', '5', 'repo')
 
 
-def test_make_delete_command_includes_list():
+def test_make_delete_command_favors_list_flag_over_config():
     flexmock(module.borgmatic.borg.flags).should_receive('make_flags').and_return(())
     flexmock(module.borgmatic.borg.flags).should_receive('make_flags').with_args(
         'list', True
@@ -184,7 +184,7 @@ def test_make_delete_command_includes_list():
 
     command = module.make_delete_command(
         repository={'path': 'repo'},
-        config={},
+        config={'list_details': False},
         local_borg_version='1.2.3',
         delete_arguments=flexmock(list_archives=True, force=0, match_archives=None, archive=None),
         global_arguments=flexmock(dry_run=False, log_json=False),
@@ -195,6 +195,30 @@ def test_make_delete_command_includes_list():
     assert command == ('borg', 'delete', '--list', 'repo')
 
 
+def test_make_delete_command_defaults_to_list_config():
+    flexmock(module.borgmatic.borg.flags).should_receive('make_flags').and_return(())
+    flexmock(module.borgmatic.borg.flags).should_receive('make_flags').with_args(
+        'list', True
+    ).and_return(('--list',))
+    flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
+    flexmock(module.borgmatic.borg.flags).should_receive('make_flags_from_arguments').and_return(())
+    flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return(
+        ('repo',)
+    )
+
+    command = module.make_delete_command(
+        repository={'path': 'repo'},
+        config={'list_details': True},
+        local_borg_version='1.2.3',
+        delete_arguments=flexmock(list_archives=None, force=0, match_archives=None, archive=None),
+        global_arguments=flexmock(dry_run=False, log_json=False),
+        local_path='borg',
+        remote_path=None,
+    )
+
+    assert command == ('borg', 'delete', '--list', 'repo')
+
+
 def test_make_delete_command_includes_force():
     flexmock(module.borgmatic.borg.flags).should_receive('make_flags').and_return(())
     flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(())
@@ -287,8 +311,12 @@ def test_make_delete_command_includes_match_archives():
     assert command == ('borg', 'delete', '--match-archives', 'sh:foo*', 'repo')
 
 
+LOGGING_ANSWER = flexmock()
+
+
 def test_delete_archives_with_archive_calls_borg_delete():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = LOGGING_ANSWER
     flexmock(module.borgmatic.borg.repo_delete).should_receive('delete_repository').never()
     flexmock(module).should_receive('make_delete_command').and_return(flexmock())
     flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return(
@@ -308,6 +336,7 @@ def test_delete_archives_with_archive_calls_borg_delete():
 
 def test_delete_archives_with_match_archives_calls_borg_delete():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = LOGGING_ANSWER
     flexmock(module.borgmatic.borg.repo_delete).should_receive('delete_repository').never()
     flexmock(module).should_receive('make_delete_command').and_return(flexmock())
     flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return(
@@ -328,6 +357,7 @@ def test_delete_archives_with_match_archives_calls_borg_delete():
 @pytest.mark.parametrize('argument_name', module.ARCHIVE_RELATED_ARGUMENT_NAMES[2:])
 def test_delete_archives_with_archive_related_argument_calls_borg_delete(argument_name):
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = LOGGING_ANSWER
     flexmock(module.borgmatic.borg.repo_delete).should_receive('delete_repository').never()
     flexmock(module).should_receive('make_delete_command').and_return(flexmock())
     flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return(
@@ -347,6 +377,7 @@ def test_delete_archives_with_archive_related_argument_calls_borg_delete(argumen
 
 def test_delete_archives_without_archive_related_argument_calls_borg_repo_delete():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = LOGGING_ANSWER
     flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True)
     flexmock(module.borgmatic.borg.repo_delete).should_receive('delete_repository').once()
     flexmock(module).should_receive('make_delete_command').never()
@@ -367,6 +398,7 @@ def test_delete_archives_without_archive_related_argument_calls_borg_repo_delete
 
 def test_delete_archives_calls_borg_delete_with_working_directory():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = LOGGING_ANSWER
     flexmock(module.borgmatic.borg.repo_delete).should_receive('delete_repository').never()
     command = flexmock()
     flexmock(module).should_receive('make_delete_command').and_return(command)

+ 46 - 4
tests/unit/borg/test_prune.py

@@ -361,7 +361,7 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_flags():
     )
 
 
-def test_prune_archives_with_stats_calls_borg_with_stats_flag_and_answer_output_log_level():
+def test_prune_archives_favors_stats_flag_over_config():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
     flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
@@ -375,14 +375,35 @@ def test_prune_archives_with_stats_calls_borg_with_stats_flag_and_answer_output_
     module.prune_archives(
         dry_run=False,
         repository_path='repo',
-        config={},
+        config={'statistics': False},
+        local_borg_version='1.2.3',
+        global_arguments=flexmock(log_json=False),
+        prune_arguments=prune_arguments,
+    )
+
+
+def test_prune_archives_defaults_to_stats_config():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
+    flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
+    flexmock(module.feature).should_receive('available').with_args(
+        module.feature.Feature.NO_PRUNE_STATS, '1.2.3'
+    ).and_return(False)
+    insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), module.borgmatic.logger.ANSWER)
+
+    prune_arguments = flexmock(stats=None, list_archives=False)
+    module.prune_archives(
+        dry_run=False,
+        repository_path='repo',
+        config={'statistics': True},
         local_borg_version='1.2.3',
         global_arguments=flexmock(log_json=False),
         prune_arguments=prune_arguments,
     )
 
 
-def test_prune_archives_with_files_calls_borg_with_list_flag_and_answer_output_log_level():
+def test_prune_archives_favors_list_flag_over_config():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
     flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
@@ -396,7 +417,28 @@ def test_prune_archives_with_files_calls_borg_with_list_flag_and_answer_output_l
     module.prune_archives(
         dry_run=False,
         repository_path='repo',
-        config={},
+        config={'list_details': False},
+        local_borg_version='1.2.3',
+        global_arguments=flexmock(log_json=False),
+        prune_arguments=prune_arguments,
+    )
+
+
+def test_prune_archives_defaults_to_list_config():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
+    flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
+    flexmock(module.feature).should_receive('available').with_args(
+        module.feature.Feature.NO_PRUNE_STATS, '1.2.3'
+    ).and_return(False)
+    insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), module.borgmatic.logger.ANSWER)
+
+    prune_arguments = flexmock(stats=False, list_archives=None)
+    module.prune_archives(
+        dry_run=False,
+        repository_path='repo',
+        config={'list_details': True},
         local_borg_version='1.2.3',
         global_arguments=flexmock(log_json=False),
         prune_arguments=prune_arguments,

+ 33 - 2
tests/unit/borg/test_transfer.py

@@ -436,7 +436,7 @@ def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
     )
 
 
-def test_transfer_archives_with_progress_calls_borg_with_progress_flag():
+def test_transfer_archives_favors_progress_flag_over_config():
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
     flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
     flexmock(module.flags).should_receive('make_flags').and_return(())
@@ -458,7 +458,7 @@ def test_transfer_archives_with_progress_calls_borg_with_progress_flag():
     module.transfer_archives(
         dry_run=False,
         repository_path='repo',
-        config={},
+        config={'progress': False},
         local_borg_version='2.3.4',
         transfer_arguments=flexmock(
             archive=None, progress=True, match_archives=None, source_repository=None
@@ -467,6 +467,37 @@ def test_transfer_archives_with_progress_calls_borg_with_progress_flag():
     )
 
 
+def test_transfer_archives_defaults_to_progress_flag():
+    flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
+    flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
+    flexmock(module.flags).should_receive('make_flags').and_return(())
+    flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
+    flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--progress',))
+    flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
+    flexmock(module.environment).should_receive('make_environment')
+    flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
+    flexmock(module).should_receive('execute_command').with_args(
+        ('borg', 'transfer', '--progress', '--repo', 'repo'),
+        output_log_level=module.borgmatic.logger.ANSWER,
+        output_file=module.DO_NOT_CAPTURE,
+        environment=None,
+        working_directory=None,
+        borg_local_path='borg',
+        borg_exit_codes=None,
+    )
+
+    module.transfer_archives(
+        dry_run=False,
+        repository_path='repo',
+        config={'progress': True},
+        local_borg_version='2.3.4',
+        transfer_arguments=flexmock(
+            archive=None, progress=None, match_archives=None, source_repository=None
+        ),
+        global_arguments=flexmock(log_json=False),
+    )
+
+
 @pytest.mark.parametrize('argument_name', ('upgrader', 'sort_by', 'first', 'last'))
 def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
     flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')