test_create.py 70 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716
  1. import logging
  2. import sys
  3. import pytest
  4. from flexmock import flexmock
  5. from borgmatic.borg import create as module
  6. from ..test_verbosity import insert_logging_mock
  7. def test_expand_home_directories_expands_tildes():
  8. flexmock(module.os.path).should_receive('expanduser').with_args('~/bar').and_return('/foo/bar')
  9. flexmock(module.os.path).should_receive('expanduser').with_args('baz').and_return('baz')
  10. paths = module.expand_home_directories(('~/bar', 'baz'))
  11. assert paths == ('/foo/bar', 'baz')
  12. def test_expand_home_directories_considers_none_as_no_directories():
  13. paths = module.expand_home_directories(None)
  14. assert paths == ()
  15. def test_write_pattern_file_writes_pattern_lines():
  16. temporary_file = flexmock(name='filename', flush=lambda: None)
  17. temporary_file.should_receive('write').with_args('R /foo\n+ /foo/bar')
  18. flexmock(module.tempfile).should_receive('NamedTemporaryFile').and_return(temporary_file)
  19. module.write_pattern_file(['R /foo', '+ /foo/bar'])
  20. def test_write_pattern_file_with_sources_writes_sources_as_roots():
  21. temporary_file = flexmock(name='filename', flush=lambda: None)
  22. temporary_file.should_receive('write').with_args('R /foo\n+ /foo/bar\nR /baz\nR /quux')
  23. flexmock(module.tempfile).should_receive('NamedTemporaryFile').and_return(temporary_file)
  24. module.write_pattern_file(['R /foo', '+ /foo/bar'], sources=['/baz', '/quux'])
  25. def test_write_pattern_file_without_patterns_but_with_sources_writes_sources_as_roots():
  26. temporary_file = flexmock(name='filename', flush=lambda: None)
  27. temporary_file.should_receive('write').with_args('R /baz\nR /quux')
  28. flexmock(module.tempfile).should_receive('NamedTemporaryFile').and_return(temporary_file)
  29. module.write_pattern_file([], sources=['/baz', '/quux'])
  30. def test_write_pattern_file_with_empty_exclude_patterns_does_not_raise():
  31. module.write_pattern_file([])
  32. def test_write_pattern_file_overwrites_existing_file():
  33. pattern_file = flexmock(name='filename', flush=lambda: None)
  34. pattern_file.should_receive('seek').with_args(0).once()
  35. pattern_file.should_receive('write').with_args('R /foo\n+ /foo/bar')
  36. flexmock(module.tempfile).should_receive('NamedTemporaryFile').never()
  37. module.write_pattern_file(['R /foo', '+ /foo/bar'], pattern_file=pattern_file)
  38. @pytest.mark.parametrize(
  39. 'filename_lists,opened_filenames',
  40. (
  41. ([('foo', 'bar'), ('baz', 'quux')], ('foo', 'bar', 'baz', 'quux')),
  42. ([None, ('foo', 'bar')], ('foo', 'bar')),
  43. ([None, None], ()),
  44. ),
  45. )
  46. def test_ensure_files_readable_opens_filenames(filename_lists, opened_filenames):
  47. for expected_filename in opened_filenames:
  48. flexmock(sys.modules['builtins']).should_receive('open').with_args(
  49. expected_filename
  50. ).and_return(flexmock(close=lambda: None))
  51. module.ensure_files_readable(*filename_lists)
  52. def test_make_pattern_flags_includes_pattern_filename_when_given():
  53. pattern_flags = module.make_pattern_flags(
  54. config={'patterns': ['R /', '- /var']}, pattern_filename='/tmp/patterns'
  55. )
  56. assert pattern_flags == ('--patterns-from', '/tmp/patterns')
  57. def test_make_pattern_flags_includes_patterns_from_filenames_when_in_config():
  58. pattern_flags = module.make_pattern_flags(config={'patterns_from': ['patterns', 'other']})
  59. assert pattern_flags == ('--patterns-from', 'patterns', '--patterns-from', 'other')
  60. def test_make_pattern_flags_includes_both_filenames_when_patterns_given_and_patterns_from_in_config():
  61. pattern_flags = module.make_pattern_flags(
  62. config={'patterns_from': ['patterns']}, pattern_filename='/tmp/patterns'
  63. )
  64. assert pattern_flags == ('--patterns-from', 'patterns', '--patterns-from', '/tmp/patterns')
  65. def test_make_pattern_flags_considers_none_patterns_from_filenames_as_empty():
  66. pattern_flags = module.make_pattern_flags(config={'patterns_from': None})
  67. assert pattern_flags == ()
  68. def test_make_exclude_flags_includes_exclude_patterns_filename_when_given():
  69. exclude_flags = module.make_exclude_flags(
  70. config={'exclude_patterns': ['*.pyc', '/var']}, exclude_filename='/tmp/excludes'
  71. )
  72. assert exclude_flags == ('--exclude-from', '/tmp/excludes')
  73. def test_make_exclude_flags_includes_exclude_from_filenames_when_in_config():
  74. exclude_flags = module.make_exclude_flags(config={'exclude_from': ['excludes', 'other']})
  75. assert exclude_flags == ('--exclude-from', 'excludes', '--exclude-from', 'other')
  76. def test_make_exclude_flags_includes_both_filenames_when_patterns_given_and_exclude_from_in_config():
  77. exclude_flags = module.make_exclude_flags(
  78. config={'exclude_from': ['excludes']}, exclude_filename='/tmp/excludes'
  79. )
  80. assert exclude_flags == ('--exclude-from', 'excludes', '--exclude-from', '/tmp/excludes')
  81. def test_make_exclude_flags_considers_none_exclude_from_filenames_as_empty():
  82. exclude_flags = module.make_exclude_flags(config={'exclude_from': None})
  83. assert exclude_flags == ()
  84. def test_make_exclude_flags_includes_exclude_caches_when_true_in_config():
  85. exclude_flags = module.make_exclude_flags(config={'exclude_caches': True})
  86. assert exclude_flags == ('--exclude-caches',)
  87. def test_make_exclude_flags_does_not_include_exclude_caches_when_false_in_config():
  88. exclude_flags = module.make_exclude_flags(config={'exclude_caches': False})
  89. assert exclude_flags == ()
  90. def test_make_exclude_flags_includes_exclude_if_present_when_in_config():
  91. exclude_flags = module.make_exclude_flags(
  92. config={'exclude_if_present': ['exclude_me', 'also_me']}
  93. )
  94. assert exclude_flags == (
  95. '--exclude-if-present',
  96. 'exclude_me',
  97. '--exclude-if-present',
  98. 'also_me',
  99. )
  100. def test_make_exclude_flags_includes_keep_exclude_tags_when_true_in_config():
  101. exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': True})
  102. assert exclude_flags == ('--keep-exclude-tags',)
  103. def test_make_exclude_flags_does_not_include_keep_exclude_tags_when_false_in_config():
  104. exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': False})
  105. assert exclude_flags == ()
  106. def test_make_exclude_flags_includes_exclude_nodump_when_true_in_config():
  107. exclude_flags = module.make_exclude_flags(config={'exclude_nodump': True})
  108. assert exclude_flags == ('--exclude-nodump',)
  109. def test_make_exclude_flags_does_not_include_exclude_nodump_when_false_in_config():
  110. exclude_flags = module.make_exclude_flags(config={'exclude_nodump': False})
  111. assert exclude_flags == ()
  112. def test_make_exclude_flags_is_empty_when_config_has_no_excludes():
  113. exclude_flags = module.make_exclude_flags(config={})
  114. assert exclude_flags == ()
  115. def test_make_list_filter_flags_with_debug_and_feature_available_includes_plus_and_minus():
  116. flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
  117. flexmock(module.feature).should_receive('available').and_return(True)
  118. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME+-'
  119. def test_make_list_filter_flags_with_info_and_feature_available_omits_plus_and_minus():
  120. flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
  121. flexmock(module.feature).should_receive('available').and_return(True)
  122. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME'
  123. def test_make_list_filter_flags_with_debug_and_feature_available_and_dry_run_includes_plus_and_minus():
  124. flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
  125. flexmock(module.feature).should_receive('available').and_return(True)
  126. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
  127. def test_make_list_filter_flags_with_info_and_feature_available_and_dry_run_includes_plus_and_minus():
  128. flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
  129. flexmock(module.feature).should_receive('available').and_return(True)
  130. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
  131. def test_make_list_filter_flags_with_debug_and_feature_not_available_includes_x():
  132. flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
  133. flexmock(module.feature).should_receive('available').and_return(False)
  134. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AMEx-'
  135. def test_make_list_filter_flags_with_info_and_feature_not_available_omits_x():
  136. flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
  137. flexmock(module.feature).should_receive('available').and_return(False)
  138. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME-'
  139. @pytest.mark.parametrize(
  140. 'character_device,block_device,fifo,expected_result',
  141. (
  142. (False, False, False, False),
  143. (True, False, False, True),
  144. (False, True, False, True),
  145. (True, True, False, True),
  146. (False, False, True, True),
  147. (False, True, True, True),
  148. (True, False, True, True),
  149. ),
  150. )
  151. def test_special_file_looks_at_file_type(character_device, block_device, fifo, expected_result):
  152. flexmock(module.os).should_receive('stat').and_return(flexmock(st_mode=flexmock()))
  153. flexmock(module.stat).should_receive('S_ISCHR').and_return(character_device)
  154. flexmock(module.stat).should_receive('S_ISBLK').and_return(block_device)
  155. flexmock(module.stat).should_receive('S_ISFIFO').and_return(fifo)
  156. assert module.special_file('/dev/special') == expected_result
  157. def test_special_file_treats_broken_symlink_as_non_special():
  158. flexmock(module.os).should_receive('stat').and_raise(FileNotFoundError)
  159. assert module.special_file('/broken/symlink') is False
  160. def test_any_parent_directories_treats_parents_as_match():
  161. module.any_parent_directories('/foo/bar.txt', ('/foo', '/etc'))
  162. def test_any_parent_directories_treats_grandparents_as_match():
  163. module.any_parent_directories('/foo/bar/baz.txt', ('/foo', '/etc'))
  164. def test_any_parent_directories_treats_unrelated_paths_as_non_match():
  165. module.any_parent_directories('/foo/bar.txt', ('/usr', '/etc'))
  166. def test_collect_special_file_paths_parses_special_files_from_borg_dry_run_file_list():
  167. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  168. 'Processing files ...\n- /foo\n+ /bar\n- /baz'
  169. )
  170. flexmock(module).should_receive('special_file').and_return(True)
  171. flexmock(module.os.path).should_receive('exists').and_return(False)
  172. flexmock(module).should_receive('any_parent_directories').never()
  173. assert module.collect_special_file_paths(
  174. ('borg', 'create'),
  175. config={},
  176. local_path=None,
  177. working_directory=None,
  178. borg_environment=None,
  179. borgmatic_runtime_directory='/run/borgmatic',
  180. ) == ('/foo', '/bar', '/baz')
  181. def test_collect_special_file_paths_skips_borgmatic_runtime_directory():
  182. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  183. '+ /foo\n- /run/borgmatic/bar\n- /baz'
  184. )
  185. flexmock(module).should_receive('special_file').and_return(True)
  186. flexmock(module.os.path).should_receive('exists').and_return(True)
  187. flexmock(module).should_receive('any_parent_directories').with_args(
  188. '/foo', ('/run/borgmatic',)
  189. ).and_return(False)
  190. flexmock(module).should_receive('any_parent_directories').with_args(
  191. '/run/borgmatic/bar', ('/run/borgmatic',)
  192. ).and_return(True)
  193. flexmock(module).should_receive('any_parent_directories').with_args(
  194. '/baz', ('/run/borgmatic',)
  195. ).and_return(False)
  196. assert module.collect_special_file_paths(
  197. ('borg', 'create'),
  198. config={},
  199. local_path=None,
  200. working_directory=None,
  201. borg_environment=None,
  202. borgmatic_runtime_directory='/run/borgmatic',
  203. ) == ('/foo', '/baz')
  204. def test_collect_special_file_paths_with_borgmatic_runtime_directory_missing_from_paths_output_errors():
  205. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  206. '+ /foo\n- /bar\n- /baz'
  207. )
  208. flexmock(module).should_receive('special_file').and_return(True)
  209. flexmock(module.os.path).should_receive('exists').and_return(True)
  210. flexmock(module).should_receive('any_parent_directories').and_return(False)
  211. with pytest.raises(ValueError):
  212. module.collect_special_file_paths(
  213. ('borg', 'create'),
  214. config={},
  215. local_path=None,
  216. working_directory=None,
  217. borg_environment=None,
  218. borgmatic_runtime_directory='/run/borgmatic',
  219. )
  220. def test_collect_special_file_paths_excludes_non_special_files():
  221. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  222. '+ /foo\n+ /bar\n+ /baz'
  223. )
  224. flexmock(module).should_receive('special_file').and_return(True).and_return(False).and_return(
  225. True
  226. )
  227. flexmock(module.os.path).should_receive('exists').and_return(False)
  228. flexmock(module).should_receive('any_parent_directories').never()
  229. assert module.collect_special_file_paths(
  230. ('borg', 'create'),
  231. config={},
  232. local_path=None,
  233. working_directory=None,
  234. borg_environment=None,
  235. borgmatic_runtime_directory='/run/borgmatic',
  236. ) == ('/foo', '/baz')
  237. def test_collect_special_file_paths_omits_exclude_no_dump_flag_from_command():
  238. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  239. ('borg', 'create', '--dry-run', '--list'),
  240. capture_stderr=True,
  241. working_directory=None,
  242. extra_environment=None,
  243. borg_local_path='borg',
  244. borg_exit_codes=None,
  245. ).and_return('Processing files ...\n- /foo\n+ /bar\n- /baz').once()
  246. flexmock(module).should_receive('special_file').and_return(True)
  247. flexmock(module.os.path).should_receive('exists').and_return(False)
  248. flexmock(module).should_receive('any_parent_directories').never()
  249. module.collect_special_file_paths(
  250. ('borg', 'create', '--exclude-nodump'),
  251. config={},
  252. local_path='borg',
  253. working_directory=None,
  254. borg_environment=None,
  255. borgmatic_runtime_directory='/run/borgmatic',
  256. )
  257. DEFAULT_ARCHIVE_NAME = '{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}' # noqa: FS003
  258. REPO_ARCHIVE_WITH_PATHS = (f'repo::{DEFAULT_ARCHIVE_NAME}', 'foo', 'bar')
  259. def test_make_base_create_produces_borg_command():
  260. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  261. flexmock(module).should_receive('expand_home_directories').and_return(())
  262. flexmock(module).should_receive('ensure_files_readable')
  263. flexmock(module).should_receive('write_pattern_file').and_return(None)
  264. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  265. flexmock(module.feature).should_receive('available').and_return(True)
  266. flexmock(module).should_receive('make_pattern_flags').and_return(())
  267. flexmock(module).should_receive('make_exclude_flags').and_return(())
  268. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  269. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  270. )
  271. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  272. module.make_base_create_command(
  273. dry_run=False,
  274. repository_path='repo',
  275. config={
  276. 'source_directories': ['foo', 'bar'],
  277. 'repositories': ['repo'],
  278. },
  279. source_directories=['foo', 'bar'],
  280. local_borg_version='1.2.3',
  281. global_arguments=flexmock(log_json=False),
  282. borgmatic_runtime_directory='/run/borgmatic',
  283. )
  284. )
  285. assert create_flags == ('borg', 'create')
  286. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  287. assert not pattern_file
  288. assert not exclude_file
  289. def test_make_base_create_command_includes_patterns_file_in_borg_command():
  290. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  291. flexmock(module).should_receive('expand_home_directories').and_return(())
  292. mock_pattern_file = flexmock(name='/tmp/patterns')
  293. flexmock(module).should_receive('write_pattern_file').and_return(mock_pattern_file).and_return(
  294. None
  295. )
  296. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  297. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  298. '{hostname}'
  299. )
  300. flexmock(module.feature).should_receive('available').and_return(True)
  301. flexmock(module).should_receive('ensure_files_readable')
  302. pattern_flags = ('--patterns-from', mock_pattern_file.name)
  303. flexmock(module).should_receive('make_pattern_flags').and_return(pattern_flags)
  304. flexmock(module).should_receive('make_exclude_flags').and_return(())
  305. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  306. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  307. )
  308. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  309. module.make_base_create_command(
  310. dry_run=False,
  311. repository_path='repo',
  312. config={
  313. 'source_directories': ['foo', 'bar'],
  314. 'repositories': ['repo'],
  315. 'patterns': ['pattern'],
  316. },
  317. source_directories=['foo', 'bar'],
  318. local_borg_version='1.2.3',
  319. global_arguments=flexmock(log_json=False),
  320. borgmatic_runtime_directory='/run/borgmatic',
  321. )
  322. )
  323. assert create_flags == ('borg', 'create') + pattern_flags
  324. assert create_positional_arguments == (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  325. assert pattern_file == mock_pattern_file
  326. assert not exclude_file
  327. def test_make_base_create_command_with_store_config_false_omits_config_files():
  328. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  329. flexmock(module).should_receive('expand_home_directories').and_return(())
  330. flexmock(module).should_receive('write_pattern_file').and_return(None)
  331. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  332. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  333. '{hostname}'
  334. )
  335. flexmock(module.feature).should_receive('available').and_return(True)
  336. flexmock(module).should_receive('ensure_files_readable')
  337. flexmock(module).should_receive('make_pattern_flags').and_return(())
  338. flexmock(module).should_receive('make_exclude_flags').and_return(())
  339. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  340. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  341. )
  342. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  343. module.make_base_create_command(
  344. dry_run=False,
  345. repository_path='repo',
  346. config={
  347. 'source_directories': ['foo', 'bar'],
  348. 'repositories': ['repo'],
  349. 'store_config_files': False,
  350. },
  351. source_directories=['foo', 'bar'],
  352. local_borg_version='1.2.3',
  353. global_arguments=flexmock(log_json=False),
  354. borgmatic_runtime_directory='/run/borgmatic',
  355. )
  356. )
  357. assert create_flags == ('borg', 'create')
  358. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  359. assert not pattern_file
  360. assert not exclude_file
  361. def test_make_base_create_command_includes_exclude_patterns_in_borg_command():
  362. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  363. flexmock(module).should_receive('expand_home_directories').and_return(('exclude',))
  364. mock_exclude_file = flexmock(name='/tmp/excludes')
  365. flexmock(module).should_receive('write_pattern_file').and_return(mock_exclude_file)
  366. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  367. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  368. '{hostname}'
  369. )
  370. flexmock(module.feature).should_receive('available').and_return(True)
  371. flexmock(module).should_receive('ensure_files_readable')
  372. flexmock(module).should_receive('make_pattern_flags').and_return(())
  373. exclude_flags = ('--exclude-from', 'excludes')
  374. flexmock(module).should_receive('make_exclude_flags').and_return(exclude_flags)
  375. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  376. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  377. )
  378. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  379. module.make_base_create_command(
  380. dry_run=False,
  381. repository_path='repo',
  382. config={
  383. 'source_directories': ['foo', 'bar'],
  384. 'repositories': ['repo'],
  385. 'exclude_patterns': ['exclude'],
  386. },
  387. source_directories=['foo', 'bar'],
  388. local_borg_version='1.2.3',
  389. global_arguments=flexmock(log_json=False),
  390. borgmatic_runtime_directory='/run/borgmatic',
  391. )
  392. )
  393. assert create_flags == ('borg', 'create') + exclude_flags
  394. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  395. assert not pattern_file
  396. assert exclude_file == mock_exclude_file
  397. @pytest.mark.parametrize(
  398. 'option_name,option_value,feature_available,option_flags',
  399. (
  400. ('checkpoint_interval', 600, True, ('--checkpoint-interval', '600')),
  401. ('checkpoint_volume', 1024, True, ('--checkpoint-volume', '1024')),
  402. ('chunker_params', '1,2,3,4', True, ('--chunker-params', '1,2,3,4')),
  403. ('compression', 'rle', True, ('--compression', 'rle')),
  404. ('one_file_system', True, True, ('--one-file-system',)),
  405. ('upload_rate_limit', 100, True, ('--upload-ratelimit', '100')),
  406. ('upload_rate_limit', 100, False, ('--remote-ratelimit', '100')),
  407. ('upload_buffer_size', 160, True, ('--upload-buffer', '160')),
  408. ('numeric_ids', True, True, ('--numeric-ids',)),
  409. ('numeric_ids', True, False, ('--numeric-owner',)),
  410. ('read_special', True, True, ('--read-special',)),
  411. ('ctime', True, True, ()),
  412. ('ctime', False, True, ('--noctime',)),
  413. ('birthtime', True, True, ()),
  414. ('birthtime', False, True, ('--nobirthtime',)),
  415. ('atime', True, True, ('--atime',)),
  416. ('atime', True, False, ()),
  417. ('atime', False, True, ()),
  418. ('atime', False, False, ('--noatime',)),
  419. ('flags', True, True, ()),
  420. ('flags', True, False, ()),
  421. ('flags', False, True, ('--noflags',)),
  422. ('flags', False, False, ('--nobsdflags',)),
  423. ('files_cache', 'ctime,size', True, ('--files-cache', 'ctime,size')),
  424. ('umask', 740, True, ('--umask', '740')),
  425. ('lock_wait', 5, True, ('--lock-wait', '5')),
  426. ),
  427. )
  428. def test_make_base_create_command_includes_configuration_option_as_command_flag(
  429. option_name, option_value, feature_available, option_flags
  430. ):
  431. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  432. flexmock(module).should_receive('expand_home_directories').and_return(())
  433. flexmock(module).should_receive('write_pattern_file').and_return(None)
  434. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  435. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  436. '{hostname}'
  437. )
  438. flexmock(module.feature).should_receive('available').and_return(feature_available)
  439. flexmock(module).should_receive('ensure_files_readable')
  440. flexmock(module).should_receive('make_pattern_flags').and_return(())
  441. flexmock(module).should_receive('make_exclude_flags').and_return(())
  442. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  443. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  444. )
  445. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  446. module.make_base_create_command(
  447. dry_run=False,
  448. repository_path='repo',
  449. config={
  450. 'source_directories': ['foo', 'bar'],
  451. 'repositories': ['repo'],
  452. option_name: option_value,
  453. },
  454. source_directories=['foo', 'bar'],
  455. local_borg_version='1.2.3',
  456. global_arguments=flexmock(log_json=False),
  457. borgmatic_runtime_directory='/run/borgmatic',
  458. )
  459. )
  460. assert create_flags == ('borg', 'create') + option_flags
  461. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  462. assert not pattern_file
  463. assert not exclude_file
  464. def test_make_base_create_command_includes_dry_run_in_borg_command():
  465. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  466. flexmock(module).should_receive('expand_home_directories').and_return(())
  467. flexmock(module).should_receive('write_pattern_file').and_return(None)
  468. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  469. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  470. '{hostname}'
  471. )
  472. flexmock(module.feature).should_receive('available').and_return(True)
  473. flexmock(module).should_receive('ensure_files_readable')
  474. flexmock(module).should_receive('make_pattern_flags').and_return(())
  475. flexmock(module).should_receive('make_exclude_flags').and_return(())
  476. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  477. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  478. )
  479. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  480. module.make_base_create_command(
  481. dry_run=True,
  482. repository_path='repo',
  483. config={
  484. 'source_directories': ['foo', 'bar'],
  485. 'repositories': ['repo'],
  486. 'exclude_patterns': ['exclude'],
  487. },
  488. source_directories=['foo', 'bar'],
  489. local_borg_version='1.2.3',
  490. global_arguments=flexmock(log_json=False),
  491. borgmatic_runtime_directory='/run/borgmatic',
  492. )
  493. )
  494. assert create_flags == ('borg', 'create', '--dry-run')
  495. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  496. assert not pattern_file
  497. assert not exclude_file
  498. def test_make_base_create_command_includes_local_path_in_borg_command():
  499. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  500. flexmock(module).should_receive('expand_home_directories').and_return(())
  501. flexmock(module).should_receive('write_pattern_file').and_return(None)
  502. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  503. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  504. '{hostname}'
  505. )
  506. flexmock(module.feature).should_receive('available').and_return(True)
  507. flexmock(module).should_receive('ensure_files_readable')
  508. flexmock(module).should_receive('make_pattern_flags').and_return(())
  509. flexmock(module).should_receive('make_exclude_flags').and_return(())
  510. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  511. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  512. )
  513. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  514. module.make_base_create_command(
  515. dry_run=False,
  516. repository_path='repo',
  517. config={
  518. 'source_directories': ['foo', 'bar'],
  519. 'repositories': ['repo'],
  520. },
  521. source_directories=['foo', 'bar'],
  522. local_borg_version='1.2.3',
  523. global_arguments=flexmock(log_json=False),
  524. borgmatic_runtime_directory='/run/borgmatic',
  525. local_path='borg1',
  526. )
  527. )
  528. assert create_flags == ('borg1', 'create')
  529. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  530. assert not pattern_file
  531. assert not exclude_file
  532. def test_make_base_create_command_includes_remote_path_in_borg_command():
  533. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  534. flexmock(module).should_receive('expand_home_directories').and_return(())
  535. flexmock(module).should_receive('write_pattern_file').and_return(None)
  536. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  537. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  538. '{hostname}'
  539. )
  540. flexmock(module.feature).should_receive('available').and_return(True)
  541. flexmock(module).should_receive('ensure_files_readable')
  542. flexmock(module).should_receive('make_pattern_flags').and_return(())
  543. flexmock(module).should_receive('make_exclude_flags').and_return(())
  544. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  545. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  546. )
  547. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  548. module.make_base_create_command(
  549. dry_run=False,
  550. repository_path='repo',
  551. config={
  552. 'source_directories': ['foo', 'bar'],
  553. 'repositories': ['repo'],
  554. },
  555. source_directories=['foo', 'bar'],
  556. local_borg_version='1.2.3',
  557. global_arguments=flexmock(log_json=False),
  558. borgmatic_runtime_directory='/run/borgmatic',
  559. remote_path='borg1',
  560. )
  561. )
  562. assert create_flags == ('borg', 'create', '--remote-path', 'borg1')
  563. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  564. assert not pattern_file
  565. assert not exclude_file
  566. def test_make_base_create_command_includes_log_json_in_borg_command():
  567. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  568. flexmock(module).should_receive('expand_home_directories').and_return(())
  569. flexmock(module).should_receive('write_pattern_file').and_return(None)
  570. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  571. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  572. '{hostname}'
  573. )
  574. flexmock(module.feature).should_receive('available').and_return(True)
  575. flexmock(module).should_receive('ensure_files_readable')
  576. flexmock(module).should_receive('make_pattern_flags').and_return(())
  577. flexmock(module).should_receive('make_exclude_flags').and_return(())
  578. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  579. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  580. )
  581. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  582. module.make_base_create_command(
  583. dry_run=False,
  584. repository_path='repo',
  585. config={
  586. 'source_directories': ['foo', 'bar'],
  587. 'repositories': ['repo'],
  588. },
  589. source_directories=['foo', 'bar'],
  590. local_borg_version='1.2.3',
  591. global_arguments=flexmock(log_json=True),
  592. borgmatic_runtime_directory='/run/borgmatic',
  593. )
  594. )
  595. assert create_flags == ('borg', 'create', '--log-json')
  596. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  597. assert not pattern_file
  598. assert not exclude_file
  599. def test_make_base_create_command_includes_list_flags_in_borg_command():
  600. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  601. flexmock(module).should_receive('expand_home_directories').and_return(())
  602. flexmock(module).should_receive('write_pattern_file').and_return(None)
  603. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  604. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  605. '{hostname}'
  606. )
  607. flexmock(module.feature).should_receive('available').and_return(True)
  608. flexmock(module).should_receive('ensure_files_readable')
  609. flexmock(module).should_receive('make_pattern_flags').and_return(())
  610. flexmock(module).should_receive('make_exclude_flags').and_return(())
  611. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  612. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  613. )
  614. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  615. module.make_base_create_command(
  616. dry_run=False,
  617. repository_path='repo',
  618. config={
  619. 'source_directories': ['foo', 'bar'],
  620. 'repositories': ['repo'],
  621. },
  622. source_directories=['foo', 'bar'],
  623. local_borg_version='1.2.3',
  624. global_arguments=flexmock(log_json=False),
  625. borgmatic_runtime_directory='/run/borgmatic',
  626. list_files=True,
  627. )
  628. )
  629. assert create_flags == ('borg', 'create', '--list', '--filter', 'FOO')
  630. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  631. assert not pattern_file
  632. assert not exclude_file
  633. def test_make_base_create_command_with_stream_processes_ignores_read_special_false_and_excludes_special_files():
  634. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  635. flexmock(module).should_receive('expand_home_directories').and_return(())
  636. flexmock(module).should_receive('write_pattern_file').and_return(None)
  637. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  638. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  639. '{hostname}'
  640. )
  641. flexmock(module.feature).should_receive('available').and_return(True)
  642. flexmock(module).should_receive('ensure_files_readable')
  643. flexmock(module).should_receive('make_pattern_flags').and_return(())
  644. flexmock(module).should_receive('make_exclude_flags').and_return(())
  645. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  646. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  647. )
  648. flexmock(module.logger).should_receive('warning').twice()
  649. flexmock(module.environment).should_receive('make_environment')
  650. flexmock(module).should_receive('collect_special_file_paths').and_return(('/dev/null',)).once()
  651. flexmock(module).should_receive('expand_home_directories').and_return(())
  652. flexmock(module).should_receive('write_pattern_file').and_return(flexmock(name='patterns'))
  653. flexmock(module).should_receive('make_exclude_flags').and_return(())
  654. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  655. module.make_base_create_command(
  656. dry_run=False,
  657. repository_path='repo',
  658. config={
  659. 'source_directories': ['foo', 'bar'],
  660. 'repositories': ['repo'],
  661. 'read_special': False,
  662. },
  663. source_directories=['foo', 'bar'],
  664. local_borg_version='1.2.3',
  665. global_arguments=flexmock(log_json=False),
  666. borgmatic_runtime_directory='/run/borgmatic',
  667. stream_processes=flexmock(),
  668. )
  669. )
  670. assert create_flags == ('borg', 'create', '--read-special')
  671. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  672. assert not pattern_file
  673. assert exclude_file
  674. def test_make_base_create_command_with_stream_processes_and_read_special_true_skip_special_files_excludes():
  675. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  676. flexmock(module).should_receive('expand_home_directories').and_return(())
  677. flexmock(module).should_receive('write_pattern_file').and_return(None)
  678. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  679. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  680. '{hostname}'
  681. )
  682. flexmock(module.feature).should_receive('available').and_return(True)
  683. flexmock(module).should_receive('ensure_files_readable')
  684. flexmock(module).should_receive('make_pattern_flags').and_return(())
  685. flexmock(module).should_receive('make_exclude_flags').and_return(())
  686. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  687. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  688. )
  689. flexmock(module.logger).should_receive('warning').never()
  690. flexmock(module).should_receive('collect_special_file_paths').never()
  691. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  692. module.make_base_create_command(
  693. dry_run=False,
  694. repository_path='repo',
  695. config={
  696. 'source_directories': ['foo', 'bar'],
  697. 'repositories': ['repo'],
  698. 'read_special': True,
  699. },
  700. source_directories=['foo', 'bar'],
  701. local_borg_version='1.2.3',
  702. global_arguments=flexmock(log_json=False),
  703. borgmatic_runtime_directory='/run/borgmatic',
  704. stream_processes=flexmock(),
  705. )
  706. )
  707. assert create_flags == ('borg', 'create', '--read-special')
  708. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  709. assert not pattern_file
  710. assert not exclude_file
  711. def test_make_base_create_command_includes_archive_name_format_in_borg_command():
  712. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  713. flexmock(module).should_receive('expand_home_directories').and_return(())
  714. flexmock(module).should_receive('write_pattern_file').and_return(None)
  715. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  716. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  717. '{hostname}'
  718. )
  719. flexmock(module.feature).should_receive('available').and_return(True)
  720. flexmock(module).should_receive('ensure_files_readable')
  721. flexmock(module).should_receive('make_pattern_flags').and_return(())
  722. flexmock(module).should_receive('make_exclude_flags').and_return(())
  723. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  724. ('repo::ARCHIVE_NAME',)
  725. )
  726. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  727. module.make_base_create_command(
  728. dry_run=False,
  729. repository_path='repo',
  730. config={
  731. 'source_directories': ['foo', 'bar'],
  732. 'repositories': ['repo'],
  733. 'archive_name_format': 'ARCHIVE_NAME',
  734. },
  735. source_directories=['foo', 'bar'],
  736. local_borg_version='1.2.3',
  737. global_arguments=flexmock(log_json=False),
  738. borgmatic_runtime_directory='/run/borgmatic',
  739. )
  740. )
  741. assert create_flags == ('borg', 'create')
  742. assert create_positional_arguments == ('repo::ARCHIVE_NAME', 'foo', 'bar')
  743. assert not pattern_file
  744. assert not exclude_file
  745. def test_make_base_create_command_includes_default_archive_name_format_in_borg_command():
  746. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  747. flexmock(module).should_receive('expand_home_directories').and_return(())
  748. flexmock(module).should_receive('write_pattern_file').and_return(None)
  749. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  750. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  751. '{hostname}'
  752. )
  753. flexmock(module.feature).should_receive('available').and_return(True)
  754. flexmock(module).should_receive('ensure_files_readable')
  755. flexmock(module).should_receive('make_pattern_flags').and_return(())
  756. flexmock(module).should_receive('make_exclude_flags').and_return(())
  757. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  758. ('repo::{hostname}',)
  759. )
  760. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  761. module.make_base_create_command(
  762. dry_run=False,
  763. repository_path='repo',
  764. config={
  765. 'source_directories': ['foo', 'bar'],
  766. 'repositories': ['repo'],
  767. },
  768. source_directories=['foo', 'bar'],
  769. local_borg_version='1.2.3',
  770. global_arguments=flexmock(log_json=False),
  771. borgmatic_runtime_directory='/run/borgmatic',
  772. )
  773. )
  774. assert create_flags == ('borg', 'create')
  775. assert create_positional_arguments == ('repo::{hostname}', 'foo', 'bar')
  776. assert not pattern_file
  777. assert not exclude_file
  778. def test_make_base_create_command_includes_archive_name_format_with_placeholders_in_borg_command():
  779. repository_archive_pattern = 'repo::Documents_{hostname}-{now}' # noqa: FS003
  780. flexmock(module).should_receive('expand_home_directories').and_return(())
  781. flexmock(module).should_receive('write_pattern_file').and_return(None)
  782. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  783. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  784. '{hostname}'
  785. )
  786. flexmock(module.feature).should_receive('available').and_return(True)
  787. flexmock(module).should_receive('ensure_files_readable')
  788. flexmock(module).should_receive('make_pattern_flags').and_return(())
  789. flexmock(module).should_receive('make_exclude_flags').and_return(())
  790. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  791. (repository_archive_pattern,)
  792. )
  793. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  794. module.make_base_create_command(
  795. dry_run=False,
  796. repository_path='repo',
  797. config={
  798. 'source_directories': ['foo', 'bar'],
  799. 'repositories': ['repo'],
  800. 'archive_name_format': 'Documents_{hostname}-{now}', # noqa: FS003
  801. },
  802. source_directories=['foo', 'bar'],
  803. local_borg_version='1.2.3',
  804. global_arguments=flexmock(log_json=False),
  805. borgmatic_runtime_directory='/run/borgmatic',
  806. )
  807. )
  808. assert create_flags == ('borg', 'create')
  809. assert create_positional_arguments == (repository_archive_pattern, 'foo', 'bar')
  810. assert not pattern_file
  811. assert not exclude_file
  812. def test_make_base_create_command_includes_repository_and_archive_name_format_with_placeholders_in_borg_command():
  813. repository_archive_pattern = '{fqdn}::Documents_{hostname}-{now}' # noqa: FS003
  814. flexmock(module).should_receive('expand_home_directories').and_return(())
  815. flexmock(module).should_receive('write_pattern_file').and_return(None)
  816. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  817. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  818. '{hostname}'
  819. )
  820. flexmock(module.feature).should_receive('available').and_return(True)
  821. flexmock(module).should_receive('ensure_files_readable')
  822. flexmock(module).should_receive('make_pattern_flags').and_return(())
  823. flexmock(module).should_receive('make_exclude_flags').and_return(())
  824. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  825. (repository_archive_pattern,)
  826. )
  827. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  828. module.make_base_create_command(
  829. dry_run=False,
  830. repository_path='{fqdn}', # noqa: FS003
  831. config={
  832. 'source_directories': ['foo', 'bar'],
  833. 'repositories': ['{fqdn}'], # noqa: FS003
  834. 'archive_name_format': 'Documents_{hostname}-{now}', # noqa: FS003
  835. },
  836. source_directories=['foo', 'bar'],
  837. local_borg_version='1.2.3',
  838. global_arguments=flexmock(log_json=False),
  839. borgmatic_runtime_directory='/run/borgmatic',
  840. )
  841. )
  842. assert create_flags == ('borg', 'create')
  843. assert create_positional_arguments == (repository_archive_pattern, 'foo', 'bar')
  844. assert not pattern_file
  845. assert not exclude_file
  846. def test_make_base_create_command_includes_extra_borg_options_in_borg_command():
  847. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  848. flexmock(module).should_receive('expand_home_directories').and_return(())
  849. flexmock(module).should_receive('write_pattern_file').and_return(None)
  850. flexmock(module).should_receive('make_list_filter_flags').and_return('FOO')
  851. flexmock(module.flags).should_receive('get_default_archive_name_format').and_return(
  852. '{hostname}'
  853. )
  854. flexmock(module.feature).should_receive('available').and_return(True)
  855. flexmock(module).should_receive('ensure_files_readable')
  856. flexmock(module).should_receive('make_pattern_flags').and_return(())
  857. flexmock(module).should_receive('make_exclude_flags').and_return(())
  858. flexmock(module.flags).should_receive('make_repository_archive_flags').and_return(
  859. (f'repo::{DEFAULT_ARCHIVE_NAME}',)
  860. )
  861. (create_flags, create_positional_arguments, pattern_file, exclude_file) = (
  862. module.make_base_create_command(
  863. dry_run=False,
  864. repository_path='repo',
  865. config={
  866. 'source_directories': ['foo', 'bar'],
  867. 'repositories': ['repo'],
  868. 'extra_borg_options': {'create': '--extra --options'},
  869. },
  870. source_directories=['foo', 'bar'],
  871. local_borg_version='1.2.3',
  872. global_arguments=flexmock(log_json=False),
  873. borgmatic_runtime_directory='/run/borgmatic',
  874. )
  875. )
  876. assert create_flags == ('borg', 'create', '--extra', '--options')
  877. assert create_positional_arguments == REPO_ARCHIVE_WITH_PATHS
  878. assert not pattern_file
  879. assert not exclude_file
  880. def test_make_base_create_command_with_non_existent_directory_and_source_directories_must_exist_raises():
  881. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  882. flexmock(module).should_receive('check_all_source_directories_exist').and_raise(ValueError)
  883. with pytest.raises(ValueError):
  884. module.make_base_create_command(
  885. dry_run=False,
  886. repository_path='repo',
  887. config={
  888. 'source_directories': ['foo', 'bar'],
  889. 'repositories': ['repo'],
  890. 'source_directories_must_exist': True,
  891. },
  892. source_directories=['foo', 'bar'],
  893. local_borg_version='1.2.3',
  894. global_arguments=flexmock(log_json=False),
  895. borgmatic_runtime_directory='/run/borgmatic',
  896. )
  897. def test_create_archive_calls_borg_with_parameters():
  898. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  899. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  900. flexmock(module).should_receive('make_base_create_command').and_return(
  901. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  902. )
  903. flexmock(module.environment).should_receive('make_environment')
  904. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  905. flexmock(module).should_receive('execute_command').with_args(
  906. ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS,
  907. output_log_level=logging.INFO,
  908. output_file=None,
  909. borg_local_path='borg',
  910. borg_exit_codes=None,
  911. working_directory=None,
  912. extra_environment=None,
  913. )
  914. module.create_archive(
  915. dry_run=False,
  916. repository_path='repo',
  917. config={
  918. 'source_directories': ['foo', 'bar'],
  919. 'repositories': ['repo'],
  920. 'exclude_patterns': None,
  921. },
  922. source_directories=['foo', 'bar'],
  923. local_borg_version='1.2.3',
  924. global_arguments=flexmock(log_json=False),
  925. borgmatic_runtime_directory='/borgmatic/run',
  926. )
  927. def test_create_archive_calls_borg_with_environment():
  928. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  929. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  930. flexmock(module).should_receive('make_base_create_command').and_return(
  931. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  932. )
  933. environment = {'BORG_THINGY': 'YUP'}
  934. flexmock(module.environment).should_receive('make_environment').and_return(environment)
  935. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  936. flexmock(module).should_receive('execute_command').with_args(
  937. ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS,
  938. output_log_level=logging.INFO,
  939. output_file=None,
  940. borg_local_path='borg',
  941. borg_exit_codes=None,
  942. working_directory=None,
  943. extra_environment=environment,
  944. )
  945. module.create_archive(
  946. dry_run=False,
  947. repository_path='repo',
  948. config={
  949. 'source_directories': ['foo', 'bar'],
  950. 'repositories': ['repo'],
  951. 'exclude_patterns': None,
  952. },
  953. source_directories=['foo', 'bar'],
  954. local_borg_version='1.2.3',
  955. global_arguments=flexmock(log_json=False),
  956. borgmatic_runtime_directory='/borgmatic/run',
  957. )
  958. def test_create_archive_with_log_info_calls_borg_with_info_parameter():
  959. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  960. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  961. flexmock(module).should_receive('make_base_create_command').and_return(
  962. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  963. )
  964. flexmock(module.environment).should_receive('make_environment')
  965. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  966. flexmock(module).should_receive('execute_command').with_args(
  967. ('borg', 'create', '--info') + REPO_ARCHIVE_WITH_PATHS,
  968. output_log_level=logging.INFO,
  969. output_file=None,
  970. borg_local_path='borg',
  971. borg_exit_codes=None,
  972. working_directory=None,
  973. extra_environment=None,
  974. )
  975. insert_logging_mock(logging.INFO)
  976. module.create_archive(
  977. dry_run=False,
  978. repository_path='repo',
  979. config={
  980. 'source_directories': ['foo', 'bar'],
  981. 'repositories': ['repo'],
  982. 'exclude_patterns': None,
  983. },
  984. source_directories=['foo', 'bar'],
  985. local_borg_version='1.2.3',
  986. global_arguments=flexmock(log_json=False),
  987. borgmatic_runtime_directory='/borgmatic/run',
  988. )
  989. def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
  990. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  991. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  992. flexmock(module).should_receive('make_base_create_command').and_return(
  993. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  994. )
  995. flexmock(module.environment).should_receive('make_environment')
  996. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  997. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  998. ('borg', 'create', '--json') + REPO_ARCHIVE_WITH_PATHS,
  999. working_directory=None,
  1000. extra_environment=None,
  1001. borg_local_path='borg',
  1002. borg_exit_codes=None,
  1003. )
  1004. insert_logging_mock(logging.INFO)
  1005. module.create_archive(
  1006. dry_run=False,
  1007. repository_path='repo',
  1008. config={
  1009. 'source_directories': ['foo', 'bar'],
  1010. 'repositories': ['repo'],
  1011. 'exclude_patterns': None,
  1012. },
  1013. source_directories=['foo', 'bar'],
  1014. local_borg_version='1.2.3',
  1015. global_arguments=flexmock(log_json=False),
  1016. borgmatic_runtime_directory='/borgmatic/run',
  1017. json=True,
  1018. )
  1019. def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
  1020. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1021. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1022. flexmock(module).should_receive('make_base_create_command').and_return(
  1023. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1024. )
  1025. flexmock(module.environment).should_receive('make_environment')
  1026. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1027. flexmock(module).should_receive('execute_command').with_args(
  1028. ('borg', 'create', '--debug', '--show-rc') + REPO_ARCHIVE_WITH_PATHS,
  1029. output_log_level=logging.INFO,
  1030. output_file=None,
  1031. borg_local_path='borg',
  1032. borg_exit_codes=None,
  1033. working_directory=None,
  1034. extra_environment=None,
  1035. )
  1036. insert_logging_mock(logging.DEBUG)
  1037. module.create_archive(
  1038. dry_run=False,
  1039. repository_path='repo',
  1040. config={
  1041. 'source_directories': ['foo', 'bar'],
  1042. 'repositories': ['repo'],
  1043. 'exclude_patterns': None,
  1044. },
  1045. source_directories=['foo', 'bar'],
  1046. local_borg_version='1.2.3',
  1047. global_arguments=flexmock(log_json=False),
  1048. borgmatic_runtime_directory='/borgmatic/run',
  1049. )
  1050. def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
  1051. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1052. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1053. flexmock(module).should_receive('make_base_create_command').and_return(
  1054. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1055. )
  1056. flexmock(module.environment).should_receive('make_environment')
  1057. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1058. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  1059. ('borg', 'create', '--json') + REPO_ARCHIVE_WITH_PATHS,
  1060. working_directory=None,
  1061. extra_environment=None,
  1062. borg_local_path='borg',
  1063. borg_exit_codes=None,
  1064. )
  1065. insert_logging_mock(logging.DEBUG)
  1066. module.create_archive(
  1067. dry_run=False,
  1068. repository_path='repo',
  1069. config={
  1070. 'source_directories': ['foo', 'bar'],
  1071. 'repositories': ['repo'],
  1072. 'exclude_patterns': None,
  1073. },
  1074. source_directories=['foo', 'bar'],
  1075. local_borg_version='1.2.3',
  1076. global_arguments=flexmock(log_json=False),
  1077. borgmatic_runtime_directory='/borgmatic/run',
  1078. json=True,
  1079. )
  1080. def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats():
  1081. # --dry-run and --stats are mutually exclusive, see:
  1082. # https://borgbackup.readthedocs.io/en/stable/usage/create.html#description
  1083. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1084. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1085. flexmock(module).should_receive('make_base_create_command').and_return(
  1086. (('borg', 'create', '--dry-run'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1087. )
  1088. flexmock(module.environment).should_receive('make_environment')
  1089. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1090. flexmock(module).should_receive('execute_command').with_args(
  1091. ('borg', 'create', '--dry-run', '--info') + REPO_ARCHIVE_WITH_PATHS,
  1092. output_log_level=logging.INFO,
  1093. output_file=None,
  1094. borg_local_path='borg',
  1095. borg_exit_codes=None,
  1096. working_directory=None,
  1097. extra_environment=None,
  1098. )
  1099. insert_logging_mock(logging.INFO)
  1100. module.create_archive(
  1101. dry_run=True,
  1102. repository_path='repo',
  1103. config={
  1104. 'source_directories': ['foo', 'bar'],
  1105. 'repositories': ['repo'],
  1106. 'exclude_patterns': None,
  1107. },
  1108. source_directories=['foo', 'bar'],
  1109. local_borg_version='1.2.3',
  1110. global_arguments=flexmock(log_json=False),
  1111. borgmatic_runtime_directory='/borgmatic/run',
  1112. stats=True,
  1113. )
  1114. def test_create_archive_with_working_directory_calls_borg_with_working_directory():
  1115. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1116. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1117. flexmock(module).should_receive('make_base_create_command').and_return(
  1118. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1119. )
  1120. flexmock(module.environment).should_receive('make_environment')
  1121. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1122. '/working/dir'
  1123. )
  1124. flexmock(module).should_receive('execute_command').with_args(
  1125. ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS,
  1126. output_log_level=logging.INFO,
  1127. output_file=None,
  1128. borg_local_path='borg',
  1129. borg_exit_codes=None,
  1130. working_directory='/working/dir',
  1131. extra_environment=None,
  1132. )
  1133. module.create_archive(
  1134. dry_run=False,
  1135. repository_path='repo',
  1136. config={
  1137. 'source_directories': ['foo', 'bar'],
  1138. 'repositories': ['repo'],
  1139. 'working_directory': '/working/dir',
  1140. 'exclude_patterns': None,
  1141. },
  1142. source_directories=['foo', 'bar'],
  1143. local_borg_version='1.2.3',
  1144. global_arguments=flexmock(log_json=False),
  1145. borgmatic_runtime_directory='/borgmatic/run',
  1146. )
  1147. def test_create_archive_with_exit_codes_calls_borg_using_them():
  1148. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1149. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1150. flexmock(module).should_receive('make_base_create_command').and_return(
  1151. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1152. )
  1153. flexmock(module.environment).should_receive('make_environment')
  1154. borg_exit_codes = flexmock()
  1155. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1156. flexmock(module).should_receive('execute_command').with_args(
  1157. ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS,
  1158. output_log_level=logging.INFO,
  1159. output_file=None,
  1160. borg_local_path='borg',
  1161. borg_exit_codes=borg_exit_codes,
  1162. working_directory=None,
  1163. extra_environment=None,
  1164. )
  1165. module.create_archive(
  1166. dry_run=False,
  1167. repository_path='repo',
  1168. config={
  1169. 'source_directories': ['foo', 'bar'],
  1170. 'repositories': ['repo'],
  1171. 'exclude_patterns': None,
  1172. 'borg_exit_codes': borg_exit_codes,
  1173. },
  1174. source_directories=['foo', 'bar'],
  1175. local_borg_version='1.2.3',
  1176. global_arguments=flexmock(log_json=False),
  1177. borgmatic_runtime_directory='/borgmatic/run',
  1178. )
  1179. def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_answer_output_log_level():
  1180. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1181. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1182. flexmock(module).should_receive('make_base_create_command').and_return(
  1183. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1184. )
  1185. flexmock(module.environment).should_receive('make_environment')
  1186. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1187. flexmock(module).should_receive('execute_command').with_args(
  1188. ('borg', 'create', '--stats') + REPO_ARCHIVE_WITH_PATHS,
  1189. output_log_level=module.borgmatic.logger.ANSWER,
  1190. output_file=None,
  1191. borg_local_path='borg',
  1192. borg_exit_codes=None,
  1193. working_directory=None,
  1194. extra_environment=None,
  1195. )
  1196. module.create_archive(
  1197. dry_run=False,
  1198. repository_path='repo',
  1199. config={
  1200. 'source_directories': ['foo', 'bar'],
  1201. 'repositories': ['repo'],
  1202. 'exclude_patterns': None,
  1203. },
  1204. source_directories=['foo', 'bar'],
  1205. local_borg_version='1.2.3',
  1206. global_arguments=flexmock(log_json=False),
  1207. borgmatic_runtime_directory='/borgmatic/run',
  1208. stats=True,
  1209. )
  1210. def test_create_archive_with_files_calls_borg_with_answer_output_log_level():
  1211. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1212. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1213. flexmock(module).should_receive('make_base_create_command').and_return(
  1214. (
  1215. ('borg', 'create', '--list', '--filter', 'FOO'),
  1216. REPO_ARCHIVE_WITH_PATHS,
  1217. flexmock(),
  1218. flexmock(),
  1219. )
  1220. )
  1221. flexmock(module.environment).should_receive('make_environment')
  1222. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1223. flexmock(module).should_receive('execute_command').with_args(
  1224. ('borg', 'create', '--list', '--filter', 'FOO') + REPO_ARCHIVE_WITH_PATHS,
  1225. output_log_level=module.borgmatic.logger.ANSWER,
  1226. output_file=None,
  1227. borg_local_path='borg',
  1228. borg_exit_codes=None,
  1229. working_directory=None,
  1230. extra_environment=None,
  1231. )
  1232. module.create_archive(
  1233. dry_run=False,
  1234. repository_path='repo',
  1235. config={
  1236. 'source_directories': ['foo', 'bar'],
  1237. 'repositories': ['repo'],
  1238. 'exclude_patterns': None,
  1239. },
  1240. source_directories=['foo', 'bar'],
  1241. local_borg_version='1.2.3',
  1242. global_arguments=flexmock(log_json=False),
  1243. borgmatic_runtime_directory='/borgmatic/run',
  1244. list_files=True,
  1245. )
  1246. def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_parameter_and_no_list():
  1247. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1248. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1249. flexmock(module).should_receive('make_base_create_command').and_return(
  1250. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1251. )
  1252. flexmock(module.environment).should_receive('make_environment')
  1253. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1254. flexmock(module).should_receive('execute_command').with_args(
  1255. ('borg', 'create', '--info', '--progress') + REPO_ARCHIVE_WITH_PATHS,
  1256. output_log_level=logging.INFO,
  1257. output_file=module.DO_NOT_CAPTURE,
  1258. borg_local_path='borg',
  1259. borg_exit_codes=None,
  1260. working_directory=None,
  1261. extra_environment=None,
  1262. )
  1263. insert_logging_mock(logging.INFO)
  1264. module.create_archive(
  1265. dry_run=False,
  1266. repository_path='repo',
  1267. config={
  1268. 'source_directories': ['foo', 'bar'],
  1269. 'repositories': ['repo'],
  1270. 'exclude_patterns': None,
  1271. },
  1272. source_directories=['foo', 'bar'],
  1273. local_borg_version='1.2.3',
  1274. global_arguments=flexmock(log_json=False),
  1275. borgmatic_runtime_directory='/borgmatic/run',
  1276. progress=True,
  1277. )
  1278. def test_create_archive_with_progress_calls_borg_with_progress_parameter():
  1279. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1280. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1281. flexmock(module).should_receive('make_base_create_command').and_return(
  1282. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1283. )
  1284. flexmock(module.environment).should_receive('make_environment')
  1285. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1286. flexmock(module).should_receive('execute_command').with_args(
  1287. ('borg', 'create', '--progress') + REPO_ARCHIVE_WITH_PATHS,
  1288. output_log_level=logging.INFO,
  1289. output_file=module.DO_NOT_CAPTURE,
  1290. borg_local_path='borg',
  1291. borg_exit_codes=None,
  1292. working_directory=None,
  1293. extra_environment=None,
  1294. )
  1295. module.create_archive(
  1296. dry_run=False,
  1297. repository_path='repo',
  1298. config={
  1299. 'source_directories': ['foo', 'bar'],
  1300. 'repositories': ['repo'],
  1301. 'exclude_patterns': None,
  1302. },
  1303. source_directories=['foo', 'bar'],
  1304. local_borg_version='1.2.3',
  1305. global_arguments=flexmock(log_json=False),
  1306. borgmatic_runtime_directory='/borgmatic/run',
  1307. progress=True,
  1308. )
  1309. def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progress_parameter():
  1310. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1311. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1312. processes = flexmock()
  1313. flexmock(module).should_receive('make_base_create_command').and_return(
  1314. (
  1315. ('borg', 'create', '--read-special'),
  1316. REPO_ARCHIVE_WITH_PATHS,
  1317. flexmock(),
  1318. flexmock(),
  1319. )
  1320. )
  1321. flexmock(module.environment).should_receive('make_environment')
  1322. create_command = (
  1323. 'borg',
  1324. 'create',
  1325. '--read-special',
  1326. '--progress',
  1327. ) + REPO_ARCHIVE_WITH_PATHS
  1328. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1329. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1330. create_command + ('--dry-run', '--list'),
  1331. processes=processes,
  1332. output_log_level=logging.INFO,
  1333. output_file=module.DO_NOT_CAPTURE,
  1334. borg_local_path='borg',
  1335. borg_exit_codes=None,
  1336. working_directory=None,
  1337. extra_environment=None,
  1338. )
  1339. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1340. create_command,
  1341. processes=processes,
  1342. output_log_level=logging.INFO,
  1343. output_file=module.DO_NOT_CAPTURE,
  1344. borg_local_path='borg',
  1345. borg_exit_codes=None,
  1346. working_directory=None,
  1347. extra_environment=None,
  1348. )
  1349. module.create_archive(
  1350. dry_run=False,
  1351. repository_path='repo',
  1352. config={
  1353. 'source_directories': ['foo', 'bar'],
  1354. 'repositories': ['repo'],
  1355. 'exclude_patterns': None,
  1356. },
  1357. source_directories=['foo', 'bar'],
  1358. local_borg_version='1.2.3',
  1359. global_arguments=flexmock(log_json=False),
  1360. borgmatic_runtime_directory='/borgmatic/run',
  1361. progress=True,
  1362. stream_processes=processes,
  1363. )
  1364. def test_create_archive_with_json_calls_borg_with_json_flag():
  1365. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1366. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1367. flexmock(module).should_receive('make_base_create_command').and_return(
  1368. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1369. )
  1370. flexmock(module.environment).should_receive('make_environment')
  1371. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1372. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  1373. ('borg', 'create', '--json') + REPO_ARCHIVE_WITH_PATHS,
  1374. working_directory=None,
  1375. extra_environment=None,
  1376. borg_local_path='borg',
  1377. borg_exit_codes=None,
  1378. ).and_return('[]')
  1379. json_output = module.create_archive(
  1380. dry_run=False,
  1381. repository_path='repo',
  1382. config={
  1383. 'source_directories': ['foo', 'bar'],
  1384. 'repositories': ['repo'],
  1385. 'exclude_patterns': None,
  1386. },
  1387. source_directories=['foo', 'bar'],
  1388. local_borg_version='1.2.3',
  1389. global_arguments=flexmock(log_json=False),
  1390. borgmatic_runtime_directory='/borgmatic/run',
  1391. json=True,
  1392. )
  1393. assert json_output == '[]'
  1394. def test_create_archive_with_stats_and_json_calls_borg_without_stats_flag():
  1395. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1396. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1397. flexmock(module).should_receive('make_base_create_command').and_return(
  1398. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1399. )
  1400. flexmock(module.environment).should_receive('make_environment')
  1401. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1402. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  1403. ('borg', 'create', '--json') + REPO_ARCHIVE_WITH_PATHS,
  1404. working_directory=None,
  1405. extra_environment=None,
  1406. borg_local_path='borg',
  1407. borg_exit_codes=None,
  1408. ).and_return('[]')
  1409. json_output = module.create_archive(
  1410. dry_run=False,
  1411. repository_path='repo',
  1412. config={
  1413. 'source_directories': ['foo*'],
  1414. 'repositories': ['repo'],
  1415. 'exclude_patterns': None,
  1416. },
  1417. source_directories=['foo', 'bar'],
  1418. local_borg_version='1.2.3',
  1419. global_arguments=flexmock(log_json=False),
  1420. borgmatic_runtime_directory='/borgmatic/run',
  1421. json=True,
  1422. stats=True,
  1423. )
  1424. assert json_output == '[]'
  1425. def test_create_archive_calls_borg_with_working_directory():
  1426. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  1427. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  1428. flexmock(module).should_receive('make_base_create_command').and_return(
  1429. (('borg', 'create'), REPO_ARCHIVE_WITH_PATHS, flexmock(), flexmock())
  1430. )
  1431. flexmock(module.environment).should_receive('make_environment')
  1432. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1433. '/working/dir'
  1434. )
  1435. flexmock(module).should_receive('execute_command').with_args(
  1436. ('borg', 'create') + REPO_ARCHIVE_WITH_PATHS,
  1437. output_log_level=logging.INFO,
  1438. output_file=None,
  1439. borg_local_path='borg',
  1440. borg_exit_codes=None,
  1441. working_directory='/working/dir',
  1442. extra_environment=None,
  1443. )
  1444. module.create_archive(
  1445. dry_run=False,
  1446. repository_path='repo',
  1447. config={
  1448. 'source_directories': ['foo', 'bar'],
  1449. 'repositories': ['repo'],
  1450. 'exclude_patterns': None,
  1451. 'working_directory': '/working/dir',
  1452. },
  1453. source_directories=['foo', 'bar'],
  1454. local_borg_version='1.2.3',
  1455. global_arguments=flexmock(log_json=False),
  1456. borgmatic_runtime_directory='/borgmatic/run',
  1457. )
  1458. def test_check_all_source_directories_exist_with_existent_directory_does_not_raise():
  1459. flexmock(module.os.path).should_receive('exists').and_return(True)
  1460. module.check_all_source_directories_exist(['foo'])
  1461. def test_check_all_source_directories_exist_with_non_existent_directory_raises():
  1462. flexmock(module.os.path).should_receive('exists').and_return(False)
  1463. with pytest.raises(ValueError):
  1464. module.check_all_source_directories_exist(['foo'])