common_ut.cpp 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972
  1. #include "test_base.h"
  2. #include <util/string/builder.h>
  3. #include <cmath>
  4. class TJsonPathCommonTest : public TJsonPathTestBase {
  5. public:
  6. TJsonPathCommonTest()
  7. : TJsonPathTestBase()
  8. {
  9. }
  10. UNIT_TEST_SUITE(TJsonPathCommonTest);
  11. UNIT_TEST(TestPrimary);
  12. UNIT_TEST(TestMemberAccess);
  13. UNIT_TEST(TestWildcardMemberAccess);
  14. UNIT_TEST(TestArrayAccess);
  15. UNIT_TEST(TestLastArrayIndex);
  16. UNIT_TEST(TestLastArrayIndexInvalid);
  17. UNIT_TEST(TestNonIntegerArrayIndex);
  18. UNIT_TEST(TestWildcardArrayAccess);
  19. UNIT_TEST(TestUnaryOperations);
  20. UNIT_TEST(TestUnaryOperationsErrors);
  21. UNIT_TEST(TestBinaryArithmeticOperations);
  22. UNIT_TEST(TestBinaryArithmeticOperationsErrors);
  23. UNIT_TEST(TestParseErrors);
  24. UNIT_TEST(TestVariables);
  25. UNIT_TEST(TestDivisionByZero);
  26. UNIT_TEST(TestInfinityResult);
  27. UNIT_TEST(TestLogicalOperations);
  28. UNIT_TEST(TestCompareOperations);
  29. UNIT_TEST(TestFilter);
  30. UNIT_TEST(TestFilterInvalid);
  31. UNIT_TEST(TestNumericMethods);
  32. UNIT_TEST(TestNumericMethodsErrors);
  33. UNIT_TEST(TestDoubleMethod);
  34. UNIT_TEST(TestDoubleMethodErrors);
  35. UNIT_TEST(TestTypeMethod);
  36. UNIT_TEST(TestSizeMethod);
  37. UNIT_TEST(TestKeyValueMethod);
  38. UNIT_TEST(TestKeyValueMethodErrors);
  39. UNIT_TEST(TestStartsWithPredicate);
  40. UNIT_TEST(TestStartsWithPredicateErrors);
  41. UNIT_TEST(TestExistsPredicate);
  42. UNIT_TEST(TestIsUnknownPredicate);
  43. UNIT_TEST(TestLikeRegexPredicate);
  44. UNIT_TEST_SUITE_END();
  45. void TestPrimary() {
  46. const TVector<TMultiOutputTestCase> testCases = {
  47. // Context object $ must return whole JSON when used alone
  48. {R"({"key": 123})", "$", {R"({"key":123})"}},
  49. {R"([1, 2, 3])", "$", {R"([1,2,3])"}},
  50. {"1.234", "$", {"1.234"}},
  51. {R"("some string")", "$", {R"("some string")"}},
  52. // Literal must not depend on input
  53. {R"({"key": 123})", "123", {"123"}},
  54. {R"([1, 2, 3])", "123", {"123"}},
  55. {"1.234", "123", {"123"}},
  56. {R"("some string")", "123", {"123"}},
  57. // Check various ways to define number literal
  58. {"1", "123.4", {"123.4"}},
  59. {"1", "0.567", {"0.567"}},
  60. {"1", "1234e-1", {"123.4"}},
  61. {"1", "567e-3", {"0.567"}},
  62. {"1", "123.4e-1", {"12.34"}},
  63. {"1", "123e3", {"123000"}},
  64. {"1", "123e+3", {"123000"}},
  65. {"1", "1.23e+1", {"12.3"}},
  66. {"1", "1.23e1", {"12.3"}},
  67. {"1", "12e0", {"12"}},
  68. {"1", "12.3e0", {"12.3"}},
  69. {"1", "0", {"0"}},
  70. {"1", "0.0", {"0"}},
  71. {"1", "0.0e0", {"0"}},
  72. // Check boolean and null literals
  73. {"1", "null", {"null"}},
  74. {"1", "false", {"false"}},
  75. {"1", "true", {"true"}},
  76. // Check string literals
  77. {"1", "\"string\"", {"\"string\""}},
  78. {"1", "\" space another space \"", {"\" space another space \""}},
  79. {"1", "\"привет\"", {"\"привет\""}},
  80. // NOTE: escaping is added by library/cpp/json
  81. {"1", "\"\r\n\t\"", {"\"\\r\\n\\t\""}},
  82. };
  83. for (const auto& testCase : testCases) {
  84. for (const auto mode : ALL_MODES) {
  85. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  86. }
  87. }
  88. }
  89. void TestMemberAccess() {
  90. const TVector<TMultiOutputTestCase> testCases = {
  91. {R"({"key": 123, "another_key": 456})", "$.key", {"123"}},
  92. {R"({"key": 123, "_another_28_key_$_": 456})", "$._another_28_key_$_", {"456"}},
  93. {R"({"key": 123, "another_key": 456})", " $.another_key ", {"456"}},
  94. {R"({"key": 123, "another_key": 456})", "$.key", {"123"}},
  95. {R"({"k\"ey": 123, "another_key": 456})", "$.\"k\\\"ey\"", {"123"}},
  96. {R"({"k\"ey": 123, "another_key": 456})", "$.'k\\\"ey'", {"123"}},
  97. {R"({"key": 123, "another_key": 456})", "$.'key'", {"123"}},
  98. {R"({"key": 123, "_another_28_key_$_": 456})", "$.'_another_28_key_$_'", {"456"}},
  99. {R"({"key": 123, "another_key": 456})", " $.'another_key' ", {"456"}},
  100. {R"({"key": 123, "another_key": 456})", "$.\"key\"", {"123"}},
  101. {R"({"key": 123, "_another_28_key_$_": 456})", "$.\"_another_28_key_$_\"", {"456"}},
  102. {R"({"key": 123, "another_key": 456})", " $.\"another_key\" ", {"456"}},
  103. {R"({"key": 123, "another key": 456})", "$.'another key'", {"456"}},
  104. {R"({"key": 123, "another key": 456})", "$.\"another key\"", {"456"}},
  105. {R"({"key": 123, "прием отбой": 456})", "$.'прием отбой'", {"456"}},
  106. {R"({"key": 123, "прием отбой": 456})", "$.\"прием отбой\"", {"456"}},
  107. {R"({"key": {"another": 456}})", "$.key.another", {"456"}},
  108. {R"({"key": {"another key": 456}})", "$.'key'.\"another key\"", {"456"}},
  109. };
  110. for (const auto& testCase : testCases) {
  111. for (const auto mode : ALL_MODES) {
  112. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  113. }
  114. }
  115. }
  116. void TestWildcardMemberAccess() {
  117. const TVector<TMultiOutputTestCase> testCases = {
  118. {R"({
  119. "first": 12,
  120. "second": 72
  121. })", "$.*", {"12", "72"}},
  122. {R"({
  123. "friends": {
  124. "Nik": {"age": 18},
  125. "Kate": {"age": 72}
  126. }
  127. })", "$.friends.*.age", {"72", "18"}},
  128. {R"({
  129. "friends": {
  130. "Nik": {"age": 18},
  131. "Kate": {"age": 72}
  132. }
  133. })", "$.*.*.*", {"72", "18"}},
  134. {R"({})", "$.*.key", {}},
  135. };
  136. for (const auto& testCase : testCases) {
  137. for (const auto mode : ALL_MODES) {
  138. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  139. }
  140. }
  141. }
  142. void TestArrayAccess() {
  143. const TVector<TMultiOutputTestCase> testCases = {
  144. {R"([1, 2, 3])", "$[0]", {"1"}},
  145. {R"([1, 2, 3, 4, 5, 6])", "$[0 to 2]", {"1", "2", "3"}},
  146. {R"([1, 2, 3, 4, 5, 6])", "$[5, 0 to 2, 0, 0, 3 to 5, 2]", {"6", "1", "2", "3", "1", "1", "4", "5", "6", "3"}},
  147. {R"({
  148. "friends": [
  149. {"name": "Nik", "age": 18},
  150. {"name": "Kate", "age": 72},
  151. {"name": "Foma", "age": 50},
  152. {"name": "Jora", "age": 60}
  153. ]
  154. })", "$.friends[1 to 3, 0].age", {"72", "50", "60", "18"}},
  155. {R"({
  156. "range": {
  157. "from": 1,
  158. "to": 2
  159. },
  160. "friends": [
  161. {"name": "Nik", "age": 18},
  162. {"name": "Kate", "age": 72},
  163. {"name": "Foma", "age": 50},
  164. {"name": "Jora", "age": 60}
  165. ]
  166. })", "$.friends[$.range.from to $.range.to].age", {"72", "50"}},
  167. {R"({
  168. "range": {
  169. "from": [1, 3, 4],
  170. "to": {"key1": 1, "key2": 2, "key3": 3}
  171. },
  172. "friends": [
  173. {"name": "Nik", "age": 18},
  174. {"name": "Kate", "age": 72},
  175. {"name": "Foma", "age": 50},
  176. {"name": "Jora", "age": 60}
  177. ]
  178. })", "$.friends[$.range.from[1] to $.range.to.key3].age", {"60"}},
  179. };
  180. for (const auto& testCase : testCases) {
  181. for (const auto mode : ALL_MODES) {
  182. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  183. }
  184. }
  185. }
  186. void TestLastArrayIndex() {
  187. const TVector<TMultiOutputTestCase> testCases = {
  188. {R"([1, 2, 3])", "$[last]", {"3"}},
  189. {R"([1, 2, 3])", "$[1 to last]", {"2", "3"}},
  190. {R"([1, 2, 3])", "$[last to last]", {"3"}},
  191. {R"([1, 2, 3, 5, 6])", "$[1, last, last, 0, 2 to last, 3]", {"2", "6", "6", "1", "3", "5", "6", "5"}},
  192. {R"([
  193. [1, 2, 3, 4],
  194. [5, 6, 7, 8]
  195. ])", "$[*][last]", {"4", "8"}},
  196. {R"({
  197. "ranges": [
  198. {"from": 1, "to": 3},
  199. {"from": 0, "to": 1}
  200. ],
  201. "friends": [
  202. {"name": "Nik", "age": 18},
  203. {"name": "Kate", "age": 72},
  204. {"name": "Foma", "age": 50},
  205. {"name": "Jora", "age": 60}
  206. ]
  207. })", "$.friends[last, $.ranges[last].from to $.ranges[last].to, 2 to last].age", {"60", "18", "72", "50", "60"}},
  208. {R"({
  209. "ranges": [
  210. {"from": 1.23, "to": 3.75},
  211. {"from": 0.58, "to": 1.00001}
  212. ],
  213. "friends": [
  214. {"name": "Nik", "age": 18},
  215. {"name": "Kate", "age": 72},
  216. {"name": "Foma", "age": 50},
  217. {"name": "Jora", "age": 60}
  218. ]
  219. })", "$.friends[last, $.ranges[last].from to $.ranges[last].to, 2 to last].age", {"60", "18", "72", "50", "60"}},
  220. };
  221. for (const auto& testCase : testCases) {
  222. for (const auto mode : ALL_MODES) {
  223. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  224. }
  225. }
  226. }
  227. void TestLastArrayIndexInvalid() {
  228. const TVector<TRuntimeErrorTestCase> testCases = {
  229. {R"({})", "last", C(TIssuesIds::JSONPATH_LAST_OUTSIDE_OF_ARRAY_SUBSCRIPT)},
  230. };
  231. for (const auto& testCase : testCases) {
  232. for (const auto mode : ALL_MODES) {
  233. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  234. }
  235. }
  236. }
  237. void TestNonIntegerArrayIndex() {
  238. const TVector<TRuntimeErrorTestCase> testCases = {
  239. {R"({
  240. "range": {
  241. "from": [1, 3, 4],
  242. "to": {"key1": 1, "key2": 2, "key3": 3}
  243. },
  244. "friends": [1, 2, 3]
  245. })", "$.friends[$.range.from[*] to $.range.to.*]", C(TIssuesIds::JSONPATH_INVALID_ARRAY_INDEX)},
  246. };
  247. for (const auto& testCase : testCases) {
  248. for (const auto mode : ALL_MODES) {
  249. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  250. }
  251. }
  252. }
  253. void TestWildcardArrayAccess() {
  254. const TVector<TMultiOutputTestCase> testCases = {
  255. {R"([1, 2, 3])", "$[*]", {"1", "2", "3"}},
  256. {R"([[1], [2], [3, 4, 5]])", "$[*][*]", {"1", "2", "3", "4", "5"}},
  257. {R"({
  258. "friends": [
  259. {"name": "Nik", "age": 18},
  260. {"name": "Kate", "age": 72},
  261. {"name": "Foma", "age": 50},
  262. {"name": "Jora", "age": 60}
  263. ]
  264. })", "$.friends[*].age", {"18", "72", "50", "60"}},
  265. };
  266. for (const auto& testCase : testCases) {
  267. for (const auto mode : ALL_MODES) {
  268. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  269. }
  270. }
  271. }
  272. void TestUnaryOperations() {
  273. const TVector<TMultiOutputTestCase> testCases = {
  274. {R"([])", "-3", {"-3"}},
  275. {R"([])", "+3", {"3"}},
  276. {R"(-1)", "-$", {"1"}},
  277. {R"(-1)", "+$", {"-1"}},
  278. {R"({
  279. "range": {
  280. "from": -1,
  281. "to": -2
  282. },
  283. "array": [1, 2, 3, 4]
  284. })", "$.array[-$.range.from to -$.range.to]", {"2", "3"}},
  285. {R"({
  286. "range": {
  287. "from": 1,
  288. "to": -2
  289. },
  290. "array": [1, 2, 3, 4]
  291. })", "$.array[+$.range.from to -$.range.to]", {"2", "3"}},
  292. {R"({
  293. "range": {
  294. "from": -1,
  295. "to": 2
  296. },
  297. "array": [1, 2, 3, 4]
  298. })", "$.array[-$.range.from to +$.range.to]", {"2", "3"}},
  299. {R"({
  300. "range": {
  301. "from": 1,
  302. "to": 2
  303. },
  304. "array": [1, 2, 3, 4]
  305. })", "$.array[+$.range.from to +$.range.to]", {"2", "3"}},
  306. {R"([1, 2, 3])", "-$[*]", {"-1", "-2", "-3"}},
  307. {"30000000000000000000000000", "-$", {"-3e+25"}},
  308. };
  309. for (const auto& testCase : testCases) {
  310. for (const auto mode : ALL_MODES) {
  311. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  312. }
  313. }
  314. }
  315. void TestUnaryOperationsErrors() {
  316. const TVector<TRuntimeErrorTestCase> testCases = {
  317. {R"({})", "-$", C(TIssuesIds::JSONPATH_INVALID_UNARY_OPERATION_ARGUMENT_TYPE)},
  318. {R"([1, 2, [], 4])", "-$[*]", C(TIssuesIds::JSONPATH_INVALID_UNARY_OPERATION_ARGUMENT_TYPE)},
  319. {R"([1, 2, {}, 4])", "-$[*]", C(TIssuesIds::JSONPATH_INVALID_UNARY_OPERATION_ARGUMENT_TYPE)},
  320. };
  321. for (const auto& testCase : testCases) {
  322. for (const auto mode : ALL_MODES) {
  323. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  324. }
  325. }
  326. }
  327. void TestBinaryArithmeticOperations() {
  328. const TVector<TMultiOutputTestCase> testCases = {
  329. {"[]", "1 + 2", {"3"}},
  330. {"[]", "1 - 2", {"-1"}},
  331. {"[]", "10 * 5", {"50"}},
  332. {"[]", "10 / 5", {"2"}},
  333. {"[]", "13 % 5", {"3"}},
  334. {"[]", "20 * 2 + 5", {"45"}},
  335. {"[]", "20 / 2 + 5", {"15"}},
  336. {"[]", "20 % 2 + 5", {"5"}},
  337. {"[]", "20 * (2 + 5)", {"140"}},
  338. {"[]", "20 / (2 + 3)", {"4"}},
  339. {"[]", "20 % (2 + 5)", {"6"}},
  340. {"[]", "5 / 2", {"2.5"}},
  341. {"[5.24 , 2.62]", "$[0] / $[1]", {"2"}},
  342. {"[5.24, 2.62]", "$[0] % $[1]", {"0"}},
  343. {"[3.753, 2.35]", "$[0] % $[1]", {"1.403"}},
  344. {"[]", "- 1 + 1", {"0"}},
  345. {"[]", "+ 1 + 1", {"2"}},
  346. {"[1, 2, 3, 4]", "$[last, last-1, last-2, last-3]", {"4", "3", "2", "1"}},
  347. };
  348. for (const auto& testCase : testCases) {
  349. for (const auto mode : ALL_MODES) {
  350. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  351. }
  352. }
  353. }
  354. void TestBinaryArithmeticOperationsErrors() {
  355. const TVector<TRuntimeErrorTestCase> testCases = {
  356. {"[1, 2, 3]", "$[*] + 1", C(TIssuesIds::JSONPATH_INVALID_BINARY_OPERATION_ARGUMENT)},
  357. {"[1, 2, 3]", "1 + $[*]", C(TIssuesIds::JSONPATH_INVALID_BINARY_OPERATION_ARGUMENT)},
  358. {"[1, 2, 3]", "$[*] + $[*]", C(TIssuesIds::JSONPATH_INVALID_BINARY_OPERATION_ARGUMENT)},
  359. {"[1, 2, 3]", "$ + 1", C(TIssuesIds::JSONPATH_INVALID_BINARY_OPERATION_ARGUMENT_TYPE)},
  360. {"[1, 2, 3]", "1 + $", C(TIssuesIds::JSONPATH_INVALID_BINARY_OPERATION_ARGUMENT_TYPE)},
  361. {"[1, 2, 3]", "$ + $", C(TIssuesIds::JSONPATH_INVALID_BINARY_OPERATION_ARGUMENT_TYPE)},
  362. };
  363. for (const auto& testCase : testCases) {
  364. for (const auto mode : ALL_MODES) {
  365. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  366. }
  367. }
  368. }
  369. void TestParseErrors() {
  370. const TVector<TString> testCases = {
  371. "strict",
  372. "strict smth.key",
  373. "strict $.",
  374. "strict $.$key",
  375. "strict $.28key",
  376. "strict $.ke^y",
  377. "strict $.привет",
  378. "strict $._пока_28_ключ_$_",
  379. " strict $.пока ",
  380. "lax",
  381. "lax smth.key",
  382. "lax $.",
  383. "lax $.$key",
  384. "lax $.28key",
  385. "lax $.ke^y",
  386. "lax $.привет",
  387. "lax $._пока_28_ключ_$_",
  388. " lax $.пока ",
  389. "12.",
  390. "12..3",
  391. "12.3e",
  392. "12.3e++1",
  393. "12.3e--1",
  394. "1e100000000000000000000000000000000",
  395. "true || false",
  396. "1 && (true == true)",
  397. "!true",
  398. "$[*] ? (@.active) . id",
  399. "!(1 > 2).type()",
  400. "(null) is unknown",
  401. "(12 * 12) is unknown",
  402. R"($ like_regex "[[[")",
  403. R"($ like_regex "[0-9]+" flag "x")",
  404. "$.first fjrfrfq fqijrhfqiwrjhfqrf qrfqr",
  405. };
  406. for (const auto& testCase : testCases) {
  407. RunParseErrorTestCase(testCase);
  408. }
  409. }
  410. void TestVariables() {
  411. TVector<TVariablesTestCase> testCases = {
  412. {"123", {{"var", "456"}}, "$ + $var", {"579"}},
  413. {"123", {{"var", "456"}}, "$var", {"456"}},
  414. {"123", {{"var", R"({"key": [1, 2, 3, 4, 5]})"}}, "$var.key[2 to last]", {"3", "4", "5"}},
  415. {"123", {{"to", "1"}, {"strict", "2"}}, "$to + $strict", {"3"}},
  416. };
  417. for (const auto& testCase : testCases) {
  418. for (const auto mode : ALL_MODES) {
  419. RunVariablesTestCase(testCase.Json, testCase.Variables, mode + testCase.JsonPath, testCase.Result);
  420. }
  421. }
  422. }
  423. void TestDivisionByZero() {
  424. const TVector<TRuntimeErrorTestCase> testCases = {
  425. {"0", "1 / $", C(TIssuesIds::JSONPATH_DIVISION_BY_ZERO)},
  426. {"0.00000000000000000001", "1 / $", C(TIssuesIds::JSONPATH_DIVISION_BY_ZERO)},
  427. };
  428. for (const auto& testCase : testCases) {
  429. for (const auto mode : ALL_MODES) {
  430. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  431. }
  432. }
  433. }
  434. void TestInfinityResult() {
  435. const double step = 1000000000;
  436. double current = step;
  437. TStringBuilder literal;
  438. TStringBuilder query;
  439. literal << '"' << step;
  440. query << step;
  441. while (!std::isinf(current)) {
  442. query << " * " << step;
  443. literal << "000000000";
  444. current *= step;
  445. }
  446. literal << '"';
  447. const TVector<TRuntimeErrorTestCase> testCases = {
  448. {"0", TString(query), C(TIssuesIds::JSONPATH_BINARY_OPERATION_RESULT_INFINITY)},
  449. {TString(literal), "$.double()", C(TIssuesIds::JSONPATH_INFINITE_NUMBER_STRING)},
  450. };
  451. for (const auto& testCase : testCases) {
  452. for (const auto mode : ALL_MODES) {
  453. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  454. }
  455. }
  456. }
  457. void TestLogicalOperations() {
  458. const TVector<TMultiOutputTestCase> testCases = {
  459. // JsonPath does not allow to use boolean literals in boolean operators.
  460. // Here we use their replacements:
  461. // 1. "(1 < true)" for "null"
  462. // 2. "(true == true)" for "true"
  463. // 3. "(true != true)" for "false"
  464. {"1", "(1 < true) || (1 < true)", {"null"}},
  465. {"1", "(1 < true) || (true != true)", {"null"}},
  466. {"1", "(1 < true) || (true == true)", {"true"}},
  467. {"1", "(true != true) || (1 < true)", {"null"}},
  468. {"1", "(true != true) || (true != true)", {"false"}},
  469. {"1", "(true != true) || (true == true)", {"true"}},
  470. {"1", "(true == true) || (1 < true)", {"true"}},
  471. {"1", "(true == true) || (true != true)", {"true"}},
  472. {"1", "(true == true) || (true == true)", {"true"}},
  473. {"1", "(1 < true) && (1 < true)", {"null"}},
  474. {"1", "(1 < true) && (true != true)", {"false"}},
  475. {"1", "(1 < true) && (true == true)", {"null"}},
  476. {"1", "(true != true) && (1 < true)", {"false"}},
  477. {"1", "(true != true) && (true != true)", {"false"}},
  478. {"1", "(true != true) && (true == true)", {"false"}},
  479. {"1", "(true == true) && (1 < true)", {"null"}},
  480. {"1", "(true == true) && (true != true)", {"false"}},
  481. {"1", "(true == true) && (true == true)", {"true"}},
  482. {"1", "(true != true) && (true != true) || (true == true)", {"true"}},
  483. {"1", "(true != true) && ((true != true) || (true == true))", {"false"}},
  484. {"1", "(true != true) || (true != true) || (true == true)", {"true"}},
  485. {"1", "(true == true) && (true == true) && (true == true) && (true != true)", {"false"}},
  486. {"1", "!(1 < true)", {"null"}},
  487. {"1", "!(true != true)", {"true"}},
  488. {"1", "!(true == true)", {"false"}},
  489. };
  490. for (const auto& testCase : testCases) {
  491. for (const auto mode : ALL_MODES) {
  492. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  493. }
  494. }
  495. }
  496. void TestCompareOperations() {
  497. const TVector<TString> operations = {"==", "<", "<=", ">", ">=", "!=", "<>"};
  498. // All compare operations between null and non-null operands are false
  499. for (const auto& op : operations) {
  500. RunTestCase("1", TStringBuilder() << "null " << op << " 1", {"false"});
  501. RunTestCase("1", TStringBuilder() << "1 " << op << " null", {"false"});
  502. }
  503. // If one of the operands is not scalar, comparison results to null
  504. for (const auto& op : operations) {
  505. RunTestCase("[[]]", TStringBuilder() << "$ " << op << " 1", {"null"});
  506. RunTestCase("[[]]", TStringBuilder() << "1 " << op << " $", {"null"});
  507. RunTestCase("[[]]", TStringBuilder() << "$ " << op << " $", {"null"});
  508. RunTestCase("{}", TStringBuilder() << "$ " << op << " 1", {"null"});
  509. RunTestCase("{}", TStringBuilder() << "1 " << op << " $", {"null"});
  510. RunTestCase("{}", TStringBuilder() << "$ " << op << " $", {"null"});
  511. }
  512. // If both operands are null, only == is true
  513. for (const auto& op : operations) {
  514. const TString result = op == "==" ? "true" : "false";
  515. RunTestCase("1", TStringBuilder() << "null " << op << " null", {result});
  516. }
  517. const TVector<TMultiOutputTestCase> testCases = {
  518. // Check comparison of numbers
  519. {"1", "1.23 < 4.56", {"true"}},
  520. {"1", "1.23 > 4.56", {"false"}},
  521. {"1", "1.23 <= 4.56", {"true"}},
  522. {"1", "1.23 >= 4.56", {"false"}},
  523. {"1", "1.23 == 1.23", {"true"}},
  524. {"1", "1.23 != 1.23", {"false"}},
  525. {"1", "1.23 <> 4.56", {"true"}},
  526. {"1", "1.00000000000000000001 == 1.00000000000000000002", {"true"}},
  527. // Check numbers of different kinds (int64 vs double)
  528. {"1", "1 < 2.33", {"true"}},
  529. {"1", "1 > 4.56", {"false"}},
  530. {"1", "1 <= 4.56", {"true"}},
  531. {"1", "1 >= 4.56", {"false"}},
  532. {"1", "1 == 1.23", {"false"}},
  533. {"1", "1 != 1.23", {"true"}},
  534. {"1", "1 <> 4.56", {"true"}},
  535. // Check comparison of strings
  536. {"1", R"("abc" < "def")", {"true"}},
  537. {"1", R"("abc" > "def")", {"false"}},
  538. {"1", R"("abc" <= "def")", {"true"}},
  539. {"1", R"("abc" >= "def")", {"false"}},
  540. {"1", R"("abc" == "abc")", {"true"}},
  541. {"1", R"("abc" != "abc")", {"false"}},
  542. {"1", R"("abc" <> "def")", {"true"}},
  543. // Check comparison of UTF8 strings
  544. // First string is U+00e9 (LATIN SMALL LETTER E WITH ACUTE), "é"
  545. // Second string is U+0065 (LATIN SMALL LETTER E) U+0301 (COMBINING ACUTE ACCENT), "é"
  546. {"1", R"("é" < "é")", {"false"}},
  547. {"1", R"("é" > "é")", {"true"}},
  548. {"1", R"("привет" == "привет")", {"true"}},
  549. // Check cross-product comparison
  550. {R"({
  551. "left": [1],
  552. "right": [4, 5, 6]
  553. })", "$.left[*] < $.right[*]", {"true"}},
  554. {R"({
  555. "left": [4, 5, 6],
  556. "right": [1]
  557. })", "$.left[*] < $.right[*]", {"false"}},
  558. {R"({
  559. "left": [1, 2, 3],
  560. "right": [4, 5, 6]
  561. })", "$.left[*] < $.right[*]", {"true"}},
  562. {R"({
  563. "left": [10, 30, 40],
  564. "right": [1, 2, 15]
  565. })", "$.left[*] < $.right[*]", {"true"}},
  566. {R"({
  567. "left": [10, 30, 40],
  568. "right": [1, 2, 3]
  569. })", "$.left[*] < $.right[*]", {"false"}},
  570. // Check incomparable types
  571. {"1", "1 < true", {"null"}},
  572. {"1", R"(true <> "def")", {"null"}},
  573. // Check error in arguments
  574. {R"({
  575. "array": [1, 2, 3, 4, 5],
  576. "invalid_index": {
  577. "key": 1
  578. }
  579. })", "$.array[$.invalid_index] < 3", {"null"}},
  580. {R"({
  581. "array": [1, 2, 3, 4, 5],
  582. "invalid_index": {
  583. "key": 1
  584. }
  585. })", "5 >= $.array[$.invalid_index]", {"null"}},
  586. };
  587. for (const auto& testCase : testCases) {
  588. for (const auto mode : ALL_MODES) {
  589. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  590. }
  591. }
  592. }
  593. void TestFilter() {
  594. const TVector<TMultiOutputTestCase> testCases = {
  595. {"[1, 2, 3]", "$[*] ? (@ > 2)", {"3"}},
  596. {R"([
  597. {"age": 18},
  598. {"age": 25},
  599. {"age": 50},
  600. {"age": 5}
  601. ])", "$[*] ? (@.age >= 18)", {R"({"age":18})", R"({"age":25})", R"({"age":50})"}},
  602. {R"([
  603. {"age": 18},
  604. {"age": 25},
  605. {"age": 50},
  606. {"age": 5}
  607. ])", "$[*] ? (@.age >= 18) ? (@.age <= 30)", {R"({"age":18})", R"({"age":25})"}},
  608. {R"([
  609. {"age": 18},
  610. {"age": 25},
  611. {"age": 50},
  612. {"age": 5}
  613. ])", "$[*] ? (@.age >= 18) ? (@.age <= 30) . age", {"18", "25"}},
  614. {R"([
  615. {"age": 18},
  616. {"age": 25},
  617. {"age": 50},
  618. {"age": 5}
  619. ])", "$[*] ? (@.age >= 18 && @.age <= 30) . age", {"18", "25"}},
  620. {R"([
  621. {"age": 18},
  622. {"age": 25},
  623. {"age": 50},
  624. {"age": 5}
  625. ])", "$[*] ? (@.age >= 18 || @.age <= 30) . age", {"18", "25", "50", "5"}},
  626. {R"([
  627. {
  628. "id": 1,
  629. "is_valid": false,
  630. "days_till_doom": 11,
  631. "age_estimation": 4
  632. },
  633. {
  634. "id": 2,
  635. "is_valid": true,
  636. "days_till_doom": 5,
  637. "age_estimation": 3
  638. },
  639. {
  640. "id": 3,
  641. "is_valid": true,
  642. "days_till_doom": 20,
  643. "age_estimation": 10
  644. },
  645. {
  646. "id": 4,
  647. "is_valid": true,
  648. "days_till_doom": 30,
  649. "age_estimation": 2
  650. }
  651. ])", "$[*] ? (@.is_valid == true && @.days_till_doom > 10 && 2 * @.age_estimation <= 12).id", {"4"}},
  652. };
  653. for (const auto& testCase : testCases) {
  654. for (const auto mode : ALL_MODES) {
  655. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  656. }
  657. }
  658. }
  659. void TestFilterInvalid() {
  660. const TVector<TRuntimeErrorTestCase> testCases = {
  661. {R"({})", "@", C(TIssuesIds::JSONPATH_FILTER_OBJECT_OUTSIDE_OF_FILTER)},
  662. };
  663. for (const auto& testCase : testCases) {
  664. for (const auto mode : ALL_MODES) {
  665. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  666. }
  667. }
  668. }
  669. void TestNumericMethods() {
  670. const TVector<TMultiOutputTestCase> testCases = {
  671. {"[-1.23, 4.56, 3, 0]", "$[*].abs()", {"1.23", "4.56", "3", "0"}},
  672. {"[-1.23, 4.56, 3, 0]", "$[*].floor()", {"-2", "4", "3", "0"}},
  673. {"[-1.23, 4.56, 3, 0]", "$[*].ceiling()", {"-1", "5", "3", "0"}},
  674. {"-123.45", "$.ceiling().abs().floor()", {"123"}},
  675. };
  676. for (const auto& testCase : testCases) {
  677. for (const auto mode : ALL_MODES) {
  678. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  679. }
  680. }
  681. }
  682. void TestNumericMethodsErrors() {
  683. const TVector<TRuntimeErrorTestCase> testCases = {
  684. {R"(["1", true, null])", "$[*].abs()", C(TIssuesIds::JSONPATH_INVALID_NUMERIC_METHOD_ARGUMENT)},
  685. {R"(["1", true, null])", "$[*].floor()", C(TIssuesIds::JSONPATH_INVALID_NUMERIC_METHOD_ARGUMENT)},
  686. {R"(["1", true, null])", "$[*].ceiling()", C(TIssuesIds::JSONPATH_INVALID_NUMERIC_METHOD_ARGUMENT)},
  687. };
  688. for (const auto& testCase : testCases) {
  689. for (const auto mode : ALL_MODES) {
  690. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  691. }
  692. }
  693. }
  694. void TestDoubleMethod() {
  695. const TVector<TMultiOutputTestCase> testCases = {
  696. {R"([
  697. "123", "123.4", "0.567", "1234e-1", "567e-3", "123.4e-1",
  698. "123e3", "123e+3", "1.23e+1", "1.23e1",
  699. "12e0", "12.3e0", "0", "0.0", "0.0e0"
  700. ])", "$[*].double()", {
  701. "123", "123.4", "0.567", "123.4", "0.567", "12.34",
  702. "123000", "123000", "12.3", "12.3",
  703. "12", "12.3", "0", "0", "0",
  704. }},
  705. {R"("-123.45e1")", "$.double().abs().floor()", {"1234"}},
  706. };
  707. for (const auto& testCase : testCases) {
  708. for (const auto mode : ALL_MODES) {
  709. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  710. }
  711. }
  712. }
  713. void TestDoubleMethodErrors() {
  714. const TVector<TRuntimeErrorTestCase> testCases = {
  715. {R"(["1", true, null])", "$[*].double()", C(TIssuesIds::JSONPATH_INVALID_DOUBLE_METHOD_ARGUMENT)},
  716. {R"("hi stranger")", "$.double()", C(TIssuesIds::JSONPATH_INVALID_NUMBER_STRING)},
  717. };
  718. for (const auto& testCase : testCases) {
  719. for (const auto mode : ALL_MODES) {
  720. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  721. }
  722. }
  723. }
  724. void TestTypeMethod() {
  725. const TVector<TMultiOutputTestCase> testCases = {
  726. {"null", "$.type()", {"\"null\""}},
  727. {"true", "$.type()", {"\"boolean\""}},
  728. {"false", "$.type()", {"\"boolean\""}},
  729. {"1", "$.type()", {"\"number\""}},
  730. {"-1", "$.type()", {"\"number\""}},
  731. {"4.56", "$.type()", {"\"number\""}},
  732. {"-4.56", "$.type()", {"\"number\""}},
  733. {"\"some string\"", "$.type()", {"\"string\""}},
  734. {"[]", "$.type()", {"\"array\""}},
  735. {"[1, 2, 3, 4]", "$.type()", {"\"array\""}},
  736. {"{}", "$.type()", {"\"object\""}},
  737. {"{\"key\": 123}", "$.type()", {"\"object\""}},
  738. };
  739. for (const auto& testCase : testCases) {
  740. for (const auto mode : ALL_MODES) {
  741. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  742. }
  743. }
  744. }
  745. void TestSizeMethod() {
  746. const TVector<TMultiOutputTestCase> testCases = {
  747. {"null", "$.size()", {"1"}},
  748. {"true", "$.size()", {"1"}},
  749. {"false", "$.size()", {"1"}},
  750. {"1", "$.size()", {"1"}},
  751. {"-1", "$.size()", {"1"}},
  752. {"4.56", "$.size()", {"1"}},
  753. {"-4.56", "$.size()", {"1"}},
  754. {"\"some string\"", "$.size()", {"1"}},
  755. {"[]", "$.size()", {"0"}},
  756. {"[1, 2, 3, 4]", "$.size()", {"4"}},
  757. {"{}", "$.size()", {"1"}},
  758. {"{\"key\": 123}", "$.size()", {"1"}},
  759. };
  760. for (const auto& testCase : testCases) {
  761. for (const auto mode : ALL_MODES) {
  762. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  763. }
  764. }
  765. }
  766. void TestKeyValueMethod() {
  767. const TVector<TMultiOutputTestCase> testCases = {
  768. {R"({
  769. "one": 1,
  770. "two": 2,
  771. "three": 3
  772. })", "$.keyvalue()", {
  773. R"({"name":"one","value":1})",
  774. R"({"name":"three","value":3})",
  775. R"({"name":"two","value":2})",
  776. }},
  777. {R"({
  778. "one": "string",
  779. "two": [1, 2, 3, 4],
  780. "three": [4, 5]
  781. })", R"($.keyvalue() ? (@.value.type() == "array" && @.value.size() > 2).name)", {"\"two\""}},
  782. };
  783. for (const auto& testCase : testCases) {
  784. for (const auto mode : ALL_MODES) {
  785. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  786. }
  787. }
  788. }
  789. void TestKeyValueMethodErrors() {
  790. const TVector<TRuntimeErrorTestCase> testCases = {
  791. {"\"string\"", "$.keyvalue()", C(TIssuesIds::JSONPATH_INVALID_KEYVALUE_METHOD_ARGUMENT)},
  792. {"[1, 2, 3, 4]", "$.keyvalue()", C(TIssuesIds::JSONPATH_INVALID_KEYVALUE_METHOD_ARGUMENT)},
  793. };
  794. for (const auto& testCase : testCases) {
  795. for (const auto mode : ALL_MODES) {
  796. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  797. }
  798. }
  799. }
  800. void TestStartsWithPredicate() {
  801. const TVector<TMultiOutputTestCase> testCases = {
  802. {"1", R"("some string" starts with "some")", {"true"}},
  803. {"1", R"("some string" starts with "string")", {"false"}},
  804. {R"(["some string", "string"])", R"($[*] ? (@ starts with "string"))", {"\"string\""}},
  805. };
  806. for (const auto& testCase : testCases) {
  807. for (const auto mode : ALL_MODES) {
  808. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  809. }
  810. }
  811. }
  812. void TestStartsWithPredicateErrors() {
  813. const TVector<TRuntimeErrorTestCase> testCases = {
  814. {R"(["first", "second"])", R"($[*] starts with "first")", C(TIssuesIds::JSONPATH_INVALID_STARTS_WITH_ARGUMENT)},
  815. {"1", R"(1 starts with "string")", C(TIssuesIds::JSONPATH_INVALID_STARTS_WITH_ARGUMENT)},
  816. };
  817. for (const auto& testCase : testCases) {
  818. for (const auto mode : ALL_MODES) {
  819. RunRuntimeErrorTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Error);
  820. }
  821. }
  822. }
  823. void TestExistsPredicate() {
  824. const TVector<TMultiOutputTestCase> testCases = {
  825. {R"({
  826. "key": 123
  827. })", "exists ($.key)", {"true"}},
  828. {"\"string\"", "exists ($ * 2)", {"null"}},
  829. {R"(["some string", 2])", "$[*] ? (exists (@ * 2))", {"2"}},
  830. };
  831. for (const auto& testCase : testCases) {
  832. for (const auto mode : ALL_MODES) {
  833. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  834. }
  835. }
  836. }
  837. void TestIsUnknownPredicate() {
  838. const TVector<TMultiOutputTestCase> testCases = {
  839. {"1", "(1 < true) is unknown", {"true"}},
  840. {"1", "(true == true) is unknown", {"false"}},
  841. {"1", "(true == false) is unknown", {"false"}},
  842. {R"(["some string", -20])", "$[*] ? ((1 < @) is unknown)", {"\"some string\""}},
  843. };
  844. for (const auto& testCase : testCases) {
  845. for (const auto mode : ALL_MODES) {
  846. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  847. }
  848. }
  849. }
  850. void TestLikeRegexPredicate() {
  851. const TVector<TMultiOutputTestCase> testCases = {
  852. {R"(["string", "123", "456"])", R"($[*] like_regex "[0-9]+")", {"true"}},
  853. {R"(["string", "another string"])", R"($[*] like_regex "[0-9]+")", {"false"}},
  854. // Case insensitive flag
  855. {R"("AbCd")", R"($ like_regex "abcd")", {"false"}},
  856. {R"("AbCd")", R"($ like_regex "abcd" flag "i")", {"true"}},
  857. {R"(["string", "123", "456"])", R"($[*] ? (@ like_regex "[0-9]+"))", {"\"123\"", "\"456\""}},
  858. };
  859. for (const auto& testCase : testCases) {
  860. for (const auto mode : ALL_MODES) {
  861. RunTestCase(testCase.Json, mode + testCase.JsonPath, testCase.Result);
  862. }
  863. }
  864. }
  865. };
  866. UNIT_TEST_SUITE_REGISTRATION(TJsonPathCommonTest);