test_apprise.py 12 KB

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