test_yedit.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. '''
  2. Unit tests for yedit
  3. '''
  4. import os
  5. import sys
  6. import unittest
  7. import mock
  8. # Removing invalid variable names for tests so that I can
  9. # keep them brief
  10. # pylint: disable=invalid-name,no-name-in-module
  11. # Disable import-error b/c our libraries aren't loaded in jenkins
  12. # pylint: disable=import-error
  13. # place yedit in our path
  14. yedit_path = os.path.join('/'.join(os.path.realpath(__file__).split('/')[:-4]), 'library') # noqa: E501
  15. sys.path.insert(0, yedit_path)
  16. from yedit import Yedit, YeditException # noqa: E402
  17. # pylint: disable=too-many-public-methods
  18. # Silly pylint, moar tests!
  19. class YeditTest(unittest.TestCase):
  20. '''
  21. Test class for yedit
  22. '''
  23. data = {'a': 'a',
  24. 'b': {'c': {'d': [{'e': 'x'}, 'f', 'g']}},
  25. } # noqa: E124
  26. filename = 'yedit_test.yml'
  27. def setUp(self):
  28. ''' setup method will create a file and set to known configuration '''
  29. yed = Yedit(YeditTest.filename)
  30. yed.yaml_dict = YeditTest.data
  31. yed.write()
  32. def test_load(self):
  33. ''' Testing a get '''
  34. yed = Yedit('yedit_test.yml')
  35. self.assertEqual(yed.yaml_dict, self.data)
  36. def test_write(self):
  37. ''' Testing a simple write '''
  38. yed = Yedit('yedit_test.yml')
  39. yed.put('key1', 1)
  40. yed.write()
  41. self.assertTrue('key1' in yed.yaml_dict)
  42. self.assertEqual(yed.yaml_dict['key1'], 1)
  43. def test_write_x_y_z(self):
  44. '''Testing a write of multilayer key'''
  45. yed = Yedit('yedit_test.yml')
  46. yed.put('x.y.z', 'modified')
  47. yed.write()
  48. yed.load()
  49. self.assertEqual(yed.get('x.y.z'), 'modified')
  50. def test_delete_a(self):
  51. '''Testing a simple delete '''
  52. yed = Yedit('yedit_test.yml')
  53. yed.delete('a')
  54. yed.write()
  55. yed.load()
  56. self.assertTrue('a' not in yed.yaml_dict)
  57. def test_delete_b_c(self):
  58. '''Testing delete of layered key '''
  59. yed = Yedit('yedit_test.yml', separator=':')
  60. yed.delete('b:c')
  61. yed.write()
  62. yed.load()
  63. self.assertTrue('b' in yed.yaml_dict)
  64. self.assertFalse('c' in yed.yaml_dict['b'])
  65. def test_create(self):
  66. '''Testing a create '''
  67. os.unlink(YeditTest.filename)
  68. yed = Yedit('yedit_test.yml')
  69. yed.create('foo', 'bar')
  70. yed.write()
  71. yed.load()
  72. self.assertTrue('foo' in yed.yaml_dict)
  73. self.assertTrue(yed.yaml_dict['foo'] == 'bar')
  74. def test_create_content(self):
  75. '''Testing a create with content '''
  76. content = {"foo": "bar"}
  77. yed = Yedit("yedit_test.yml", content)
  78. yed.write()
  79. yed.load()
  80. self.assertTrue('foo' in yed.yaml_dict)
  81. self.assertTrue(yed.yaml_dict['foo'], 'bar')
  82. def test_array_insert(self):
  83. '''Testing a create with content '''
  84. yed = Yedit("yedit_test.yml", separator=':')
  85. yed.put('b:c:d[0]', 'inject')
  86. self.assertTrue(yed.get('b:c:d[0]') == 'inject')
  87. def test_array_insert_first_index(self):
  88. '''Testing a create with content '''
  89. yed = Yedit("yedit_test.yml", separator=':')
  90. yed.put('b:c:d[0]', 'inject')
  91. self.assertTrue(yed.get('b:c:d[1]') == 'f')
  92. def test_array_insert_second_index(self):
  93. '''Testing a create with content '''
  94. yed = Yedit("yedit_test.yml", separator=':')
  95. yed.put('b:c:d[0]', 'inject')
  96. self.assertTrue(yed.get('b:c:d[2]') == 'g')
  97. def test_dict_array_dict_access(self):
  98. '''Testing a create with content'''
  99. yed = Yedit("yedit_test.yml", separator=':')
  100. yed.put('b:c:d[0]', [{'x': {'y': 'inject'}}])
  101. self.assertTrue(yed.get('b:c:d[0]:[0]:x:y') == 'inject')
  102. def test_dict_array_dict_replace(self):
  103. '''Testing multilevel delete'''
  104. yed = Yedit("yedit_test.yml", separator=':')
  105. yed.put('b:c:d[0]', [{'x': {'y': 'inject'}}])
  106. yed.put('b:c:d[0]:[0]:x:y', 'testing')
  107. self.assertTrue('b' in yed.yaml_dict)
  108. self.assertTrue('c' in yed.yaml_dict['b'])
  109. self.assertTrue('d' in yed.yaml_dict['b']['c'])
  110. self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'], list))
  111. self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0], list))
  112. self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0][0], dict))
  113. self.assertTrue('y' in yed.yaml_dict['b']['c']['d'][0][0]['x'])
  114. self.assertTrue(yed.yaml_dict['b']['c']['d'][0][0]['x']['y'] == 'testing') # noqa: E501
  115. def test_dict_array_dict_remove(self):
  116. '''Testing multilevel delete'''
  117. yed = Yedit("yedit_test.yml", separator=':')
  118. yed.put('b:c:d[0]', [{'x': {'y': 'inject'}}])
  119. yed.delete('b:c:d[0]:[0]:x:y')
  120. self.assertTrue('b' in yed.yaml_dict)
  121. self.assertTrue('c' in yed.yaml_dict['b'])
  122. self.assertTrue('d' in yed.yaml_dict['b']['c'])
  123. self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'], list))
  124. self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0], list))
  125. self.assertTrue(isinstance(yed.yaml_dict['b']['c']['d'][0][0], dict))
  126. self.assertFalse('y' in yed.yaml_dict['b']['c']['d'][0][0]['x'])
  127. def test_key_exists_in_dict(self):
  128. '''Testing exist in dict'''
  129. yed = Yedit("yedit_test.yml", separator=':')
  130. yed.put('b:c:d[0]', [{'x': {'y': 'inject'}}])
  131. self.assertTrue(yed.exists('b:c', 'd'))
  132. def test_key_exists_in_list(self):
  133. '''Testing exist in list'''
  134. yed = Yedit("yedit_test.yml", separator=':')
  135. yed.put('b:c:d[0]', [{'x': {'y': 'inject'}}])
  136. self.assertTrue(yed.exists('b:c:d', [{'x': {'y': 'inject'}}]))
  137. self.assertFalse(yed.exists('b:c:d', [{'x': {'y': 'test'}}]))
  138. def test_update_to_list_with_index(self):
  139. '''Testing update to list with index'''
  140. yed = Yedit("yedit_test.yml", separator=':')
  141. yed.put('x:y:z', [1, 2, 3])
  142. yed.update('x:y:z', [5, 6], index=2)
  143. self.assertTrue(yed.get('x:y:z') == [1, 2, [5, 6]])
  144. self.assertTrue(yed.exists('x:y:z', [5, 6]))
  145. self.assertFalse(yed.exists('x:y:z', 4))
  146. def test_update_to_list_with_curr_value(self):
  147. '''Testing update to list with index'''
  148. yed = Yedit("yedit_test.yml", separator=':')
  149. yed.put('x:y:z', [1, 2, 3])
  150. yed.update('x:y:z', [5, 6], curr_value=3)
  151. self.assertTrue(yed.get('x:y:z') == [1, 2, [5, 6]])
  152. self.assertTrue(yed.exists('x:y:z', [5, 6]))
  153. self.assertFalse(yed.exists('x:y:z', 4))
  154. def test_update_to_list(self):
  155. '''Testing update to list'''
  156. yed = Yedit("yedit_test.yml", separator=':')
  157. yed.put('x:y:z', [1, 2, 3])
  158. yed.update('x:y:z', [5, 6])
  159. self.assertTrue(yed.get('x:y:z') == [1, 2, 3, [5, 6]])
  160. self.assertTrue(yed.exists('x:y:z', [5, 6]))
  161. self.assertFalse(yed.exists('x:y:z', 4))
  162. def test_append_twice_to_list(self):
  163. '''Testing append to list'''
  164. yed = Yedit("yedit_test.yml", separator=':')
  165. yed.put('x:y:z', [1, 2, 3])
  166. yed.append('x:y:z', [5, 6])
  167. yed.append('x:y:z', [5, 6])
  168. self.assertTrue(yed.get('x:y:z') == [1, 2, 3, [5, 6], [5, 6]])
  169. self.assertFalse(yed.exists('x:y:z', 4))
  170. def test_add_item_to_dict(self):
  171. '''Testing update to dict'''
  172. yed = Yedit("yedit_test.yml", separator=':')
  173. yed.put('x:y:z', {'a': 1, 'b': 2})
  174. yed.update('x:y:z', {'c': 3, 'd': 4})
  175. self.assertTrue(yed.get('x:y:z') == {'a': 1, 'b': 2, 'c': 3, 'd': 4})
  176. self.assertTrue(yed.exists('x:y:z', {'c': 3}))
  177. def test_first_level_dict_with_none_value(self):
  178. '''test dict value with none value'''
  179. yed = Yedit(content={'a': None}, separator=":")
  180. yed.put('a:b:c', 'test')
  181. self.assertTrue(yed.get('a:b:c') == 'test')
  182. self.assertTrue(yed.get('a:b'), {'c': 'test'})
  183. def test_adding_yaml_variable(self):
  184. '''test dict value with none value'''
  185. yed = Yedit("yedit_test.yml", separator=':')
  186. yed.put('z:y', '{{test}}')
  187. self.assertTrue(yed.get('z:y') == '{{test}}')
  188. def test_keys_with_underscore(self):
  189. '''test dict value with none value'''
  190. yed = Yedit("yedit_test.yml", separator=':')
  191. yed.put('z_:y_y', {'test': '{{test}}'})
  192. self.assertTrue(yed.get('z_:y_y') == {'test': '{{test}}'})
  193. def test_first_level_array_update(self):
  194. '''test update on top level array'''
  195. yed = Yedit(content=[{'a': 1}, {'b': 2}, {'b': 3}], separator=':')
  196. yed.update('', {'c': 4})
  197. self.assertTrue({'c': 4} in yed.get(''))
  198. def test_first_level_array_delete(self):
  199. '''test remove top level key'''
  200. yed = Yedit(content=[{'a': 1}, {'b': 2}, {'b': 3}])
  201. yed.delete('')
  202. self.assertTrue({'b': 3} not in yed.get(''))
  203. def test_first_level_array_get(self):
  204. '''test dict value with none value'''
  205. yed = Yedit(content=[{'a': 1}, {'b': 2}, {'b': 3}])
  206. yed.get('')
  207. self.assertTrue([{'a': 1}, {'b': 2}, {'b': 3}] == yed.yaml_dict)
  208. def test_pop_list_item(self):
  209. '''test dict value with none value'''
  210. yed = Yedit(content=[{'a': 1}, {'b': 2}, {'b': 3}], separator=':')
  211. yed.pop('', {'b': 2})
  212. self.assertTrue([{'a': 1}, {'b': 3}] == yed.yaml_dict)
  213. def test_pop_list_item_2(self):
  214. '''test dict value with none value'''
  215. z = list(range(10))
  216. yed = Yedit(content=z, separator=':')
  217. yed.pop('', 5)
  218. z.pop(5)
  219. self.assertTrue(z == yed.yaml_dict)
  220. def test_pop_dict_key(self):
  221. '''test dict value with none value'''
  222. yed = Yedit(content={'a': {'b': {'c': 1, 'd': 2}}}, separator='#')
  223. yed.pop('a#b', 'c')
  224. self.assertTrue({'a': {'b': {'d': 2}}} == yed.yaml_dict)
  225. def test_accessing_path_with_unexpected_objects(self):
  226. '''test providing source path objects that differ from current object state'''
  227. yed = Yedit(content={'a': {'b': {'c': ['d', 'e']}}})
  228. with self.assertRaises(YeditException):
  229. yed.put('a.b.c.d', 'x')
  230. def test_creating_new_objects_with_embedded_list(self):
  231. '''test creating new objects with an embedded list in the creation path'''
  232. yed = Yedit(content={'a': {'b': 12}})
  233. with self.assertRaises(YeditException):
  234. yed.put('new.stuff[0].here', 'value')
  235. def test_creating_new_objects_with_trailing_list(self):
  236. '''test creating new object(s) where the final piece is a list'''
  237. yed = Yedit(content={'a': {'b': 12}})
  238. with self.assertRaises(YeditException):
  239. yed.put('new.stuff.here[0]', 'item')
  240. def test_empty_key_with_int_value(self):
  241. '''test editing top level with not list or dict'''
  242. yed = Yedit(content={'a': {'b': 12}})
  243. result = yed.put('', 'b')
  244. self.assertFalse(result[0])
  245. def test_setting_separator(self):
  246. '''test editing top level with not list or dict'''
  247. yed = Yedit(content={'a': {'b': 12}})
  248. yed.separator = ':'
  249. self.assertEqual(yed.separator, ':')
  250. def test_remove_all(self):
  251. '''test removing all data'''
  252. data = Yedit.remove_entry({'a': {'b': 12}}, '')
  253. self.assertTrue(data)
  254. def test_remove_list_entry(self):
  255. '''test removing list entry'''
  256. data = {'a': {'b': [{'c': 3}]}}
  257. results = Yedit.remove_entry(data, 'a.b[0]')
  258. self.assertTrue(results)
  259. self.assertTrue(data, {'a': {'b': []}})
  260. def test_parse_value_string_true(self):
  261. '''test parse_value'''
  262. results = Yedit.parse_value('true', 'str')
  263. self.assertEqual(results, 'true')
  264. def test_parse_value_bool_true(self):
  265. '''test parse_value'''
  266. results = Yedit.parse_value('true', 'bool')
  267. self.assertTrue(results)
  268. def test_parse_value_bool_exception(self):
  269. '''test parse_value'''
  270. with self.assertRaises(YeditException):
  271. Yedit.parse_value('TTT', 'bool')
  272. @mock.patch('yedit.Yedit.write')
  273. def test_run_ansible_basic(self, mock_write):
  274. '''test parse_value'''
  275. params = {
  276. 'src': None,
  277. 'backup': False,
  278. 'separator': '.',
  279. 'state': 'present',
  280. 'edits': [],
  281. 'value': None,
  282. 'key': None,
  283. 'content': {'a': {'b': {'c': 1}}},
  284. 'content_type': '',
  285. }
  286. results = Yedit.run_ansible(params)
  287. mock_write.side_effect = [
  288. (True, params['content']),
  289. ]
  290. self.assertFalse(results['changed'])
  291. @mock.patch('yedit.Yedit.write')
  292. def test_run_ansible_and_write(self, mock_write):
  293. '''test parse_value'''
  294. params = {
  295. 'src': '/tmp/test',
  296. 'backup': False,
  297. 'separator': '.',
  298. 'state': 'present',
  299. 'edits': [],
  300. 'value': None,
  301. 'key': None,
  302. 'content': {'a': {'b': {'c': 1}}},
  303. 'content_type': '',
  304. }
  305. results = Yedit.run_ansible(params)
  306. mock_write.side_effect = [
  307. (True, params['content']),
  308. ]
  309. self.assertTrue(results['changed'])
  310. def tearDown(self):
  311. '''TearDown method'''
  312. os.unlink(YeditTest.filename)