Add WithMeta unit tests
[ep-engine.git] / tests / module_tests / evp_store_with_meta.cc
1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil
2  * -*- */
3 /*
4  *     Copyright 2017 Couchbase, Inc
5  *
6  *   Licensed under the Apache License, Version 2.0 (the "License");
7  *   you may not use this file except in compliance with the License.
8  *   You may obtain a copy of the License at
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *   Unless required by applicable law or agreed to in writing, software
13  *   distributed under the License is distributed on an "AS IS" BASIS,
14  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *   See the License for the specific language governing permissions and
16  *   limitations under the License.
17  */
18
19 #include "evp_store_single_threaded_test.h"
20
21 class WithMetaTest : public SingleThreadedEPBucketTest {
22 public:
23     void SetUp() override {
24         SingleThreadedEPBucketTest::SetUp();
25         store->setVBucketState(vbid, vbucket_state_active, false);
26         expiry = ep_real_time() + 31557600; // +1 year in seconds
27     }
28
29     void enableLww() {
30         if (!config_string.empty()) {
31             config_string += ";";
32         }
33         config_string += "conflict_resolution_type=lww";
34     }
35
36     /**
37      * Build a *_with_meta packet, defaulting a number of arguments (keeping
38      * some of the test bodies smaller)
39      */
40     std::vector<char> buildWithMeta(protocol_binary_command op,
41                                     ItemMetaData itemMeta,
42                                     const std::string& key,
43                                     const std::string& value) const {
44         return buildWithMetaPacket(op,
45                                    0 /*datatype*/,
46                                    vbid,
47                                    0 /*opaque*/,
48                                    0 /*cas*/,
49                                    itemMeta,
50                                    key,
51                                    value,
52                                    {},
53                                    0);
54     }
55
56     /**
57      * Given a buffer of data representing a with_meta packet, update the meta
58      * Allows test to avoid lots of allocation/copying when creating inputs.
59      */
60     static void updateMeta(std::vector<char>& wm,
61                            uint64_t cas,
62                            uint64_t revSeq,
63                            uint32_t flags,
64                            uint32_t exp) {
65         auto packet = reinterpret_cast<protocol_binary_request_set_with_meta*>(
66                 wm.data());
67         packet->message.body.cas = htonll(cas);
68         packet->message.body.seqno = htonll(revSeq);
69         packet->message.body.expiration = htonl(exp);
70         packet->message.body.flags = flags;
71     }
72
73     /**
74      * Given a buffer of data representing a with_meta packet, update the meta
75      * Allows test to avoid lots of allocation/copying when creating inputs.
76      */
77     static void updateMeta(std::vector<char>& wm,
78                            const ItemMetaData& itemMeta) {
79         updateMeta(wm,
80                    itemMeta.cas,
81                    itemMeta.revSeqno,
82                    itemMeta.flags,
83                    uint32_t(itemMeta.exptime));
84     }
85
86     /**
87      * Call the correct engine function for the op (set vs delete)
88      */
89     ENGINE_ERROR_CODE callEngine(protocol_binary_command op,
90                                  std::vector<char>& wm) {
91         if (op == PROTOCOL_BINARY_CMD_DEL_WITH_META ||
92             op == PROTOCOL_BINARY_CMD_DELQ_WITH_META) {
93             return engine->deleteWithMeta(
94                     cookie,
95                     reinterpret_cast<protocol_binary_request_delete_with_meta*>(
96                             wm.data()),
97                     this->addResponse,
98                     DocNamespace::DefaultCollection);
99         } else {
100             return engine->setWithMeta(
101                     cookie,
102                     reinterpret_cast<protocol_binary_request_set_with_meta*>(
103                             wm.data()),
104                     this->addResponse,
105                     DocNamespace::DefaultCollection);
106         }
107     }
108
109     /**
110      * Get the item and check its value, if called for a delete, assuming
111      * delete with value
112      */
113     void checkGetItem(
114             const std::string& key,
115             const std::string& expectedValue,
116             ItemMetaData expectedMeta,
117             ENGINE_ERROR_CODE expectedGetReturnValue = ENGINE_SUCCESS) {
118         auto result = store->get({key, DocNamespace::DefaultCollection},
119                                  vbid,
120                                  nullptr,
121                                  GET_DELETED_VALUE);
122
123         ASSERT_EQ(expectedGetReturnValue, result.getStatus());
124
125         if (expectedGetReturnValue == ENGINE_SUCCESS) {
126             EXPECT_EQ(0,
127                       strncmp(expectedValue.data(),
128                               result.getValue()->getData(),
129                               result.getValue()->getNBytes()));
130             EXPECT_EQ(expectedMeta.cas, result.getValue()->getCas());
131             EXPECT_EQ(expectedMeta.revSeqno, result.getValue()->getRevSeqno());
132             EXPECT_EQ(expectedMeta.flags, result.getValue()->getFlags());
133             EXPECT_EQ(expectedMeta.exptime, result.getValue()->getExptime());
134             delete result.getValue();
135         }
136     }
137
138     void oneOp(protocol_binary_command op,
139                ItemMetaData itemMeta,
140                int options,
141                protocol_binary_response_status expectedResponseStatus,
142                const std::string& key,
143                const std::string& value) {
144         auto swm = buildWithMetaPacket(op,
145                                        0 /*datatype*/,
146                                        vbid,
147                                        0 /*opaque*/,
148                                        0 /*cas*/,
149                                        itemMeta,
150                                        key,
151                                        value,
152                                        {},
153                                        options);
154         EXPECT_EQ(ENGINE_SUCCESS, callEngine(op, swm));
155         EXPECT_EQ(expectedResponseStatus, getAddResponseStatus());
156     }
157
158     /**
159      * Run one op and check the result
160      */
161     void oneOpAndCheck(protocol_binary_command op,
162                        ItemMetaData itemMeta,
163                        int options,
164                        bool withValue,
165                        protocol_binary_response_status expectedResponseStatus,
166                        ENGINE_ERROR_CODE expectedGetReturnValue) {
167         std::string key = "mykey";
168         std::string value;
169         if (withValue) {
170             value = createXattrValue("myvalue"); // xattr but stored as raw
171         }
172         oneOp(op,
173               itemMeta,
174               options,
175               expectedResponseStatus,
176               key,
177               value);
178         checkGetItem(key, value, itemMeta, expectedGetReturnValue);
179     }
180
181     // *_with_meta with winning mutations
182     struct TestData {
183         ItemMetaData meta;
184         protocol_binary_response_status expectedStatus;
185     };
186
187     /**
188      * The conflict_win test is reused by seqno/lww and is intended to
189      * test each winning op/meta input
190      */
191     void conflict_win(protocol_binary_command op,
192                       int options,
193                       const std::array<TestData, 4>& testData,
194                       const ItemMetaData& itemMeta);
195     /**
196      * The conflict_win test is reused by seqno/lww and is intended to
197      * test each winning op/meta input
198      */
199     void conflict_lose(protocol_binary_command op,
200                        int options,
201                        bool withValue,
202                        const std::array<TestData, 4>& testData,
203                        const ItemMetaData& itemMeta);
204
205     /**
206      * The conflict_del_lose_xattr test demonstrates how a delete never gets
207      * to compare xattrs when in conflict.
208      */
209     void conflict_del_lose_xattr(protocol_binary_command op,
210                                  int options,
211                                  bool withValue);
212
213     /**
214      * The conflict_lose_xattr test demonstrates how a set gets
215      * to compare xattrs when in conflict, and the server doc would win.
216      */
217     void conflict_lose_xattr(protocol_binary_command op,
218                              int options,
219                              bool withValue);
220     /**
221      * Initialise an expiry value which allows us to set/get items without them
222      * expiring, i.e. a few years of expiry wiggle room
223      */
224     time_t expiry;
225 };
226
227 class WithMetaLwwTest : public WithMetaTest {
228 public:
229     void SetUp() override {
230         enableLww();
231         WithMetaTest::SetUp();
232     }
233 };
234
235 class DelWithMetaTest
236         : public WithMetaTest,
237           public ::testing::WithParamInterface<
238                   ::testing::tuple<bool, protocol_binary_command>> {
239 public:
240     void SetUp() override {
241         withValue = ::testing::get<0>(GetParam());
242         op = ::testing::get<1>(GetParam());
243         WithMetaTest::SetUp();
244     }
245
246     protocol_binary_command op;
247     bool withValue;
248 };
249
250 class DelWithMetaLwwTest
251         : public WithMetaTest,
252           public ::testing::WithParamInterface<
253                   ::testing::tuple<bool, protocol_binary_command>> {
254 public:
255     void SetUp() override {
256         withValue = ::testing::get<0>(GetParam());
257         op = ::testing::get<1>(GetParam());
258         enableLww();
259         WithMetaTest::SetUp();
260     }
261
262     protocol_binary_command op;
263     bool withValue;
264 };
265
266 class AllWithMetaTest
267         : public WithMetaTest,
268           public ::testing::WithParamInterface<protocol_binary_command> {};
269
270 class AddSetWithMetaTest
271         : public WithMetaTest,
272           public ::testing::WithParamInterface<protocol_binary_command> {};
273
274 class AddSetWithMetaLwwTest
275         : public WithMetaTest,
276           public ::testing::WithParamInterface<protocol_binary_command> {
277 public:
278     void SetUp() override {
279         enableLww();
280         WithMetaTest::SetUp();
281     }
282 };
283
284 TEST_P(AddSetWithMetaTest, basic) {
285     ItemMetaData itemMeta{0xdeadbeef, 0xf00dcafe, 0xfacefeed, expiry};
286     oneOpAndCheck(GetParam(),
287                   itemMeta,
288                   0, // no-options
289                   true /*set a value*/,
290                   PROTOCOL_BINARY_RESPONSE_SUCCESS,
291                   ENGINE_SUCCESS);
292 }
293
294 TEST_F(WithMetaTest, basicAdd) {
295     ItemMetaData itemMeta{0xdeadbeef, 0xf00dcafe, 0xfacefeed, expiry};
296     oneOpAndCheck(PROTOCOL_BINARY_CMD_ADD_WITH_META,
297                   itemMeta,
298                   0, // no-options
299                   true /*set a value*/,
300                   PROTOCOL_BINARY_RESPONSE_SUCCESS,
301                   ENGINE_SUCCESS);
302
303     oneOpAndCheck(PROTOCOL_BINARY_CMD_ADD_WITH_META,
304                   itemMeta,
305                   0, // no-options
306                   true /*set a value*/,
307                   PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, // can't do a second add
308                   ENGINE_SUCCESS); // can still get the key
309 }
310
311 TEST_P(DelWithMetaTest, basic) {
312     ItemMetaData itemMeta{0xdeadbeef, 0xf00dcafe, 0xfacefeed, expiry};
313     // A delete_w_meta against an empty bucket queues a BGFetch (get = ewblock)
314     // A delete_w_meta(with_value) sets the new value (get = success)
315     oneOpAndCheck(op,
316                   itemMeta,
317                   0, // no-options
318                   withValue,
319                   PROTOCOL_BINARY_RESPONSE_SUCCESS,
320                   withValue ? ENGINE_SUCCESS : ENGINE_EWOULDBLOCK);
321 }
322
323 TEST_P(AllWithMetaTest, invalidCas) {
324     // 0 CAS in the item meta is invalid
325     ItemMetaData itemMeta{0 /*cas*/, 0, 0, 0};
326     oneOpAndCheck(GetParam(),
327                   itemMeta,
328                   0, // no-options
329                   true /*set a value*/,
330                   PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS,
331                   ENGINE_KEY_ENOENT);
332
333     // -1 CAS in the item meta is invalid
334     itemMeta.cas = ~0ull;
335     oneOpAndCheck(GetParam(),
336                   itemMeta,
337                   0, // no-options
338                   true /*set a value*/,
339                   PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS,
340                   ENGINE_KEY_ENOENT);
341 }
342
343 TEST_P(DelWithMetaTest, invalidCas) {
344     // 0 CAS in the item meta is invalid
345     ItemMetaData itemMeta{0 /*cas*/, 0, 0, 0};
346     oneOpAndCheck(op,
347                   itemMeta,
348                   0, // no-options
349                   withValue /*set a value*/,
350                   PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS,
351                   ENGINE_KEY_ENOENT);
352
353     // -1 CAS in the item meta is invalid
354     itemMeta.cas = ~0ull;
355     oneOpAndCheck(op,
356                   itemMeta,
357                   0, // no-options
358                   withValue /*set a value*/,
359                   PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS,
360                   ENGINE_KEY_ENOENT);
361 }
362
363 TEST_P(AllWithMetaTest, failForceAccept) {
364     // FORCE_ACCEPT_WITH_META_OPS not allowed unless we're LWW
365     ItemMetaData itemMeta{1, 0, 0, expiry};
366     oneOpAndCheck(GetParam(),
367                   itemMeta,
368                   FORCE_ACCEPT_WITH_META_OPS,
369                   true /*set a value*/,
370                   PROTOCOL_BINARY_RESPONSE_EINVAL,
371                   ENGINE_KEY_ENOENT);
372 }
373
374 TEST_P(AddSetWithMetaLwwTest, allowForceAccept) {
375     // FORCE_ACCEPT_WITH_META_OPS ok on LWW
376     ItemMetaData itemMeta{1, 0, 0, expiry};
377     oneOpAndCheck(GetParam(),
378                   itemMeta,
379                   FORCE_ACCEPT_WITH_META_OPS,
380                   true /*set a value*/,
381                   PROTOCOL_BINARY_RESPONSE_SUCCESS,
382                   ENGINE_SUCCESS);
383 }
384
385 TEST_P(DelWithMetaLwwTest, allowForceAccept) {
386     // FORCE_ACCEPT_WITH_META_OPS ok on LWW
387     ItemMetaData itemMeta{1, 0, 0, expiry};
388     oneOpAndCheck(op,
389                   itemMeta,
390                   FORCE_ACCEPT_WITH_META_OPS,
391                   withValue,
392                   PROTOCOL_BINARY_RESPONSE_SUCCESS,
393                   withValue ? ENGINE_SUCCESS : ENGINE_EWOULDBLOCK);
394 }
395
396 TEST_P(AllWithMetaTest, regenerateCASInvalid) {
397     // REGENERATE_CAS cannot be by itself
398     ItemMetaData itemMeta{1, 0, 0, expiry};
399     oneOpAndCheck(GetParam(),
400                   itemMeta,
401                   REGENERATE_CAS,
402                   true,
403                   PROTOCOL_BINARY_RESPONSE_EINVAL,
404                   ENGINE_KEY_ENOENT);
405 }
406
407 TEST_P(AllWithMetaTest, regenerateCAS) {
408     // Test that
409     uint64_t cas = 1;
410     auto swm =
411             buildWithMetaPacket(GetParam(),
412                                 0 /*datatype*/,
413                                 vbid /*vbucket*/,
414                                 0 /*opaque*/,
415                                 0 /*cas*/,
416                                 {cas, 0, 0, 0},
417                                 "mykey",
418                                 "myvalue",
419                                 {},
420                                 SKIP_CONFLICT_RESOLUTION_FLAG | REGENERATE_CAS);
421
422     EXPECT_EQ(ENGINE_SUCCESS, callEngine(GetParam(), swm));
423     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
424     auto result = store->get({"mykey", DocNamespace::DefaultCollection},
425                              vbid,
426                              nullptr,
427                              GET_DELETED_VALUE);
428     ASSERT_EQ(ENGINE_SUCCESS, result.getStatus());
429     EXPECT_NE(cas, result.getValue()->getCas()) << "CAS didn't change";
430     delete result.getValue();
431 }
432
433 TEST_P(AllWithMetaTest, invalid_extlen) {
434     // extlen must be very specific values
435     std::string key = "mykey";
436     std::string value = "myvalue";
437     ItemMetaData itemMeta{1, 0, 0, 0};
438
439     auto swm = buildWithMetaPacket(GetParam(),
440                                    0 /*datatype*/,
441                                    vbid /*vbucket*/,
442                                    0 /*opaque*/,
443                                    0 /*cas*/,
444                                    itemMeta,
445                                    key,
446                                    value);
447
448     auto packet = reinterpret_cast<protocol_binary_request_set_with_meta*>(
449             swm.data());
450     // futz the extlen (yes yes AFL fuzz would be ace)
451     for (uint8_t e = 0; e < 0xff; e++) {
452         if (e == 24 || e == 26 || e == 28 || e == 30) {
453             // Skip the valid sizes
454             continue;
455         }
456         packet->message.header.request.extlen = e;
457         EXPECT_EQ(ENGINE_SUCCESS, callEngine(GetParam(), swm));
458         EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_EINVAL, getAddResponseStatus());
459         checkGetItem(key, value, itemMeta, ENGINE_KEY_ENOENT);
460     }
461 }
462
463 TEST_P(AllWithMetaTest, nmvb) {
464     std::string key = "mykey";
465     std::string value = "myvalue";
466     auto swm = buildWithMetaPacket(GetParam(),
467                                    0 /*datatype*/,
468                                    vbid + 1 /*vbucket*/,
469                                    0 /*opaque*/,
470                                    0 /*cas*/,
471                                    {1, 0, 0, 0},
472                                    key,
473                                    value);
474     EXPECT_EQ(ENGINE_SUCCESS, callEngine(GetParam(), swm));
475     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, getAddResponseStatus());
476
477     // Set a dead VB
478     EXPECT_EQ(ENGINE_SUCCESS,
479               store->setVBucketState(vbid + 1, vbucket_state_dead, false));
480     EXPECT_EQ(ENGINE_SUCCESS, callEngine(GetParam(), swm));
481     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, getAddResponseStatus());
482
483     // update the VB in the packet to the pending one
484     auto packet = reinterpret_cast<protocol_binary_request_header*>(swm.data());
485     packet->request.vbucket = htons(vbid + 2);
486     EXPECT_EQ(ENGINE_SUCCESS,
487               store->setVBucketState(vbid + 2, vbucket_state_pending, false));
488     EXPECT_EQ(ENGINE_EWOULDBLOCK, callEngine(GetParam(), swm));
489     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
490
491     // Re-run the op now active, else we have a memory leak
492     EXPECT_EQ(ENGINE_SUCCESS,
493               store->setVBucketState(vbid + 2, vbucket_state_active, false));
494     EXPECT_EQ(ENGINE_SUCCESS, callEngine(GetParam(), swm));
495 }
496
497 TEST_P(AllWithMetaTest, takeoverBackedup) {
498     ItemMetaData itemMeta{1, 0, 0, expiry};
499     auto swm = buildWithMetaPacket(GetParam(),
500                                    0 /*datatype*/,
501                                    vbid /*vbucket*/,
502                                    0 /*opaque*/,
503                                    0 /*cas*/,
504                                    itemMeta,
505                                    "mykey",
506                                    "myvalue");
507
508     store->getVBucket(vbid)->setTakeoverBackedUpState(true);
509     oneOpAndCheck(GetParam(),
510                   itemMeta,
511                   0,
512                   true,
513                   PROTOCOL_BINARY_RESPONSE_ETMPFAIL,
514                   ENGINE_KEY_ENOENT);
515 }
516
517 TEST_P(AllWithMetaTest, degraded) {
518     ItemMetaData itemMeta{1, 0, 0, expiry};
519     auto swm = buildWithMetaPacket(GetParam(),
520                                    0 /*datatype*/,
521                                    vbid /*vbucket*/,
522                                    0 /*opaque*/,
523                                    0 /*cas*/,
524                                    itemMeta,
525                                    "mykey",
526                                    "myvalue");
527
528     engine->public_enableTraffic(false);
529     oneOpAndCheck(GetParam(),
530                   itemMeta,
531                   0,
532                   true,
533                   PROTOCOL_BINARY_RESPONSE_ETMPFAIL,
534                   ENGINE_KEY_ENOENT);
535 }
536
537 void WithMetaTest::conflict_lose(protocol_binary_command op,
538                                  int options,
539                                  bool withValue,
540                                  const std::array<TestData, 4>& testData,
541                                  const ItemMetaData& itemMeta) {
542     std::string value;
543     if (withValue) {
544         value = createXattrValue("myvalue");
545     }
546     std::string key = "mykey";
547     // First add a document so we have something to conflict with
548     auto swm = buildWithMetaPacket(PROTOCOL_BINARY_CMD_ADD_WITH_META,
549                                    0,
550                                    vbid /*vbucket*/,
551                                    0 /*opaque*/,
552                                    0 /*cas*/,
553                                    itemMeta,
554                                    key,
555                                    value,
556                                    {},
557                                    options);
558
559     EXPECT_EQ(ENGINE_SUCCESS,
560               callEngine(PROTOCOL_BINARY_CMD_ADD_WITH_META, swm));
561     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
562
563     for (const auto& td : testData) {
564         oneOp(op, td.meta, options, td.expectedStatus, key, value);
565     }
566 }
567
568 // store a document then <op>_with_meta with equal ItemMeta but xattr on
569 void WithMetaTest::conflict_del_lose_xattr(protocol_binary_command op,
570                                            int options,
571                                            bool withValue) {
572     ItemMetaData itemMeta{
573             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
574     std::string value;
575     if (withValue) {
576         value = createXattrValue("myvalue");
577     }
578     std::string key = "mykey";
579     // First add a document so we have something to conflict with
580     auto swm = buildWithMetaPacket(PROTOCOL_BINARY_CMD_ADD_WITH_META,
581                                    0 /*xattr off*/,
582                                    vbid /*vbucket*/,
583                                    0 /*opaque*/,
584                                    0 /*cas*/,
585                                    itemMeta,
586                                    key,
587                                    value,
588                                    {},
589                                    options);
590
591     EXPECT_EQ(ENGINE_SUCCESS,
592               callEngine(PROTOCOL_BINARY_CMD_ADD_WITH_META, swm));
593     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
594
595     // revSeqno/cas/exp/flags equal, xattr on, conflict (a set would win)
596     swm = buildWithMetaPacket(op,
597                               PROTOCOL_BINARY_DATATYPE_XATTR,
598                               vbid /*vbucket*/,
599                               0 /*opaque*/,
600                               0 /*cas*/,
601                               itemMeta,
602                               key,
603                               value,
604                               {},
605                               options);
606     EXPECT_EQ(ENGINE_SUCCESS, callEngine(op, swm));
607     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, getAddResponseStatus());
608 }
609
610 void WithMetaTest::conflict_lose_xattr(protocol_binary_command op,
611                                        int options,
612                                        bool withValue) {
613     ItemMetaData itemMeta{
614             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
615     std::string value;
616     if (withValue) {
617         value = createXattrValue("myvalue");
618     }
619     std::string key = "mykey";
620     // First add a document so we have something to conflict with
621     auto swm = buildWithMetaPacket(PROTOCOL_BINARY_CMD_ADD_WITH_META,
622                                    PROTOCOL_BINARY_DATATYPE_XATTR,
623                                    vbid /*vbucket*/,
624                                    0 /*opaque*/,
625                                    0 /*cas*/,
626                                    itemMeta,
627                                    key,
628                                    value,
629                                    {},
630                                    options);
631
632     EXPECT_EQ(ENGINE_SUCCESS,
633               callEngine(PROTOCOL_BINARY_CMD_ADD_WITH_META, swm));
634     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
635
636     // revSeqno/cas/exp/flags equal, xattr off, conflict (a set would win)
637     swm = buildWithMetaPacket(op,
638                               0,
639                               vbid /*vbucket*/,
640                               0 /*opaque*/,
641                               0 /*cas*/,
642                               itemMeta,
643                               key,
644                               value,
645                               {},
646                               options);
647     EXPECT_EQ(ENGINE_SUCCESS, callEngine(op, swm));
648     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, getAddResponseStatus());
649 }
650
651 TEST_P(DelWithMetaTest, conflict_lose) {
652     ItemMetaData itemMeta{
653             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
654
655     // Conflict test order: 1) seqno 2) cas 3) expiry 4) flags 5) xattr
656     // However deletes only check 1 and 2.
657
658     std::array<TestData, 4> data;
659
660     // 1) revSeqno is less and everything else larger. Expect conflict
661     data[0] = {{101, 99, 101, expiry + 1},
662                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
663     // 2. revSeqno is equal, cas is less, others are larger. Expect conflict
664     data[1] = {{99, 100, 101, expiry + 1},
665                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
666     // 3. revSeqno/cas/flags equal, exp larger. Conflict as exp not checked
667     data[2] = {{100, 100, 100, expiry + 1},
668                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
669     // 4. revSeqno/cas/exp equal, flags larger. Conflict as exp not checked
670     data[3] = {{100, 100, 200, expiry}, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
671
672     conflict_lose(op, 0, withValue, data, itemMeta);
673 }
674
675 TEST_P(DelWithMetaLwwTest, conflict_lose) {
676     ItemMetaData itemMeta{
677             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
678
679     // Conflict test order: 1) cas 2) seqno 3) expiry 4) flags 5) xattr
680     // However deletes only check 1 and 2.
681
682     std::array<TestData, 4> data;
683     // 1) cas is less and everything else larger. Expect conflict
684     data[0] = {{99, 101, 101, expiry + 1},
685                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
686     // 2. cas is equal, revSeqno is less, others are larger. Expect conflict
687     data[1] = {{100, 99, 101, expiry + 1},
688                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
689     // 3. revSeqno/cas/flags equal, exp larger. Conflict as exp not checked
690     data[2] = {{100, 100, 100, expiry + 1},
691                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
692     // 4. revSeqno/cas/exp equal, flags larger. Conflict as exp not checked
693     data[3] = {{100, 100, 200, expiry}, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
694
695     conflict_lose(op, FORCE_ACCEPT_WITH_META_OPS, withValue, data, itemMeta);
696 }
697
698 TEST_P(DelWithMetaTest, conflict_xattr_lose) {
699     conflict_del_lose_xattr(op, 0, withValue);
700 }
701
702 TEST_P(DelWithMetaLwwTest, conflict_xattr_lose) {
703     conflict_del_lose_xattr(op, FORCE_ACCEPT_WITH_META_OPS, withValue);
704 }
705
706 TEST_P(AddSetWithMetaTest, conflict_lose) {
707     ItemMetaData itemMeta{
708             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
709
710     // Conflict test order: 1) seqno 2) cas 3) expiry 4) flags 5) xattr
711     std::array<TestData, 4> data;
712     // 1) revSeqno is less and everything else larger. Expect conflict
713     data[0] = {{101, 99, 101, expiry + 1},
714                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
715     // 2. revSeqno is equal, cas is less, others are larger. Expect conflict
716     data[1] = {{99, 100, 101, expiry + 1},
717                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
718     // 3. revSeqno/cas equal, flags larger, exp less, conflict
719     data[2] = {{100, 100, 101, expiry - 1},
720                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
721     // 4. revSeqno/cas/exp equal, flags less, conflict
722     data[3] = {{100, 100, 99, expiry}, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
723
724     conflict_lose(
725             GetParam(), 0 /*options*/, true /*withValue*/, data, itemMeta);
726 }
727
728 TEST_P(AddSetWithMetaLwwTest, conflict_lose) {
729     ItemMetaData itemMeta{
730             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
731
732     // Conflict test order: 1) cas 2) seqno 3) expiry 4) flags 5) xattr
733     std::array<TestData, 4> data;
734     // 1) cas is less and everything else larger. Expect conflict
735     data[0] = {{99, 101, 101, expiry + 1},
736                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
737     // 2. cas is equal, revSeq is less, others are larger. Expect conflict
738     data[1] = {{100, 99, 101, expiry + 1},
739                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
740     // 3. revSeqno/cas equal, flags larger, exp less, conflict
741     data[2] = {{100, 100, 101, expiry - 1},
742                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
743     // 4. revSeqno/cas/exp equal, flags less, conflict
744     data[3] = {{100, 100, 99, expiry}, PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS};
745
746     conflict_lose(GetParam(), FORCE_ACCEPT_WITH_META_OPS, true, data, itemMeta);
747 }
748
749 TEST_P(AddSetWithMetaTest, conflict_xattr_lose) {
750     conflict_lose_xattr(GetParam(), 0 /*options*/, true /*withvalue*/);
751 }
752
753 TEST_P(AddSetWithMetaLwwTest, conflict_xattr_lose) {
754     conflict_lose_xattr(
755             GetParam(), FORCE_ACCEPT_WITH_META_OPS, true /*withvalue*/);
756 }
757
758 // This test will store an item with this meta data then store again
759 // using the testData entries
760 void WithMetaTest::conflict_win(protocol_binary_command op,
761                                 int options,
762                                 const std::array<TestData, 4>& testData,
763                                 const ItemMetaData& itemMeta) {
764     bool isDelete = op == PROTOCOL_BINARY_CMD_DEL_WITH_META ||
765                     op == PROTOCOL_BINARY_CMD_DELQ_WITH_META;
766     bool isSet = op == PROTOCOL_BINARY_CMD_SET_WITH_META ||
767                  op == PROTOCOL_BINARY_CMD_SETQ_WITH_META;
768
769     int counter = 0;
770     for (auto& td : testData) {
771         // Set our "target" (new key each iteration)
772         std::string key = "mykey" + std::to_string(counter);
773         key.push_back(op); // and the op for test uniqueness
774         std::string value = "newvalue" + std::to_string(counter);
775         auto swm = buildWithMetaPacket(PROTOCOL_BINARY_CMD_SET_WITH_META,
776                                        0 /*datatype*/,
777                                        vbid /*vbucket*/,
778                                        0 /*opaque*/,
779                                        0 /*cas*/,
780                                        itemMeta,
781                                        key,
782                                        "myvalue",
783                                        {},
784                                        options);
785
786         EXPECT_EQ(ENGINE_SUCCESS,
787                   callEngine(PROTOCOL_BINARY_CMD_SET_WITH_META, swm));
788         EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus())
789                 << "Failed to set the target key:" << key;
790
791         // Next the test packet (always with a value).
792         auto wm = buildWithMetaPacket(op,
793                                       0 /*datatype*/,
794                                       vbid /*vbucket*/,
795                                       0 /*opaque*/,
796                                       0 /*cas*/,
797                                       td.meta,
798                                       key,
799                                       value,
800                                       {},
801                                       options);
802
803         // Now set/add/del against the item using the test iteration metadata
804         EXPECT_EQ(ENGINE_SUCCESS, callEngine(op, wm));
805
806         auto status = getAddResponseStatus();
807         if (isDelete) {
808             EXPECT_EQ(td.expectedStatus, status)
809                     << "Failed delete for iteration " << counter;
810             if (status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
811                 checkGetItem(key, value, td.meta);
812             }
813         } else {
814             EXPECT_TRUE(isSet);
815             EXPECT_EQ(td.expectedStatus, status) << "Failed set for iteration "
816                                                  << counter;
817             checkGetItem(key, value, td.meta);
818         }
819         counter++;
820     }
821
822     // ... Finally give an Item with a datatype (not xattr)
823     std::string key = "mykey" + std::to_string(counter);
824     key.push_back(op); // and the op for test uniqueness
825     auto swm = buildWithMetaPacket(PROTOCOL_BINARY_CMD_ADD_WITH_META,
826                                    PROTOCOL_BINARY_DATATYPE_JSON,
827                                    vbid /*vbucket*/,
828                                    0 /*opaque*/,
829                                    0 /*cas*/,
830                                    itemMeta,
831                                    key,
832                                    "myvalue",
833                                    {},
834                                    options);
835
836     EXPECT_EQ(ENGINE_SUCCESS,
837               callEngine(PROTOCOL_BINARY_CMD_ADD_WITH_META, swm));
838     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
839
840     // And test same cas/seq/exp/flags but marked with xattr
841     auto xattrValue = createXattrValue("xattr_value");
842     swm = buildWithMetaPacket(op,
843                               PROTOCOL_BINARY_DATATYPE_XATTR,
844                               vbid /*vbucket*/,
845                               0 /*opaque*/,
846                               0 /*cas*/,
847                               itemMeta,
848                               key,
849                               xattrValue,
850                               {},
851                               options);
852     EXPECT_EQ(ENGINE_SUCCESS, callEngine(op, swm));
853
854     if (isSet) {
855         EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
856         checkGetItem(key, xattrValue, itemMeta);
857     } else {
858         EXPECT_TRUE(isDelete);
859         // del fails as conflict resolution won't get to the XATTR test
860         EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS, getAddResponseStatus());
861     }
862 }
863
864 TEST_F(WithMetaTest, set_conflict_win) {
865     ItemMetaData itemMeta{
866             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
867     std::array<TestData, 4> data = {
868             {{{100, 101, 100, expiry}, // ... mutate with higher seq
869               PROTOCOL_BINARY_RESPONSE_SUCCESS},
870              {{101, 100, 100, expiry}, // ... mutate with same but higher cas
871               PROTOCOL_BINARY_RESPONSE_SUCCESS},
872              {{100,
873                100,
874                100,
875                expiry + 1}, // ... mutate with same but higher exp
876               PROTOCOL_BINARY_RESPONSE_SUCCESS},
877              {{100, 100, 101, expiry}, // ... mutate with same but higher flags
878               PROTOCOL_BINARY_RESPONSE_SUCCESS}}};
879
880     conflict_win(PROTOCOL_BINARY_CMD_SET_WITH_META, 0, data, itemMeta);
881     conflict_win(PROTOCOL_BINARY_CMD_SETQ_WITH_META, 0, data, itemMeta);
882 }
883
884 TEST_F(WithMetaTest, del_conflict_win) {
885     ItemMetaData itemMeta{
886             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
887     std::array<TestData, 4> data = {{
888             {{100, 101, 100, expiry}, // ... mutate with higher seq
889              PROTOCOL_BINARY_RESPONSE_SUCCESS},
890             {{101, 100, 100, expiry}, // ... mutate with same but higher cas
891              PROTOCOL_BINARY_RESPONSE_SUCCESS},
892             {{100, 100, 100, expiry + 1}, // ... mutate with same but higher exp
893              PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS}, // delete ignores expiry
894             {{100, 100, 101, expiry}, // ... mutate with same but higher flags
895              PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS} // delete ignores flags
896     }};
897
898     conflict_win(PROTOCOL_BINARY_CMD_DEL_WITH_META, 0, data, itemMeta);
899     conflict_win(PROTOCOL_BINARY_CMD_DELQ_WITH_META, 0, data, itemMeta);
900 }
901
902 TEST_F(WithMetaLwwTest, set_conflict_win) {
903     ItemMetaData itemMeta{
904             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
905     std::array<TestData, 4> data = {
906             {{{101, 100, 100, expiry}, // ... mutate with higher cas
907               PROTOCOL_BINARY_RESPONSE_SUCCESS},
908              {{100, 101, 100, expiry}, // ... mutate with same but higher seq
909               PROTOCOL_BINARY_RESPONSE_SUCCESS},
910              {{100,
911                100,
912                100,
913                expiry + 1}, // ... mutate with same but higher exp
914               PROTOCOL_BINARY_RESPONSE_SUCCESS},
915              {{100, 100, 101, expiry}, // ... mutate with same but higher flags
916               PROTOCOL_BINARY_RESPONSE_SUCCESS}}};
917
918     conflict_win(PROTOCOL_BINARY_CMD_SET_WITH_META,
919                  FORCE_ACCEPT_WITH_META_OPS,
920                  data,
921                  itemMeta);
922     conflict_win(PROTOCOL_BINARY_CMD_SETQ_WITH_META,
923                  FORCE_ACCEPT_WITH_META_OPS,
924                  data,
925                  itemMeta);
926 }
927
928 TEST_F(WithMetaLwwTest, del_conflict_win) {
929     ItemMetaData itemMeta{
930             100 /*cas*/, 100 /*revSeq*/, 100 /*flags*/, expiry /*expiry*/};
931     std::array<TestData, 4> data = {{
932             {{101, 100, 100, expiry}, // ... mutate with higher cas
933              PROTOCOL_BINARY_RESPONSE_SUCCESS},
934             {{100, 101, 100, expiry}, // ... mutate with same but higher seq
935              PROTOCOL_BINARY_RESPONSE_SUCCESS},
936             {{100, 100, 100, expiry + 1}, // ... mutate with same but higher exp
937              PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS}, // delete ignores expiry
938             {{100, 100, 101, expiry}, // ... mutate with same but higher flags
939              PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS} // delete ignores flags
940     }};
941
942     conflict_win(PROTOCOL_BINARY_CMD_DEL_WITH_META,
943                  FORCE_ACCEPT_WITH_META_OPS,
944                  data,
945                  itemMeta);
946     conflict_win(PROTOCOL_BINARY_CMD_DELQ_WITH_META,
947                  FORCE_ACCEPT_WITH_META_OPS,
948                  data,
949                  itemMeta);
950 }
951
952 TEST_P(AllWithMetaTest, markJSON) {
953     // Write a XATTR doc with JSON body, expect the doc to be marked as JSON
954     auto value = createXattrValue(R"({"json":"yesplease"})");
955     auto swm = buildWithMetaPacket(GetParam(),
956                                    PROTOCOL_BINARY_DATATYPE_XATTR,
957                                    vbid /*vbucket*/,
958                                    0 /*opaque*/,
959                                    0 /*cas*/,
960                                    {100, 100, 100, expiry},
961                                    "json",
962                                    value);
963     EXPECT_EQ(ENGINE_SUCCESS, callEngine(GetParam(), swm));
964     EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, getAddResponseStatus());
965
966     auto result = store->get({"json", DocNamespace::DefaultCollection},
967                              vbid,
968                              nullptr,
969                              GET_DELETED_VALUE);
970     ASSERT_EQ(ENGINE_SUCCESS, result.getStatus());
971     EXPECT_EQ(0,
972               strncmp(value.data(),
973                       result.getValue()->getData(),
974                       result.getValue()->getNBytes()));
975     EXPECT_EQ(PROTOCOL_BINARY_DATATYPE_JSON | PROTOCOL_BINARY_DATATYPE_XATTR,
976               result.getValue()->getDataType());
977     delete result.getValue();
978 }
979
980 auto opcodeValues = ::testing::Values(PROTOCOL_BINARY_CMD_SET_WITH_META,
981                                       PROTOCOL_BINARY_CMD_SETQ_WITH_META,
982                                       PROTOCOL_BINARY_CMD_ADD_WITH_META,
983                                       PROTOCOL_BINARY_CMD_ADDQ_WITH_META,
984                                       PROTOCOL_BINARY_CMD_DEL_WITH_META,
985                                       PROTOCOL_BINARY_CMD_DELQ_WITH_META);
986
987 auto addSetOpcodeValues = ::testing::Values(PROTOCOL_BINARY_CMD_SET_WITH_META,
988                                             PROTOCOL_BINARY_CMD_SETQ_WITH_META,
989                                             PROTOCOL_BINARY_CMD_ADD_WITH_META,
990                                             PROTOCOL_BINARY_CMD_ADDQ_WITH_META);
991
992 auto deleteOpcodeValues = ::testing::Values(PROTOCOL_BINARY_CMD_DEL_WITH_META,
993                                             PROTOCOL_BINARY_CMD_DELQ_WITH_META);
994
995 struct PrintToStringCombinedName {
996     std::string
997     operator()(const ::testing::TestParamInfo<
998                ::testing::tuple<bool, protocol_binary_command>>& info) const {
999         std::string rv = std::to_string(::testing::get<1>(info.param));
1000         if (::testing::get<0>(info.param)) {
1001             rv += "_with_value";
1002         }
1003         return rv;
1004     }
1005 };
1006
1007 INSTANTIATE_TEST_CASE_P(DelWithMeta,
1008                         DelWithMetaTest,
1009                         ::testing::Combine(::testing::Bool(),
1010                                            deleteOpcodeValues),
1011                         PrintToStringCombinedName());
1012
1013 INSTANTIATE_TEST_CASE_P(DelWithMetaLww,
1014                         DelWithMetaLwwTest,
1015                         ::testing::Combine(::testing::Bool(),
1016                                            deleteOpcodeValues),
1017                         PrintToStringCombinedName());
1018
1019 INSTANTIATE_TEST_CASE_P(AddSetWithMeta,
1020                         AddSetWithMetaTest,
1021                         addSetOpcodeValues,
1022                         ::testing::PrintToStringParamName());
1023
1024 INSTANTIATE_TEST_CASE_P(AddSetWithMetaLww,
1025                         AddSetWithMetaLwwTest,
1026                         addSetOpcodeValues,
1027                         ::testing::PrintToStringParamName());
1028
1029 INSTANTIATE_TEST_CASE_P(AddSetDelMeta,
1030                         AllWithMetaTest,
1031                         opcodeValues,
1032                         ::testing::PrintToStringParamName());