xref: /6.0.3/subjson/tests/t_ops.cc (revision 4b8ca4d1)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3*     Copyright 2015 Couchbase, Inc
4*
5*   Licensed under the Apache License, Version 2.0 (the "License");
6*   you may not use this file except in compliance with the License.
7*   You may obtain a copy of the License at
8*
9*       http://www.apache.org/licenses/LICENSE-2.0
10*
11*   Unless required by applicable law or agreed to in writing, software
12*   distributed under the License is distributed on an "AS IS" BASIS,
13*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14*   See the License for the specific language governing permissions and
15*   limitations under the License.
16*/
17#include "subdoc-tests-common.h"
18#include "subdoc/validate.h"
19using std::string;
20using std::cerr;
21using std::endl;
22using Subdoc::Operation;
23using Subdoc::Match;
24using Subdoc::Loc;
25using Subdoc::Error;
26using Subdoc::Command;
27using Subdoc::Validator;
28using Subdoc::Util;
29using Subdoc::Limits;
30using Subdoc::Result;
31
32class OpTests : public ::testing::Test {
33protected:
34  virtual void SetUp() {
35      op.clear();
36  }
37
38  Operation op;
39  Result res;
40
41  string getNewDoc();
42  void getAssignNewDoc(string& newdoc);
43  Error runOp(Command, const char *path, const char *value = NULL, size_t nvalue = 0);
44};
45
46string
47OpTests::getNewDoc()
48{
49    string ret;
50    for (auto ii : res.newdoc()) {
51        ret.append(ii.at, ii.length);
52    }
53
54    // validate
55    int rv = Validator::validate(ret, op.parser());
56    std::stringstream ss;
57    if (rv != JSONSL_ERROR_SUCCESS) {
58        Util::dump_newdoc(res);
59    }
60    EXPECT_EQ(JSONSL_ERROR_SUCCESS, rv)
61        << Util::jsonerr(static_cast<jsonsl_error_t>(rv));
62    return ret;
63}
64
65void
66OpTests::getAssignNewDoc(string& newdoc)
67{
68    newdoc = getNewDoc();
69    op.set_doc(newdoc);
70}
71
72Error
73OpTests::runOp(Command opcode, const char *path, const char *value, size_t nvalue)
74{
75    op.clear();
76    if (value != NULL) {
77        if (nvalue == 0) {
78            nvalue = strlen(value);
79        }
80        op.set_value(value, nvalue);
81    }
82    op.set_code(opcode);
83    op.set_result_buf(&res);
84    return op.op_exec(path, strlen(path));
85}
86
87#include "big_json.inc.h"
88TEST_F(OpTests, testOperations)
89{
90    string newdoc;
91    op.set_doc(SAMPLE_big_json, strlen(SAMPLE_big_json));
92    ASSERT_EQ(Error::SUCCESS, runOp(Command::GET, "name"));
93    ASSERT_EQ("\"Allagash Brewing\"", Util::match_match(op.match()));
94    ASSERT_EQ(Error::SUCCESS, runOp(Command::EXISTS, "name"));
95
96    Error rv = runOp(Command::REMOVE, "address");
97    ASSERT_TRUE(rv.success());
98
99    getAssignNewDoc(newdoc);
100    // Should return in KEY_ENOENT
101    ASSERT_EQ(Error::PATH_ENOENT, runOp(Command::GET, "address"));
102
103    // Insert something back, maybe :)
104    rv = runOp(Command::DICT_ADD, "address", "\"123 Main St.\"");
105    ASSERT_TRUE(rv.success());
106
107    getAssignNewDoc(newdoc);
108    ASSERT_EQ(Error::SUCCESS, runOp(Command::GET, "address"));
109    ASSERT_EQ("\"123 Main St.\"", Util::match_match(op.match()));
110
111    // Replace the value now:
112    rv = runOp(Command::REPLACE, "address", "\"33 Marginal Rd.\"");
113    ASSERT_TRUE(rv.success());
114    getAssignNewDoc(newdoc);
115    ASSERT_EQ(Error::SUCCESS, runOp(Command::GET, "address"));
116    ASSERT_EQ("\"33 Marginal Rd.\"", Util::match_match(op.match()));
117
118    // Get it back:
119    op.set_doc(SAMPLE_big_json, strlen(SAMPLE_big_json));
120    // add non-existent path
121    rv = runOp(Command::DICT_ADD, "foo.bar.baz", "[1,2,3]");
122    ASSERT_EQ(Error::PATH_ENOENT, rv);
123
124    rv = runOp(Command::DICT_ADD_P, "foo.bar.baz", "[1,2,3]");
125    ASSERT_TRUE(rv.success());
126    getNewDoc();
127}
128
129// Mainly checks that we can perform generic DELETE and GET operations
130// on array indices
131TEST_F(OpTests, testGenericOps)
132{
133    op.set_doc(SAMPLE_big_json, strlen(SAMPLE_big_json));
134    Error rv;
135    string newdoc;
136
137    rv = runOp(Command::REMOVE, "address[0]");
138    ASSERT_TRUE(rv.success());
139
140    getAssignNewDoc(newdoc);
141    rv = runOp(Command::GET, "address[0]");
142    ASSERT_EQ(Error::PATH_ENOENT, rv);
143
144    rv = runOp(Command::REPLACE, "address",
145        "[\"500 B St.\", \"Anytown\", \"USA\"]");
146    ASSERT_TRUE(rv.success());
147    getAssignNewDoc(newdoc);
148    rv = runOp(Command::GET, "address[2]");
149    ASSERT_TRUE(rv.success());
150    ASSERT_EQ("\"USA\"", Util::match_match(op.match()));
151
152    rv = runOp(Command::REPLACE, "address[1]", "\"Sacramento\"");
153    ASSERT_TRUE(rv.success());
154    getAssignNewDoc(newdoc);
155
156    rv = runOp(Command::GET, "address[1]");
157    ASSERT_TRUE(rv.success());
158    ASSERT_EQ("\"Sacramento\"", Util::match_match(op.match()));
159}
160
161TEST_F(OpTests, testReplaceArrayDeep)
162{
163    // Create an array at max level.
164    string deep;
165    for (size_t ii = 0; ii < Limits::MAX_COMPONENTS - 1; ii++) {
166        deep += "[";
167    }
168    deep += "1";
169    for (size_t ii = 0; ii < Limits::MAX_COMPONENTS - 1; ii++) {
170        deep += "]";
171    }
172    op.set_doc(deep);
173
174    // Sanity check - should be able to access maximum depth.
175    string one_minus_max_path;
176    for (size_t ii = 0; ii < Limits::MAX_COMPONENTS - 2; ii++) {
177        one_minus_max_path += "[0]";
178    }
179    string max_path(one_minus_max_path + "[0]");
180    Error rv = runOp(Command::GET, one_minus_max_path.c_str());
181    ASSERT_TRUE(rv.success());
182    ASSERT_EQ("[1]", Util::match_match(op.match()));
183
184    // Should be able to replace the array element with a different one.
185    rv = runOp(Command::REPLACE, max_path.c_str(), "2");
186    ASSERT_TRUE(rv.success());
187    string newdoc;
188    getAssignNewDoc(newdoc);
189    rv = runOp(Command::GET, one_minus_max_path.c_str());
190    ASSERT_TRUE(rv.success());
191    EXPECT_EQ("[2]", Util::match_match(op.match()));
192
193    // Should be able to replace the last level array with a different
194    // (larger) one.
195    rv = runOp(Command::REPLACE, one_minus_max_path.c_str(), "[3,4]");
196    ASSERT_TRUE(rv.success());
197    getAssignNewDoc(newdoc);
198    rv = runOp(Command::GET, one_minus_max_path.c_str());
199    ASSERT_TRUE(rv.success());
200    ASSERT_EQ("[3,4]", Util::match_match(op.match()));
201
202    // Should not be able to make it any deeper (already at maximum).
203    rv = runOp(Command::REPLACE, one_minus_max_path.c_str(), "[[5]]");
204    ASSERT_EQ(Error::VALUE_ETOODEEP, rv);
205}
206
207TEST_F(OpTests, testListOps)
208{
209    string doc = "{}";
210    op.set_doc(doc);
211
212    Error rv = runOp(Command::DICT_UPSERT, "array", "[]");
213    ASSERT_TRUE(rv.success());
214    getAssignNewDoc(doc);
215
216    // Test append:
217    rv = runOp(Command::ARRAY_APPEND, "array", "1");
218    ASSERT_TRUE(rv.success());
219    getAssignNewDoc(doc);
220
221    rv = runOp(Command::GET, "array[0]");
222    ASSERT_TRUE(rv.success());
223    ASSERT_EQ("1", Util::match_match(op.match()));
224
225    rv = runOp(Command::ARRAY_PREPEND, "array", "0");
226    ASSERT_TRUE(rv.success());
227    getAssignNewDoc(doc);
228
229    rv = runOp(Command::GET, "array[0]");
230    ASSERT_TRUE(rv.success());
231    ASSERT_EQ("0", Util::match_match(op.match()));
232    rv = runOp(Command::GET, "array[1]");
233    ASSERT_TRUE(rv.success());
234    ASSERT_EQ("1", Util::match_match(op.match()));
235
236    rv = runOp(Command::ARRAY_APPEND, "array", "2");
237    ASSERT_TRUE(rv.success());
238    getAssignNewDoc(doc);
239
240    rv = runOp(Command::GET, "array[2]");
241    ASSERT_TRUE(rv.success());
242    ASSERT_EQ("2", Util::match_match(op.match()));
243
244    rv = runOp(Command::ARRAY_APPEND, "array", "{\"foo\":\"bar\"}");
245    ASSERT_TRUE(rv.success());
246    getAssignNewDoc(doc);
247
248    rv = runOp(Command::GET, "array[3].foo");
249    ASSERT_TRUE(rv.success());
250    ASSERT_EQ("\"bar\"", Util::match_match(op.match()));
251
252    // Test the various POP commands
253    rv = runOp(Command::REMOVE, "array[0]");
254    ASSERT_TRUE(rv.success());
255    ASSERT_EQ("0", Util::match_match(op.match()));
256    getAssignNewDoc(doc);
257
258    rv = runOp(Command::GET, "array[0]");
259
260    rv = runOp(Command::REMOVE, "array[-1]");
261    ASSERT_TRUE(rv.success());
262    ASSERT_EQ("{\"foo\":\"bar\"}", Util::match_match(op.match()));
263    getAssignNewDoc(doc);
264
265    rv = runOp(Command::REMOVE, "array[-1]");
266    ASSERT_TRUE(rv.success());
267    ASSERT_EQ("2", Util::match_match(op.match()));
268
269    // Special prepend operations
270    doc = "{}";
271    op.set_doc(doc);
272
273    // test prepend without _p
274    rv = runOp(Command::ARRAY_PREPEND, "array", "123");
275    ASSERT_EQ(Error::PATH_ENOENT, rv);
276
277    rv = runOp(Command::ARRAY_PREPEND_P, "array", "123");
278    ASSERT_TRUE(rv.success());
279    getAssignNewDoc(doc);
280
281    // Now ensure the contents are the same
282    rv = runOp(Command::GET, "array[0]");
283    ASSERT_TRUE(rv.success());
284    ASSERT_EQ("123", Util::match_match(op.match()));
285
286    // Remove the first element, making it empty
287    rv = runOp(Command::REMOVE, "array[0]");
288    ASSERT_TRUE(rv.success());
289    getAssignNewDoc(doc);
290
291    // Prepend the first element (singleton)
292    rv = runOp(Command::ARRAY_PREPEND, "array", "123");
293    ASSERT_TRUE(rv.success());
294    getAssignNewDoc(doc);
295
296    rv = runOp(Command::GET, "array[0]");
297    ASSERT_TRUE(rv.success());
298    ASSERT_EQ("123", Util::match_match(op.match()));
299}
300
301TEST_F(OpTests, testArrayMultivalue)
302{
303    string doc = "{\"array\":[4,5,6]}";
304    Error rv;
305    op.set_doc(doc);
306
307    rv = runOp(Command::ARRAY_PREPEND, "array", "1,2,3");
308    ASSERT_TRUE(rv.success()) << rv;
309    getAssignNewDoc(doc);
310
311    rv = runOp(Command::GET, "array");
312    ASSERT_TRUE(rv.success());
313    ASSERT_EQ("[1,2,3,4,5,6]", Util::match_match(op.match()));
314
315    rv = runOp(Command::ARRAY_APPEND, "array", "7,8,9");
316    ASSERT_TRUE(rv.success());
317    getAssignNewDoc(doc);
318
319    rv = runOp(Command::GET, "array");
320    ASSERT_TRUE(rv.success());
321    ASSERT_EQ("[1,2,3,4,5,6,7,8,9]", Util::match_match(op.match()));
322
323    rv = runOp(Command::ARRAY_INSERT, "array[3]", "-3,-2,-1");
324    ASSERT_TRUE(rv.success());
325    getAssignNewDoc(doc);
326
327    rv = runOp(Command::GET, "array[4]");
328    ASSERT_TRUE(rv.success());
329    ASSERT_EQ("-2", Util::match_match(op.match()));
330}
331
332TEST_F(OpTests, testArrayOpsNested)
333{
334    const string array("[0,[1,[2]],{\"key\":\"val\"}]");
335    op.set_doc(array);
336    Error rv;
337
338    rv = runOp(Command::REMOVE, "[1][1][0]");
339    EXPECT_TRUE(rv.success());
340    EXPECT_EQ("[0,[1,[]],{\"key\":\"val\"}]", getNewDoc());
341
342    string array2;
343    getAssignNewDoc(array2);
344    rv = runOp(Command::REMOVE, "[1][1]");
345    EXPECT_TRUE(rv.success());
346    EXPECT_EQ("[0,[1],{\"key\":\"val\"}]", getNewDoc());
347}
348
349// Toplevel array with two elements.
350TEST_F(OpTests, testArrayDelete)
351{
352    // Toplevel array deletions
353    const string array("[1,2]");
354    op.set_doc(array);
355    Error rv;
356
357    // Delete beginning element.
358    rv = runOp(Command::REMOVE, "[0]");
359    EXPECT_TRUE(rv.success());
360    EXPECT_EQ("[2]", getNewDoc());
361
362    // Delete end element.
363    rv = runOp(Command::REMOVE, "[1]");
364    EXPECT_TRUE(rv.success());
365    EXPECT_EQ("[1]", getNewDoc());
366
367    // One element array. Delete last (final) element (via [0]).
368    const string array2("[1]");
369    op.set_doc(array2);
370
371    rv = runOp(Command::REMOVE, "[0]");
372    EXPECT_TRUE(rv.success());
373    EXPECT_EQ("[]", getNewDoc());
374
375    // Delete last element via [-1].
376    rv = runOp(Command::REMOVE, "[-1]");
377    EXPECT_TRUE(rv.success());
378    EXPECT_EQ("[]", getNewDoc());
379}
380
381TEST_F(OpTests, testDictDelete)
382{
383    const string dict("{\"0\": 1,\"1\": 2.0}");
384    op.set_doc(dict);
385    Error rv;
386
387    // Delete element
388    rv = runOp(Command::REMOVE, "0");
389    EXPECT_TRUE(rv.success());
390
391    // Check it's gone.
392    string doc;
393    getAssignNewDoc(doc);
394    rv = runOp(Command::EXISTS, "0");
395    ASSERT_EQ(Error::PATH_ENOENT, rv);
396}
397
398TEST_F(OpTests, testUnique)
399{
400    string json = "{}";
401    string doc;
402    Error rv;
403
404    op.set_doc(json);
405
406    rv = runOp(Command::ARRAY_ADD_UNIQUE_P, "unique", "\"value\"");
407    ASSERT_TRUE(rv.success());
408    getAssignNewDoc(doc);
409
410    rv = runOp(Command::ARRAY_ADD_UNIQUE, "unique", "\"value\"");
411    ASSERT_EQ(Error::DOC_EEXISTS, rv);
412
413    rv = runOp(Command::ARRAY_ADD_UNIQUE, "unique", "1");
414    ASSERT_TRUE(rv.success());
415    getAssignNewDoc(doc);
416
417    rv = runOp(Command::ARRAY_ADD_UNIQUE, "unique", "\"1\"");
418    ASSERT_TRUE(rv.success());
419    getAssignNewDoc(doc);
420
421    rv = runOp(Command::ARRAY_ADD_UNIQUE, "unique", "[]");
422    ASSERT_EQ(Error::VALUE_CANTINSERT, rv) << "Cannot unique-add non-primitive";
423
424    rv = runOp(Command::ARRAY_ADD_UNIQUE, "unique", "1,2,3");
425    ASSERT_EQ(Error::VALUE_CANTINSERT, rv) << "Cannot unique-add multivalue";
426
427    rv = runOp(Command::ARRAY_APPEND, "unique", "[]");
428    ASSERT_TRUE(rv.success());
429    getAssignNewDoc(doc);
430
431    rv = runOp(Command::ARRAY_ADD_UNIQUE, "unique", "2");
432    ASSERT_EQ(Error::PATH_MISMATCH, rv) <<
433            "Mismatch with array containing non-primitive elements";
434}
435
436TEST_F(OpTests, testUniqueToplevel)
437{
438    string json("[]");
439    string doc;
440    Error rv;
441
442    op.set_doc(json);
443
444    rv = runOp(Command::ARRAY_ADD_UNIQUE_P, "", "0");
445    ASSERT_TRUE(rv.success());
446    getAssignNewDoc(doc);
447
448    rv = runOp(Command::ARRAY_ADD_UNIQUE_P, "", "0");
449    ASSERT_EQ(Error::DOC_EEXISTS, rv);
450}
451
452#ifndef INT64_MIN
453#define INT64_MIN (-9223372036854775807LL-1)
454#define INT64_MAX 9223372036854775807LL
455#endif
456
457TEST_F(OpTests, testNumeric)
458{
459    string doc = "{}";
460    Error rv;
461    op.set_doc(doc);
462
463    // Can we make a simple counter?
464    rv = runOp(Command::COUNTER_P, "counter", "1");
465    ASSERT_TRUE(rv.success());
466    ASSERT_EQ("1", Util::match_match(op.match()));
467    getAssignNewDoc(doc);
468
469    rv = runOp(Command::COUNTER, "counter", "-101");
470    ASSERT_TRUE(rv.success());
471    ASSERT_EQ("-100", Util::match_match(op.match()));
472    getAssignNewDoc(doc);
473
474    // Get it raw
475    rv = runOp(Command::GET, "counter");
476    ASSERT_TRUE(rv.success());
477    ASSERT_EQ("-100", Util::match_match(op.match()));
478
479    rv = runOp(Command::COUNTER, "counter", "1");
480    ASSERT_TRUE(rv.success());
481    ASSERT_EQ("-99", Util::match_match(op.match()));
482    getAssignNewDoc(doc);
483
484    // Try with other things
485    string dummy = std::to_string(INT64_MAX);
486    rv = runOp(Command::COUNTER, "counter", dummy.c_str());
487    ASSERT_TRUE(rv.success());
488    ASSERT_EQ(std::to_string(INT64_MAX-99), Util::match_match(op.match()));
489    getAssignNewDoc(doc);
490
491    dummy = "-" + std::to_string(INT64_MAX-99);
492    rv = runOp(Command::COUNTER, "counter", dummy.c_str());
493    ASSERT_TRUE(rv.success());
494    ASSERT_EQ("0", Util::match_match(op.match()));
495    getAssignNewDoc(doc);
496
497    rv = runOp(Command::DICT_ADD_P, "counter2", "9999999999999999999999999999999");
498    ASSERT_TRUE(rv.success());
499    getAssignNewDoc(doc);
500
501    rv = runOp(Command::COUNTER, "counter2", "1");
502    ASSERT_EQ(Error::NUM_E2BIG, rv);
503
504    rv = runOp(Command::DICT_ADD_P, "counter3", "3.14");
505    ASSERT_TRUE(rv.success());
506    getAssignNewDoc(doc);
507
508    rv = runOp(Command::COUNTER, "counter3", "1");
509    ASSERT_EQ(Error::PATH_MISMATCH, rv);
510
511    doc = "[]";
512    op.set_doc(doc);
513    rv = runOp(Command::COUNTER, "[0]", "42");
514    ASSERT_EQ(Error::PATH_ENOENT, rv);
515
516    // Try with a _P variant. Should still be the same
517    rv = runOp(Command::COUNTER_P, "[0]", "42");
518    ASSERT_EQ(Error::PATH_ENOENT, rv);
519
520    rv = runOp(Command::ARRAY_APPEND, "", "-20");
521    ASSERT_TRUE(rv.success());
522    getAssignNewDoc(doc);
523
524    rv = runOp(Command::COUNTER, "[0]", "1");
525    ASSERT_TRUE(rv.success());
526    ASSERT_EQ("-19", Util::match_match(op.match()));
527}
528
529TEST_F(OpTests, testBadNumFormat)
530{
531    string doc = "{}";
532    op.set_doc(doc);
533
534    ASSERT_EQ(Error::DELTA_EINVAL, runOp(Command::COUNTER_P, "pth", "bad"));
535    ASSERT_EQ(Error::DELTA_EINVAL, runOp(Command::COUNTER_P, "pth", "3.14"));
536    ASSERT_EQ(Error::DELTA_EINVAL, runOp(Command::COUNTER_P, "pth", "-"));
537    ASSERT_EQ(Error::DELTA_EINVAL, runOp(Command::COUNTER_P, "pth", "43f"));
538    ASSERT_EQ(Error::DELTA_EINVAL, runOp(Command::COUNTER_P, "pth", "0"));
539}
540
541TEST_F(OpTests, testNumericLimits)
542{
543    // Check we can increment from int64_t::max()-1 to max() successfully.
544    const int64_t max = std::numeric_limits<int64_t>::max();
545    const string one_minus_max("{\"counter\":" + std::to_string(max - 1) + "}");
546    op.set_doc(one_minus_max);
547
548    Error rv = runOp(Command::COUNTER, "counter", "1");
549    ASSERT_TRUE(rv.success());
550    ASSERT_EQ(std::to_string(max), Util::match_match(op.match()));
551
552    // Incrementing across the limit (max()-1 incremented by 2) should fail.
553    op.set_doc(one_minus_max);
554
555    rv = runOp(Command::COUNTER, "counter", "2");
556    ASSERT_EQ(Error::DELTA_OVERFLOW, rv);
557
558    // Same for int64_t::min() - 1 and decrement.
559    const int64_t min = std::numeric_limits<int64_t>::min();
560    const string one_plus_min("{\"counter\":" + std::to_string(min + 1) + "}");
561    op.set_doc(one_plus_min);
562
563    rv = runOp(Command::COUNTER, "counter", "-1");
564    ASSERT_TRUE(rv.success());
565    ASSERT_EQ(std::to_string(min), Util::match_match(op.match()));
566
567    // Decrementing across the limit (min()-1 decremented by 2) should fail.
568    op.set_doc(one_plus_min);
569
570    rv = runOp(Command::COUNTER, "counter", "-2");
571    ASSERT_EQ(Error::DELTA_OVERFLOW, rv);
572}
573
574TEST_F(OpTests, testValueValidation)
575{
576    string json = "{}";
577    string doc;
578    Error rv;
579    op.set_doc(doc);
580
581    rv = runOp(Command::DICT_ADD_P, "foo.bar.baz", "INVALID");
582    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
583
584    rv = runOp(Command::DICT_ADD_P, "foo.bar.baz", "1,2,3,4");
585    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
586
587    // FIXME: Should we allow this? Could be more performant, but might also
588    // be confusing!
589    rv = runOp(Command::DICT_ADD_P, "foo.bar.baz", "1,\"k2\":2");
590    ASSERT_TRUE(rv.success());
591
592    // Dict key without a colon or value.
593    rv = runOp(Command::DICT_ADD, "bad_dict", "{ \"foo\" }");
594    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
595
596    rv = runOp(Command::DICT_ADD, "bad_dict", "{ \"foo\": }");
597    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
598
599    // Dict without a colon or value.
600    rv = runOp(Command::DICT_ADD_P, "bad_dict", "{ \"foo\" }");
601    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
602
603    // Dict without a colon.
604    rv = runOp(Command::DICT_ADD_P, "bad_dict", "{ \"foo\": }");
605    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
606
607    // null with incorrect name.
608    rv = runOp(Command::DICT_ADD_P, "bad_null", "nul");
609    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
610
611    // invalid float (more than one decimal point).
612    rv = runOp(Command::DICT_ADD_P, "bad_float1", "2.0.0");
613    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
614
615    // invalid float (no digit after the '.').
616    rv = runOp(Command::DICT_ADD_P, "bad_float2", "2.");
617    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
618
619    // invalid float (no exponential after the 'e').
620    rv = runOp(Command::DICT_ADD_P, "bad_float3", "2.0e");
621    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
622
623    // invalid float (no digits after the exponential sign).
624    rv = runOp(Command::DICT_ADD_P, "bad_float4", "2.0e+");
625    EXPECT_EQ(Error::VALUE_CANTINSERT, rv);
626}
627
628
629TEST_F(OpTests, testNegativeIndex)
630{
631    string json = "[1,2,3,4,5,6]";
632    op.set_doc(json);
633
634    Error rv = runOp(Command::GET, "[-1]");
635    ASSERT_TRUE(rv.success());
636    ASSERT_EQ("6", Util::match_match(op.match()));
637
638    json = "[1,2,3,[4,5,6,[7,8,9]]]";
639    op.set_doc(json);
640    rv = runOp(Command::GET, "[-1].[-1].[-1]");
641    ASSERT_TRUE(rv.success());
642    ASSERT_EQ("9", Util::match_match(op.match()));
643
644    string doc;
645    rv = runOp(Command::REMOVE, "[-1].[-1].[-1]");
646    ASSERT_TRUE(rv.success());
647    getAssignNewDoc(doc);
648
649    // Can we PUSH values with a negative index?
650    rv = runOp(Command::ARRAY_APPEND, "[-1].[-1]", "10");
651    ASSERT_TRUE(rv.success());
652    getAssignNewDoc(doc);
653
654
655    rv = runOp(Command::GET, "[-1].[-1].[-1]");
656    ASSERT_TRUE(rv.success());
657    ASSERT_EQ("10", Util::match_match(op.match()));
658
659    // Intermixed paths:
660    json = "{\"k1\": [\"first\", {\"k2\":[6,7,8]},\"last\"] }";
661    op.set_doc(json);
662
663    rv = runOp(Command::GET, "k1[-1]");
664    ASSERT_TRUE(rv.success());
665    ASSERT_EQ("\"last\"", Util::match_match(op.match()));
666
667    rv = runOp(Command::GET, "k1[1].k2[-1]");
668    ASSERT_TRUE(rv.success());
669    ASSERT_EQ("8", Util::match_match(op.match()));
670}
671
672TEST_F(OpTests, testRootOps)
673{
674    string json = "[]";
675    op.set_doc(json);
676    Error rv;
677
678    rv = runOp(Command::GET, "");
679    ASSERT_TRUE(rv.success());
680    ASSERT_EQ("[]", Util::match_match(op.match()));
681
682    rv = runOp(Command::ARRAY_APPEND, "", "null");
683    ASSERT_TRUE(rv.success());
684    getAssignNewDoc(json);
685
686    rv = runOp(Command::GET, "");
687    ASSERT_EQ("[null]", Util::match_match(op.match()));
688
689    // Deleting root element should be CANTINSERT
690    rv = runOp(Command::REMOVE, "");
691    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
692}
693
694TEST_F(OpTests, testMismatch)
695{
696    string doc = "{}";
697    op.set_doc(doc);
698    Error rv;
699
700    rv = runOp(Command::ARRAY_APPEND, "", "null");
701    ASSERT_EQ(Error::PATH_MISMATCH, rv);
702
703    doc = "[]";
704    op.set_doc(doc);
705    rv = runOp(Command::DICT_UPSERT, "", "blah");
706    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
707
708    rv = runOp(Command::DICT_UPSERT, "key", "blah");
709    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
710
711    doc = "[null]";
712    op.set_doc(doc);
713    rv = runOp(Command::DICT_UPSERT, "", "blah");
714    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
715
716    rv = runOp(Command::DICT_UPSERT, "key", "blah");
717    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
718
719    rv = runOp(Command::ARRAY_APPEND_P, "foo.bar", "null");
720    ASSERT_EQ(Error::PATH_MISMATCH, rv);
721}
722
723TEST_F(OpTests, testWhitespace)
724{
725    string doc = "[ 1, 2, 3,       4        ]";
726    op.set_doc(doc);
727    Error rv;
728
729    rv = runOp(Command::GET, "[-1]");
730    ASSERT_TRUE(rv.success());
731    ASSERT_EQ("4", Util::match_match(op.match()));
732}
733
734TEST_F(OpTests, testTooDeep)
735{
736    std::string deep = "{\"array\":";
737    for (size_t ii = 0; ii < (Limits::MAX_COMPONENTS+1) * 2; ii++) {
738        deep += "[";
739    }
740    for (size_t ii = 0; ii < (Limits::MAX_COMPONENTS+1) * 2; ii++) {
741        deep += "]";
742    }
743
744    op.set_doc(deep);
745
746    Error rv = runOp(Command::GET, "dummy.path");
747    ASSERT_EQ(Error::DOC_ETOODEEP, rv);
748
749    // Try with a really deep path:
750    std::string dp = "dummy";
751    for (size_t ii = 0; ii < (Limits::MAX_COMPONENTS+1) * 2; ii++) {
752        dp += ".dummy";
753    }
754    rv = runOp(Command::GET, dp.c_str());
755    ASSERT_EQ(Error::PATH_E2BIG, rv);
756}
757
758TEST_F(OpTests, testTooDeepDict) {
759    // Verify that we cannot create too deep a document with DICT_ADD
760    // Should be able to do the maximum depth:
761    std::string deep_dict("{");
762    for (size_t ii = 1; ii < Limits::MAX_COMPONENTS; ii++) {
763        deep_dict += "\"" + std::to_string(ii) + "\": {";
764    }
765    for (size_t ii = 0; ii < Limits::MAX_COMPONENTS; ii++) {
766        deep_dict += "}";
767    }
768    op.set_doc(deep_dict);
769
770    // Create base path at one less than the max.
771    std::string one_less_max_path(std::to_string(1));
772    for (size_t depth = 2; depth < Limits::MAX_COMPONENTS -1; depth++) {
773        one_less_max_path += std::string(".") + std::to_string(depth);
774    }
775
776    const std::string max_valid_path(one_less_max_path + "." +
777                                     std::to_string(Limits::MAX_COMPONENTS-1));
778    // Assert we can access elements at the max depth (before we start
779    // attempting to add more).
780    Error rv = runOp(Command::GET, max_valid_path.c_str());
781    ASSERT_TRUE(rv.success());
782    ASSERT_EQ("{}", Util::match_match(op.match()));
783
784    // Should be able to add an element as the same level as the max.
785    const std::string equal_max_path(one_less_max_path + ".sibling_max");
786    rv = runOp(Command::DICT_ADD, equal_max_path.c_str(), "\"also at max depth\"");
787    EXPECT_TRUE(rv.success()) << rv;
788    std::string newDoc;
789    getAssignNewDoc(newDoc);
790
791    // Attempts to add one level deeper should fail.
792    std::string too_long_path(max_valid_path + ".too_long");
793    rv = runOp(Command::DICT_ADD, too_long_path.c_str(), "\"past max depth\"");
794    EXPECT_EQ(Error::PATH_E2BIG, rv);
795}
796
797TEST_F(OpTests, testArrayInsert) {
798    string doc("[1,2,4,5]");
799    op.set_doc(doc);
800    Error rv = runOp(Command::ARRAY_INSERT, "[2]", "3");
801    ASSERT_TRUE(rv.success()) << "Insert op recognized";
802    getAssignNewDoc(doc);
803    ASSERT_EQ("[1,2,3,4,5]", doc) << "Insert works correctly in-between";
804
805    // Do an effective 'prepend'
806    rv = runOp(Command::ARRAY_INSERT, "[0]", "0");
807    ASSERT_TRUE(rv.success()) << "Insert at position 0 OK";
808    getAssignNewDoc(doc);
809    ASSERT_EQ("[0,1,2,3,4,5]", doc) << "Insert at posititon 0 matches";
810
811    // Do an effective 'append'
812    rv = runOp(Command::ARRAY_INSERT, "[6]", "6");
813    ASSERT_TRUE(rv.success()) << "Insert at posititon $SIZE ok";
814    getAssignNewDoc(doc);
815    ASSERT_EQ("[0,1,2,3,4,5,6]", doc) << "Insert at position $SIZE matches";
816
817    // Reset the doc
818    doc = "[1,2,3,5]";
819    op.set_doc(doc);
820    // -1 is not a valid insertion point.
821    rv = runOp(Command::ARRAY_INSERT, "[-1]", "4");
822    ASSERT_EQ(Error::PATH_EINVAL, rv) << "Terminal negative index invalid for insert.";
823
824    // Insert at out-of-bounds element
825    doc = "[1,2,3]";
826    op.set_doc(doc);
827    rv = runOp(Command::ARRAY_INSERT, "[4]", "null");
828    ASSERT_EQ(Error::PATH_ENOENT, rv) << "Fails with out-of-bound index";
829
830    // Insert not using array syntax
831    rv = runOp(Command::ARRAY_INSERT, "[0].anything", "null");
832    ASSERT_EQ(Error::PATH_EINVAL, rv) << "Using non-array parent in path fails";
833
834    doc = "{}";
835    op.set_doc(doc);
836    // Insert with missing parent
837    rv = runOp(Command::ARRAY_INSERT, "non_exist[0]", "null");
838    ASSERT_EQ(Error::PATH_ENOENT, rv) << "Fails with missing parent";
839
840    doc = "[]";
841    op.set_doc(doc);
842    rv = runOp(Command::ARRAY_INSERT, "[0]", "blah");
843    ASSERT_EQ(Error::VALUE_CANTINSERT, rv) << "CANT_INSERT on invalid JSON value";
844
845    doc = "{}";
846    op.set_doc(doc);
847    rv = runOp(Command::ARRAY_INSERT, "[0]", "null");
848    ASSERT_EQ(Error::PATH_MISMATCH, rv) << "Fails with dict parent";
849}
850
851TEST_F(OpTests, testEmpty) {
852    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::DICT_ADD, "p"));
853    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::DICT_UPSERT, "p"));
854    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::REPLACE, "p"));
855    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::ARRAY_APPEND, "p"));
856    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::ARRAY_PREPEND, "p"));
857    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::ARRAY_ADD_UNIQUE, "p"));
858    ASSERT_EQ(Error::VALUE_EMPTY, runOp(Command::ARRAY_INSERT, "p[0]"));
859}
860
861// When using the built-in result context, ensure the internal buffers are
862// cleared between operations
863TEST_F(OpTests, ensureRepeatable) {
864    string doc = "{}";
865    Error rv;
866
867    op.set_doc(doc);
868    rv = runOp(Command::DICT_UPSERT_P, "foo.bar", "true");
869    ASSERT_TRUE(rv.success());
870    getAssignNewDoc(doc);
871
872    res.clear();
873    rv = runOp(Command::DICT_UPSERT_P, "bar.baz", "false");
874    ASSERT_TRUE(rv.success());
875    getAssignNewDoc(doc);
876}
877
878TEST_F(OpTests, testDeleteNestedArray)
879{
880    string doc = "[0,[10,20,[100]],{\"key\":\"value\"}]";
881    Error rv;
882    op.set_doc(doc);
883
884    rv = runOp(Command::GET, "[1]");
885    ASSERT_EQ(Error::SUCCESS, rv);
886    ASSERT_EQ("[10,20,[100]]", Util::match_match(op.match()));
887
888    rv = runOp(Command::REMOVE, "[1][2][0]");
889    ASSERT_EQ(Error::SUCCESS, rv);
890    getAssignNewDoc(doc);
891
892    rv = runOp(Command::GET, "[1]");
893    ASSERT_EQ(Error::SUCCESS, rv);
894    ASSERT_EQ("[10,20,[]]", Util::match_match(op.match()));
895
896    rv = runOp(Command::REMOVE, "[1][2]");
897    ASSERT_EQ(Error::SUCCESS, rv);
898    getAssignNewDoc(doc);
899
900    rv = runOp(Command::GET, "[1]");
901    ASSERT_EQ(Error::SUCCESS, rv);
902    ASSERT_EQ("[10,20]", Util::match_match(op.match()));
903
904    rv = runOp(Command::REMOVE, "[1]");
905    ASSERT_EQ(Error::SUCCESS, rv);
906    getAssignNewDoc(doc);
907
908    rv = runOp(Command::GET, "[1]");
909    ASSERT_EQ(Error::SUCCESS, rv);
910    ASSERT_EQ("{\"key\":\"value\"}", Util::match_match(op.match()));
911
912}
913
914TEST_F(OpTests, testEscapedJson)
915{
916    string doc = "{\"" "\\" "\"quoted\":true}";
917    Error rv;
918    op.set_doc(doc);
919    rv = runOp(Command::GET, "\\\"quoted");
920    ASSERT_EQ(Error::SUCCESS, rv);
921    ASSERT_EQ("true", Util::match_match(op.match()));
922
923    // Try with insertion
924    rv = runOp(Command::DICT_UPSERT_P, "another.\\\"nested.field", "null");
925    ASSERT_EQ(Error::SUCCESS, rv);
926
927    getAssignNewDoc(doc);
928
929    ASSERT_EQ(Error::PATH_EINVAL, runOp(Command::GET, "\"missing.quote"));
930}
931
932TEST_F(OpTests, testUpsertArrayIndex)
933{
934    // This test verifies some corner cases where there is a missing
935    // array index which would normally be treated like a dictionary.
936    // Ensure that we never automatically add an array index without
937    // explicit array operations.
938
939    string doc = "{\"array\":[null]}";
940    Error rv;
941    op.set_doc(doc);
942
943    rv = runOp(Command::DICT_UPSERT, "array[0]", "true");
944    ASSERT_EQ(Error::PATH_EINVAL, rv);
945
946    rv = runOp(Command::DICT_UPSERT, "array[1]", "true");
947    ASSERT_EQ(Error::PATH_EINVAL, rv);
948
949    rv = runOp(Command::DICT_UPSERT, "array[-1]", "true");
950    ASSERT_EQ(Error::PATH_EINVAL, rv);
951
952    rv = runOp(Command::ARRAY_APPEND_P, "array[1]", "true");
953    ASSERT_EQ(Error::PATH_ENOENT, rv);
954
955    rv = runOp(Command::ARRAY_APPEND_P, "array[2]", "true");
956    ASSERT_EQ(Error::PATH_ENOENT, rv);
957
958    rv = runOp(Command::ARRAY_ADD_UNIQUE_P, "array[2]", "true");
959    ASSERT_EQ(Error::PATH_ENOENT, rv);
960
961    rv = runOp(Command::COUNTER_P, "array[1]", "100");
962    ASSERT_EQ(Error::PATH_ENOENT, rv);
963}
964
965TEST_F(OpTests, testRootAppend)
966{
967    // Tests append on an empty path, which should use an optimized codebase
968    string doc("[]");
969    Error rv;
970    op.set_doc(doc);
971
972    rv = runOp(Command::ARRAY_APPEND, "", "1");
973    ASSERT_TRUE(rv.success());
974    getAssignNewDoc(doc);
975
976    rv = runOp(Command::GET, "[0]");
977    ASSERT_TRUE(rv.success());
978    ASSERT_EQ("1", Util::match_match(op.match()));
979
980    // Perform add_unique again
981    rv = runOp(Command::ARRAY_ADD_UNIQUE, "", "1");
982    ASSERT_EQ(Error::DOC_EEXISTS, rv);
983
984    rv = runOp(Command::ARRAY_APPEND, "", "2");
985    ASSERT_TRUE(rv.success());
986    getAssignNewDoc(doc);
987    rv = runOp(Command::GET, "[1]");
988    ASSERT_TRUE(rv.success());
989    ASSERT_EQ("2", Util::match_match(op.match()));
990
991    // Try one more
992    rv = runOp(Command::ARRAY_APPEND, "", "3");
993    ASSERT_TRUE(rv.success());
994    getAssignNewDoc(doc);
995    rv = runOp(Command::GET, "[2]");
996    ASSERT_TRUE(rv.success());
997    ASSERT_EQ("3", Util::match_match(op.match()));
998
999    // See how well we handle errors
1000    doc = "nonjson";
1001    op.set_doc(doc);
1002    rv = runOp(Command::ARRAY_APPEND, "", "123");
1003    ASSERT_EQ(Error::DOC_NOTJSON, rv);
1004
1005    doc = "{}";
1006    op.set_doc(doc);
1007    rv = runOp(Command::ARRAY_APPEND, "", "123");
1008    ASSERT_EQ(Error::PATH_MISMATCH, rv);
1009
1010    doc = "[[]]";
1011    op.set_doc(doc);
1012    rv = runOp(Command::ARRAY_APPEND, "[0]", "123");
1013    ASSERT_TRUE(rv.success());
1014    getAssignNewDoc(doc);
1015    rv = runOp(Command::GET, "[0][0]");
1016    ASSERT_TRUE(rv.success());
1017    ASSERT_EQ("123", Util::match_match(op.match()));
1018
1019    doc = "[0, {\"1\": 1}]";
1020    op.set_doc(doc);
1021    rv = runOp(Command::ARRAY_APPEND, "", "123");
1022    ASSERT_TRUE(rv.success());
1023    getAssignNewDoc(doc);
1024    rv = runOp(Command::GET, "[-1]");
1025    ASSERT_TRUE(rv.success());
1026    ASSERT_EQ("123", Util::match_match(op.match()));
1027
1028    // Because we don't parse the array, ]] is valid.
1029    /*
1030    doc = "]]";
1031    op.set_doc(doc);
1032    rv = runOp(Command::ARRAY_APPEND, "", "123");
1033    ASSERT_EQ(Error::DOC_NOTJSON, rv);
1034    */
1035
1036    // This won't work either because we don't validate against terminating
1037    // JSON tokens.
1038    /*
1039    doc = "{]";
1040    op.set_doc(doc);
1041    rv = runOp(Command::ARRAY_APPEND, "", "123");
1042    ASSERT_EQ(Error::DOC_NOTJSON, rv);
1043    */
1044
1045    // This follows the same codepath as "normal", but good to verify just
1046    // in case
1047    op.set_doc("[]");
1048    rv = runOp(Command::ARRAY_APPEND, "", "notjson");
1049    ASSERT_EQ(Error::VALUE_CANTINSERT, rv);
1050}
1051