test_flags.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. import pytest
  2. from flexmock import flexmock
  3. from borgmatic.borg import flags as module
  4. def test_make_flags_formats_string_value():
  5. assert module.make_flags('foo', 'bar') == ('--foo', 'bar')
  6. def test_make_flags_formats_integer_value():
  7. assert module.make_flags('foo', 3) == ('--foo', '3')
  8. def test_make_flags_formats_true_value():
  9. assert module.make_flags('foo', True) == ('--foo',)
  10. def test_make_flags_omits_false_value():
  11. assert module.make_flags('foo', False) == ()
  12. def test_make_flags_formats_name_with_underscore():
  13. assert module.make_flags('posix_me_harder', 'okay') == ('--posix-me-harder', 'okay')
  14. def test_make_flags_from_arguments_flattens_and_sorts_multiple_arguments():
  15. flexmock(module).should_receive('make_flags').with_args('foo', 'bar').and_return(('foo', 'bar'))
  16. flexmock(module).should_receive('make_flags').with_args('baz', 'quux').and_return(
  17. ('baz', 'quux'),
  18. )
  19. arguments = flexmock(foo='bar', baz='quux')
  20. assert module.make_flags_from_arguments(arguments) == ('baz', 'quux', 'foo', 'bar')
  21. def test_make_flags_from_arguments_excludes_underscored_argument_names():
  22. flexmock(module).should_receive('make_flags').with_args('foo', 'bar').and_return(('foo', 'bar'))
  23. arguments = flexmock(foo='bar', _baz='quux')
  24. assert module.make_flags_from_arguments(arguments) == ('foo', 'bar')
  25. def test_make_flags_from_arguments_omits_excludes():
  26. flexmock(module).should_receive('make_flags').with_args('foo', 'bar').and_return(('foo', 'bar'))
  27. arguments = flexmock(foo='bar', baz='quux')
  28. assert module.make_flags_from_arguments(arguments, excludes=('baz', 'other')) == ('foo', 'bar')
  29. def test_make_repository_flags_with_borg_features_includes_repo_flag():
  30. flexmock(module.feature).should_receive('available').and_return(True)
  31. assert module.make_repository_flags(repository_path='repo', local_borg_version='1.2.3') == (
  32. '--repo',
  33. 'repo',
  34. )
  35. def test_make_repository_flags_without_borg_features_includes_omits_flag():
  36. flexmock(module.feature).should_receive('available').and_return(False)
  37. assert module.make_repository_flags(repository_path='repo', local_borg_version='1.2.3') == (
  38. 'repo',
  39. )
  40. def test_make_repository_archive_flags_with_borg_features_separates_repository_and_archive():
  41. flexmock(module.feature).should_receive('available').and_return(True)
  42. assert module.make_repository_archive_flags(
  43. repository_path='repo',
  44. archive='archive',
  45. local_borg_version='1.2.3',
  46. ) == (
  47. '--repo',
  48. 'repo',
  49. 'archive',
  50. )
  51. def test_make_repository_archive_flags_with_borg_features_joins_repository_and_archive():
  52. flexmock(module.feature).should_receive('available').and_return(False)
  53. assert module.make_repository_archive_flags(
  54. repository_path='repo',
  55. archive='archive',
  56. local_borg_version='1.2.3',
  57. ) == ('repo::archive',)
  58. def test_get_default_archive_name_format_with_archive_series_feature_uses_series_archive_name_format():
  59. flexmock(module.feature).should_receive('available').and_return(True)
  60. assert (
  61. module.get_default_archive_name_format(local_borg_version='1.2.3')
  62. == module.DEFAULT_ARCHIVE_NAME_FORMAT_WITH_SERIES
  63. )
  64. def test_get_default_archive_name_format_without_archive_series_feature_uses_non_series_archive_name_format():
  65. flexmock(module.feature).should_receive('available').and_return(False)
  66. assert (
  67. module.get_default_archive_name_format(local_borg_version='1.2.3')
  68. == module.DEFAULT_ARCHIVE_NAME_FORMAT_WITHOUT_SERIES
  69. )
  70. @pytest.mark.parametrize(
  71. 'match_archives,archive_name_format,feature_available,expected_result',
  72. (
  73. (None, None, True, ('--match-archives', 'sh:{hostname}-*')),
  74. (None, '', True, ('--match-archives', 'sh:{hostname}-*')),
  75. (
  76. 're:foo-.*',
  77. '{hostname}-{now}',
  78. True,
  79. ('--match-archives', 're:foo-.*'),
  80. ),
  81. (
  82. 'sh:foo-*',
  83. '{hostname}-{now}',
  84. False,
  85. ('--glob-archives', 'foo-*'),
  86. ),
  87. (
  88. 'foo-*',
  89. '{hostname}-{now}',
  90. False,
  91. ('--glob-archives', 'foo-*'),
  92. ),
  93. (
  94. None,
  95. '{hostname}-docs-{now}',
  96. True,
  97. ('--match-archives', 'sh:{hostname}-docs-*'),
  98. ),
  99. (
  100. None,
  101. '{utcnow}-docs-{user}',
  102. True,
  103. ('--match-archives', 'sh:*-docs-{user}'),
  104. ),
  105. (None, '{fqdn}-{pid}', True, ('--match-archives', 'sh:{fqdn}-*')),
  106. (
  107. None,
  108. 'stuff-{now:%Y-%m-%dT%H:%M:%S.%f}',
  109. True,
  110. ('--match-archives', 'sh:stuff-*'),
  111. ),
  112. (
  113. None,
  114. '{hostname}-docs-{now}',
  115. False,
  116. ('--glob-archives', '{hostname}-docs-*'),
  117. ),
  118. (
  119. None,
  120. '{now}',
  121. False,
  122. (),
  123. ),
  124. (
  125. None,
  126. '{now}',
  127. True,
  128. (),
  129. ),
  130. (
  131. None,
  132. '{utcnow}-docs-{user}',
  133. False,
  134. ('--glob-archives', '*-docs-{user}'),
  135. ),
  136. (
  137. '*',
  138. '{now}',
  139. True,
  140. (),
  141. ),
  142. (
  143. '*',
  144. '{now}',
  145. False,
  146. (),
  147. ),
  148. (
  149. 're:.*',
  150. '{now}',
  151. True,
  152. (),
  153. ),
  154. (
  155. 'sh:*',
  156. '{now}',
  157. True,
  158. (),
  159. ),
  160. (
  161. 'abcdefabcdef',
  162. None,
  163. True,
  164. ('--match-archives', 'aid:abcdefabcdef'),
  165. ),
  166. (
  167. 'aid:abcdefabcdef',
  168. None,
  169. True,
  170. ('--match-archives', 'aid:abcdefabcdef'),
  171. ),
  172. ),
  173. )
  174. def test_make_match_archives_flags_makes_flags_with_globs(
  175. match_archives,
  176. archive_name_format,
  177. feature_available,
  178. expected_result,
  179. ):
  180. flexmock(module.feature).should_receive('available').and_return(feature_available)
  181. flexmock(module).should_receive('get_default_archive_name_format').and_return(
  182. module.DEFAULT_ARCHIVE_NAME_FORMAT_WITHOUT_SERIES,
  183. )
  184. assert (
  185. module.make_match_archives_flags(
  186. match_archives,
  187. archive_name_format,
  188. local_borg_version=flexmock(),
  189. )
  190. == expected_result
  191. )
  192. def test_make_match_archives_flags_accepts_default_archive_name_format():
  193. flexmock(module.feature).should_receive('available').and_return(True)
  194. assert (
  195. module.make_match_archives_flags(
  196. match_archives=None,
  197. archive_name_format=None,
  198. local_borg_version=flexmock(),
  199. default_archive_name_format='*',
  200. )
  201. == ()
  202. )
  203. def test_warn_for_aggressive_archive_flags_without_archive_flags_bails():
  204. flexmock(module.logger).should_receive('warning').never()
  205. module.warn_for_aggressive_archive_flags(('borg', '--do-stuff'), '{}')
  206. def test_warn_for_aggressive_archive_flags_with_glob_archives_and_zero_archives_warns():
  207. flexmock(module.logger).should_receive('warning').twice()
  208. module.warn_for_aggressive_archive_flags(
  209. ('borg', '--glob-archives', 'foo*'),
  210. '{"archives": []}',
  211. )
  212. def test_warn_for_aggressive_archive_flags_with_match_archives_and_zero_archives_warns():
  213. flexmock(module.logger).should_receive('warning').twice()
  214. module.warn_for_aggressive_archive_flags(
  215. ('borg', '--match-archives', 'foo*'),
  216. '{"archives": []}',
  217. )
  218. def test_warn_for_aggressive_archive_flags_with_glob_archives_and_one_archive_does_not_warn():
  219. flexmock(module.logger).should_receive('warning').never()
  220. module.warn_for_aggressive_archive_flags(
  221. ('borg', '--glob-archives', 'foo*'),
  222. '{"archives": [{"name": "foo"]}',
  223. )
  224. def test_warn_for_aggressive_archive_flags_with_match_archives_and_one_archive_does_not_warn():
  225. flexmock(module.logger).should_receive('warning').never()
  226. module.warn_for_aggressive_archive_flags(
  227. ('borg', '--match-archives', 'foo*'),
  228. '{"archives": [{"name": "foo"]}',
  229. )
  230. def test_warn_for_aggressive_archive_flags_with_glob_archives_and_invalid_json_does_not_warn():
  231. flexmock(module.logger).should_receive('warning').never()
  232. module.warn_for_aggressive_archive_flags(('borg', '--glob-archives', 'foo*'), '{"archives": [}')
  233. def test_warn_for_aggressive_archive_flags_with_glob_archives_and_json_missing_archives_does_not_warn():
  234. flexmock(module.logger).should_receive('warning').never()
  235. module.warn_for_aggressive_archive_flags(('borg', '--glob-archives', 'foo*'), '{}')
  236. def test_omit_flag_removes_flag_from_arguments():
  237. assert module.omit_flag(('borg', 'create', '--flag', '--other'), '--flag') == (
  238. 'borg',
  239. 'create',
  240. '--other',
  241. )
  242. def test_omit_flag_without_flag_present_passes_through_arguments():
  243. assert module.omit_flag(('borg', 'create', '--other'), '--flag') == (
  244. 'borg',
  245. 'create',
  246. '--other',
  247. )
  248. def test_omit_flag_and_value_removes_flag_and_value_from_arguments():
  249. assert module.omit_flag_and_value(
  250. ('borg', 'create', '--flag', 'value', '--other'),
  251. '--flag',
  252. ) == (
  253. 'borg',
  254. 'create',
  255. '--other',
  256. )
  257. def test_omit_flag_and_value_with_equals_sign_removes_flag_and_value_from_arguments():
  258. assert module.omit_flag_and_value(('borg', 'create', '--flag=value', '--other'), '--flag') == (
  259. 'borg',
  260. 'create',
  261. '--other',
  262. )
  263. def test_omit_flag_and_value_without_flag_present_passes_through_arguments():
  264. assert module.omit_flag_and_value(('borg', 'create', '--other'), '--flag') == (
  265. 'borg',
  266. 'create',
  267. '--other',
  268. )
  269. def test_make_exclude_flags_includes_exclude_caches_when_true_in_config():
  270. exclude_flags = module.make_exclude_flags(config={'exclude_caches': True})
  271. assert exclude_flags == ('--exclude-caches',)
  272. def test_make_exclude_flags_does_not_include_exclude_caches_when_false_in_config():
  273. exclude_flags = module.make_exclude_flags(config={'exclude_caches': False})
  274. assert exclude_flags == ()
  275. def test_make_exclude_flags_includes_exclude_if_present_when_in_config():
  276. exclude_flags = module.make_exclude_flags(
  277. config={'exclude_if_present': ['exclude_me', 'also_me']},
  278. )
  279. assert exclude_flags == (
  280. '--exclude-if-present',
  281. 'exclude_me',
  282. '--exclude-if-present',
  283. 'also_me',
  284. )
  285. def test_make_exclude_flags_includes_keep_exclude_tags_when_true_in_config():
  286. exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': True})
  287. assert exclude_flags == ('--keep-exclude-tags',)
  288. def test_make_exclude_flags_does_not_include_keep_exclude_tags_when_false_in_config():
  289. exclude_flags = module.make_exclude_flags(config={'keep_exclude_tags': False})
  290. assert exclude_flags == ()
  291. def test_make_exclude_flags_includes_exclude_nodump_when_true_in_config():
  292. exclude_flags = module.make_exclude_flags(config={'exclude_nodump': True})
  293. assert exclude_flags == ('--exclude-nodump',)
  294. def test_make_exclude_flags_does_not_include_exclude_nodump_when_false_in_config():
  295. exclude_flags = module.make_exclude_flags(config={'exclude_nodump': False})
  296. assert exclude_flags == ()
  297. def test_make_exclude_flags_is_empty_when_config_has_no_excludes():
  298. exclude_flags = module.make_exclude_flags(config={})
  299. assert exclude_flags == ()
  300. def test_make_list_filter_flags_with_debug_and_feature_available_includes_plus_and_minus():
  301. flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
  302. flexmock(module.feature).should_receive('available').and_return(True)
  303. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME+-'
  304. def test_make_list_filter_flags_with_info_and_feature_available_omits_plus_and_minus():
  305. flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
  306. flexmock(module.feature).should_receive('available').and_return(True)
  307. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME'
  308. def test_make_list_filter_flags_with_debug_and_feature_available_and_dry_run_includes_plus_and_minus():
  309. flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
  310. flexmock(module.feature).should_receive('available').and_return(True)
  311. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
  312. def test_make_list_filter_flags_with_info_and_feature_available_and_dry_run_includes_plus_and_minus():
  313. flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
  314. flexmock(module.feature).should_receive('available').and_return(True)
  315. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=True) == 'AME+-'
  316. def test_make_list_filter_flags_with_debug_and_feature_not_available_includes_x():
  317. flexmock(module.logger).should_receive('isEnabledFor').and_return(True)
  318. flexmock(module.feature).should_receive('available').and_return(False)
  319. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AMEx-'
  320. def test_make_list_filter_flags_with_info_and_feature_not_available_omits_x():
  321. flexmock(module.logger).should_receive('isEnabledFor').and_return(False)
  322. flexmock(module.feature).should_receive('available').and_return(False)
  323. assert module.make_list_filter_flags(local_borg_version=flexmock(), dry_run=False) == 'AME-'