test_list.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import argparse
  2. import logging
  3. import pytest
  4. from flexmock import flexmock
  5. from borgmatic.borg import list as module
  6. from ..test_verbosity import insert_logging_mock
  7. BORG_LIST_LATEST_ARGUMENTS = (
  8. '--last',
  9. '1',
  10. '--short',
  11. 'repo',
  12. )
  13. def test_resolve_archive_name_passes_through_non_latest_archive_name():
  14. archive = 'myhost-2030-01-01T14:41:17.647620'
  15. assert module.resolve_archive_name('repo', archive, storage_config={}) == archive
  16. def test_resolve_archive_name_calls_borg_with_parameters():
  17. expected_archive = 'archive-name'
  18. flexmock(module.environment).should_receive('make_environment')
  19. flexmock(module).should_receive('execute_command').with_args(
  20. ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
  21. output_log_level=None,
  22. borg_local_path='borg',
  23. extra_environment=None,
  24. ).and_return(expected_archive + '\n')
  25. assert module.resolve_archive_name('repo', 'latest', storage_config={}) == expected_archive
  26. def test_resolve_archive_name_with_log_info_calls_borg_with_info_parameter():
  27. expected_archive = 'archive-name'
  28. flexmock(module.environment).should_receive('make_environment')
  29. flexmock(module).should_receive('execute_command').with_args(
  30. ('borg', 'list', '--info') + BORG_LIST_LATEST_ARGUMENTS,
  31. output_log_level=None,
  32. borg_local_path='borg',
  33. extra_environment=None,
  34. ).and_return(expected_archive + '\n')
  35. insert_logging_mock(logging.INFO)
  36. assert module.resolve_archive_name('repo', 'latest', storage_config={}) == expected_archive
  37. def test_resolve_archive_name_with_log_debug_calls_borg_with_debug_parameter():
  38. expected_archive = 'archive-name'
  39. flexmock(module.environment).should_receive('make_environment')
  40. flexmock(module).should_receive('execute_command').with_args(
  41. ('borg', 'list', '--debug', '--show-rc') + BORG_LIST_LATEST_ARGUMENTS,
  42. output_log_level=None,
  43. borg_local_path='borg',
  44. extra_environment=None,
  45. ).and_return(expected_archive + '\n')
  46. insert_logging_mock(logging.DEBUG)
  47. assert module.resolve_archive_name('repo', 'latest', storage_config={}) == expected_archive
  48. def test_resolve_archive_name_with_local_path_calls_borg_via_local_path():
  49. expected_archive = 'archive-name'
  50. flexmock(module.environment).should_receive('make_environment')
  51. flexmock(module).should_receive('execute_command').with_args(
  52. ('borg1', 'list') + BORG_LIST_LATEST_ARGUMENTS,
  53. output_log_level=None,
  54. borg_local_path='borg1',
  55. extra_environment=None,
  56. ).and_return(expected_archive + '\n')
  57. assert (
  58. module.resolve_archive_name('repo', 'latest', storage_config={}, local_path='borg1')
  59. == expected_archive
  60. )
  61. def test_resolve_archive_name_with_remote_path_calls_borg_with_remote_path_parameters():
  62. expected_archive = 'archive-name'
  63. flexmock(module.environment).should_receive('make_environment')
  64. flexmock(module).should_receive('execute_command').with_args(
  65. ('borg', 'list', '--remote-path', 'borg1') + BORG_LIST_LATEST_ARGUMENTS,
  66. output_log_level=None,
  67. borg_local_path='borg',
  68. extra_environment=None,
  69. ).and_return(expected_archive + '\n')
  70. assert (
  71. module.resolve_archive_name('repo', 'latest', storage_config={}, remote_path='borg1')
  72. == expected_archive
  73. )
  74. def test_resolve_archive_name_without_archives_raises():
  75. flexmock(module.environment).should_receive('make_environment')
  76. flexmock(module).should_receive('execute_command').with_args(
  77. ('borg', 'list') + BORG_LIST_LATEST_ARGUMENTS,
  78. output_log_level=None,
  79. borg_local_path='borg',
  80. extra_environment=None,
  81. ).and_return('')
  82. with pytest.raises(ValueError):
  83. module.resolve_archive_name('repo', 'latest', storage_config={})
  84. def test_resolve_archive_name_with_lock_wait_calls_borg_with_lock_wait_parameters():
  85. expected_archive = 'archive-name'
  86. flexmock(module.environment).should_receive('make_environment')
  87. flexmock(module).should_receive('execute_command').with_args(
  88. ('borg', 'list', '--lock-wait', 'okay') + BORG_LIST_LATEST_ARGUMENTS,
  89. output_log_level=None,
  90. borg_local_path='borg',
  91. extra_environment=None,
  92. ).and_return(expected_archive + '\n')
  93. assert (
  94. module.resolve_archive_name('repo', 'latest', storage_config={'lock_wait': 'okay'})
  95. == expected_archive
  96. )
  97. def test_make_list_command_includes_log_info():
  98. insert_logging_mock(logging.INFO)
  99. command = module.make_list_command(
  100. repository='repo',
  101. storage_config={},
  102. list_arguments=flexmock(archive=None, paths=None, json=False),
  103. )
  104. assert command == ('borg', 'list', '--info', 'repo')
  105. def test_make_list_command_includes_json_but_not_info():
  106. insert_logging_mock(logging.INFO)
  107. command = module.make_list_command(
  108. repository='repo',
  109. storage_config={},
  110. list_arguments=flexmock(archive=None, paths=None, json=True),
  111. )
  112. assert command == ('borg', 'list', '--json', 'repo')
  113. def test_make_list_command_includes_log_debug():
  114. insert_logging_mock(logging.DEBUG)
  115. command = module.make_list_command(
  116. repository='repo',
  117. storage_config={},
  118. list_arguments=flexmock(archive=None, paths=None, json=False),
  119. )
  120. assert command == ('borg', 'list', '--debug', '--show-rc', 'repo')
  121. def test_make_list_command_includes_json_but_not_debug():
  122. insert_logging_mock(logging.DEBUG)
  123. command = module.make_list_command(
  124. repository='repo',
  125. storage_config={},
  126. list_arguments=flexmock(archive=None, paths=None, json=True),
  127. )
  128. assert command == ('borg', 'list', '--json', 'repo')
  129. def test_make_list_command_includes_json():
  130. command = module.make_list_command(
  131. repository='repo',
  132. storage_config={},
  133. list_arguments=flexmock(archive=None, paths=None, json=True),
  134. )
  135. assert command == ('borg', 'list', '--json', 'repo')
  136. def test_make_list_command_includes_lock_wait():
  137. command = module.make_list_command(
  138. repository='repo',
  139. storage_config={'lock_wait': 5},
  140. list_arguments=flexmock(archive=None, paths=None, json=False),
  141. )
  142. assert command == ('borg', 'list', '--lock-wait', '5', 'repo')
  143. def test_make_list_command_includes_archive():
  144. command = module.make_list_command(
  145. repository='repo',
  146. storage_config={},
  147. list_arguments=flexmock(archive='archive', paths=None, json=False),
  148. )
  149. assert command == ('borg', 'list', 'repo::archive')
  150. def test_make_list_command_includes_archive_and_path():
  151. command = module.make_list_command(
  152. repository='repo',
  153. storage_config={},
  154. list_arguments=flexmock(archive='archive', paths=['var/lib'], json=False),
  155. )
  156. assert command == ('borg', 'list', 'repo::archive', 'var/lib')
  157. def test_make_list_command_includes_local_path():
  158. command = module.make_list_command(
  159. repository='repo',
  160. storage_config={},
  161. list_arguments=flexmock(archive=None, paths=None, json=False),
  162. local_path='borg2',
  163. )
  164. assert command == ('borg2', 'list', 'repo')
  165. def test_make_list_command_includes_remote_path():
  166. command = module.make_list_command(
  167. repository='repo',
  168. storage_config={},
  169. list_arguments=flexmock(archive=None, paths=None, json=False),
  170. remote_path='borg2',
  171. )
  172. assert command == ('borg', 'list', '--remote-path', 'borg2', 'repo')
  173. def test_make_list_command_includes_short():
  174. command = module.make_list_command(
  175. repository='repo',
  176. storage_config={},
  177. list_arguments=flexmock(archive=None, paths=None, json=False, short=True),
  178. )
  179. assert command == ('borg', 'list', '--short', 'repo')
  180. @pytest.mark.parametrize(
  181. 'argument_name',
  182. (
  183. 'prefix',
  184. 'glob_archives',
  185. 'sort_by',
  186. 'first',
  187. 'last',
  188. 'exclude',
  189. 'exclude_from',
  190. 'pattern',
  191. 'patterns_from',
  192. ),
  193. )
  194. def test_make_list_command_includes_additional_flags(argument_name):
  195. command = module.make_list_command(
  196. repository='repo',
  197. storage_config={},
  198. list_arguments=flexmock(
  199. archive=None,
  200. paths=None,
  201. json=False,
  202. find_paths=None,
  203. format=None,
  204. **{argument_name: 'value'}
  205. ),
  206. )
  207. assert command == ('borg', 'list', '--' + argument_name.replace('_', '-'), 'value', 'repo')
  208. def test_make_find_paths_considers_none_as_empty_paths():
  209. assert module.make_find_paths(None) == ()
  210. def test_make_find_paths_passes_through_patterns():
  211. find_paths = (
  212. 'fm:*',
  213. 'sh:**/*.txt',
  214. 're:^.*$',
  215. 'pp:root/somedir',
  216. 'pf:root/foo.txt',
  217. 'R /',
  218. 'r /',
  219. 'p /',
  220. 'P /',
  221. '+ /',
  222. '- /',
  223. '! /',
  224. )
  225. assert module.make_find_paths(find_paths) == find_paths
  226. def test_make_find_paths_adds_globs_to_path_fragments():
  227. assert module.make_find_paths(('foo.txt',)) == ('sh:**/*foo.txt*/**',)
  228. def test_list_archives_calls_borg_with_parameters():
  229. list_arguments = argparse.Namespace(archive=None, paths=None, json=False, find_paths=None)
  230. flexmock(module).should_receive('make_list_command').with_args(
  231. repository='repo',
  232. storage_config={},
  233. list_arguments=list_arguments,
  234. local_path='borg',
  235. remote_path=None,
  236. ).and_return(('borg', 'list', 'repo'))
  237. flexmock(module).should_receive('make_find_paths').and_return(())
  238. flexmock(module.environment).should_receive('make_environment')
  239. flexmock(module).should_receive('execute_command').with_args(
  240. ('borg', 'list', 'repo'),
  241. output_log_level=logging.WARNING,
  242. borg_local_path='borg',
  243. extra_environment=None,
  244. ).once()
  245. module.list_archives(
  246. repository='repo', storage_config={}, list_arguments=list_arguments,
  247. )
  248. def test_list_archives_with_json_suppresses_most_borg_output():
  249. list_arguments = argparse.Namespace(archive=None, paths=None, json=True, find_paths=None)
  250. flexmock(module).should_receive('make_list_command').with_args(
  251. repository='repo',
  252. storage_config={},
  253. list_arguments=list_arguments,
  254. local_path='borg',
  255. remote_path=None,
  256. ).and_return(('borg', 'list', 'repo'))
  257. flexmock(module).should_receive('make_find_paths').and_return(())
  258. flexmock(module.environment).should_receive('make_environment')
  259. flexmock(module).should_receive('execute_command').with_args(
  260. ('borg', 'list', 'repo'),
  261. output_log_level=None,
  262. borg_local_path='borg',
  263. extra_environment=None,
  264. ).once()
  265. module.list_archives(
  266. repository='repo', storage_config={}, list_arguments=list_arguments,
  267. )
  268. def test_list_archives_calls_borg_with_local_path():
  269. list_arguments = argparse.Namespace(archive=None, paths=None, json=False, find_paths=None)
  270. flexmock(module).should_receive('make_list_command').with_args(
  271. repository='repo',
  272. storage_config={},
  273. list_arguments=list_arguments,
  274. local_path='borg2',
  275. remote_path=None,
  276. ).and_return(('borg2', 'list', 'repo'))
  277. flexmock(module).should_receive('make_find_paths').and_return(())
  278. flexmock(module.environment).should_receive('make_environment')
  279. flexmock(module).should_receive('execute_command').with_args(
  280. ('borg2', 'list', 'repo'),
  281. output_log_level=logging.WARNING,
  282. borg_local_path='borg2',
  283. extra_environment=None,
  284. ).once()
  285. module.list_archives(
  286. repository='repo', storage_config={}, list_arguments=list_arguments, local_path='borg2',
  287. )
  288. def test_list_archives_calls_borg_multiple_times_with_find_paths():
  289. glob_paths = ('**/*foo.txt*/**',)
  290. list_arguments = argparse.Namespace(
  291. archive=None, paths=None, json=False, find_paths=['foo.txt'], format=None
  292. )
  293. flexmock(module).should_receive('make_list_command').and_return(
  294. ('borg', 'list', 'repo')
  295. ).and_return(('borg', 'list', 'repo::archive1')).and_return(('borg', 'list', 'repo::archive2'))
  296. flexmock(module).should_receive('make_find_paths').and_return(glob_paths)
  297. flexmock(module.environment).should_receive('make_environment')
  298. flexmock(module).should_receive('execute_command').with_args(
  299. ('borg', 'list', 'repo'),
  300. output_log_level=None,
  301. borg_local_path='borg',
  302. extra_environment=None,
  303. ).and_return(
  304. 'archive1 Sun, 2022-05-29 15:27:04 [abc]\narchive2 Mon, 2022-05-30 19:47:15 [xyz]'
  305. ).once()
  306. flexmock(module.environment).should_receive('make_environment')
  307. flexmock(module).should_receive('execute_command').with_args(
  308. ('borg', 'list', 'repo::archive1') + glob_paths,
  309. output_log_level=logging.WARNING,
  310. borg_local_path='borg',
  311. extra_environment=None,
  312. ).once()
  313. flexmock(module).should_receive('execute_command').with_args(
  314. ('borg', 'list', 'repo::archive2') + glob_paths,
  315. output_log_level=logging.WARNING,
  316. borg_local_path='borg',
  317. extra_environment=None,
  318. ).once()
  319. module.list_archives(
  320. repository='repo', storage_config={}, list_arguments=list_arguments,
  321. )
  322. def test_list_archives_calls_borg_with_archive():
  323. list_arguments = argparse.Namespace(archive='archive', paths=None, json=False, find_paths=None)
  324. flexmock(module).should_receive('make_list_command').with_args(
  325. repository='repo',
  326. storage_config={},
  327. list_arguments=list_arguments,
  328. local_path='borg',
  329. remote_path=None,
  330. ).and_return(('borg', 'list', 'repo::archive'))
  331. flexmock(module).should_receive('make_find_paths').and_return(())
  332. flexmock(module.environment).should_receive('make_environment')
  333. flexmock(module).should_receive('execute_command').with_args(
  334. ('borg', 'list', 'repo::archive'),
  335. output_log_level=logging.WARNING,
  336. borg_local_path='borg',
  337. extra_environment=None,
  338. ).once()
  339. module.list_archives(
  340. repository='repo', storage_config={}, list_arguments=list_arguments,
  341. )