# -*- coding: utf-8 -*- """ Tests for salt.utils.json """ # Import Python libs from __future__ import absolute_import, print_function, unicode_literals import textwrap # Import Salt libs import salt.utils.files import salt.utils.json import salt.utils.platform import salt.utils.stringutils from salt.ext import six # Import Salt Testing libs from tests.support.helpers import with_tempfile from tests.support.mock import MagicMock, patch from tests.support.unit import LOREM_IPSUM, TestCase, skipIf class JSONTestCase(TestCase): data = { "спам": "яйца", "list": [1, 2, "three"], "dict": {"subdict": {"спам": "яйца"}}, "True": False, "float": 1.5, "None": None, } serialized = salt.utils.stringutils.to_str( '{"None": null, "True": false, "dict": {"subdict": {"спам": "яйца"}}, "float": 1.5, "list": [1, 2, "three"], "спам": "яйца"}' ) serialized_indent4 = salt.utils.stringutils.to_str( textwrap.dedent( """\ { "None": null, "True": false, "dict": { "subdict": { "спам": "яйца" } }, "float": 1.5, "list": [ 1, 2, "three" ], "спам": "яйца" }""" ) ) def test_find_json(self): test_sample_json = """ { "glossary": { "title": "example glossary", "GlossDiv": { "title": "S", "GlossList": { "GlossEntry": { "ID": "SGML", "SortAs": "SGML", "GlossTerm": "Standard Generalized Markup Language", "Acronym": "SGML", "Abbrev": "ISO 8879:1986", "GlossDef": { "para": "A meta-markup language, used to create markup languages such as DocBook.", "GlossSeeAlso": ["GML", "XML"] }, "GlossSee": "markup" } } } } } """ expected_ret = { "glossary": { "GlossDiv": { "GlossList": { "GlossEntry": { "GlossDef": { "GlossSeeAlso": ["GML", "XML"], "para": "A meta-markup language, used to create markup languages such as DocBook.", }, "GlossSee": "markup", "Acronym": "SGML", "GlossTerm": "Standard Generalized Markup Language", "SortAs": "SGML", "Abbrev": "ISO 8879:1986", "ID": "SGML", } }, "title": "S", }, "title": "example glossary", } } # First test the valid JSON ret = salt.utils.json.find_json(test_sample_json) self.assertDictEqual(ret, expected_ret) # Now pre-pend some garbage and re-test garbage_prepend_json = "{0}{1}".format(LOREM_IPSUM, test_sample_json) ret = salt.utils.json.find_json(garbage_prepend_json) self.assertDictEqual(ret, expected_ret) # Test to see if a ValueError is raised if no JSON is passed in self.assertRaises(ValueError, salt.utils.json.find_json, LOREM_IPSUM) @skipIf( salt.utils.platform.is_windows(), "skip until we figure out what to do about decoding unicode on windows", ) @skipIf(not six.PY2, "Test only needed on Python 2") def test_find_json_unicode_splitlines(self): """ Tests a case in salt-ssh where a unicode string is split into a list of str types by .splitlines(). """ raw = '{"foo": "öäü"}' mock_split = MagicMock(return_value=[raw.encode("utf8")]) with patch.object(salt.utils.json, "__split", mock_split): ret = salt.utils.json.find_json(raw) self.assertEqual(ret, {"foo": "öäü"}) def test_dumps_loads(self): """ Test dumping to and loading from a string """ # Dump with no indentation ret = salt.utils.json.dumps(self.data, sort_keys=True) # Make sure the result is as expected self.assertEqual(ret, self.serialized) # Loading it should be equal to the original data self.assertEqual(salt.utils.json.loads(ret), self.data) # Dump with 4 spaces of indentation ret = salt.utils.json.dumps(self.data, sort_keys=True, indent=4) # Make sure the result is as expected. Note that in Python 2, dumping # results in trailing whitespace on lines ending in a comma. So, for a # proper comparison, we will have to run rstrip on each line of the # return and then stitch it back together. ret = str("\n").join( [x.rstrip() for x in ret.splitlines()] ) # future lint: disable=blacklisted-function self.assertEqual(ret, self.serialized_indent4) # Loading it should be equal to the original data self.assertEqual(salt.utils.json.loads(ret), self.data) @with_tempfile() def test_dump_load(self, json_out): """ Test dumping to and loading from a file handle """ with salt.utils.files.fopen(json_out, "wb") as fp_: fp_.write(salt.utils.stringutils.to_bytes(salt.utils.json.dumps(self.data))) with salt.utils.files.fopen(json_out, "rb") as fp_: ret = salt.utils.json.loads(salt.utils.stringutils.to_unicode(fp_.read())) # Loading should be equal to the original data self.assertEqual(ret, self.data)