test_pushover.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. import pytest
  2. from flexmock import flexmock
  3. import borgmatic.hooks.monitoring.monitor
  4. from borgmatic.hooks.monitoring import pushover as module
  5. def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_send_to_pushover():
  6. '''
  7. This test should be the minimum working configuration. The "message"
  8. should be auto populated with the default value which is the state name.
  9. '''
  10. hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
  11. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  12. 'resolve_credential'
  13. ).replace_with(lambda value, config: value)
  14. flexmock(module.logger).should_receive('warning').never()
  15. flexmock(module.requests).should_receive('post').with_args(
  16. 'https://api.pushover.net/1/messages.json',
  17. headers={'Content-type': 'application/x-www-form-urlencoded'},
  18. data={
  19. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  20. 'user': '983hfe0of902lkjfa2amanfgui',
  21. 'message': 'fail',
  22. },
  23. timeout=int,
  24. ).and_return(flexmock(ok=True)).once()
  25. module.ping_monitor(
  26. hook_config,
  27. {},
  28. 'config.yaml',
  29. borgmatic.hooks.monitoring.monitor.State.FAIL,
  30. monitoring_log_level=1,
  31. dry_run=False,
  32. )
  33. def test_ping_monitor_config_with_minimum_config_start_state_backup_not_send_to_pushover_exit_early():
  34. '''
  35. This test should exit early since the hook config does not specify the
  36. 'start' state. Only the 'fail' state is enabled by default.
  37. '''
  38. hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
  39. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  40. 'resolve_credential'
  41. ).replace_with(lambda value, config: value)
  42. flexmock(module.logger).should_receive('warning').never()
  43. flexmock(module.requests).should_receive('post').never()
  44. module.ping_monitor(
  45. hook_config,
  46. {},
  47. 'config.yaml',
  48. borgmatic.hooks.monitoring.monitor.State.START,
  49. monitoring_log_level=1,
  50. dry_run=False,
  51. )
  52. def test_ping_monitor_start_state_backup_default_message_successfully_send_to_pushover():
  53. '''
  54. This test should send a notification to Pushover on backup start
  55. since the state has been configured. It should default to sending
  56. the name of the state as the 'message' since it is not
  57. explicitly declared in the state config.
  58. '''
  59. hook_config = {
  60. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  61. 'user': '983hfe0of902lkjfa2amanfgui',
  62. 'states': {'start', 'fail', 'finish'},
  63. }
  64. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  65. 'resolve_credential'
  66. ).replace_with(lambda value, config: value)
  67. flexmock(module.logger).should_receive('warning').never()
  68. flexmock(module.requests).should_receive('post').with_args(
  69. 'https://api.pushover.net/1/messages.json',
  70. headers={'Content-type': 'application/x-www-form-urlencoded'},
  71. data={
  72. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  73. 'user': '983hfe0of902lkjfa2amanfgui',
  74. 'message': 'start',
  75. },
  76. timeout=int,
  77. ).and_return(flexmock(ok=True)).once()
  78. module.ping_monitor(
  79. hook_config,
  80. {},
  81. 'config.yaml',
  82. borgmatic.hooks.monitoring.monitor.State.START,
  83. monitoring_log_level=1,
  84. dry_run=False,
  85. )
  86. def test_ping_monitor_start_state_backup_custom_message_successfully_send_to_pushover():
  87. '''
  88. This test should send a notification to Pushover on backup start
  89. since the state has been configured. It should send a custom
  90. 'message' since it is explicitly declared in the state config.
  91. '''
  92. hook_config = {
  93. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  94. 'user': '983hfe0of902lkjfa2amanfgui',
  95. 'states': {'start', 'fail', 'finish'},
  96. 'start': {'message': 'custom start message'},
  97. }
  98. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  99. 'resolve_credential'
  100. ).replace_with(lambda value, config: value)
  101. flexmock(module.logger).should_receive('warning').never()
  102. flexmock(module.requests).should_receive('post').with_args(
  103. 'https://api.pushover.net/1/messages.json',
  104. headers={'Content-type': 'application/x-www-form-urlencoded'},
  105. data={
  106. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  107. 'user': '983hfe0of902lkjfa2amanfgui',
  108. 'message': 'custom start message',
  109. },
  110. timeout=int,
  111. ).and_return(flexmock(ok=True)).once()
  112. module.ping_monitor(
  113. hook_config,
  114. {},
  115. 'config.yaml',
  116. borgmatic.hooks.monitoring.monitor.State.START,
  117. monitoring_log_level=1,
  118. dry_run=False,
  119. )
  120. def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_uses_expire_and_retry_defaults():
  121. '''
  122. This simulates priority level 2 being set but expiry and retry are
  123. not declared. This should set retry and expiry to their defaults.
  124. '''
  125. hook_config = {
  126. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  127. 'user': '983hfe0of902lkjfa2amanfgui',
  128. 'states': {'start', 'fail', 'finish'},
  129. 'start': {'priority': 2},
  130. }
  131. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  132. 'resolve_credential'
  133. ).replace_with(lambda value, config: value)
  134. flexmock(module.logger).should_receive('warning').never()
  135. flexmock(module.requests).should_receive('post').with_args(
  136. 'https://api.pushover.net/1/messages.json',
  137. headers={'Content-type': 'application/x-www-form-urlencoded'},
  138. data={
  139. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  140. 'user': '983hfe0of902lkjfa2amanfgui',
  141. 'message': 'start',
  142. 'priority': 2,
  143. 'retry': 30,
  144. 'expire': 600,
  145. },
  146. timeout=int,
  147. ).and_return(flexmock(ok=True)).once()
  148. module.ping_monitor(
  149. hook_config,
  150. {},
  151. 'config.yaml',
  152. borgmatic.hooks.monitoring.monitor.State.START,
  153. monitoring_log_level=1,
  154. dry_run=False,
  155. )
  156. def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_declared_with_expire_no_retry_success():
  157. '''
  158. This simulates priority level 2 and expiry being set but retry is
  159. not declared. This should set retry to the default.
  160. '''
  161. hook_config = {
  162. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  163. 'user': '983hfe0of902lkjfa2amanfgui',
  164. 'states': {'start', 'fail', 'finish'},
  165. 'start': {'priority': 2, 'expire': 600},
  166. }
  167. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  168. 'resolve_credential'
  169. ).replace_with(lambda value, config: value)
  170. flexmock(module.logger).should_receive('warning').never()
  171. flexmock(module.requests).should_receive('post').with_args(
  172. 'https://api.pushover.net/1/messages.json',
  173. headers={'Content-type': 'application/x-www-form-urlencoded'},
  174. data={
  175. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  176. 'user': '983hfe0of902lkjfa2amanfgui',
  177. 'message': 'start',
  178. 'priority': 2,
  179. 'retry': 30,
  180. 'expire': 600,
  181. },
  182. timeout=int,
  183. ).and_return(flexmock(ok=True)).once()
  184. module.ping_monitor(
  185. hook_config,
  186. {},
  187. 'config.yaml',
  188. borgmatic.hooks.monitoring.monitor.State.START,
  189. monitoring_log_level=1,
  190. dry_run=False,
  191. )
  192. def test_ping_monitor_start_state_backup_default_message_with_priority_emergency_declared_no_expire_with_retry_success():
  193. '''
  194. This simulates priority level 2 and retry being set but expire is
  195. not declared. This should set expire to the default.
  196. '''
  197. hook_config = {
  198. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  199. 'user': '983hfe0of902lkjfa2amanfgui',
  200. 'states': {'start', 'fail', 'finish'},
  201. 'start': {'priority': 2, 'retry': 30},
  202. }
  203. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  204. 'resolve_credential'
  205. ).replace_with(lambda value, config: value)
  206. flexmock(module.logger).should_receive('warning').never()
  207. flexmock(module.requests).should_receive('post').with_args(
  208. 'https://api.pushover.net/1/messages.json',
  209. headers={'Content-type': 'application/x-www-form-urlencoded'},
  210. data={
  211. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  212. 'user': '983hfe0of902lkjfa2amanfgui',
  213. 'message': 'start',
  214. 'priority': 2,
  215. 'retry': 30,
  216. 'expire': 600,
  217. },
  218. timeout=int,
  219. ).and_return(flexmock(ok=True)).once()
  220. module.ping_monitor(
  221. hook_config,
  222. {},
  223. 'config.yaml',
  224. borgmatic.hooks.monitoring.monitor.State.START,
  225. monitoring_log_level=1,
  226. dry_run=False,
  227. )
  228. def test_ping_monitor_start_state_backup_default_message_with_priority_high_declared_expire_and_retry_raises():
  229. '''
  230. This simulates priority level 1, retry and expiry being set. Since expire
  231. and retry are only used for priority level 2, they should not be included
  232. in the request sent to Pushover. This test verifies that a ValueError is
  233. raised.
  234. '''
  235. hook_config = {
  236. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  237. 'user': '983hfe0of902lkjfa2amanfgui',
  238. 'states': {'start', 'fail', 'finish'},
  239. 'start': {'priority': 1, 'expire': 30, 'retry': 30},
  240. }
  241. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  242. 'resolve_credential'
  243. ).replace_with(lambda value, config: value)
  244. flexmock(module.logger).should_receive('warning').never()
  245. flexmock(module.requests).should_receive('post').never()
  246. with pytest.raises(ValueError):
  247. module.ping_monitor(
  248. hook_config,
  249. {},
  250. 'config.yaml',
  251. borgmatic.hooks.monitoring.monitor.State.START,
  252. monitoring_log_level=1,
  253. dry_run=False,
  254. )
  255. def test_ping_monitor_start_state_backup_based_on_documentation_advanced_example_success():
  256. '''
  257. Here is a test of what is provided in the monitor-your-backups.md file
  258. as an 'advanced example'. This test runs the start state.
  259. '''
  260. hook_config = {
  261. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  262. 'user': '983hfe0of902lkjfa2amanfgui',
  263. 'states': {'start', 'fail', 'finish'},
  264. 'start': {
  265. 'message': 'Backup <b>Started</b>',
  266. 'priority': -2,
  267. 'title': 'Backup Started',
  268. 'html': 1,
  269. 'ttl': 10,
  270. },
  271. 'fail': {
  272. 'message': 'Backup <font color="#ff6961">Failed</font>',
  273. 'priority': 2,
  274. 'expire': 600,
  275. 'retry': 30,
  276. 'device': 'pixel8',
  277. 'title': 'Backup Failed',
  278. 'html': 1,
  279. 'sound': 'siren',
  280. 'url': 'https://ticketing-system.example.com/login',
  281. 'url_title': 'Login to ticketing system',
  282. },
  283. 'finish': {
  284. 'message': 'Backup <font color="#77dd77">Finished</font>',
  285. 'priority': 0,
  286. 'title': 'Backup Finished',
  287. 'html': 1,
  288. 'ttl': 60,
  289. 'url': 'https://ticketing-system.example.com/login',
  290. 'url_title': 'Login to ticketing system',
  291. },
  292. }
  293. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  294. 'resolve_credential'
  295. ).replace_with(lambda value, config: value)
  296. flexmock(module.logger).should_receive('warning').never()
  297. flexmock(module.requests).should_receive('post').with_args(
  298. 'https://api.pushover.net/1/messages.json',
  299. headers={'Content-type': 'application/x-www-form-urlencoded'},
  300. data={
  301. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  302. 'user': '983hfe0of902lkjfa2amanfgui',
  303. 'message': 'Backup <b>Started</b>',
  304. 'priority': -2,
  305. 'title': 'Backup Started',
  306. 'html': 1,
  307. 'ttl': 10,
  308. },
  309. timeout=int,
  310. ).and_return(flexmock(ok=True)).once()
  311. module.ping_monitor(
  312. hook_config,
  313. {},
  314. 'config.yaml',
  315. borgmatic.hooks.monitoring.monitor.State.START,
  316. monitoring_log_level=1,
  317. dry_run=False,
  318. )
  319. def test_ping_monitor_fail_state_backup_based_on_documentation_advanced_example_success():
  320. '''
  321. Here is a test of what is provided in the monitor-your-backups.md file
  322. as an 'advanced example'. This test runs the fail state.
  323. '''
  324. hook_config = {
  325. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  326. 'user': '983hfe0of902lkjfa2amanfgui',
  327. 'states': {'start', 'fail', 'finish'},
  328. 'start': {
  329. 'message': 'Backup <b>Started</b>',
  330. 'priority': -2,
  331. 'title': 'Backup Started',
  332. 'html': 1,
  333. 'ttl': 10,
  334. },
  335. 'fail': {
  336. 'message': 'Backup <font color="#ff6961">Failed</font>',
  337. 'priority': 2,
  338. 'expire': 600,
  339. 'retry': 30,
  340. 'device': 'pixel8',
  341. 'title': 'Backup Failed',
  342. 'html': 1,
  343. 'sound': 'siren',
  344. 'url': 'https://ticketing-system.example.com/login',
  345. 'url_title': 'Login to ticketing system',
  346. },
  347. 'finish': {
  348. 'message': 'Backup <font color="#77dd77">Finished</font>',
  349. 'priority': 0,
  350. 'title': 'Backup Finished',
  351. 'html': 1,
  352. 'ttl': 60,
  353. 'url': 'https://ticketing-system.example.com/login',
  354. 'url_title': 'Login to ticketing system',
  355. },
  356. }
  357. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  358. 'resolve_credential'
  359. ).replace_with(lambda value, config: value)
  360. flexmock(module.logger).should_receive('warning').never()
  361. flexmock(module.requests).should_receive('post').with_args(
  362. 'https://api.pushover.net/1/messages.json',
  363. headers={'Content-type': 'application/x-www-form-urlencoded'},
  364. data={
  365. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  366. 'user': '983hfe0of902lkjfa2amanfgui',
  367. 'message': 'Backup <font color="#ff6961">Failed</font>',
  368. 'priority': 2,
  369. 'expire': 600,
  370. 'retry': 30,
  371. 'device': 'pixel8',
  372. 'title': 'Backup Failed',
  373. 'html': 1,
  374. 'sound': 'siren',
  375. 'url': 'https://ticketing-system.example.com/login',
  376. 'url_title': 'Login to ticketing system',
  377. },
  378. timeout=int,
  379. ).and_return(flexmock(ok=True)).once()
  380. module.ping_monitor(
  381. hook_config,
  382. {},
  383. 'config.yaml',
  384. borgmatic.hooks.monitoring.monitor.State.FAIL,
  385. monitoring_log_level=1,
  386. dry_run=False,
  387. )
  388. def test_ping_monitor_finish_state_backup_based_on_documentation_advanced_example_success():
  389. '''
  390. Here is a test of what is provided in the monitor-your-backups.md file
  391. as an 'advanced example'. This test runs the finish state.
  392. '''
  393. hook_config = {
  394. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  395. 'user': '983hfe0of902lkjfa2amanfgui',
  396. 'states': {'start', 'fail', 'finish'},
  397. 'start': {
  398. 'message': 'Backup <b>Started</b>',
  399. 'priority': -2,
  400. 'title': 'Backup Started',
  401. 'html': 1,
  402. 'ttl': 10,
  403. },
  404. 'fail': {
  405. 'message': 'Backup <font color="#ff6961">Failed</font>',
  406. 'priority': 2,
  407. 'expire': 600,
  408. 'retry': 30,
  409. 'device': 'pixel8',
  410. 'title': 'Backup Failed',
  411. 'html': 1,
  412. 'sound': 'siren',
  413. 'url': 'https://ticketing-system.example.com/login',
  414. 'url_title': 'Login to ticketing system',
  415. },
  416. 'finish': {
  417. 'message': 'Backup <font color="#77dd77">Finished</font>',
  418. 'priority': 0,
  419. 'title': 'Backup Finished',
  420. 'html': 1,
  421. 'ttl': 60,
  422. 'url': 'https://ticketing-system.example.com/login',
  423. 'url_title': 'Login to ticketing system',
  424. },
  425. }
  426. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  427. 'resolve_credential'
  428. ).replace_with(lambda value, config: value)
  429. flexmock(module.logger).should_receive('warning').never()
  430. flexmock(module.requests).should_receive('post').with_args(
  431. 'https://api.pushover.net/1/messages.json',
  432. headers={'Content-type': 'application/x-www-form-urlencoded'},
  433. data={
  434. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  435. 'user': '983hfe0of902lkjfa2amanfgui',
  436. 'message': 'Backup <font color="#77dd77">Finished</font>',
  437. 'priority': 0,
  438. 'title': 'Backup Finished',
  439. 'html': 1,
  440. 'ttl': 60,
  441. 'url': 'https://ticketing-system.example.com/login',
  442. 'url_title': 'Login to ticketing system',
  443. },
  444. timeout=int,
  445. ).and_return(flexmock(ok=True)).once()
  446. module.ping_monitor(
  447. hook_config,
  448. {},
  449. 'config.yaml',
  450. borgmatic.hooks.monitoring.monitor.State.FINISH,
  451. monitoring_log_level=1,
  452. dry_run=False,
  453. )
  454. def test_ping_monitor_config_with_minimum_config_fail_state_backup_successfully_send_to_pushover_dry_run():
  455. '''
  456. This test should be the minimum working configuration. The "message"
  457. should be auto populated with the default value which is the state name.
  458. '''
  459. hook_config = {'token': 'ksdjfwoweijfvwoeifvjmwghagy92', 'user': '983hfe0of902lkjfa2amanfgui'}
  460. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  461. 'resolve_credential'
  462. ).replace_with(lambda value, config: value)
  463. flexmock(module.logger).should_receive('warning').never()
  464. flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True)).never()
  465. module.ping_monitor(
  466. hook_config,
  467. {},
  468. 'config.yaml',
  469. borgmatic.hooks.monitoring.monitor.State.FAIL,
  470. monitoring_log_level=1,
  471. dry_run=True,
  472. )
  473. def test_ping_monitor_config_incorrect_state_exit_early():
  474. '''
  475. This test should exit early since the start state is not declared in the configuration.
  476. '''
  477. hook_config = {
  478. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  479. 'user': '983hfe0of902lkjfa2amanfgui',
  480. }
  481. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  482. 'resolve_credential'
  483. ).replace_with(lambda value, config: value)
  484. flexmock(module.logger).should_receive('warning').never()
  485. flexmock(module.requests).should_receive('post').and_return(flexmock(ok=True)).never()
  486. module.ping_monitor(
  487. hook_config,
  488. {},
  489. 'config.yaml',
  490. borgmatic.hooks.monitoring.monitor.State.START,
  491. monitoring_log_level=1,
  492. dry_run=True,
  493. )
  494. def test_ping_monitor_push_post_error_bails():
  495. '''
  496. This test simulates the Pushover servers not responding with a 200 OK. We
  497. should raise for status and warn then exit.
  498. '''
  499. hook_config = hook_config = {
  500. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  501. 'user': '983hfe0of902lkjfa2amanfgui',
  502. }
  503. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  504. 'resolve_credential'
  505. ).replace_with(lambda value, config: value)
  506. push_response = flexmock(ok=False)
  507. push_response.should_receive('raise_for_status').and_raise(
  508. module.requests.ConnectionError
  509. ).once()
  510. flexmock(module.requests).should_receive('post').with_args(
  511. 'https://api.pushover.net/1/messages.json',
  512. headers={'Content-type': 'application/x-www-form-urlencoded'},
  513. data={
  514. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  515. 'user': '983hfe0of902lkjfa2amanfgui',
  516. 'message': 'fail',
  517. },
  518. timeout=int,
  519. ).and_return(push_response).once()
  520. flexmock(module.logger).should_receive('warning').once()
  521. module.ping_monitor(
  522. hook_config,
  523. {},
  524. 'config.yaml',
  525. borgmatic.hooks.monitoring.monitor.State.FAIL,
  526. monitoring_log_level=1,
  527. dry_run=False,
  528. )
  529. def test_ping_monitor_credential_error_bails():
  530. hook_config = hook_config = {
  531. 'token': 'ksdjfwoweijfvwoeifvjmwghagy92',
  532. 'user': '983hfe0of902lkjfa2amanfgui',
  533. }
  534. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  535. 'resolve_credential'
  536. ).and_raise(ValueError)
  537. flexmock(module.requests).should_receive('post').never()
  538. flexmock(module.logger).should_receive('warning').once()
  539. module.ping_monitor(
  540. hook_config,
  541. {},
  542. 'config.yaml',
  543. borgmatic.hooks.monitoring.monitor.State.FAIL,
  544. monitoring_log_level=1,
  545. dry_run=False,
  546. )