test_normalize.py 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. import pytest
  2. from flexmock import flexmock
  3. from borgmatic.config import normalize as module
  4. @pytest.mark.parametrize(
  5. 'config,expected_config,produces_logs',
  6. (
  7. (
  8. {'location': {'foo': 'bar', 'baz': 'quux'}},
  9. {'foo': 'bar', 'baz': 'quux'},
  10. True,
  11. ),
  12. (
  13. {'retention': {'foo': 'bar', 'baz': 'quux'}},
  14. {'foo': 'bar', 'baz': 'quux'},
  15. True,
  16. ),
  17. (
  18. {'consistency': {'foo': 'bar', 'baz': 'quux'}},
  19. {'foo': 'bar', 'baz': 'quux'},
  20. True,
  21. ),
  22. (
  23. {'output': {'foo': 'bar', 'baz': 'quux'}},
  24. {'foo': 'bar', 'baz': 'quux'},
  25. True,
  26. ),
  27. (
  28. {'hooks': {'foo': 'bar', 'baz': 'quux'}},
  29. {'foo': 'bar', 'baz': 'quux'},
  30. True,
  31. ),
  32. (
  33. {'location': {'foo': 'bar'}, 'storage': {'baz': 'quux'}},
  34. {'foo': 'bar', 'baz': 'quux'},
  35. True,
  36. ),
  37. (
  38. {'foo': 'bar', 'baz': 'quux'},
  39. {'foo': 'bar', 'baz': 'quux'},
  40. False,
  41. ),
  42. (
  43. {'location': {'prefix': 'foo'}, 'consistency': {'prefix': 'foo'}},
  44. {'prefix': 'foo'},
  45. True,
  46. ),
  47. (
  48. {'location': {'prefix': 'foo'}, 'consistency': {'prefix': 'foo'}},
  49. {'prefix': 'foo'},
  50. True,
  51. ),
  52. (
  53. {'location': {'prefix': 'foo'}, 'consistency': {'bar': 'baz'}},
  54. {'prefix': 'foo', 'bar': 'baz'},
  55. True,
  56. ),
  57. (
  58. {'storage': {'umask': 'foo'}, 'hooks': {'umask': 'foo'}},
  59. {'umask': 'foo'},
  60. True,
  61. ),
  62. (
  63. {'storage': {'umask': 'foo'}, 'hooks': {'umask': 'foo'}},
  64. {'umask': 'foo'},
  65. True,
  66. ),
  67. (
  68. {'storage': {'umask': 'foo'}, 'hooks': {'bar': 'baz'}},
  69. {'umask': 'foo', 'bar': 'baz'},
  70. True,
  71. ),
  72. (
  73. {'location': {'bar': 'baz'}, 'consistency': {'prefix': 'foo'}},
  74. {'bar': 'baz', 'prefix': 'foo'},
  75. True,
  76. ),
  77. (
  78. {'location': {}, 'consistency': {'prefix': 'foo'}},
  79. {'prefix': 'foo'},
  80. True,
  81. ),
  82. (
  83. {},
  84. {},
  85. False,
  86. ),
  87. ),
  88. )
  89. def test_normalize_sections_moves_section_options_to_global_scope(
  90. config, expected_config, produces_logs
  91. ):
  92. logs = module.normalize_sections('test.yaml', config)
  93. assert config == expected_config
  94. if produces_logs:
  95. assert logs
  96. else:
  97. assert logs == []
  98. def test_normalize_sections_with_different_prefix_values_raises():
  99. config = {'location': {'prefix': 'foo'}, 'consistency': {'prefix': 'bar'}}
  100. with pytest.raises(ValueError):
  101. module.normalize_sections('test.yaml', config)
  102. def test_normalize_sections_with_different_umask_values_raises():
  103. config = {'storage': {'umask': 'foo'}, 'hooks': {'umask': 'bar'}}
  104. with pytest.raises(ValueError):
  105. module.normalize_sections('test.yaml', config)
  106. def test_normalize_sections_with_only_scalar_raises():
  107. config = 33
  108. with pytest.raises(ValueError):
  109. module.normalize_sections('test.yaml', config)
  110. @pytest.mark.parametrize(
  111. 'config,expected_config,produces_logs',
  112. (
  113. (
  114. {'exclude_if_present': '.nobackup'},
  115. {'exclude_if_present': ['.nobackup']},
  116. True,
  117. ),
  118. (
  119. {'exclude_if_present': ['.nobackup']},
  120. {'exclude_if_present': ['.nobackup']},
  121. False,
  122. ),
  123. (
  124. {'store_config_files': False},
  125. {'bootstrap': {'store_config_files': False}},
  126. True,
  127. ),
  128. (
  129. {'source_directories': ['foo', 'bar']},
  130. {'source_directories': ['foo', 'bar']},
  131. False,
  132. ),
  133. (
  134. {'compression': 'yes_please'},
  135. {'compression': 'yes_please'},
  136. False,
  137. ),
  138. (
  139. {'healthchecks': 'https://example.com'},
  140. {'healthchecks': {'ping_url': 'https://example.com'}},
  141. True,
  142. ),
  143. (
  144. {'cronitor': 'https://example.com'},
  145. {'cronitor': {'ping_url': 'https://example.com'}},
  146. True,
  147. ),
  148. (
  149. {'pagerduty': 'https://example.com'},
  150. {'pagerduty': {'integration_key': 'https://example.com'}},
  151. True,
  152. ),
  153. (
  154. {'cronhub': 'https://example.com'},
  155. {'cronhub': {'ping_url': 'https://example.com'}},
  156. True,
  157. ),
  158. (
  159. {'checks': ['archives']},
  160. {'checks': [{'name': 'archives'}]},
  161. True,
  162. ),
  163. (
  164. {'checks': ['archives']},
  165. {'checks': [{'name': 'archives'}]},
  166. True,
  167. ),
  168. (
  169. {'numeric_owner': False},
  170. {'numeric_ids': False},
  171. True,
  172. ),
  173. (
  174. {'bsd_flags': False},
  175. {'flags': False},
  176. True,
  177. ),
  178. (
  179. {'remote_rate_limit': False},
  180. {'upload_rate_limit': False},
  181. True,
  182. ),
  183. (
  184. {'repositories': ['foo@bar:/repo']},
  185. {'repositories': [{'path': 'ssh://foo@bar/repo'}]},
  186. True,
  187. ),
  188. (
  189. {'repositories': ['foo@bar:repo']},
  190. {'repositories': [{'path': 'ssh://foo@bar/./repo'}]},
  191. True,
  192. ),
  193. (
  194. {'repositories': ['foo@bar:~/repo']},
  195. {'repositories': [{'path': 'ssh://foo@bar/~/repo'}]},
  196. True,
  197. ),
  198. (
  199. {'repositories': ['ssh://foo@bar:1234/repo']},
  200. {'repositories': [{'path': 'ssh://foo@bar:1234/repo'}]},
  201. True,
  202. ),
  203. (
  204. {'repositories': ['sftp://foo@bar:1234/repo']},
  205. {'repositories': [{'path': 'sftp://foo@bar:1234/repo'}]},
  206. True,
  207. ),
  208. (
  209. {'repositories': ['rclone:host:repo']},
  210. {'repositories': [{'path': 'rclone:host:repo'}]},
  211. True,
  212. ),
  213. (
  214. {'repositories': ['file:///repo']},
  215. {'repositories': [{'path': '/repo'}]},
  216. True,
  217. ),
  218. (
  219. {'repositories': [{'path': 'first'}, 'file:///repo']},
  220. {'repositories': [{'path': 'first'}, {'path': '/repo'}]},
  221. True,
  222. ),
  223. (
  224. {'repositories': [{'path': 'foo@bar:/repo', 'label': 'foo'}]},
  225. {'repositories': [{'path': 'ssh://foo@bar/repo', 'label': 'foo'}]},
  226. True,
  227. ),
  228. (
  229. {'repositories': [{'path': 'file:///repo', 'label': 'foo'}]},
  230. {'repositories': [{'path': '/repo', 'label': 'foo'}]},
  231. False,
  232. ),
  233. (
  234. {'repositories': [{'path': '/repo', 'label': 'foo'}]},
  235. {'repositories': [{'path': '/repo', 'label': 'foo'}]},
  236. False,
  237. ),
  238. (
  239. {'prefix': 'foo'},
  240. {'prefix': 'foo'},
  241. True,
  242. ),
  243. ),
  244. )
  245. def test_normalize_applies_hard_coded_normalization_to_config(
  246. config, expected_config, produces_logs
  247. ):
  248. flexmock(module).should_receive('normalize_sections').and_return([])
  249. logs = module.normalize('test.yaml', config)
  250. expected_config.setdefault('bootstrap', {})
  251. assert config == expected_config
  252. if produces_logs:
  253. assert logs
  254. else:
  255. assert logs == []
  256. def test_normalize_config_with_borgmatic_source_directory_warns():
  257. flexmock(module).should_receive('normalize_sections').and_return([])
  258. logs = module.normalize('test.yaml', {'borgmatic_source_directory': '~/.borgmatic'})
  259. assert len(logs) == 1
  260. assert logs[0].levelno == module.logging.WARNING
  261. assert 'borgmatic_source_directory' in logs[0].msg