test_create.py 68 KB

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