test_borgmatic.py 46 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142
  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. expected = flexmock()
  357. flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
  358. config_filename=object,
  359. repository={'path': 'repo'},
  360. location={'repositories': []},
  361. storage=object,
  362. hooks={},
  363. hook_context={'repository': 'repo', 'repositories': '', 'log_file': 'foo'},
  364. local_borg_version=object,
  365. create_arguments=object,
  366. global_arguments=object,
  367. dry_run_label='',
  368. local_path=object,
  369. remote_path=object,
  370. ).once().and_return(expected)
  371. result = tuple(
  372. module.run_actions(
  373. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  374. config_filename=flexmock(),
  375. location={'repositories': []},
  376. storage=flexmock(),
  377. retention=flexmock(),
  378. consistency=flexmock(),
  379. hooks={},
  380. local_path=flexmock(),
  381. remote_path=flexmock(),
  382. local_borg_version=flexmock(),
  383. repository={'path': 'repo'},
  384. )
  385. )
  386. assert result == (expected,)
  387. def test_run_actions_runs_transfer():
  388. flexmock(module).should_receive('add_custom_log_levels')
  389. flexmock(module.command).should_receive('execute_hook')
  390. flexmock(borgmatic.actions.transfer).should_receive('run_transfer').once()
  391. tuple(
  392. module.run_actions(
  393. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'transfer': flexmock()},
  394. config_filename=flexmock(),
  395. location={'repositories': []},
  396. storage=flexmock(),
  397. retention=flexmock(),
  398. consistency=flexmock(),
  399. hooks={},
  400. local_path=flexmock(),
  401. remote_path=flexmock(),
  402. local_borg_version=flexmock(),
  403. repository={'path': 'repo'},
  404. )
  405. )
  406. def test_run_actions_runs_create():
  407. flexmock(module).should_receive('add_custom_log_levels')
  408. flexmock(module.command).should_receive('execute_hook')
  409. expected = flexmock()
  410. flexmock(borgmatic.actions.create).should_receive('run_create').and_yield(expected).once()
  411. result = tuple(
  412. module.run_actions(
  413. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  414. config_filename=flexmock(),
  415. location={'repositories': []},
  416. storage=flexmock(),
  417. retention=flexmock(),
  418. consistency=flexmock(),
  419. hooks={},
  420. local_path=flexmock(),
  421. remote_path=flexmock(),
  422. local_borg_version=flexmock(),
  423. repository={'path': 'repo'},
  424. )
  425. )
  426. assert result == (expected,)
  427. def test_run_actions_runs_prune():
  428. flexmock(module).should_receive('add_custom_log_levels')
  429. flexmock(module.command).should_receive('execute_hook')
  430. flexmock(borgmatic.actions.prune).should_receive('run_prune').once()
  431. tuple(
  432. module.run_actions(
  433. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'prune': flexmock()},
  434. config_filename=flexmock(),
  435. location={'repositories': []},
  436. storage=flexmock(),
  437. retention=flexmock(),
  438. consistency=flexmock(),
  439. hooks={},
  440. local_path=flexmock(),
  441. remote_path=flexmock(),
  442. local_borg_version=flexmock(),
  443. repository={'path': 'repo'},
  444. )
  445. )
  446. def test_run_actions_runs_compact():
  447. flexmock(module).should_receive('add_custom_log_levels')
  448. flexmock(module.command).should_receive('execute_hook')
  449. flexmock(borgmatic.actions.compact).should_receive('run_compact').once()
  450. tuple(
  451. module.run_actions(
  452. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'compact': flexmock()},
  453. config_filename=flexmock(),
  454. location={'repositories': []},
  455. storage=flexmock(),
  456. retention=flexmock(),
  457. consistency=flexmock(),
  458. hooks={},
  459. local_path=flexmock(),
  460. remote_path=flexmock(),
  461. local_borg_version=flexmock(),
  462. repository={'path': 'repo'},
  463. )
  464. )
  465. def test_run_actions_runs_check_when_repository_enabled_for_checks():
  466. flexmock(module).should_receive('add_custom_log_levels')
  467. flexmock(module.command).should_receive('execute_hook')
  468. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
  469. flexmock(borgmatic.actions.check).should_receive('run_check').once()
  470. tuple(
  471. module.run_actions(
  472. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  473. config_filename=flexmock(),
  474. location={'repositories': []},
  475. storage=flexmock(),
  476. retention=flexmock(),
  477. consistency=flexmock(),
  478. hooks={},
  479. local_path=flexmock(),
  480. remote_path=flexmock(),
  481. local_borg_version=flexmock(),
  482. repository={'path': 'repo'},
  483. )
  484. )
  485. def test_run_actions_skips_check_when_repository_not_enabled_for_checks():
  486. flexmock(module).should_receive('add_custom_log_levels')
  487. flexmock(module.command).should_receive('execute_hook')
  488. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(False)
  489. flexmock(borgmatic.actions.check).should_receive('run_check').never()
  490. tuple(
  491. module.run_actions(
  492. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  493. config_filename=flexmock(),
  494. location={'repositories': []},
  495. storage=flexmock(),
  496. retention=flexmock(),
  497. consistency=flexmock(),
  498. hooks={},
  499. local_path=flexmock(),
  500. remote_path=flexmock(),
  501. local_borg_version=flexmock(),
  502. repository={'path': 'repo'},
  503. )
  504. )
  505. def test_run_actions_runs_extract():
  506. flexmock(module).should_receive('add_custom_log_levels')
  507. flexmock(module.command).should_receive('execute_hook')
  508. flexmock(borgmatic.actions.extract).should_receive('run_extract').once()
  509. tuple(
  510. module.run_actions(
  511. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'extract': flexmock()},
  512. config_filename=flexmock(),
  513. location={'repositories': []},
  514. storage=flexmock(),
  515. retention=flexmock(),
  516. consistency=flexmock(),
  517. hooks={},
  518. local_path=flexmock(),
  519. remote_path=flexmock(),
  520. local_borg_version=flexmock(),
  521. repository={'path': 'repo'},
  522. )
  523. )
  524. def test_run_actions_runs_export_tar():
  525. flexmock(module).should_receive('add_custom_log_levels')
  526. flexmock(module.command).should_receive('execute_hook')
  527. flexmock(borgmatic.actions.export_tar).should_receive('run_export_tar').once()
  528. tuple(
  529. module.run_actions(
  530. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'export-tar': flexmock()},
  531. config_filename=flexmock(),
  532. location={'repositories': []},
  533. storage=flexmock(),
  534. retention=flexmock(),
  535. consistency=flexmock(),
  536. hooks={},
  537. local_path=flexmock(),
  538. remote_path=flexmock(),
  539. local_borg_version=flexmock(),
  540. repository={'path': 'repo'},
  541. )
  542. )
  543. def test_run_actions_runs_mount():
  544. flexmock(module).should_receive('add_custom_log_levels')
  545. flexmock(module.command).should_receive('execute_hook')
  546. flexmock(borgmatic.actions.mount).should_receive('run_mount').once()
  547. tuple(
  548. module.run_actions(
  549. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'mount': flexmock()},
  550. config_filename=flexmock(),
  551. location={'repositories': []},
  552. storage=flexmock(),
  553. retention=flexmock(),
  554. consistency=flexmock(),
  555. hooks={},
  556. local_path=flexmock(),
  557. remote_path=flexmock(),
  558. local_borg_version=flexmock(),
  559. repository={'path': 'repo'},
  560. )
  561. )
  562. def test_run_actions_runs_restore():
  563. flexmock(module).should_receive('add_custom_log_levels')
  564. flexmock(module.command).should_receive('execute_hook')
  565. flexmock(borgmatic.actions.restore).should_receive('run_restore').once()
  566. tuple(
  567. module.run_actions(
  568. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'restore': flexmock()},
  569. config_filename=flexmock(),
  570. location={'repositories': []},
  571. storage=flexmock(),
  572. retention=flexmock(),
  573. consistency=flexmock(),
  574. hooks={},
  575. local_path=flexmock(),
  576. remote_path=flexmock(),
  577. local_borg_version=flexmock(),
  578. repository={'path': 'repo'},
  579. )
  580. )
  581. def test_run_actions_runs_rlist():
  582. flexmock(module).should_receive('add_custom_log_levels')
  583. flexmock(module.command).should_receive('execute_hook')
  584. expected = flexmock()
  585. flexmock(borgmatic.actions.rlist).should_receive('run_rlist').and_yield(expected).once()
  586. result = tuple(
  587. module.run_actions(
  588. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'rlist': flexmock()},
  589. config_filename=flexmock(),
  590. location={'repositories': []},
  591. storage=flexmock(),
  592. retention=flexmock(),
  593. consistency=flexmock(),
  594. hooks={},
  595. local_path=flexmock(),
  596. remote_path=flexmock(),
  597. local_borg_version=flexmock(),
  598. repository={'path': 'repo'},
  599. )
  600. )
  601. assert result == (expected,)
  602. def test_run_actions_runs_list():
  603. flexmock(module).should_receive('add_custom_log_levels')
  604. flexmock(module.command).should_receive('execute_hook')
  605. expected = flexmock()
  606. flexmock(borgmatic.actions.list).should_receive('run_list').and_yield(expected).once()
  607. result = tuple(
  608. module.run_actions(
  609. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'list': flexmock()},
  610. config_filename=flexmock(),
  611. location={'repositories': []},
  612. storage=flexmock(),
  613. retention=flexmock(),
  614. consistency=flexmock(),
  615. hooks={},
  616. local_path=flexmock(),
  617. remote_path=flexmock(),
  618. local_borg_version=flexmock(),
  619. repository={'path': 'repo'},
  620. )
  621. )
  622. assert result == (expected,)
  623. def test_run_actions_runs_rinfo():
  624. flexmock(module).should_receive('add_custom_log_levels')
  625. flexmock(module.command).should_receive('execute_hook')
  626. expected = flexmock()
  627. flexmock(borgmatic.actions.rinfo).should_receive('run_rinfo').and_yield(expected).once()
  628. result = tuple(
  629. module.run_actions(
  630. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'rinfo': flexmock()},
  631. config_filename=flexmock(),
  632. location={'repositories': []},
  633. storage=flexmock(),
  634. retention=flexmock(),
  635. consistency=flexmock(),
  636. hooks={},
  637. local_path=flexmock(),
  638. remote_path=flexmock(),
  639. local_borg_version=flexmock(),
  640. repository={'path': 'repo'},
  641. )
  642. )
  643. assert result == (expected,)
  644. def test_run_actions_runs_info():
  645. flexmock(module).should_receive('add_custom_log_levels')
  646. flexmock(module.command).should_receive('execute_hook')
  647. expected = flexmock()
  648. flexmock(borgmatic.actions.info).should_receive('run_info').and_yield(expected).once()
  649. result = tuple(
  650. module.run_actions(
  651. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'info': flexmock()},
  652. config_filename=flexmock(),
  653. location={'repositories': []},
  654. storage=flexmock(),
  655. retention=flexmock(),
  656. consistency=flexmock(),
  657. hooks={},
  658. local_path=flexmock(),
  659. remote_path=flexmock(),
  660. local_borg_version=flexmock(),
  661. repository={'path': 'repo'},
  662. )
  663. )
  664. assert result == (expected,)
  665. def test_run_actions_runs_break_lock():
  666. flexmock(module).should_receive('add_custom_log_levels')
  667. flexmock(module.command).should_receive('execute_hook')
  668. flexmock(borgmatic.actions.break_lock).should_receive('run_break_lock').once()
  669. tuple(
  670. module.run_actions(
  671. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'break-lock': flexmock()},
  672. config_filename=flexmock(),
  673. location={'repositories': []},
  674. storage=flexmock(),
  675. retention=flexmock(),
  676. consistency=flexmock(),
  677. hooks={},
  678. local_path=flexmock(),
  679. remote_path=flexmock(),
  680. local_borg_version=flexmock(),
  681. repository={'path': 'repo'},
  682. )
  683. )
  684. def test_run_actions_runs_borg():
  685. flexmock(module).should_receive('add_custom_log_levels')
  686. flexmock(module.command).should_receive('execute_hook')
  687. flexmock(borgmatic.actions.borg).should_receive('run_borg').once()
  688. tuple(
  689. module.run_actions(
  690. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'borg': flexmock()},
  691. config_filename=flexmock(),
  692. location={'repositories': []},
  693. storage=flexmock(),
  694. retention=flexmock(),
  695. consistency=flexmock(),
  696. hooks={},
  697. local_path=flexmock(),
  698. remote_path=flexmock(),
  699. local_borg_version=flexmock(),
  700. repository={'path': 'repo'},
  701. )
  702. )
  703. def test_run_actions_runs_multiple_actions_in_argument_order():
  704. flexmock(module).should_receive('add_custom_log_levels')
  705. flexmock(module.command).should_receive('execute_hook')
  706. flexmock(borgmatic.actions.borg).should_receive('run_borg').once().ordered()
  707. flexmock(borgmatic.actions.restore).should_receive('run_restore').once().ordered()
  708. tuple(
  709. module.run_actions(
  710. arguments={
  711. 'global': flexmock(dry_run=False, log_file='foo'),
  712. 'borg': flexmock(),
  713. 'restore': flexmock(),
  714. },
  715. config_filename=flexmock(),
  716. location={'repositories': []},
  717. storage=flexmock(),
  718. retention=flexmock(),
  719. consistency=flexmock(),
  720. hooks={},
  721. local_path=flexmock(),
  722. remote_path=flexmock(),
  723. local_borg_version=flexmock(),
  724. repository={'path': 'repo'},
  725. )
  726. )
  727. def test_load_configurations_collects_parsed_configurations_and_logs():
  728. configuration = flexmock()
  729. other_configuration = flexmock()
  730. test_expected_logs = [flexmock(), flexmock()]
  731. other_expected_logs = [flexmock(), flexmock()]
  732. flexmock(module.validate).should_receive('parse_configuration').and_return(
  733. configuration, test_expected_logs
  734. ).and_return(other_configuration, other_expected_logs)
  735. configs, logs = tuple(module.load_configurations(('test.yaml', 'other.yaml')))
  736. assert configs == {'test.yaml': configuration, 'other.yaml': other_configuration}
  737. assert logs == test_expected_logs + other_expected_logs
  738. def test_load_configurations_logs_warning_for_permission_error():
  739. flexmock(module.validate).should_receive('parse_configuration').and_raise(PermissionError)
  740. configs, logs = tuple(module.load_configurations(('test.yaml',)))
  741. assert configs == {}
  742. assert {log.levelno for log in logs} == {logging.WARNING}
  743. def test_load_configurations_logs_critical_for_parse_error():
  744. flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
  745. configs, logs = tuple(module.load_configurations(('test.yaml',)))
  746. assert configs == {}
  747. assert {log.levelno for log in logs} == {logging.CRITICAL}
  748. def test_log_record_does_not_raise():
  749. module.log_record(levelno=1, foo='bar', baz='quux')
  750. def test_log_record_with_suppress_does_not_raise():
  751. module.log_record(levelno=1, foo='bar', baz='quux', suppress_log=True)
  752. def test_log_error_records_generates_output_logs_for_message_only():
  753. flexmock(module).should_receive('log_record').replace_with(dict)
  754. logs = tuple(module.log_error_records('Error'))
  755. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  756. def test_log_error_records_generates_output_logs_for_called_process_error():
  757. flexmock(module).should_receive('log_record').replace_with(dict)
  758. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  759. logs = tuple(
  760. module.log_error_records('Error', subprocess.CalledProcessError(1, 'ls', 'error output'))
  761. )
  762. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  763. assert any(log for log in logs if 'error output' in str(log))
  764. def test_log_error_records_generates_logs_for_value_error():
  765. flexmock(module).should_receive('log_record').replace_with(dict)
  766. logs = tuple(module.log_error_records('Error', ValueError()))
  767. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  768. def test_log_error_records_generates_logs_for_os_error():
  769. flexmock(module).should_receive('log_record').replace_with(dict)
  770. logs = tuple(module.log_error_records('Error', OSError()))
  771. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  772. def test_log_error_records_generates_nothing_for_other_error():
  773. flexmock(module).should_receive('log_record').replace_with(dict)
  774. logs = tuple(module.log_error_records('Error', KeyError()))
  775. assert logs == ()
  776. def test_get_local_path_uses_configuration_value():
  777. assert module.get_local_path({'test.yaml': {'location': {'local_path': 'borg1'}}}) == 'borg1'
  778. def test_get_local_path_without_location_defaults_to_borg():
  779. assert module.get_local_path({'test.yaml': {}}) == 'borg'
  780. def test_get_local_path_without_local_path_defaults_to_borg():
  781. assert module.get_local_path({'test.yaml': {'location': {}}}) == 'borg'
  782. def test_collect_configuration_run_summary_logs_info_for_success():
  783. flexmock(module.command).should_receive('execute_hook').never()
  784. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  785. flexmock(module).should_receive('run_configuration').and_return([])
  786. arguments = {}
  787. logs = tuple(
  788. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  789. )
  790. assert {log.levelno for log in logs} == {logging.INFO}
  791. def test_collect_configuration_run_summary_executes_hooks_for_create():
  792. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  793. flexmock(module).should_receive('run_configuration').and_return([])
  794. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  795. logs = tuple(
  796. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  797. )
  798. assert {log.levelno for log in logs} == {logging.INFO}
  799. def test_collect_configuration_run_summary_logs_info_for_success_with_extract():
  800. flexmock(module.validate).should_receive('guard_single_repository_selected')
  801. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  802. flexmock(module).should_receive('run_configuration').and_return([])
  803. arguments = {'extract': flexmock(repository='repo')}
  804. logs = tuple(
  805. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  806. )
  807. assert {log.levelno for log in logs} == {logging.INFO}
  808. def test_collect_configuration_run_summary_logs_extract_with_repository_error():
  809. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  810. ValueError
  811. )
  812. expected_logs = (flexmock(),)
  813. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  814. arguments = {'extract': flexmock(repository='repo')}
  815. logs = tuple(
  816. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  817. )
  818. assert logs == expected_logs
  819. def test_collect_configuration_run_summary_logs_info_for_success_with_mount():
  820. flexmock(module.validate).should_receive('guard_single_repository_selected')
  821. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  822. flexmock(module).should_receive('run_configuration').and_return([])
  823. arguments = {'mount': flexmock(repository='repo')}
  824. logs = tuple(
  825. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  826. )
  827. assert {log.levelno for log in logs} == {logging.INFO}
  828. def test_collect_configuration_run_summary_logs_mount_with_repository_error():
  829. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  830. ValueError
  831. )
  832. expected_logs = (flexmock(),)
  833. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  834. arguments = {'mount': flexmock(repository='repo')}
  835. logs = tuple(
  836. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  837. )
  838. assert logs == expected_logs
  839. def test_collect_configuration_run_summary_logs_missing_configs_error():
  840. arguments = {'global': flexmock(config_paths=[])}
  841. expected_logs = (flexmock(),)
  842. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  843. logs = tuple(module.collect_configuration_run_summary_logs({}, arguments=arguments))
  844. assert logs == expected_logs
  845. def test_collect_configuration_run_summary_logs_pre_hook_error():
  846. flexmock(module.command).should_receive('execute_hook').and_raise(ValueError)
  847. expected_logs = (flexmock(),)
  848. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  849. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  850. logs = tuple(
  851. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  852. )
  853. assert logs == expected_logs
  854. def test_collect_configuration_run_summary_logs_post_hook_error():
  855. flexmock(module.command).should_receive('execute_hook').and_return(None).and_raise(ValueError)
  856. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  857. flexmock(module).should_receive('run_configuration').and_return([])
  858. expected_logs = (flexmock(),)
  859. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  860. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  861. logs = tuple(
  862. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  863. )
  864. assert expected_logs[0] in logs
  865. def test_collect_configuration_run_summary_logs_for_list_with_archive_and_repository_error():
  866. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  867. ValueError
  868. )
  869. expected_logs = (flexmock(),)
  870. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  871. arguments = {'list': flexmock(repository='repo', archive='test')}
  872. logs = tuple(
  873. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  874. )
  875. assert logs == expected_logs
  876. def test_collect_configuration_run_summary_logs_info_for_success_with_list():
  877. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  878. flexmock(module).should_receive('run_configuration').and_return([])
  879. arguments = {'list': flexmock(repository='repo', archive=None)}
  880. logs = tuple(
  881. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  882. )
  883. assert {log.levelno for log in logs} == {logging.INFO}
  884. def test_collect_configuration_run_summary_logs_run_configuration_error():
  885. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  886. flexmock(module).should_receive('run_configuration').and_return(
  887. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))]
  888. )
  889. flexmock(module).should_receive('log_error_records').and_return([])
  890. arguments = {}
  891. logs = tuple(
  892. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  893. )
  894. assert {log.levelno for log in logs} == {logging.CRITICAL}
  895. def test_collect_configuration_run_summary_logs_run_umount_error():
  896. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  897. flexmock(module).should_receive('run_configuration').and_return([])
  898. flexmock(module.borg_umount).should_receive('unmount_archive').and_raise(OSError)
  899. flexmock(module).should_receive('log_error_records').and_return(
  900. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))]
  901. )
  902. arguments = {'umount': flexmock(mount_point='/mnt')}
  903. logs = tuple(
  904. module.collect_configuration_run_summary_logs({'test.yaml': {}}, arguments=arguments)
  905. )
  906. assert {log.levelno for log in logs} == {logging.INFO, logging.CRITICAL}
  907. def test_collect_configuration_run_summary_logs_outputs_merged_json_results():
  908. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  909. flexmock(module).should_receive('run_configuration').and_return(['foo', 'bar']).and_return(
  910. ['baz']
  911. )
  912. stdout = flexmock()
  913. stdout.should_receive('write').with_args('["foo", "bar", "baz"]').once()
  914. flexmock(module.sys).stdout = stdout
  915. arguments = {}
  916. tuple(
  917. module.collect_configuration_run_summary_logs(
  918. {'test.yaml': {}, 'test2.yaml': {}}, arguments=arguments
  919. )
  920. )