test_apprise.py 12 KB

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