test_apprise.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import apprise
  2. from apprise import NotifyFormat, NotifyType
  3. from flexmock import flexmock
  4. import borgmatic.hooks.monitoring.monitor
  5. from borgmatic.hooks.monitoring import apprise as module
  6. TOPIC = 'borgmatic-unit-testing'
  7. def mock_apprise():
  8. apprise_mock = flexmock(
  9. add=lambda servers: None,
  10. notify=lambda title, body, body_format, notify_type: None,
  11. )
  12. flexmock(apprise.Apprise).new_instances(apprise_mock)
  13. return apprise_mock
  14. def test_initialize_monitor_with_send_logs_false_does_not_add_handler():
  15. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('add_handler').never()
  16. module.initialize_monitor(
  17. hook_config={'send_logs': False},
  18. config={},
  19. config_filename='test.yaml',
  20. monitoring_log_level=1,
  21. dry_run=False,
  22. )
  23. def test_initialize_monitor_with_send_logs_true_adds_handler_with_default_log_size_limit():
  24. truncation_indicator_length = 4
  25. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  26. 'Forgetful_buffering_handler',
  27. ).with_args(
  28. module.HANDLER_IDENTIFIER,
  29. module.DEFAULT_LOGS_SIZE_LIMIT_BYTES - truncation_indicator_length,
  30. 1,
  31. ).once()
  32. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('add_handler').once()
  33. module.initialize_monitor(
  34. hook_config={'send_logs': True},
  35. config={},
  36. config_filename='test.yaml',
  37. monitoring_log_level=1,
  38. dry_run=False,
  39. )
  40. def test_initialize_monitor_without_send_logs_adds_handler_with_default_log_size_limit():
  41. truncation_indicator_length = 4
  42. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  43. 'Forgetful_buffering_handler',
  44. ).with_args(
  45. module.HANDLER_IDENTIFIER,
  46. module.DEFAULT_LOGS_SIZE_LIMIT_BYTES - truncation_indicator_length,
  47. 1,
  48. ).once()
  49. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('add_handler').once()
  50. module.initialize_monitor(
  51. hook_config={},
  52. config={},
  53. config_filename='test.yaml',
  54. monitoring_log_level=1,
  55. dry_run=False,
  56. )
  57. def test_ping_monitor_respects_dry_run():
  58. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  59. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  60. 'format_buffered_logs_for_payload',
  61. ).and_return('loggy log')
  62. mock_apprise().should_receive('notify').never()
  63. module.ping_monitor(
  64. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]},
  65. {},
  66. 'config.yaml',
  67. borgmatic.hooks.monitoring.monitor.State.FAIL,
  68. monitoring_log_level=1,
  69. dry_run=True,
  70. )
  71. def test_ping_monitor_with_no_states_does_not_notify():
  72. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never()
  73. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  74. 'format_buffered_logs_for_payload',
  75. ).never()
  76. mock_apprise().should_receive('notify').never()
  77. module.ping_monitor(
  78. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': []},
  79. {},
  80. 'config.yaml',
  81. borgmatic.hooks.monitoring.monitor.State.FAIL,
  82. monitoring_log_level=1,
  83. dry_run=True,
  84. )
  85. def test_ping_monitor_notifies_fail_by_default():
  86. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  87. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  88. 'format_buffered_logs_for_payload',
  89. ).and_return('')
  90. mock_apprise().should_receive('notify').with_args(
  91. title='A borgmatic FAIL event happened',
  92. body='A borgmatic FAIL event happened',
  93. body_format=NotifyFormat.TEXT,
  94. notify_type=NotifyType.FAILURE,
  95. ).once()
  96. for state in borgmatic.hooks.monitoring.monitor.State:
  97. module.ping_monitor(
  98. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]},
  99. {},
  100. 'config.yaml',
  101. state,
  102. monitoring_log_level=1,
  103. dry_run=False,
  104. )
  105. def test_ping_monitor_with_logs_appends_logs_to_body():
  106. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  107. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  108. 'format_buffered_logs_for_payload',
  109. ).and_return('loggy log')
  110. mock_apprise().should_receive('notify').with_args(
  111. title='A borgmatic FAIL event happened',
  112. body='A borgmatic FAIL event happened\n\nloggy log',
  113. body_format=NotifyFormat.TEXT,
  114. notify_type=NotifyType.FAILURE,
  115. ).once()
  116. for state in borgmatic.hooks.monitoring.monitor.State:
  117. module.ping_monitor(
  118. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]},
  119. {},
  120. 'config.yaml',
  121. state,
  122. monitoring_log_level=1,
  123. dry_run=False,
  124. )
  125. def test_ping_monitor_with_finish_default_config_notifies():
  126. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  127. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  128. 'format_buffered_logs_for_payload',
  129. ).and_return('')
  130. mock_apprise().should_receive('notify').with_args(
  131. title='A borgmatic FINISH event happened',
  132. body='A borgmatic FINISH event happened',
  133. body_format=NotifyFormat.TEXT,
  134. notify_type=NotifyType.SUCCESS,
  135. ).once()
  136. module.ping_monitor(
  137. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['finish']},
  138. {},
  139. 'config.yaml',
  140. borgmatic.hooks.monitoring.monitor.State.FINISH,
  141. monitoring_log_level=1,
  142. dry_run=False,
  143. )
  144. def test_ping_monitor_with_start_default_config_notifies():
  145. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never()
  146. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  147. 'format_buffered_logs_for_payload',
  148. ).never()
  149. mock_apprise().should_receive('notify').with_args(
  150. title='A borgmatic START event happened',
  151. body='A borgmatic START event happened',
  152. body_format=NotifyFormat.TEXT,
  153. notify_type=NotifyType.INFO,
  154. ).once()
  155. module.ping_monitor(
  156. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['start']},
  157. {},
  158. 'config.yaml',
  159. borgmatic.hooks.monitoring.monitor.State.START,
  160. monitoring_log_level=1,
  161. dry_run=False,
  162. )
  163. def test_ping_monitor_with_fail_default_config_notifies():
  164. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  165. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  166. 'format_buffered_logs_for_payload',
  167. ).and_return('')
  168. mock_apprise().should_receive('notify').with_args(
  169. title='A borgmatic FAIL event happened',
  170. body='A borgmatic FAIL event happened',
  171. body_format=NotifyFormat.TEXT,
  172. notify_type=NotifyType.FAILURE,
  173. ).once()
  174. module.ping_monitor(
  175. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['fail']},
  176. {},
  177. 'config.yaml',
  178. borgmatic.hooks.monitoring.monitor.State.FAIL,
  179. monitoring_log_level=1,
  180. dry_run=False,
  181. )
  182. def test_ping_monitor_with_log_default_config_notifies():
  183. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  184. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  185. 'format_buffered_logs_for_payload',
  186. ).and_return('')
  187. mock_apprise().should_receive('notify').with_args(
  188. title='A borgmatic LOG event happened',
  189. body='A borgmatic LOG event happened',
  190. body_format=NotifyFormat.TEXT,
  191. notify_type=NotifyType.INFO,
  192. ).once()
  193. module.ping_monitor(
  194. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}], 'states': ['log']},
  195. {},
  196. 'config.yaml',
  197. borgmatic.hooks.monitoring.monitor.State.LOG,
  198. monitoring_log_level=1,
  199. dry_run=False,
  200. )
  201. def test_ping_monitor_passes_through_custom_message_title():
  202. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  203. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  204. 'format_buffered_logs_for_payload',
  205. ).and_return('')
  206. mock_apprise().should_receive('notify').with_args(
  207. title='foo',
  208. body='bar',
  209. body_format=NotifyFormat.TEXT,
  210. notify_type=NotifyType.FAILURE,
  211. ).once()
  212. module.ping_monitor(
  213. {
  214. 'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}],
  215. 'states': ['fail'],
  216. 'fail': {'title': 'foo', 'body': 'bar'},
  217. },
  218. {},
  219. 'config.yaml',
  220. borgmatic.hooks.monitoring.monitor.State.FAIL,
  221. monitoring_log_level=1,
  222. dry_run=False,
  223. )
  224. def test_ping_monitor_passes_through_custom_message_body():
  225. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  226. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  227. 'format_buffered_logs_for_payload',
  228. ).and_return('')
  229. mock_apprise().should_receive('notify').with_args(
  230. title='',
  231. body='baz',
  232. body_format=NotifyFormat.TEXT,
  233. notify_type=NotifyType.FAILURE,
  234. ).once()
  235. module.ping_monitor(
  236. {
  237. 'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}],
  238. 'states': ['fail'],
  239. 'fail': {'body': 'baz'},
  240. },
  241. {},
  242. 'config.yaml',
  243. borgmatic.hooks.monitoring.monitor.State.FAIL,
  244. monitoring_log_level=1,
  245. dry_run=False,
  246. )
  247. def test_ping_monitor_passes_through_custom_message_body_and_appends_logs():
  248. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  249. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  250. 'format_buffered_logs_for_payload',
  251. ).and_return('loggy log')
  252. mock_apprise().should_receive('notify').with_args(
  253. title='',
  254. body='baz\n\nloggy log',
  255. body_format=NotifyFormat.TEXT,
  256. notify_type=NotifyType.FAILURE,
  257. ).once()
  258. module.ping_monitor(
  259. {
  260. 'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}],
  261. 'states': ['fail'],
  262. 'fail': {'body': 'baz'},
  263. },
  264. {},
  265. 'config.yaml',
  266. borgmatic.hooks.monitoring.monitor.State.FAIL,
  267. monitoring_log_level=1,
  268. dry_run=False,
  269. )
  270. def test_ping_monitor_pings_multiple_services():
  271. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  272. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  273. 'format_buffered_logs_for_payload',
  274. ).and_return('')
  275. mock_apprise().should_receive('add').with_args([f'ntfys://{TOPIC}', f'ntfy://{TOPIC}']).once()
  276. module.ping_monitor(
  277. {
  278. 'services': [
  279. {'url': f'ntfys://{TOPIC}', 'label': 'ntfys'},
  280. {'url': f'ntfy://{TOPIC}', 'label': 'ntfy'},
  281. ],
  282. },
  283. {},
  284. 'config.yaml',
  285. borgmatic.hooks.monitoring.monitor.State.FAIL,
  286. monitoring_log_level=1,
  287. dry_run=False,
  288. )
  289. def test_ping_monitor_logs_info_for_no_services():
  290. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler').never()
  291. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  292. 'format_buffered_logs_for_payload',
  293. ).never()
  294. flexmock(module.logger).should_receive('info').once()
  295. module.ping_monitor(
  296. {'services': []},
  297. {},
  298. 'config.yaml',
  299. borgmatic.hooks.monitoring.monitor.State.FAIL,
  300. monitoring_log_level=1,
  301. dry_run=False,
  302. )
  303. def test_ping_monitor_logs_warning_when_notify_fails():
  304. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('get_handler')
  305. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive(
  306. 'format_buffered_logs_for_payload',
  307. ).and_return('')
  308. mock_apprise().should_receive('notify').and_return(False)
  309. flexmock(module.logger).should_receive('warning').once()
  310. for state in borgmatic.hooks.monitoring.monitor.State:
  311. module.ping_monitor(
  312. {'services': [{'url': f'ntfys://{TOPIC}', 'label': 'ntfys'}]},
  313. {},
  314. 'config.yaml',
  315. state,
  316. monitoring_log_level=1,
  317. dry_run=False,
  318. )
  319. def test_destroy_monitor_does_not_raise():
  320. flexmock(module.borgmatic.hooks.monitoring.logs).should_receive('remove_handler')
  321. module.destroy_monitor(
  322. hook_config={},
  323. config={},
  324. monitoring_log_level=1,
  325. dry_run=False,
  326. )