test_borgmatic.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138
  1. import logging
  2. import subprocess
  3. import time
  4. from flexmock import flexmock
  5. import borgmatic.hooks.command
  6. from borgmatic.commands import borgmatic as module
  7. def test_run_configuration_runs_actions_for_each_repository():
  8. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  9. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  10. expected_results = [flexmock(), flexmock()]
  11. flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
  12. expected_results[1:]
  13. )
  14. config = {'location': {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}}
  15. arguments = {'global': flexmock(monitoring_verbosity=1)}
  16. results = list(module.run_configuration('test.yaml', config, arguments))
  17. assert results == expected_results
  18. def test_run_configuration_with_invalid_borg_version_errors():
  19. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  20. flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
  21. flexmock(module.command).should_receive('execute_hook').never()
  22. flexmock(module.dispatch).should_receive('call_hooks').never()
  23. flexmock(module).should_receive('run_actions').never()
  24. config = {'location': {'repositories': ['foo']}}
  25. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'prune': flexmock()}
  26. list(module.run_configuration('test.yaml', config, arguments))
  27. def test_run_configuration_logs_monitor_start_error():
  28. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  29. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  30. flexmock(module.dispatch).should_receive('call_hooks').and_raise(OSError).and_return(
  31. None
  32. ).and_return(None).and_return(None)
  33. expected_results = [flexmock()]
  34. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  35. flexmock(module).should_receive('run_actions').never()
  36. config = {'location': {'repositories': ['foo']}}
  37. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  38. results = list(module.run_configuration('test.yaml', config, arguments))
  39. assert results == expected_results
  40. def test_run_configuration_bails_for_monitor_start_soft_failure():
  41. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  42. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  43. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  44. flexmock(module.dispatch).should_receive('call_hooks').and_raise(error)
  45. flexmock(module).should_receive('log_error_records').never()
  46. flexmock(module).should_receive('run_actions').never()
  47. config = {'location': {'repositories': ['foo']}}
  48. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  49. results = list(module.run_configuration('test.yaml', config, arguments))
  50. assert results == []
  51. def test_run_configuration_logs_actions_error():
  52. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  53. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  54. flexmock(module.command).should_receive('execute_hook')
  55. flexmock(module.dispatch).should_receive('call_hooks')
  56. expected_results = [flexmock()]
  57. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  58. flexmock(module).should_receive('run_actions').and_raise(OSError)
  59. config = {'location': {'repositories': [{'path': 'foo'}]}}
  60. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  61. results = list(module.run_configuration('test.yaml', config, arguments))
  62. assert results == expected_results
  63. def test_run_configuration_bails_for_actions_soft_failure():
  64. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  65. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  66. flexmock(module.dispatch).should_receive('call_hooks')
  67. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  68. flexmock(module).should_receive('run_actions').and_raise(error)
  69. flexmock(module).should_receive('log_error_records').never()
  70. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  71. config = {'location': {'repositories': [{'path': 'foo'}]}}
  72. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  73. results = list(module.run_configuration('test.yaml', config, arguments))
  74. assert results == []
  75. def test_run_configuration_logs_monitor_log_error():
  76. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  77. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  78. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  79. None
  80. ).and_raise(OSError)
  81. expected_results = [flexmock()]
  82. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  83. flexmock(module).should_receive('run_actions').and_return([])
  84. config = {'location': {'repositories': [{'path': 'foo'}]}}
  85. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  86. results = list(module.run_configuration('test.yaml', config, arguments))
  87. assert results == expected_results
  88. def test_run_configuration_bails_for_monitor_log_soft_failure():
  89. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  90. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  91. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  92. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  93. None
  94. ).and_raise(error)
  95. flexmock(module).should_receive('log_error_records').never()
  96. flexmock(module).should_receive('run_actions').and_return([])
  97. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  98. config = {'location': {'repositories': [{'path': 'foo'}]}}
  99. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  100. results = list(module.run_configuration('test.yaml', config, arguments))
  101. assert results == []
  102. def test_run_configuration_logs_monitor_finish_error():
  103. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  104. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  105. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  106. None
  107. ).and_return(None).and_raise(OSError)
  108. expected_results = [flexmock()]
  109. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  110. flexmock(module).should_receive('run_actions').and_return([])
  111. config = {'location': {'repositories': [{'path': 'foo'}]}}
  112. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  113. results = list(module.run_configuration('test.yaml', config, arguments))
  114. assert results == expected_results
  115. def test_run_configuration_bails_for_monitor_finish_soft_failure():
  116. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  117. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  118. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  119. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  120. None
  121. ).and_raise(None).and_raise(error)
  122. flexmock(module).should_receive('log_error_records').never()
  123. flexmock(module).should_receive('run_actions').and_return([])
  124. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  125. config = {'location': {'repositories': [{'path': 'foo'}]}}
  126. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  127. results = list(module.run_configuration('test.yaml', config, arguments))
  128. assert results == []
  129. def test_run_configuration_logs_on_error_hook_error():
  130. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  131. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  132. flexmock(module.command).should_receive('execute_hook').and_raise(OSError)
  133. expected_results = [flexmock(), flexmock()]
  134. flexmock(module).should_receive('log_error_records').and_return(
  135. expected_results[:1]
  136. ).and_return(expected_results[1:])
  137. flexmock(module).should_receive('run_actions').and_raise(OSError)
  138. config = {'location': {'repositories': [{'path': 'foo'}]}}
  139. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  140. results = list(module.run_configuration('test.yaml', config, arguments))
  141. assert results == expected_results
  142. def test_run_configuration_bails_for_on_error_hook_soft_failure():
  143. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  144. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  145. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  146. flexmock(module.command).should_receive('execute_hook').and_raise(error)
  147. expected_results = [flexmock()]
  148. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  149. flexmock(module).should_receive('run_actions').and_raise(OSError)
  150. config = {'location': {'repositories': [{'path': 'foo'}]}}
  151. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  152. results = list(module.run_configuration('test.yaml', config, arguments))
  153. assert results == expected_results
  154. def test_run_configuration_retries_soft_error():
  155. # Run action first fails, second passes
  156. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  157. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  158. flexmock(module.command).should_receive('execute_hook')
  159. flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
  160. flexmock(module).should_receive('log_error_records').and_return([flexmock()]).once()
  161. config = {'location': {'repositories': [{'path': 'foo'}]}, 'storage': {'retries': 1}}
  162. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  163. results = list(module.run_configuration('test.yaml', config, arguments))
  164. assert results == []
  165. def test_run_configuration_retries_hard_error():
  166. # Run action fails twice
  167. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  168. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  169. flexmock(module.command).should_receive('execute_hook')
  170. flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
  171. flexmock(module).should_receive('log_error_records').with_args(
  172. 'foo: Error running actions for repository',
  173. OSError,
  174. levelno=logging.WARNING,
  175. log_command_error_output=True,
  176. ).and_return([flexmock()])
  177. error_logs = [flexmock()]
  178. flexmock(module).should_receive('log_error_records').with_args(
  179. 'foo: Error running actions for repository', OSError,
  180. ).and_return(error_logs)
  181. config = {'location': {'repositories': [{'path': 'foo'}]}, 'storage': {'retries': 1}}
  182. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  183. results = list(module.run_configuration('test.yaml', config, arguments))
  184. assert results == error_logs
  185. def test_run_configuration_repos_ordered():
  186. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  187. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  188. flexmock(module.command).should_receive('execute_hook')
  189. flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
  190. expected_results = [flexmock(), flexmock()]
  191. flexmock(module).should_receive('log_error_records').with_args(
  192. 'foo: Error running actions for repository', OSError
  193. ).and_return(expected_results[:1]).ordered()
  194. flexmock(module).should_receive('log_error_records').with_args(
  195. 'bar: Error running actions for repository', OSError
  196. ).and_return(expected_results[1:]).ordered()
  197. config = {'location': {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}}
  198. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  199. results = list(module.run_configuration('test.yaml', config, arguments))
  200. assert results == expected_results
  201. def test_run_configuration_retries_round_robin():
  202. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  203. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  204. flexmock(module.command).should_receive('execute_hook')
  205. flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
  206. flexmock(module).should_receive('log_error_records').with_args(
  207. 'foo: Error running actions for repository',
  208. OSError,
  209. levelno=logging.WARNING,
  210. log_command_error_output=True,
  211. ).and_return([flexmock()]).ordered()
  212. flexmock(module).should_receive('log_error_records').with_args(
  213. 'bar: Error running actions for repository',
  214. OSError,
  215. levelno=logging.WARNING,
  216. log_command_error_output=True,
  217. ).and_return([flexmock()]).ordered()
  218. foo_error_logs = [flexmock()]
  219. flexmock(module).should_receive('log_error_records').with_args(
  220. 'foo: Error running actions for repository', OSError
  221. ).and_return(foo_error_logs).ordered()
  222. bar_error_logs = [flexmock()]
  223. flexmock(module).should_receive('log_error_records').with_args(
  224. 'bar: Error running actions for repository', OSError
  225. ).and_return(bar_error_logs).ordered()
  226. config = {
  227. 'location': {'repositories': [{'path': 'foo'}, {'path': 'bar'}]},
  228. 'storage': {'retries': 1},
  229. }
  230. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  231. results = list(module.run_configuration('test.yaml', config, arguments))
  232. assert results == foo_error_logs + bar_error_logs
  233. def test_run_configuration_retries_one_passes():
  234. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  235. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  236. flexmock(module.command).should_receive('execute_hook')
  237. flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
  238. []
  239. ).and_raise(OSError).times(4)
  240. flexmock(module).should_receive('log_error_records').with_args(
  241. 'foo: Error running actions for repository',
  242. OSError,
  243. levelno=logging.WARNING,
  244. log_command_error_output=True,
  245. ).and_return([flexmock()]).ordered()
  246. flexmock(module).should_receive('log_error_records').with_args(
  247. 'bar: Error running actions for repository',
  248. OSError,
  249. levelno=logging.WARNING,
  250. log_command_error_output=True,
  251. ).and_return(flexmock()).ordered()
  252. error_logs = [flexmock()]
  253. flexmock(module).should_receive('log_error_records').with_args(
  254. 'bar: Error running actions for repository', OSError
  255. ).and_return(error_logs).ordered()
  256. config = {
  257. 'location': {'repositories': [{'path': 'foo'}, {'path': 'bar'}]},
  258. 'storage': {'retries': 1},
  259. }
  260. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  261. results = list(module.run_configuration('test.yaml', config, arguments))
  262. assert results == error_logs
  263. def test_run_configuration_retry_wait():
  264. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  265. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  266. flexmock(module.command).should_receive('execute_hook')
  267. flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
  268. flexmock(module).should_receive('log_error_records').with_args(
  269. 'foo: Error running actions for repository',
  270. OSError,
  271. levelno=logging.WARNING,
  272. log_command_error_output=True,
  273. ).and_return([flexmock()]).ordered()
  274. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  275. flexmock(module).should_receive('log_error_records').with_args(
  276. 'foo: Error running actions for repository',
  277. OSError,
  278. levelno=logging.WARNING,
  279. log_command_error_output=True,
  280. ).and_return([flexmock()]).ordered()
  281. flexmock(time).should_receive('sleep').with_args(20).and_return().ordered()
  282. flexmock(module).should_receive('log_error_records').with_args(
  283. 'foo: Error running actions for repository',
  284. OSError,
  285. levelno=logging.WARNING,
  286. log_command_error_output=True,
  287. ).and_return([flexmock()]).ordered()
  288. flexmock(time).should_receive('sleep').with_args(30).and_return().ordered()
  289. error_logs = [flexmock()]
  290. flexmock(module).should_receive('log_error_records').with_args(
  291. 'foo: Error running actions for repository', OSError
  292. ).and_return(error_logs).ordered()
  293. config = {
  294. 'location': {'repositories': [{'path': 'foo'}]},
  295. 'storage': {'retries': 3, 'retry_wait': 10},
  296. }
  297. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  298. results = list(module.run_configuration('test.yaml', config, arguments))
  299. assert results == error_logs
  300. def test_run_configuration_retries_timeout_multiple_repos():
  301. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  302. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  303. flexmock(module.command).should_receive('execute_hook')
  304. flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
  305. []
  306. ).and_raise(OSError).times(4)
  307. flexmock(module).should_receive('log_error_records').with_args(
  308. 'foo: Error running actions for repository',
  309. OSError,
  310. levelno=logging.WARNING,
  311. log_command_error_output=True,
  312. ).and_return([flexmock()]).ordered()
  313. flexmock(module).should_receive('log_error_records').with_args(
  314. 'bar: Error running actions for repository',
  315. OSError,
  316. levelno=logging.WARNING,
  317. log_command_error_output=True,
  318. ).and_return([flexmock()]).ordered()
  319. # Sleep before retrying foo (and passing)
  320. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  321. # Sleep before retrying bar (and failing)
  322. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  323. error_logs = [flexmock()]
  324. flexmock(module).should_receive('log_error_records').with_args(
  325. 'bar: Error running actions for repository', OSError
  326. ).and_return(error_logs).ordered()
  327. config = {
  328. 'location': {'repositories': [{'path': 'foo'}, {'path': 'bar'}]},
  329. 'storage': {'retries': 1, 'retry_wait': 10},
  330. }
  331. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  332. results = list(module.run_configuration('test.yaml', config, arguments))
  333. assert results == error_logs
  334. def test_run_actions_runs_rcreate():
  335. flexmock(module).should_receive('add_custom_log_levels')
  336. flexmock(module.command).should_receive('execute_hook')
  337. flexmock(borgmatic.actions.rcreate).should_receive('run_rcreate').once()
  338. tuple(
  339. module.run_actions(
  340. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'rcreate': flexmock()},
  341. config_filename=flexmock(),
  342. location={'repositories': []},
  343. storage=flexmock(),
  344. retention=flexmock(),
  345. consistency=flexmock(),
  346. hooks={},
  347. local_path=flexmock(),
  348. remote_path=flexmock(),
  349. local_borg_version=flexmock(),
  350. repository={'path': 'repo'},
  351. )
  352. )
  353. def test_run_actions_adds_log_file_to_hook_context():
  354. flexmock(module).should_receive('add_custom_log_levels')
  355. flexmock(module.command).should_receive('execute_hook')
  356. flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
  357. config_filename=flexmock(),
  358. repository={'path': 'repo'},
  359. location={'repositories': []},
  360. storage=flexmock(),
  361. hooks={},
  362. hook_context={'log_file': 'foo'},
  363. local_borg_version=flexmock(),
  364. create_arguments=flexmock(),
  365. global_arguments=flexmock(dry_run=False, log_file='foo'),
  366. dry_run_label='',
  367. local_path=flexmock(),
  368. remote_path=flexmock(),
  369. ).once()
  370. tuple(
  371. module.run_actions(
  372. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  373. config_filename=flexmock(),
  374. location={'repositories': []},
  375. storage=flexmock(),
  376. retention=flexmock(),
  377. consistency=flexmock(),
  378. hooks={},
  379. local_path=flexmock(),
  380. remote_path=flexmock(),
  381. local_borg_version=flexmock(),
  382. repository={'path': 'repo'},
  383. )
  384. )
  385. def test_run_actions_runs_transfer():
  386. flexmock(module).should_receive('add_custom_log_levels')
  387. flexmock(module.command).should_receive('execute_hook')
  388. flexmock(borgmatic.actions.transfer).should_receive('run_transfer').once()
  389. tuple(
  390. module.run_actions(
  391. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'transfer': flexmock()},
  392. config_filename=flexmock(),
  393. location={'repositories': []},
  394. storage=flexmock(),
  395. retention=flexmock(),
  396. consistency=flexmock(),
  397. hooks={},
  398. local_path=flexmock(),
  399. remote_path=flexmock(),
  400. local_borg_version=flexmock(),
  401. repository={'path': 'repo'},
  402. )
  403. )
  404. def test_run_actions_runs_create():
  405. flexmock(module).should_receive('add_custom_log_levels')
  406. flexmock(module.command).should_receive('execute_hook')
  407. expected = flexmock()
  408. flexmock(borgmatic.actions.create).should_receive('run_create').and_yield(expected).once()
  409. result = tuple(
  410. module.run_actions(
  411. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  412. config_filename=flexmock(),
  413. location={'repositories': []},
  414. storage=flexmock(),
  415. retention=flexmock(),
  416. consistency=flexmock(),
  417. hooks={},
  418. local_path=flexmock(),
  419. remote_path=flexmock(),
  420. local_borg_version=flexmock(),
  421. repository={'path': 'repo'},
  422. )
  423. )
  424. assert result == (expected,)
  425. def test_run_actions_runs_prune():
  426. flexmock(module).should_receive('add_custom_log_levels')
  427. flexmock(module.command).should_receive('execute_hook')
  428. flexmock(borgmatic.actions.prune).should_receive('run_prune').once()
  429. tuple(
  430. module.run_actions(
  431. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'prune': flexmock()},
  432. config_filename=flexmock(),
  433. location={'repositories': []},
  434. storage=flexmock(),
  435. retention=flexmock(),
  436. consistency=flexmock(),
  437. hooks={},
  438. local_path=flexmock(),
  439. remote_path=flexmock(),
  440. local_borg_version=flexmock(),
  441. repository={'path': 'repo'},
  442. )
  443. )
  444. def test_run_actions_runs_compact():
  445. flexmock(module).should_receive('add_custom_log_levels')
  446. flexmock(module.command).should_receive('execute_hook')
  447. flexmock(borgmatic.actions.compact).should_receive('run_compact').once()
  448. tuple(
  449. module.run_actions(
  450. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'compact': flexmock()},
  451. config_filename=flexmock(),
  452. location={'repositories': []},
  453. storage=flexmock(),
  454. retention=flexmock(),
  455. consistency=flexmock(),
  456. hooks={},
  457. local_path=flexmock(),
  458. remote_path=flexmock(),
  459. local_borg_version=flexmock(),
  460. repository={'path': 'repo'},
  461. )
  462. )
  463. def test_run_actions_runs_check_when_repository_enabled_for_checks():
  464. flexmock(module).should_receive('add_custom_log_levels')
  465. flexmock(module.command).should_receive('execute_hook')
  466. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
  467. flexmock(borgmatic.actions.check).should_receive('run_check').once()
  468. tuple(
  469. module.run_actions(
  470. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  471. config_filename=flexmock(),
  472. location={'repositories': []},
  473. storage=flexmock(),
  474. retention=flexmock(),
  475. consistency=flexmock(),
  476. hooks={},
  477. local_path=flexmock(),
  478. remote_path=flexmock(),
  479. local_borg_version=flexmock(),
  480. repository={'path': 'repo'},
  481. )
  482. )
  483. def test_run_actions_skips_check_when_repository_not_enabled_for_checks():
  484. flexmock(module).should_receive('add_custom_log_levels')
  485. flexmock(module.command).should_receive('execute_hook')
  486. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(False)
  487. flexmock(borgmatic.actions.check).should_receive('run_check').never()
  488. tuple(
  489. module.run_actions(
  490. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  491. config_filename=flexmock(),
  492. location={'repositories': []},
  493. storage=flexmock(),
  494. retention=flexmock(),
  495. consistency=flexmock(),
  496. hooks={},
  497. local_path=flexmock(),
  498. remote_path=flexmock(),
  499. local_borg_version=flexmock(),
  500. repository={'path': 'repo'},
  501. )
  502. )
  503. def test_run_actions_runs_extract():
  504. flexmock(module).should_receive('add_custom_log_levels')
  505. flexmock(module.command).should_receive('execute_hook')
  506. flexmock(borgmatic.actions.extract).should_receive('run_extract').once()
  507. tuple(
  508. module.run_actions(
  509. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'extract': flexmock()},
  510. config_filename=flexmock(),
  511. location={'repositories': []},
  512. storage=flexmock(),
  513. retention=flexmock(),
  514. consistency=flexmock(),
  515. hooks={},
  516. local_path=flexmock(),
  517. remote_path=flexmock(),
  518. local_borg_version=flexmock(),
  519. repository={'path': 'repo'},
  520. )
  521. )
  522. def test_run_actions_runs_export_tar():
  523. flexmock(module).should_receive('add_custom_log_levels')
  524. flexmock(module.command).should_receive('execute_hook')
  525. flexmock(borgmatic.actions.export_tar).should_receive('run_export_tar').once()
  526. tuple(
  527. module.run_actions(
  528. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'export-tar': flexmock()},
  529. config_filename=flexmock(),
  530. location={'repositories': []},
  531. storage=flexmock(),
  532. retention=flexmock(),
  533. consistency=flexmock(),
  534. hooks={},
  535. local_path=flexmock(),
  536. remote_path=flexmock(),
  537. local_borg_version=flexmock(),
  538. repository={'path': 'repo'},
  539. )
  540. )
  541. def test_run_actions_runs_mount():
  542. flexmock(module).should_receive('add_custom_log_levels')
  543. flexmock(module.command).should_receive('execute_hook')
  544. flexmock(borgmatic.actions.mount).should_receive('run_mount').once()
  545. tuple(
  546. module.run_actions(
  547. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'mount': flexmock()},
  548. config_filename=flexmock(),
  549. location={'repositories': []},
  550. storage=flexmock(),
  551. retention=flexmock(),
  552. consistency=flexmock(),
  553. hooks={},
  554. local_path=flexmock(),
  555. remote_path=flexmock(),
  556. local_borg_version=flexmock(),
  557. repository={'path': 'repo'},
  558. )
  559. )
  560. def test_run_actions_runs_restore():
  561. flexmock(module).should_receive('add_custom_log_levels')
  562. flexmock(module.command).should_receive('execute_hook')
  563. flexmock(borgmatic.actions.restore).should_receive('run_restore').once()
  564. tuple(
  565. module.run_actions(
  566. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'restore': flexmock()},
  567. config_filename=flexmock(),
  568. location={'repositories': []},
  569. storage=flexmock(),
  570. retention=flexmock(),
  571. consistency=flexmock(),
  572. hooks={},
  573. local_path=flexmock(),
  574. remote_path=flexmock(),
  575. local_borg_version=flexmock(),
  576. repository={'path': 'repo'},
  577. )
  578. )
  579. def test_run_actions_runs_rlist():
  580. flexmock(module).should_receive('add_custom_log_levels')
  581. flexmock(module.command).should_receive('execute_hook')
  582. expected = flexmock()
  583. flexmock(borgmatic.actions.rlist).should_receive('run_rlist').and_yield(expected).once()
  584. result = tuple(
  585. module.run_actions(
  586. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'rlist': flexmock()},
  587. config_filename=flexmock(),
  588. location={'repositories': []},
  589. storage=flexmock(),
  590. retention=flexmock(),
  591. consistency=flexmock(),
  592. hooks={},
  593. local_path=flexmock(),
  594. remote_path=flexmock(),
  595. local_borg_version=flexmock(),
  596. repository={'path': 'repo'},
  597. )
  598. )
  599. assert result == (expected,)
  600. def test_run_actions_runs_list():
  601. flexmock(module).should_receive('add_custom_log_levels')
  602. flexmock(module.command).should_receive('execute_hook')
  603. expected = flexmock()
  604. flexmock(borgmatic.actions.list).should_receive('run_list').and_yield(expected).once()
  605. result = tuple(
  606. module.run_actions(
  607. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'list': flexmock()},
  608. config_filename=flexmock(),
  609. location={'repositories': []},
  610. storage=flexmock(),
  611. retention=flexmock(),
  612. consistency=flexmock(),
  613. hooks={},
  614. local_path=flexmock(),
  615. remote_path=flexmock(),
  616. local_borg_version=flexmock(),
  617. repository={'path': 'repo'},
  618. )
  619. )
  620. assert result == (expected,)
  621. def test_run_actions_runs_rinfo():
  622. flexmock(module).should_receive('add_custom_log_levels')
  623. flexmock(module.command).should_receive('execute_hook')
  624. expected = flexmock()
  625. flexmock(borgmatic.actions.rinfo).should_receive('run_rinfo').and_yield(expected).once()
  626. result = tuple(
  627. module.run_actions(
  628. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'rinfo': flexmock()},
  629. config_filename=flexmock(),
  630. location={'repositories': []},
  631. storage=flexmock(),
  632. retention=flexmock(),
  633. consistency=flexmock(),
  634. hooks={},
  635. local_path=flexmock(),
  636. remote_path=flexmock(),
  637. local_borg_version=flexmock(),
  638. repository={'path': 'repo'},
  639. )
  640. )
  641. assert result == (expected,)
  642. def test_run_actions_runs_info():
  643. flexmock(module).should_receive('add_custom_log_levels')
  644. flexmock(module.command).should_receive('execute_hook')
  645. expected = flexmock()
  646. flexmock(borgmatic.actions.info).should_receive('run_info').and_yield(expected).once()
  647. result = tuple(
  648. module.run_actions(
  649. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'info': flexmock()},
  650. config_filename=flexmock(),
  651. location={'repositories': []},
  652. storage=flexmock(),
  653. retention=flexmock(),
  654. consistency=flexmock(),
  655. hooks={},
  656. local_path=flexmock(),
  657. remote_path=flexmock(),
  658. local_borg_version=flexmock(),
  659. repository={'path': 'repo'},
  660. )
  661. )
  662. assert result == (expected,)
  663. def test_run_actions_runs_break_lock():
  664. flexmock(module).should_receive('add_custom_log_levels')
  665. flexmock(module.command).should_receive('execute_hook')
  666. flexmock(borgmatic.actions.break_lock).should_receive('run_break_lock').once()
  667. tuple(
  668. module.run_actions(
  669. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'break-lock': flexmock()},
  670. config_filename=flexmock(),
  671. location={'repositories': []},
  672. storage=flexmock(),
  673. retention=flexmock(),
  674. consistency=flexmock(),
  675. hooks={},
  676. local_path=flexmock(),
  677. remote_path=flexmock(),
  678. local_borg_version=flexmock(),
  679. repository={'path': 'repo'},
  680. )
  681. )
  682. def test_run_actions_runs_borg():
  683. flexmock(module).should_receive('add_custom_log_levels')
  684. flexmock(module.command).should_receive('execute_hook')
  685. flexmock(borgmatic.actions.borg).should_receive('run_borg').once()
  686. tuple(
  687. module.run_actions(
  688. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'borg': flexmock()},
  689. config_filename=flexmock(),
  690. location={'repositories': []},
  691. storage=flexmock(),
  692. retention=flexmock(),
  693. consistency=flexmock(),
  694. hooks={},
  695. local_path=flexmock(),
  696. remote_path=flexmock(),
  697. local_borg_version=flexmock(),
  698. repository={'path': 'repo'},
  699. )
  700. )
  701. def test_run_actions_runs_multiple_actions_in_argument_order():
  702. flexmock(module).should_receive('add_custom_log_levels')
  703. flexmock(module.command).should_receive('execute_hook')
  704. flexmock(borgmatic.actions.borg).should_receive('run_borg').once().ordered()
  705. flexmock(borgmatic.actions.restore).should_receive('run_restore').once().ordered()
  706. tuple(
  707. module.run_actions(
  708. arguments={
  709. 'global': flexmock(dry_run=False, log_file='foo'),
  710. 'borg': flexmock(),
  711. 'restore': flexmock(),
  712. },
  713. config_filename=flexmock(),
  714. location={'repositories': []},
  715. storage=flexmock(),
  716. retention=flexmock(),
  717. consistency=flexmock(),
  718. hooks={},
  719. local_path=flexmock(),
  720. remote_path=flexmock(),
  721. local_borg_version=flexmock(),
  722. repository={'path': 'repo'},
  723. )
  724. )
  725. def test_load_configurations_collects_parsed_configurations_and_logs():
  726. configuration = flexmock()
  727. other_configuration = flexmock()
  728. test_expected_logs = [flexmock(), flexmock()]
  729. other_expected_logs = [flexmock(), flexmock()]
  730. flexmock(module.validate).should_receive('parse_configuration').and_return(
  731. configuration, test_expected_logs
  732. ).and_return(other_configuration, other_expected_logs)
  733. configs, logs = tuple(module.load_configurations(('test.yaml', 'other.yaml')))
  734. assert configs == {'test.yaml': configuration, 'other.yaml': other_configuration}
  735. assert logs == test_expected_logs + other_expected_logs
  736. def test_load_configurations_logs_warning_for_permission_error():
  737. flexmock(module.validate).should_receive('parse_configuration').and_raise(PermissionError)
  738. configs, logs = tuple(module.load_configurations(('test.yaml',)))
  739. assert configs == {}
  740. assert {log.levelno for log in logs} == {logging.WARNING}
  741. def test_load_configurations_logs_critical_for_parse_error():
  742. flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
  743. configs, logs = tuple(module.load_configurations(('test.yaml',)))
  744. assert configs == {}
  745. assert {log.levelno for log in logs} == {logging.CRITICAL}
  746. def test_log_record_does_not_raise():
  747. module.log_record(levelno=1, foo='bar', baz='quux')
  748. def test_log_record_with_suppress_does_not_raise():
  749. module.log_record(levelno=1, foo='bar', baz='quux', suppress_log=True)
  750. def test_log_error_records_generates_output_logs_for_message_only():
  751. flexmock(module).should_receive('log_record').replace_with(dict)
  752. logs = tuple(module.log_error_records('Error'))
  753. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  754. def test_log_error_records_generates_output_logs_for_called_process_error():
  755. flexmock(module).should_receive('log_record').replace_with(dict)
  756. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  757. logs = tuple(
  758. module.log_error_records('Error', subprocess.CalledProcessError(1, 'ls', 'error output'))
  759. )
  760. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  761. assert any(log for log in logs if 'error output' in str(log))
  762. def test_log_error_records_generates_logs_for_value_error():
  763. flexmock(module).should_receive('log_record').replace_with(dict)
  764. logs = tuple(module.log_error_records('Error', ValueError()))
  765. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  766. def test_log_error_records_generates_logs_for_os_error():
  767. flexmock(module).should_receive('log_record').replace_with(dict)
  768. logs = tuple(module.log_error_records('Error', OSError()))
  769. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  770. def test_log_error_records_generates_nothing_for_other_error():
  771. flexmock(module).should_receive('log_record').replace_with(dict)
  772. logs = tuple(module.log_error_records('Error', KeyError()))
  773. assert logs == ()
  774. def test_get_local_path_uses_configuration_value():
  775. assert module.get_local_path({'test.yaml': {'location': {'local_path': 'borg1'}}}) == 'borg1'
  776. def test_get_local_path_without_location_defaults_to_borg():
  777. assert module.get_local_path({'test.yaml': {}}) == 'borg'
  778. def test_get_local_path_without_local_path_defaults_to_borg():
  779. assert module.get_local_path({'test.yaml': {'location': {}}}) == 'borg'
  780. def test_collect_configuration_run_summary_logs_info_for_success():
  781. flexmock(module.command).should_receive('execute_hook').never()
  782. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  783. flexmock(module).should_receive('run_configuration').and_return([])
  784. arguments = {}
  785. logs = tuple(
  786. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  787. )
  788. assert {log.levelno for log in logs} == {logging.INFO}
  789. def test_collect_configuration_run_summary_executes_hooks_for_create():
  790. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  791. flexmock(module).should_receive('run_configuration').and_return([])
  792. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  793. logs = tuple(
  794. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  795. )
  796. assert {log.levelno for log in logs} == {logging.INFO}
  797. def test_collect_configuration_run_summary_logs_info_for_success_with_extract():
  798. flexmock(module.validate).should_receive('guard_single_repository_selected')
  799. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  800. flexmock(module).should_receive('run_configuration').and_return([])
  801. arguments = {'extract': flexmock(repository='repo')}
  802. logs = tuple(
  803. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  804. )
  805. assert {log.levelno for log in logs} == {logging.INFO}
  806. def test_collect_configuration_run_summary_logs_extract_with_repository_error():
  807. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  808. ValueError
  809. )
  810. expected_logs = (flexmock(),)
  811. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  812. arguments = {'extract': flexmock(repository='repo')}
  813. logs = tuple(
  814. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  815. )
  816. assert logs == expected_logs
  817. def test_collect_configuration_run_summary_logs_info_for_success_with_mount():
  818. flexmock(module.validate).should_receive('guard_single_repository_selected')
  819. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  820. flexmock(module).should_receive('run_configuration').and_return([])
  821. arguments = {'mount': flexmock(repository='repo')}
  822. logs = tuple(
  823. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  824. )
  825. assert {log.levelno for log in logs} == {logging.INFO}
  826. def test_collect_configuration_run_summary_logs_mount_with_repository_error():
  827. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  828. ValueError
  829. )
  830. expected_logs = (flexmock(),)
  831. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  832. arguments = {'mount': flexmock(repository='repo')}
  833. logs = tuple(
  834. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  835. )
  836. assert logs == expected_logs
  837. def test_collect_configuration_run_summary_logs_missing_configs_error():
  838. arguments = {'global': flexmock(config_paths=[])}
  839. expected_logs = (flexmock(),)
  840. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  841. logs = tuple(module.collect_configuration_run_summary_logs({}, arguments=arguments))
  842. assert logs == expected_logs
  843. def test_collect_configuration_run_summary_logs_pre_hook_error():
  844. flexmock(module.command).should_receive('execute_hook').and_raise(ValueError)
  845. expected_logs = (flexmock(),)
  846. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  847. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  848. logs = tuple(
  849. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  850. )
  851. assert logs == expected_logs
  852. def test_collect_configuration_run_summary_logs_post_hook_error():
  853. flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(ValueError)
  854. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  855. flexmock(module).should_receive('run_configuration').and_return([])
  856. expected_logs = (flexmock(),)
  857. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  858. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  859. logs = tuple(
  860. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  861. )
  862. assert expected_logs[0] in logs
  863. def test_collect_configuration_run_summary_logs_for_list_with_archive_and_repository_error():
  864. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  865. ValueError
  866. )
  867. expected_logs = (flexmock(),)
  868. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  869. arguments = {'list': flexmock(repository='repo', archive='test')}
  870. logs = tuple(
  871. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  872. )
  873. assert logs == expected_logs
  874. def test_collect_configuration_run_summary_logs_info_for_success_with_list():
  875. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  876. flexmock(module).should_receive('run_configuration').and_return([])
  877. arguments = {'list': flexmock(repository='repo', archive=None)}
  878. logs = tuple(
  879. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  880. )
  881. assert {log.levelno for log in logs} == {logging.INFO}
  882. def test_collect_configuration_run_summary_logs_run_configuration_error():
  883. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  884. flexmock(module).should_receive('run_configuration').and_return(
  885. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))]
  886. )
  887. flexmock(module).should_receive('log_error_records').and_return([])
  888. arguments = {}
  889. logs = tuple(
  890. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  891. )
  892. assert {log.levelno for log in logs} == {logging.CRITICAL}
  893. def test_collect_configuration_run_summary_logs_run_umount_error():
  894. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  895. flexmock(module).should_receive('run_configuration').and_return([])
  896. flexmock(module.borg_umount).should_receive('unmount_archive').and_raise(OSError)
  897. flexmock(module).should_receive('log_error_records').and_return(
  898. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))]
  899. )
  900. arguments = {'umount': flexmock(mount_point='/mnt')}
  901. logs = tuple(
  902. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  903. )
  904. assert {log.levelno for log in logs} == {logging.INFO, logging.CRITICAL}
  905. def test_collect_configuration_run_summary_logs_outputs_merged_json_results():
  906. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  907. flexmock(module).should_receive('run_configuration').and_return(['foo', 'bar']).and_return(
  908. ['baz']
  909. )
  910. stdout = flexmock()
  911. stdout.should_receive('write').with_args('["foo", "bar", "baz"]').once()
  912. flexmock(module.sys).stdout = stdout
  913. arguments = {}
  914. tuple(
  915. module.collect_configuration_run_summary_logs(
  916. {'test.yaml': {}, 'test2.yaml': {}}, arguments=arguments
  917. )
  918. )