1
0

test_serializers.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. # -*- coding: utf-8 -*-
  2. # Import python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. from textwrap import dedent
  5. # Import Salt Testing libs
  6. from tests.support.unit import skipIf, TestCase
  7. # Import 3rd party libs
  8. import jinja2
  9. import yaml as _yaml # future lint: disable=blacklisted-import
  10. from salt.ext import six
  11. # Import salt libs
  12. import salt.serializers.configparser as configparser
  13. import salt.serializers.json as json
  14. import salt.serializers.yaml as yaml
  15. import salt.serializers.yamlex as yamlex
  16. import salt.serializers.msgpack as msgpack
  17. import salt.serializers.python as python
  18. import salt.serializers.toml as toml
  19. from salt.serializers.yaml import EncryptedString
  20. from salt.serializers import SerializationError
  21. from salt.utils.odict import OrderedDict
  22. # Import test support libs
  23. from tests.support.helpers import flaky
  24. SKIP_MESSAGE = '%s is unavailable, have prerequisites been met?'
  25. @flaky(condition=six.PY3)
  26. class TestSerializers(TestCase):
  27. @skipIf(not json.available, SKIP_MESSAGE % 'json')
  28. def test_serialize_json(self):
  29. data = {
  30. "foo": "bar"
  31. }
  32. serialized = json.serialize(data)
  33. assert serialized == '{"foo": "bar"}', serialized
  34. deserialized = json.deserialize(serialized)
  35. assert deserialized == data, deserialized
  36. @skipIf(not yaml.available, SKIP_MESSAGE % 'yaml')
  37. def test_serialize_yaml(self):
  38. data = {
  39. "foo": "bar",
  40. "encrypted_data": EncryptedString("foo")
  41. }
  42. # The C dumper produces unquoted strings when serializing an
  43. # EncryptedString, while the non-C dumper produces quoted strings.
  44. expected = '{encrypted_data: !encrypted foo, foo: bar}' \
  45. if hasattr(_yaml, 'CSafeDumper') \
  46. else "{encrypted_data: !encrypted 'foo', foo: bar}"
  47. serialized = yaml.serialize(data)
  48. assert serialized == expected, serialized
  49. deserialized = yaml.deserialize(serialized)
  50. assert deserialized == data, deserialized
  51. @skipIf(not yaml.available, SKIP_MESSAGE % 'sls')
  52. def test_serialize_sls(self):
  53. data = {
  54. "foo": "bar"
  55. }
  56. serialized = yamlex.serialize(data)
  57. assert serialized == '{foo: bar}', serialized
  58. deserialized = yamlex.deserialize(serialized)
  59. assert deserialized == data, deserialized
  60. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  61. def test_serialize_complex_sls(self):
  62. data = OrderedDict([
  63. ("foo", 1),
  64. ("bar", 2),
  65. ("baz", True),
  66. ])
  67. serialized = yamlex.serialize(data)
  68. assert serialized == '{foo: 1, bar: 2, baz: true}', serialized
  69. deserialized = yamlex.deserialize(serialized)
  70. assert deserialized == data, deserialized
  71. @skipIf(not yaml.available, SKIP_MESSAGE % 'yaml')
  72. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  73. def test_compare_sls_vs_yaml(self):
  74. src = '{foo: 1, bar: 2, baz: {qux: true}}'
  75. sls_data = yamlex.deserialize(src)
  76. yml_data = yaml.deserialize(src)
  77. # ensure that sls & yaml have the same base
  78. assert isinstance(sls_data, dict)
  79. assert isinstance(yml_data, dict)
  80. assert sls_data == yml_data
  81. # ensure that sls is ordered, while yaml not
  82. assert isinstance(sls_data, OrderedDict)
  83. assert not isinstance(yml_data, OrderedDict)
  84. @skipIf(not yaml.available, SKIP_MESSAGE % 'yaml')
  85. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  86. @skipIf(six.PY3, 'Flaky on Python 3.')
  87. def test_compare_sls_vs_yaml_with_jinja(self):
  88. tpl = '{{ data }}'
  89. env = jinja2.Environment()
  90. src = '{foo: 1, bar: 2, baz: {qux: true}}'
  91. sls_src = env.from_string(tpl).render(data=yamlex.deserialize(src))
  92. yml_src = env.from_string(tpl).render(data=yaml.deserialize(src))
  93. sls_data = yamlex.deserialize(sls_src)
  94. yml_data = yaml.deserialize(yml_src)
  95. # ensure that sls & yaml have the same base
  96. assert isinstance(sls_data, dict)
  97. assert isinstance(yml_data, dict)
  98. # The below has been commented out because something the loader test
  99. # is modifying the yaml renderer to render things to unicode. Without
  100. # running the loader test, the below passes. Even reloading the module
  101. # from disk does not reset its internal state (per the Python docs).
  102. ##
  103. #assert sls_data == yml_data
  104. # ensure that sls is ordered, while yaml not
  105. assert isinstance(sls_data, OrderedDict)
  106. assert not isinstance(yml_data, OrderedDict)
  107. # prove that yaml does not handle well with OrderedDict
  108. # while sls is jinja friendly.
  109. obj = OrderedDict([
  110. ('foo', 1),
  111. ('bar', 2),
  112. ('baz', {'qux': True})
  113. ])
  114. sls_obj = yamlex.deserialize(yamlex.serialize(obj))
  115. try:
  116. yml_obj = yaml.deserialize(yaml.serialize(obj))
  117. except SerializationError:
  118. # BLAAM! yaml was unable to serialize OrderedDict,
  119. # but it's not the purpose of the current test.
  120. yml_obj = obj.copy()
  121. sls_src = env.from_string(tpl).render(data=sls_obj)
  122. yml_src = env.from_string(tpl).render(data=yml_obj)
  123. final_obj = yaml.deserialize(sls_src)
  124. assert obj == final_obj
  125. # BLAAM! yml_src is not valid !
  126. final_obj = OrderedDict(yaml.deserialize(yml_src))
  127. assert obj != final_obj, 'Objects matched! {} == {}'.format(obj, final_obj)
  128. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  129. def test_sls_aggregate(self):
  130. src = dedent("""
  131. a: lol
  132. foo: !aggregate hello
  133. bar: !aggregate [1, 2, 3]
  134. baz: !aggregate
  135. a: 42
  136. b: 666
  137. c: the beast
  138. """).strip()
  139. # test that !aggregate is correctly parsed
  140. sls_obj = yamlex.deserialize(src)
  141. assert sls_obj == {
  142. 'a': 'lol',
  143. 'foo': ['hello'],
  144. 'bar': [1, 2, 3],
  145. 'baz': {
  146. 'a': 42,
  147. 'b': 666,
  148. 'c': 'the beast'
  149. }
  150. }, sls_obj
  151. assert dedent("""
  152. a: lol
  153. foo: [hello]
  154. bar: [1, 2, 3]
  155. baz: {a: 42, b: 666, c: the beast}
  156. """).strip() == yamlex.serialize(sls_obj), sls_obj
  157. # test that !aggregate aggregates scalars
  158. src = dedent("""
  159. placeholder: !aggregate foo
  160. placeholder: !aggregate bar
  161. placeholder: !aggregate baz
  162. """).strip()
  163. sls_obj = yamlex.deserialize(src)
  164. assert sls_obj == {'placeholder': ['foo', 'bar', 'baz']}, sls_obj
  165. # test that !aggregate aggregates lists
  166. src = dedent("""
  167. placeholder: !aggregate foo
  168. placeholder: !aggregate [bar, baz]
  169. placeholder: !aggregate []
  170. placeholder: !aggregate ~
  171. """).strip()
  172. sls_obj = yamlex.deserialize(src)
  173. assert sls_obj == {'placeholder': ['foo', 'bar', 'baz']}, sls_obj
  174. # test that !aggregate aggregates dicts
  175. src = dedent("""
  176. placeholder: !aggregate {foo: 42}
  177. placeholder: !aggregate {bar: null}
  178. placeholder: !aggregate {baz: inga}
  179. """).strip()
  180. sls_obj = yamlex.deserialize(src)
  181. assert sls_obj == {
  182. 'placeholder': {
  183. 'foo': 42,
  184. 'bar': None,
  185. 'baz': 'inga'
  186. }
  187. }, sls_obj
  188. # test that !aggregate aggregates deep dicts
  189. src = dedent("""
  190. placeholder: {foo: !aggregate {foo: 42}}
  191. placeholder: {foo: !aggregate {bar: null}}
  192. placeholder: {foo: !aggregate {baz: inga}}
  193. """).strip()
  194. sls_obj = yamlex.deserialize(src)
  195. assert sls_obj == {
  196. 'placeholder': {
  197. 'foo': {
  198. 'foo': 42,
  199. 'bar': None,
  200. 'baz': 'inga'
  201. }
  202. }
  203. }, sls_obj
  204. # test that {foo: !aggregate bar} and {!aggregate foo: bar}
  205. # are roughly equivalent.
  206. src = dedent("""
  207. placeholder: {!aggregate foo: {foo: 42}}
  208. placeholder: {!aggregate foo: {bar: null}}
  209. placeholder: {!aggregate foo: {baz: inga}}
  210. """).strip()
  211. sls_obj = yamlex.deserialize(src)
  212. assert sls_obj == {
  213. 'placeholder': {
  214. 'foo': {
  215. 'foo': 42,
  216. 'bar': None,
  217. 'baz': 'inga'
  218. }
  219. }
  220. }, sls_obj
  221. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  222. def test_sls_reset(self):
  223. src = dedent("""
  224. placeholder: {!aggregate foo: {foo: 42}}
  225. placeholder: {!aggregate foo: {bar: null}}
  226. !reset placeholder: {!aggregate foo: {baz: inga}}
  227. """).strip()
  228. sls_obj = yamlex.deserialize(src)
  229. assert sls_obj == {
  230. 'placeholder': {
  231. 'foo': {
  232. 'baz': 'inga'
  233. }
  234. }
  235. }, sls_obj
  236. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  237. def test_sls_repr(self):
  238. """
  239. Ensure that obj __repr__ and __str__ methods are yaml friendly.
  240. """
  241. def convert(obj):
  242. return yamlex.deserialize(yamlex.serialize(obj))
  243. sls_obj = convert(OrderedDict([('foo', 'bar'), ('baz', 'qux')]))
  244. # ensure that repr and str are yaml friendly
  245. assert sls_obj.__str__() == '{foo: bar, baz: qux}'
  246. assert sls_obj.__repr__() == '{foo: bar, baz: qux}'
  247. # ensure that repr and str are already quoted
  248. assert sls_obj['foo'].__str__() == '"bar"'
  249. assert sls_obj['foo'].__repr__() == '"bar"'
  250. @skipIf(not yamlex.available, SKIP_MESSAGE % 'sls')
  251. def test_sls_micking_file_merging(self):
  252. def convert(obj):
  253. return yamlex.deserialize(yamlex.serialize(obj))
  254. # let say that we have 2 pillar files
  255. src1 = dedent("""
  256. a: first
  257. b: !aggregate first
  258. c:
  259. subkey1: first
  260. subkey2: !aggregate first
  261. """).strip()
  262. src2 = dedent("""
  263. a: second
  264. b: !aggregate second
  265. c:
  266. subkey2: !aggregate second
  267. subkey3: second
  268. """).strip()
  269. sls_obj1 = yamlex.deserialize(src1)
  270. sls_obj2 = yamlex.deserialize(src2)
  271. sls_obj3 = yamlex.merge_recursive(sls_obj1, sls_obj2)
  272. assert sls_obj3 == {
  273. 'a': 'second',
  274. 'b': ['first', 'second'],
  275. 'c': {
  276. 'subkey2': ['first', 'second'],
  277. 'subkey3': 'second'
  278. }
  279. }, sls_obj3
  280. @skipIf(not msgpack.available, SKIP_MESSAGE % 'msgpack')
  281. def test_msgpack(self):
  282. data = OrderedDict([
  283. ("foo", 1),
  284. ("bar", 2),
  285. ("baz", True),
  286. ])
  287. serialized = msgpack.serialize(data)
  288. deserialized = msgpack.deserialize(serialized)
  289. assert deserialized == data, deserialized
  290. @skipIf(not python.available, SKIP_MESSAGE % 'python')
  291. def test_serialize_python(self):
  292. data = {'foo': 'bar'}
  293. serialized = python.serialize(data)
  294. expected = repr({'foo': 'bar'})
  295. assert serialized == expected, serialized
  296. @skipIf(not configparser.available, SKIP_MESSAGE % 'configparser')
  297. def test_configparser(self):
  298. data = {'foo': {'bar': 'baz'}}
  299. # configparser appends empty lines
  300. serialized = configparser.serialize(data).strip()
  301. assert serialized == "[foo]\nbar = baz", serialized
  302. deserialized = configparser.deserialize(serialized)
  303. assert deserialized == data, deserialized
  304. @skipIf(not toml.available, SKIP_MESSAGE % 'toml')
  305. def test_serialize_toml(self):
  306. data = {
  307. "foo": "bar"
  308. }
  309. serialized = toml.serialize(data)
  310. assert serialized == 'foo = "bar"\n', serialized
  311. deserialized = toml.deserialize(serialized)
  312. assert deserialized == data, deserialized