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