test_yamlloader.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Unit tests for salt.utils.yamlloader.SaltYamlSafeLoader
  4. '''
  5. # Import python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import collections
  8. import textwrap
  9. # Import Salt Libs
  10. from yaml.constructor import ConstructorError
  11. from salt.utils.yamlloader import SaltYamlSafeLoader
  12. import salt.utils.files
  13. from salt.ext import six
  14. # Import Salt Testing Libs
  15. from tests.support.unit import TestCase, skipIf
  16. from tests.support.mock import patch, NO_MOCK, NO_MOCK_REASON, mock_open
  17. # Import 3rd-party libs
  18. from salt.ext import six
  19. @skipIf(NO_MOCK, NO_MOCK_REASON)
  20. class YamlLoaderTestCase(TestCase):
  21. '''
  22. TestCase for salt.utils.yamlloader module
  23. '''
  24. @staticmethod
  25. def render_yaml(data):
  26. '''
  27. Takes a YAML string, puts it into a mock file, passes that to the YAML
  28. SaltYamlSafeLoader and then returns the rendered/parsed YAML data
  29. '''
  30. if six.PY2:
  31. # On Python 2, data read from a filehandle will not already be
  32. # unicode, so we need to encode it first to properly simulate
  33. # reading from a file. This is because unicode_literals is imported
  34. # and all of the data to be used in mock_open will be a unicode
  35. # type. Encoding will make it a str.
  36. data = salt.utils.data.encode(data)
  37. with patch('salt.utils.files.fopen', mock_open(read_data=data)) as mocked_file:
  38. with salt.utils.files.fopen(mocked_file) as mocked_stream:
  39. return SaltYamlSafeLoader(mocked_stream).get_data()
  40. @staticmethod
  41. def raise_error(value):
  42. raise TypeError('{0!r} is not a unicode string'.format(value)) # pylint: disable=repr-flag-used-in-string
  43. def assert_unicode(self, value):
  44. '''
  45. Make sure the entire data structure is unicode
  46. '''
  47. if six.PY3:
  48. return
  49. if isinstance(value, six.string_types):
  50. if not isinstance(value, six.text_type):
  51. self.raise_error(value)
  52. elif isinstance(value, collections.Mapping):
  53. for k, v in six.iteritems(value):
  54. self.assert_unicode(k)
  55. self.assert_unicode(v)
  56. elif isinstance(value, collections.Iterable):
  57. for item in value:
  58. self.assert_unicode(item)
  59. def assert_matches(self, ret, expected):
  60. self.assertEqual(ret, expected)
  61. self.assert_unicode(ret)
  62. def test_yaml_basics(self):
  63. '''
  64. Test parsing an ordinary path
  65. '''
  66. self.assert_matches(
  67. self.render_yaml(textwrap.dedent('''\
  68. p1:
  69. - alpha
  70. - beta''')),
  71. {'p1': ['alpha', 'beta']}
  72. )
  73. def test_yaml_merge(self):
  74. '''
  75. Test YAML anchors
  76. '''
  77. # Simple merge test
  78. self.assert_matches(
  79. self.render_yaml(textwrap.dedent('''\
  80. p1: &p1
  81. v1: alpha
  82. p2:
  83. <<: *p1
  84. v2: beta''')),
  85. {'p1': {'v1': 'alpha'}, 'p2': {'v1': 'alpha', 'v2': 'beta'}}
  86. )
  87. # Test that keys/nodes are overwritten
  88. self.assert_matches(
  89. self.render_yaml(textwrap.dedent('''\
  90. p1: &p1
  91. v1: alpha
  92. p2:
  93. <<: *p1
  94. v1: new_alpha''')),
  95. {'p1': {'v1': 'alpha'}, 'p2': {'v1': 'new_alpha'}}
  96. )
  97. # Test merging of lists
  98. self.assert_matches(
  99. self.render_yaml(textwrap.dedent('''\
  100. p1: &p1
  101. v1: &v1
  102. - t1
  103. - t2
  104. p2:
  105. v2: *v1''')),
  106. {"p2": {"v2": ["t1", "t2"]}, "p1": {"v1": ["t1", "t2"]}}
  107. )
  108. def test_yaml_duplicates(self):
  109. '''
  110. Test that duplicates still throw an error
  111. '''
  112. with self.assertRaises(ConstructorError):
  113. self.render_yaml(textwrap.dedent('''\
  114. p1: alpha
  115. p1: beta'''))
  116. with self.assertRaises(ConstructorError):
  117. self.render_yaml(textwrap.dedent('''\
  118. p1: &p1
  119. v1: alpha
  120. p2:
  121. <<: *p1
  122. v2: beta
  123. v2: betabeta'''))
  124. def test_yaml_with_plain_scalars(self):
  125. '''
  126. Test that plain (i.e. unqoted) string and non-string scalars are
  127. properly handled
  128. '''
  129. self.assert_matches(
  130. self.render_yaml(textwrap.dedent('''\
  131. foo:
  132. b: {foo: bar, one: 1, list: [1, two, 3]}''')),
  133. {'foo': {'b': {'foo': 'bar', 'one': 1, 'list': [1, 'two', 3]}}}
  134. )