test_js_variable_to_python.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. # -*- coding: utf-8 -*-
  2. """
  3. Tests for the function searx.utils.js_variable_to_python
  4. The tests are copied from https://github.com/Nykakin/chompjs/blob/c1501b5cd82c0044539875331745b820e7bfd067/chompjs/test_parser.py
  5. Comment out tests do not pass
  6. """
  7. import math
  8. from parameterized import parameterized
  9. from searx.utils import js_variable_to_python
  10. from tests import SearxTestCase
  11. class TestParser(SearxTestCase):
  12. @parameterized.expand(
  13. [
  14. ("{'hello': 'world'}", {'hello': 'world'}),
  15. ("{'hello': 'world', 'my': 'master'}", {'hello': 'world', 'my': 'master'}),
  16. (
  17. "{'hello': 'world', 'my': {'master': 'of Orion'}, 'test': 'xx'}",
  18. {'hello': 'world', 'my': {'master': 'of Orion'}, 'test': 'xx'},
  19. ),
  20. ("{}", {}),
  21. ]
  22. )
  23. def test_parse_object(self, js, expected_py):
  24. py = js_variable_to_python(js)
  25. self.assertEqual(py, expected_py)
  26. @parameterized.expand(
  27. [
  28. ("[]", []),
  29. ("[[[]]]", [[[]]]),
  30. ("[[[1]]]", [[[1]]]),
  31. ("[1]", [1]),
  32. ("[1, 2, 3, 4]", [1, 2, 3, 4]),
  33. ("['h', 'e', 'l', 'l', 'o']", ['h', 'e', 'l', 'l', 'o']),
  34. ("[[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]", [[[[[[[[[[[[[[[1]]]]]]]]]]]]]]]),
  35. ]
  36. )
  37. def test_parse_list(self, js, expected_py):
  38. py = js_variable_to_python(js)
  39. self.assertEqual(py, expected_py)
  40. @parameterized.expand(
  41. [
  42. ("{'hello': [], 'world': [0]}", {'hello': [], 'world': [0]}),
  43. ("{'hello': [1, 2, 3, 4]}", {'hello': [1, 2, 3, 4]}),
  44. ("[{'a':12}, {'b':33}]", [{'a': 12}, {'b': 33}]),
  45. (
  46. "[false, {'true': true, `pies`: \"kot\"}, false,]",
  47. [False, {"true": True, 'pies': 'kot'}, False],
  48. ),
  49. (
  50. "{a:1,b:1,c:1,d:1,e:1,f:1,g:1,h:1,i:1,j:1}",
  51. {k: 1 for k in 'abcdefghij'},
  52. ),
  53. (
  54. "{'a':[{'b':1},{'c':[{'d':{'f':{'g':[1,2]}}},{'e':1}]}]}",
  55. {'a': [{'b': 1}, {'c': [{'d': {'f': {'g': [1, 2]}}}, {'e': 1}]}]},
  56. ),
  57. ]
  58. )
  59. def test_parse_mixed(self, js, expected_py):
  60. py = js_variable_to_python(js)
  61. self.assertEqual(py, expected_py)
  62. @parameterized.expand(
  63. [
  64. ("{'hello': 12, 'world': 10002.21}", {'hello': 12, 'world': 10002.21}),
  65. ("[12, -323, 0.32, -32.22, .2, - 4]", [12, -323, 0.32, -32.22, 0.2, -4]),
  66. ('{"a": -12, "b": - 5}', {'a': -12, 'b': -5}),
  67. ("{'a': true, 'b': false, 'c': null}", {'a': True, 'b': False, 'c': None}),
  68. ("[\"\\uD834\\uDD1E\"]", ['𝄞']),
  69. ("{'a': '123\\'456\\n'}", {'a': "123'456\n"}),
  70. ("['\u00E9']", ['é']),
  71. ('{"cache":{"\u002Ftest\u002F": 0}}', {'cache': {'/test/': 0}}),
  72. ('{"a": 3.125e7}', {'a': 3.125e7}),
  73. ('''{"a": "b\\'"}''', {'a': "b'"}),
  74. ('{"a": .99, "b": -.1}', {"a": 0.99, "b": -0.1}),
  75. ('["/* ... */", "// ..."]', ["/* ... */", "// ..."]),
  76. ('{"inclusions":["/*","/"]}', {'inclusions': ['/*', '/']}),
  77. ]
  78. )
  79. def test_parse_standard_values(self, js, expected_py):
  80. py = js_variable_to_python(js)
  81. self.assertEqual(py, expected_py)
  82. def test_parse_nan(self):
  83. js = '{"A": NaN}'
  84. py = js_variable_to_python(js)
  85. self.assertTrue(math.isnan(py["A"]))
  86. @parameterized.expand(
  87. [
  88. ("{abc: 100, dev: 200}", {'abc': 100, 'dev': 200}),
  89. ("{abcdefghijklmnopqrstuvwxyz: 12}", {"abcdefghijklmnopqrstuvwxyz": 12}),
  90. # (
  91. # "{age: function(yearBorn,thisYear) {return thisYear - yearBorn;}}",
  92. # {"age": "function(yearBorn,thisYear) {return thisYear - yearBorn;}"}
  93. # ),
  94. # (
  95. # "{\"abc\": function() {return '])))))))))))))))';}}",
  96. # {"abc": "function() {return '])))))))))))))))';}"},
  97. # ),
  98. ('{"a": undefined}', {"a": None}), # chompjs returns {"a": "undefined"}
  99. ('[undefined, undefined]', [None, None]), # chompjs returns ["undefined", "undefined"]
  100. ("{_a: 1, $b: 2}", {"_a": 1, "$b": 2}),
  101. # ("{regex: /a[^d]{1,12}/i}", {'regex': '/a[^d]{1,12}/i'}),
  102. # ("{'a': function(){return '\"'}}", {'a': 'function(){return \'"\'}'}),
  103. ("{1: 1, 2: 2, 3: 3, 4: 4}", {'1': 1, '2': 2, '3': 3, '4': 4}),
  104. ("{'a': 121.}", {'a': 121.0}),
  105. ]
  106. )
  107. def test_parse_strange_values(self, js, expected_py):
  108. py = js_variable_to_python(js)
  109. self.assertEqual(py, expected_py)
  110. @parameterized.expand(
  111. [
  112. # ('{"a": {"b": [12, 13, 14]}}text text', {"a": {"b": [12, 13, 14]}}),
  113. # ('var test = {"a": {"b": [12, 13, 14]}}', {"a": {"b": [12, 13, 14]}}),
  114. ('{"a":\r\n10}', {'a': 10}),
  115. ("{'foo': 0,\r\n}", {'foo': 0}),
  116. ("{truefalse: 0, falsefalse: 1, nullnull: 2}", {'truefalse': 0, 'falsefalse': 1, 'nullnull': 2}),
  117. ]
  118. )
  119. def test_strange_input(self, js, expected_py):
  120. py = js_variable_to_python(js)
  121. self.assertEqual(py, expected_py)
  122. @parameterized.expand(
  123. [
  124. ("[0]", [0]),
  125. ("[1]", [1]),
  126. ("[12]", [12]),
  127. ("[12_12]", [1212]),
  128. # ("[0x12]", [18]),
  129. # ("[0xab]", [171]),
  130. # ("[0xAB]", [171]),
  131. # ("[0X12]", [18]),
  132. # ("[0Xab]", [171]),
  133. # ("[0XAB]", [171]),
  134. # ("[01234]", [668]),
  135. # ("[0o1234]", [668]),
  136. # ("[0O1234]", [668]),
  137. # ("[0b1111]", [15]),
  138. # ("[0B1111]", [15]),
  139. ("[-0]", [-0]),
  140. ("[-1]", [-1]),
  141. ("[-12]", [-12]),
  142. ("[-12_12]", [-1212]),
  143. # ("[-0x12]", [-18]),
  144. # ("[-0xab]", [-171]),
  145. # ("[-0xAB]", [-171]),
  146. # ("[-0X12]", [-18]),
  147. # ("[-0Xab]", [-171]),
  148. # ("[-0XAB]", [-171]),
  149. # ("[-01234]", [-668]),
  150. # ("[-0o1234]", [-668]),
  151. # ("[-0O1234]", [-668]),
  152. # ("[-0b1111]", [-15]),
  153. # ("[-0B1111]", [-15]),
  154. ]
  155. )
  156. def test_integer_numeric_values(self, js, expected_py):
  157. py = js_variable_to_python(js)
  158. self.assertEqual(py, expected_py)
  159. @parameterized.expand(
  160. [
  161. ("[0.32]", [0.32]),
  162. ("[-0.32]", [-0.32]),
  163. ("[.32]", [0.32]),
  164. ("[-.32]", [-0.32]),
  165. ("[12.]", [12.0]),
  166. ("[-12.]", [-12.0]),
  167. ("[12.32]", [12.32]),
  168. ("[-12.12]", [-12.12]),
  169. ("[3.1415926]", [3.1415926]),
  170. ("[.123456789]", [0.123456789]),
  171. ("[.0123]", [0.0123]),
  172. ("[0.0123]", [0.0123]),
  173. ("[-.0123]", [-0.0123]),
  174. ("[-0.0123]", [-0.0123]),
  175. ("[3.1E+12]", [3.1e12]),
  176. ("[3.1e+12]", [3.1e12]),
  177. ("[.1e-23]", [0.1e-23]),
  178. ("[.1e-23]", [0.1e-23]),
  179. ]
  180. )
  181. def test_float_numeric_values(self, js, expected_py):
  182. py = js_variable_to_python(js)
  183. self.assertEqual(py, expected_py)
  184. # @parameterized.expand([
  185. # ('["Test\\nDrive"]\n{"Test": "Drive"}', [['Test\nDrive'], {'Test': 'Drive'}]),
  186. # ])
  187. # def test_jsonlines(self, js, expected_py):
  188. # py = js_variable_to_python(js)
  189. # self.assertEqual(py, expected_py)
  190. class TestParserExceptions(SearxTestCase):
  191. @parameterized.expand(
  192. [
  193. ('}{', ValueError),
  194. ('', ValueError),
  195. (None, ValueError),
  196. ]
  197. )
  198. def test_exceptions(self, js, expected_exception):
  199. with self.assertRaises(expected_exception):
  200. js_variable_to_python(js)
  201. @parameterized.expand(
  202. [
  203. ("{whose: 's's', category_name: '>'}", ValueError),
  204. ]
  205. )
  206. def test_malformed_input(self, in_data, expected_exception):
  207. with self.assertRaises(expected_exception):
  208. js_variable_to_python(in_data)
  209. @parameterized.expand(
  210. [
  211. (
  212. '{"test": """}',
  213. ValueError,
  214. 'js_variable_to_python creates invalid JSON',
  215. ),
  216. ]
  217. )
  218. def test_error_messages(self, js, expected_exception, expected_exception_text):
  219. with self.assertRaisesRegex(expected_exception, expected_exception_text):
  220. js_variable_to_python(js)
  221. # class TestOptions(SearxTestCase):
  222. # @parameterized.expand(
  223. # [
  224. # ('{\\\"a\\\": 12}', {'a': 12}),
  225. # ]
  226. # )
  227. # def test_unicode_escape(self, js, expected_py):
  228. # py = js_variable_to_python(js)
  229. # self.assertEqual(py, expected_py)
  230. class TestParseJsonObjects(SearxTestCase):
  231. @parameterized.expand(
  232. [
  233. # ("", []),
  234. # ("aaaaaaaaaaaaaaaa", []),
  235. # (" ", []),
  236. (" {'a': 12}", [{'a': 12}]),
  237. # ("[1, 2, 3, 4]xxxxxxxxxxxxxxxxxxxxxxxx", [[1, 2, 3, 4]]),
  238. # ("[12] [13] [14]", [[12], [13], [14]]),
  239. # ("[10] {'a': [1, 1, 1,]}", [[10], {'a': [1, 1, 1]}]),
  240. # ("[1][1][1]", [[1], [1], [1]]),
  241. # ("[1] [2] {'a': ", [[1], [2]]),
  242. # ("[]", [[]]),
  243. # ("[][][][]", [[], [], [], []]),
  244. ("{}", [{}]),
  245. # ("{}{}{}{}", [{}, {}, {}, {}]),
  246. # ("{{}}{{}}", []),
  247. # ("[[]][[]]", [[[]], [[]]]),
  248. # ("{am: 'ab'}\n{'ab': 'xx'}", [{'am': 'ab'}, {'ab': 'xx'}]),
  249. # (
  250. # 'function(a, b, c){ /* ... */ }({"a": 12}, Null, [1, 2, 3])',
  251. # [{}, {'a': 12}, [1, 2, 3]],
  252. # ),
  253. # ('{"a": 12, broken}{"c": 100}', [{'c': 100}]),
  254. # ('[12,,,,21][211,,,][12,12][12,,,21]', [[12, 12]]),
  255. ]
  256. )
  257. def test_parse_json_objects(self, js, expected_py):
  258. py_in_list = [js_variable_to_python(js)]
  259. self.assertEqual(py_in_list, expected_py)