diff --git a/borgmatic/actions/diff.py b/borgmatic/actions/diff.py index a40c4a5a..32b0cf5b 100644 --- a/borgmatic/actions/diff.py +++ b/borgmatic/actions/diff.py @@ -38,10 +38,20 @@ def run_diff( local_path, remote_path, ) + second_archive = borgmatic.borg.repo_list.resolve_archive_name( + repository['path'], + diff_arguments.second_archive, + config, + local_borg_version, + global_arguments, + local_path, + remote_path, + ) borgmatic.borg.diff.diff( repository['path'], archive, + second_archive, config, local_borg_version, diff_arguments, diff --git a/borgmatic/borg/diff.py b/borgmatic/borg/diff.py index 27b68d8b..8bff7429 100644 --- a/borgmatic/borg/diff.py +++ b/borgmatic/borg/diff.py @@ -14,6 +14,7 @@ logger = logging.getLogger(__name__) def diff( repository, archive, + second_archive, config, local_borg_version, diff_arguments, @@ -23,7 +24,7 @@ def diff( patterns=None, ): ''' - Given a local or remote repository path, an archive name, a configuration dict, the local Borg + Given a local or remote repository path, two archive names, a configuration dict, the local Borg version string, an argparse.Namespace of diff arguments, an argparse.Namespace of global arguments, optional local and remote Borg paths, executes the diff command with the given arguments. @@ -64,26 +65,19 @@ def diff( ) + exclude_flags + numeric_ids_flags + + (('--same-chunker-params',) if diff_arguments.same_chunker_params else ()) + + (('--sort-by', ','.join(diff_arguments.sort_keys)) if diff_arguments.sort_keys else ()) + + (('--content-only',) if diff_arguments.content_only else ()) + (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ()) + ( - ( - flags.make_repository_flags(repository, local_borg_version) - + flags.make_match_archives_flags( - archive or config.get('match_archives'), - config.get('archive_name_format'), - local_borg_version, - ) - ) + (*flags.make_repository_flags(repository, local_borg_version), archive) if borgmatic.borg.feature.available( borgmatic.borg.feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version, ) else flags.make_repository_archive_flags(repository, archive, local_borg_version) ) - + (('--same-chunker-params',) if diff_arguments.same_chunker_params else ()) - + (('--sort-by', ','.join(diff_arguments.sort_keys)) if diff_arguments.sort_keys else ()) - + (('--content-only',) if diff_arguments.content_only else ()) - + (diff_arguments.second_archive,) + + (second_archive,) ) borgmatic.execute.execute_command( diff --git a/docs/how-to/inspect-your-backups.md b/docs/how-to/inspect-your-backups.md index 1a151f90..02997c9f 100644 --- a/docs/how-to/inspect-your-backups.md +++ b/docs/how-to/inspect-your-backups.md @@ -133,5 +133,5 @@ Note that, by default, `borgmatic diff` compares everything in the archives; tha is, patterns are _not_ taken into consideration. If you require this, supply the `--only-patterns` flag. -See the [`borgbackup`](https://borgbackup.readthedocs.io/en/stable/usage/diff.html) +See the [Borg](https://borgbackup.readthedocs.io/en/stable/usage/diff.html) documentation for information on output format, what is compared, and more. diff --git a/tests/unit/actions/test_diff.py b/tests/unit/actions/test_diff.py index ab1bf71e..8b2824a2 100644 --- a/tests/unit/actions/test_diff.py +++ b/tests/unit/actions/test_diff.py @@ -6,7 +6,7 @@ from borgmatic.actions import diff as module def test_run_diff_calls_borg_diff(): flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'archive' - ) + ).and_return('archive2') flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return( flexmock(), ) @@ -42,7 +42,7 @@ def test_run_diff_with_only_patterns(): ) flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return( 'archive' - ) + ).and_return('archive2') flexmock(module.borgmatic.borg.diff).should_receive('diff').once() module.borgmatic.actions.diff.run_diff( diff --git a/tests/unit/borg/test_diff.py b/tests/unit/borg/test_diff.py index 2828e630..040a94ee 100644 --- a/tests/unit/borg/test_diff.py +++ b/tests/unit/borg/test_diff.py @@ -4,136 +4,37 @@ from flexmock import flexmock from borgmatic.borg import diff as module +LOGGING_ANSWER = flexmock() -def test_diff_calls_execute_command(): - flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(False) - flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive( - 'make_repository_archive_flags' - ).and_return(()) + +def test_diff_calls_borg_with_archives(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( flexmock(name='test') ) - flexmock(module.borgmatic.execute).should_receive('execute_command').once() - flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return({}) - - module.borgmatic.borg.diff.diff( - repository='repo', - archive='archive', - config={}, - local_borg_version=None, - diff_arguments=flexmock( - same_chunker_params=False, - sort_keys=[], - content_only=False, - second_archive=None, - only_patterns=False, - ), - global_arguments=flexmock(), - local_path='borg', - remote_path=None, - patterns=[], - ) - - -def test_diff_with_numeric_ids_flag(): flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) - flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive( - 'make_repository_archive_flags' - ).and_return(()) - flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( - flexmock(name='test') - ) - flexmock(module.borgmatic.execute).should_receive('execute_command').once() - flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return({}) - - module.borgmatic.borg.diff.diff( - repository='repo', - archive='archive', - config={'numeric_ids': True}, - local_borg_version=None, - diff_arguments=flexmock( - same_chunker_params=False, - sort_keys=[], - content_only=False, - second_archive=None, - only_patterns=False, - ), - global_arguments=flexmock(), - local_path='borg', - remote_path=None, - patterns=[], - ) - - -def test_diff_with_numeric_ids_flag_false(): - flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) - flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive( - 'make_repository_archive_flags' - ).and_return(()) - flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( - flexmock(name='test') - ) - flexmock(module.borgmatic.execute).should_receive('execute_command').once() - flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return({}) - - module.borgmatic.borg.diff.diff( - repository='repo', - archive='archive', - config={'numeric_ids': False}, - local_borg_version=None, - diff_arguments=flexmock( - same_chunker_params=False, - sort_keys=[], - content_only=False, - second_archive=None, - only_patterns=False, - ), - global_arguments=flexmock(), - local_path='borg', - remote_path=None, - patterns=[], - ) - - -def test_diff_with_only_patterns(): - # Mock the feature check - flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) - flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( ('--repo', 'repo') ) - flexmock(module.borgmatic.borg.flags).should_receive('make_match_archives_flags').and_return(()) - flexmock(module.borgmatic.borg.flags).should_receive( - 'make_repository_archive_flags' - ).and_return(()) - + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() environment = flexmock() flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( environment ) - - flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( - '/tmp/test_patterns' - ) - - expected_command = ( - 'borg', - 'diff', - '--log-json', # Add this flag - '--repo', - 'repo', - None, - ) - flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( - full_command=expected_command, - output_log_level=logging.ANSWER, + full_command=( + 'borg', + 'diff', + '--log-json', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, environment=environment, working_directory=None, borg_local_path='borg', @@ -143,13 +44,350 @@ def test_diff_with_only_patterns(): module.borgmatic.borg.diff.diff( repository='repo', archive='archive', + second_archive='archive2', config={}, local_borg_version=None, diff_arguments=flexmock( same_chunker_params=False, sort_keys=[], content_only=False, - second_archive=None, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_local_path_calls_borg_with_it(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg6', + 'diff', + '--log-json', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg6', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg6', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_remote_path_calls_borg_with_it(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--remote-path', + 'borg7', + '--log-json', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path='borg7', + patterns=[], + ) + + +def test_diff_with_lock_wait_calls_borg_with_it(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--lock-wait', + '5', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={'lock_wait': 5}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_log_level_info_calls_borg_with_info_flag(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.INFO) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--info', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_log_level_debug_calls_borg_with_debug_flags(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.logger).should_receive('isEnabledFor').and_return(logging.DEBUG) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--debug', + '--show-rc', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_only_patterns_calls_borg_with_configured_pattern_paths(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + flexmock(module).should_receive('write_patterns_file').and_return( + flexmock(name='/tmp/test_patterns') + ).once() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--patterns-from', + '/tmp/test_patterns', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', only_patterns=True, ), global_arguments=flexmock(), @@ -157,3 +395,455 @@ def test_diff_with_only_patterns(): remote_path=None, patterns=[], ) + + +def test_diff_with_exclude_config_calls_borg_with_exclude_flags(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return( + ('--exclude', 'stuff') + ) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--exclude', + 'stuff', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_numeric_ids_calls_borg_with_numeric_ids_flag(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--numeric-ids', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={'numeric_ids': True}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_numeric_ids_and_feature_not_available_calls_borg_with_numeric_owner_flag(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').with_args( + module.borgmatic.borg.feature.Feature.NUMERIC_IDS, object + ).and_return(False) + flexmock(module.borgmatic.borg.feature).should_receive('available').with_args( + module.borgmatic.borg.feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, object + ).and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--numeric-owner', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={'numeric_ids': True}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_same_chunker_params_calls_borg_with_it(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--same-chunker-params', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=True, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_sort_keys_calls_borg_with_formatted_sort_by_flags(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--sort-by', + 'foo,bar,baz', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=['foo', 'bar', 'baz'], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_content_only_calls_borg_with_it(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--content-only', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=True, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_with_extra_borg_options_calls_borg_with_them(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').and_return(True) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').and_return( + ('--repo', 'repo') + ) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_archive_flags').never() + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + '--extra', + '--option', + '--repo', + 'repo', + 'archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={'extra_borg_options': {'diff': '--extra --option'}}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + ) + + +def test_diff_without_separate_repository_archive_feature_available_calls_borg_joined_repository_archive(): + flexmock(module.logging).ANSWER = LOGGING_ANSWER + flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') + flexmock(module.borgmatic.borg.flags).should_receive('make_exclude_flags').and_return(()) + flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None) + flexmock(module.borgmatic.borg.pattern).should_receive('write_patterns_file').and_return( + flexmock(name='test') + ) + flexmock(module.borgmatic.borg.feature).should_receive('available').with_args( + module.borgmatic.borg.feature.Feature.NUMERIC_IDS, object + ).and_return(True) + flexmock(module.borgmatic.borg.feature).should_receive('available').with_args( + module.borgmatic.borg.feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, object + ).and_return(False) + flexmock(module.borgmatic.borg.flags).should_receive('make_repository_flags').never() + flexmock(module.borgmatic.borg.flags).should_receive( + 'make_repository_archive_flags' + ).and_return(('repo::archive',)) + environment = flexmock() + flexmock(module.borgmatic.borg.environment).should_receive('make_environment').and_return( + environment + ) + flexmock(module.borgmatic.execute).should_receive('execute_command').with_args( + full_command=( + 'borg', + 'diff', + '--log-json', + 'repo::archive', + 'archive2', + ), + output_log_level=LOGGING_ANSWER, + environment=environment, + working_directory=None, + borg_local_path='borg', + borg_exit_codes=None, + ).once() + + module.borgmatic.borg.diff.diff( + repository='repo', + archive='archive', + second_archive='archive2', + config={}, + local_borg_version=None, + diff_arguments=flexmock( + same_chunker_params=False, + sort_keys=[], + content_only=False, + second_archive='archive2', + only_patterns=False, + ), + global_arguments=flexmock(), + local_path='borg', + remote_path=None, + patterns=[], + )