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
18#include "testapp_subdoc.h"
19#include "testapp_client_test.h"
20#include <cstring>
21#include <limits>
22#include <string>
23#include <vector>
24
25#include <cJSON.h>
26#include <platform/cb_malloc.h>
27#include <memcached/protocol_binary.h>
28#include <memcached/util.h> // for memcached_protocol_errcode_2_text()
29
30#include "../utilities/protocol2text.h"
31
32/*
33 * testapp testcases for sub-document API - single path.
34 */
35
36// Maximum depth for a document (and path) is 32. Create documents
37// that large and one bigger to test with.
38const int MAX_SUBDOC_PATH_COMPONENTS = 32;
39
40std::ostream& operator<<(std::ostream& os, const BinprotSubdocCommand& obj)
41{
42    os << "[cmd:" << memcached_opcode_2_text(obj.getOp())
43       << " key:" << obj.getKey()
44       << " path:" << obj.getPath() << " value:" << obj.getValue()
45       << " flags:" << obj.getFlags() << " cas:" << obj.getCas() << "]";
46    return os;
47}
48
49/* Encodes and sends a sub-document command, without waiting for any response.
50 */
51void send_subdoc_cmd(const BinprotSubdocCommand& cmd) {
52    std::vector<uint8_t> buf;
53    cmd.encode(buf);
54    safe_send(buf.data(), buf.size(), false);
55}
56
57static void recv_subdoc_response(const BinprotSubdocCommand& cmd,
58                                 protocol_binary_response_status err,
59                                 BinprotSubdocResponse& resp)
60{
61    std::vector<uint8_t> buf;
62    if (!safe_recv_packet(buf)) {
63        ADD_FAILURE() << "Failed to recv subdoc response";
64        return;
65    }
66
67    protocol_binary_response_no_extras tempResponse;
68    memcpy(&tempResponse, buf.data(), sizeof tempResponse);
69    mcbp_validate_response_header(&tempResponse, cmd.getOp(), err);
70    // safe_recv_packet does ntohl already!
71    resp.assign(std::move(buf));
72}
73
74uint64_t recv_subdoc_response(protocol_binary_command expected_cmd,
75                              protocol_binary_response_status expected_status,
76                              const std::string& expected_value) {
77    union {
78        protocol_binary_response_subdocument response;
79        char bytes[1024];
80    } receive;
81
82    if (!safe_recv_packet(receive.bytes, sizeof(receive.bytes))) {
83        ADD_FAILURE() << "Failed to recv subdoc response";
84        return -1;
85    }
86
87    mcbp_validate_response_header(
88        (protocol_binary_response_no_extras*)&receive.response,
89        expected_cmd, expected_status);
90
91    const protocol_binary_response_header* header = &receive.response.message.header;
92
93    const char* val_ptr = receive.bytes + sizeof(*header) +
94                          header->response.extlen;
95    const size_t vallen = header->response.bodylen - header->response.extlen;
96
97    if (!expected_value.empty() &&
98        (expected_cmd != PROTOCOL_BINARY_CMD_SUBDOC_EXISTS)) {
99        const std::string val(val_ptr, val_ptr + vallen);
100        EXPECT_EQ(expected_value, val);
101    } else {
102        // Expect zero length on success (on error the error message string is
103        // returned).
104        if (header->response.status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
105            EXPECT_EQ(0u, vallen);
106        }
107    }
108    return header->response.cas;
109}
110
111// Overload for multi-lookup responses
112uint64_t recv_subdoc_response(protocol_binary_command expected_cmd,
113                              protocol_binary_response_status expected_status,
114                              const std::vector<SubdocMultiLookupResult>& expected_results) {
115    union {
116        protocol_binary_response_subdocument response;
117        char bytes[1024];
118    } receive;
119
120    safe_recv_packet(receive.bytes, sizeof(receive.bytes));
121
122    mcbp_validate_response_header(
123        (protocol_binary_response_no_extras*)&receive.response,
124        expected_cmd, expected_status);
125
126    // Decode body and check against expected_results
127    const auto& header = receive.response.message.header;
128    const char* val_ptr = receive.bytes + sizeof(header) +
129                          header.response.extlen;
130    const size_t vallen = header.response.bodylen + header.response.extlen;
131
132    size_t offset = 0;
133    for (unsigned int ii = 0; ii < expected_results.size(); ii++) {
134        const size_t result_header_len = sizeof(uint16_t) + sizeof(uint32_t);
135        if (offset + result_header_len > vallen) {
136            ADD_FAILURE() << "Remaining value length too short for expected result header";
137            return -1;
138        }
139
140        const auto& exp_result = expected_results[ii];
141        const char* result_header = val_ptr + offset;
142        uint16_t status = ntohs(*reinterpret_cast<const uint16_t*>(result_header));
143        EXPECT_EQ(exp_result.first, protocol_binary_response_status(status))
144            << "Lookup result[" << ii << "]: status different";
145
146        uint32_t result_len = ntohl(*reinterpret_cast<const uint32_t*>
147                (result_header + sizeof(uint16_t)));
148        EXPECT_EQ(exp_result.second.size(), result_len)
149            << "Lookup result[" << ii << "]: length different";
150
151        if (offset + result_header_len + result_len > vallen) {
152            ADD_FAILURE() << "Remaining value length too short for expected result value";
153            return -1;
154        }
155
156        std::string result_value(result_header + result_header_len, result_len);
157        EXPECT_EQ(exp_result.second, result_value)
158            << "Lookup result[" << ii << "]: value differs";
159
160        offset += result_header_len + result_len;
161    }
162
163    return header.response.cas;
164}
165
166// Allow GTest to print out std::vectors as part of EXPECT/ ASSERT error
167// messages.
168namespace std {
169template <typename T>
170std::ostream& operator<< (std::ostream& os, const std::vector<T>& v)
171{
172    os << '[';
173    for (auto& e : v) {
174        os << " " << e;
175    }
176    os << ']';
177    return os;
178}
179
180// Specialization for uint8_t to print as hex.
181template <>
182std::ostream& operator<< (std::ostream& os, const std::vector<uint8_t>& v)
183{
184    os << '[' << std::hex;
185    for (auto& e : v) {
186        os << " " << std::setw(2) << std::setfill('0') << (e & 0xff);
187    }
188    os << ']';
189    return os;
190}
191} // namespace std;
192
193// Overload for multi-mutation responses
194uint64_t recv_subdoc_response(protocol_binary_command expected_cmd,
195                              protocol_binary_response_status expected_status,
196                              const std::vector<SubdocMultiMutationResult>& expected_results) {
197    union {
198        protocol_binary_response_subdocument response;
199        char bytes[1024];
200    } receive;
201
202    safe_recv_packet(receive.bytes, sizeof(receive.bytes));
203
204    mcbp_validate_response_header(
205        (protocol_binary_response_no_extras*)&receive.response,
206        expected_cmd, expected_status);
207
208    // TODO: Check extras for subdoc command and mutation / seqno (if enabled).
209
210    // Decode body and check against expected_results
211    const auto& header = receive.response.message.header;
212    const char* val_ptr = receive.bytes + sizeof(header) +
213                          header.response.extlen;
214    const size_t vallen = header.response.bodylen - header.response.extlen;
215    std::string value(val_ptr, val_ptr + vallen);
216
217    if (expected_status == PROTOCOL_BINARY_RESPONSE_SUCCESS) {
218        if (enabled_hello_features.count(mcbp::Feature::MUTATION_SEQNO) > 0) {
219            EXPECT_EQ(16, header.response.extlen);
220        } else {
221            EXPECT_EQ(0u, header.response.extlen);
222        }
223
224        for (const auto& result : expected_results) {
225            // Should always have at least 7 bytes in result -
226            // index, status, resultlen.
227            EXPECT_GE(value.size(),
228                      sizeof(uint8_t) + sizeof(uint16_t) + sizeof(uint32_t));
229
230            // Extract fields from result spec and validate.
231            uint8_t actual_index = *reinterpret_cast<uint8_t*>(&value[0]);
232            value.erase(value.begin());
233            EXPECT_EQ(result.index, actual_index);
234
235            uint16_t actual_status = ntohs(*reinterpret_cast<uint16_t*>(&value[0]));
236            value.erase(value.begin(), value.begin() + 2);
237            EXPECT_EQ(result.status, actual_status);
238
239            uint32_t actual_resultlen = ntohl(*reinterpret_cast<uint32_t*>(&value[0]));
240            value.erase(value.begin(), value.begin() + 4);
241            EXPECT_EQ(result.result.size(), actual_resultlen);
242
243            std::string actual_result = value.substr(0, actual_resultlen);
244            value.erase(value.begin(), value.begin() + actual_resultlen);
245            EXPECT_EQ(result.result, actual_result);
246        }
247        // Should have consumed all of the value.
248        EXPECT_EQ(0u, value.size());
249
250    } else if (expected_status == PROTOCOL_BINARY_RESPONSE_SUBDOC_MULTI_PATH_FAILURE) {
251        // Specific path failed - should have a 3-byte body containing
252        // specific status and index of first failing spec.
253        EXPECT_EQ(3, vallen) << "Incorrect value:'" << std::string(val_ptr, vallen) << '"';
254        uint8_t actual_fail_index = *val_ptr;
255        uint16_t actual_fail_spec_status =
256                ntohs(*reinterpret_cast<const uint16_t*>(val_ptr + sizeof(actual_fail_index)));
257        EXPECT_EQ(1, expected_results.size());
258        EXPECT_EQ(expected_results[0].index, actual_fail_index);
259        EXPECT_EQ(expected_results[0].status, actual_fail_spec_status);
260    } else {
261        // Top-level error - should have zero body.
262        EXPECT_EQ(0u, vallen);
263    }
264
265    return header.response.cas;
266}
267
268::testing::AssertionResult subdoc_verify_cmd(const BinprotSubdocCommand& cmd,
269                                             protocol_binary_response_status err,
270                                             const std::string& value,
271                                             BinprotSubdocResponse& resp)
272{
273    using ::testing::AssertionSuccess;
274    using ::testing::AssertionFailure;
275
276    send_subdoc_cmd(cmd);
277    recv_subdoc_response(cmd, err, resp);
278    if (::testing::Test::HasFailure()) {
279        return AssertionFailure();
280    }
281
282    if (!value.empty() && cmd.getOp() != PROTOCOL_BINARY_CMD_SUBDOC_EXISTS) {
283        if (value != resp.getValue()) {
284            return AssertionFailure()
285                    << "Value mismatch for " << cmd << std::endl
286                    << "  Expected: " << value << std::endl
287                    << "  Got: " << resp.getValue() << std::endl;
288        }
289    }
290    return AssertionSuccess();
291}
292
293// Overload for multi-lookup commands.
294uint64_t expect_subdoc_cmd(const SubdocMultiLookupCmd& cmd,
295                           protocol_binary_response_status expected_status,
296                           const std::vector<SubdocMultiLookupResult>& expected_results) {
297    std::vector<char> payload = cmd.encode();
298    safe_send(payload.data(), payload.size(), false);
299
300    return recv_subdoc_response(PROTOCOL_BINARY_CMD_SUBDOC_MULTI_LOOKUP,
301                                expected_status, expected_results);
302}
303
304// Overload for multi-mutation commands.
305uint64_t expect_subdoc_cmd(const SubdocMultiMutationCmd& cmd,
306                           protocol_binary_response_status expected_status,
307                           const std::vector<SubdocMultiMutationResult>& expected_results) {
308    std::vector<char> payload = cmd.encode();
309    safe_send(payload.data(), payload.size(), false);
310
311    return recv_subdoc_response(cmd.command, expected_status,
312                                expected_results);
313}
314
315void store_object(const std::string& key,
316                  const std::string& value,
317                  bool JSON, bool compress) {
318    const char* payload = value.c_str();
319    size_t payload_len = value.size();
320    char* deflated = NULL;
321    if (compress) {
322        payload_len = compress_document(payload, payload_len, &deflated);
323        payload = deflated;
324    }
325
326    set_datatype_feature(true);
327    store_object_w_datatype(key.c_str(), payload, payload_len, compress, JSON);
328    set_datatype_feature(false);
329    if (compress) {
330        cb_free(deflated);
331    }
332}
333
334// Ensure the execution of the operation is successful
335#define EXPECT_SD_OK(cmd) EXPECT_PRED_FORMAT1(subdoc_pred_ok, cmd)
336#define ASSERT_SD_OK(cmd) ASSERT_PRED_FORMAT1(subdoc_pred_ok, cmd)
337
338// Ensure the execution of the operation returns an error
339#define EXPECT_SD_ERR(cmd, err) EXPECT_PRED_FORMAT2(subdoc_pred_errcode, cmd, err)
340
341// Ensure the returned value is equal to val
342#define EXPECT_SD_VALEQ(cmd, val) EXPECT_PRED_FORMAT2(subdoc_pred_value, cmd, val)
343#define ASSERT_SD_VALEQ(cmd, val) ASSERT_PRED_FORMAT2(subdoc_pred_value, cmd, val)
344
345// Ensure the path p in the document k is equal to v
346#define EXPECT_SD_GET(k, p, v) EXPECT_SD_VALEQ(\
347    BinprotSubdocCommand().setOp(PROTOCOL_BINARY_CMD_SUBDOC_GET).setKey(k).setPath(p), v)
348
349// Ensure that the given error AND value are returned
350#define EXPECT_SUBDOC_CMD(cmd, err, val) EXPECT_PRED_FORMAT3(subdoc_pred_compat, cmd, err, val)
351
352// Ensure the given error and value are returned. resp is used as an 'out'
353// value.
354#define EXPECT_SUBDOC_CMD_RESP(cmd, err, val, resp) EXPECT_PRED_FORMAT4(subdoc_pred_full, cmd, err, val, resp)
355
356// Non JSON document, optionally compressed. Subdoc commands should fail.
357void test_subdoc_get_binary(bool compress,
358                            protocol_binary_command cmd,
359                            MemcachedConnection& conn) {
360    const char not_JSON[] = "not; json";
361    store_object("binary", not_JSON);
362
363    unique_cJSON_ptr stats(cJSON_Parse(
364            cJSON_GetObjectItem(conn.stats("responses detailed").get(),
365                                "responses")
366                    ->valuestring));
367    int notJsonCount = cJSON_GetObjectItem(stats.get(), "c6")->valueint;
368    // a). Check that access fails with DOC_NOTJSON
369    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "binary", "[0]"), PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON);
370
371    unique_cJSON_ptr statsNew(cJSON_Parse(
372            cJSON_GetObjectItem(conn.stats("responses").get(), "responses")
373                    ->valuestring));
374    EXPECT_EQ(notJsonCount + 1,
375              cJSON_GetObjectItem(statsNew.get(), "c6")->valueint);
376
377    delete_object("binary");
378}
379
380TEST_P(McdTestappTest, SubdocGet_BinaryRaw) {
381    test_subdoc_get_binary(/*compress*/ false,
382                           PROTOCOL_BINARY_CMD_SUBDOC_GET,
383                           getConnection());
384}
385TEST_P(McdTestappTest, SubdocGet_BinaryCompressed) {
386    TESTAPP_SKIP_IF_NO_COMPRESSION();
387    test_subdoc_get_binary(
388            /*compress*/ true, PROTOCOL_BINARY_CMD_SUBDOC_GET, getConnection());
389}
390
391TEST_P(McdTestappTest, SubdocExists_BinaryRaw) {
392    test_subdoc_get_binary(/*compress*/ false,
393                           PROTOCOL_BINARY_CMD_SUBDOC_EXISTS,
394                           getConnection());
395}
396TEST_P(McdTestappTest, SubdocExists_BinaryCompressed) {
397    TESTAPP_SKIP_IF_NO_COMPRESSION();
398    test_subdoc_get_binary(/*compress*/ true,
399                           PROTOCOL_BINARY_CMD_SUBDOC_EXISTS,
400                           getConnection());
401}
402
403// retrieve from a JSON document consisting of a toplevel array.
404void test_subdoc_fetch_array_simple(bool compressed, protocol_binary_command cmd) {
405
406    ASSERT_TRUE((cmd == PROTOCOL_BINARY_CMD_SUBDOC_GET) ||
407                (cmd == PROTOCOL_BINARY_CMD_SUBDOC_EXISTS));
408
409    const char array[] = "[ 0, \"one\", 2.0 ]";
410    store_object("array", array, /*JSON*/true, compressed);
411
412    // a). Check successful access to each array element.
413    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "array", "[0]"), "0");
414    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "array", "[1]"), "\"one\"");
415    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "array", "[2]"), "2.0");
416
417    // b). Check successful access to last element (using -1).
418    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "array", "[-1]"), "2.0");
419
420    // c). Check -2 treated as invalid index (only -1 permitted).
421    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array", "[-2]"),
422        PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL);
423    reconnect_to_server();
424
425    // d). Check failure accessing out-of-range index.
426    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array", "[3]"),
427        PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
428    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array", "[9999]"),
429                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
430
431    // e). Check failure accessing array as dict.
432    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array", "missing_key"),
433                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
434    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array", "[2].nothing_here"),
435                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
436
437    // f). Check path longer than SUBDOC_PATH_MAX_LENGTH is invalid.
438    std::string too_long_path(1024 + 1, '.');
439    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array", too_long_path),
440                  PROTOCOL_BINARY_RESPONSE_EINVAL);
441    reconnect_to_server();
442
443    // g). Check that incorrect flags (i.e. non-zero) is invalid.
444    EXPECT_SD_ERR(BinprotSubdocCommand(cmd,
445                                       "array",
446                                       "[0]",
447                                       "",
448                                       SUBDOC_FLAG_MKDIR_P),
449                  PROTOCOL_BINARY_RESPONSE_EINVAL);
450    reconnect_to_server();
451
452    delete_object("array");
453}
454
455TEST_P(McdTestappTest, SubdocGet_ArraySimpleRaw) {
456    test_subdoc_fetch_array_simple(/*compressed*/false,
457                                   PROTOCOL_BINARY_CMD_SUBDOC_GET);
458}
459TEST_P(McdTestappTest, SubdocGet_ArraySimpleCompressed) {
460    TESTAPP_SKIP_IF_NO_COMPRESSION();
461    test_subdoc_fetch_array_simple(/*compressed*/true,
462                                   PROTOCOL_BINARY_CMD_SUBDOC_GET);
463}
464
465TEST_P(McdTestappTest, SubdocExists_ArraySimpleRaw) {
466    test_subdoc_fetch_array_simple(/*compressed*/false,
467                                   PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
468}
469TEST_P(McdTestappTest, SubdocExists_ArraySimpleCompressed) {
470    TESTAPP_SKIP_IF_NO_COMPRESSION();
471    test_subdoc_fetch_array_simple(/*compressed*/true,
472                                   PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
473}
474
475// JSON document containing toplevel dict.
476void test_subdoc_fetch_dict_simple(bool compressed,
477                                   protocol_binary_command cmd) {
478
479    ASSERT_TRUE((cmd == PROTOCOL_BINARY_CMD_SUBDOC_GET) ||
480                (cmd == PROTOCOL_BINARY_CMD_SUBDOC_EXISTS));
481
482    const char dict[] = "{ \"int\": 1,"
483                        "  \"string\": \"two\","
484                        "  \"true\": true,"
485                        "  \"false\": false }";
486    store_object("dict", dict, /*JSON*/true, compressed);
487
488    // a). Check successful access to each dict element.
489    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict", "int"), "1");
490    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict", "string"), "\"two\"");
491    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict", "true"), "true");
492    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict", "false"), "false");
493
494    // b). Check failure accessing non-existent keys.
495    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", "missing_key"),
496                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
497
498    // c). Check failure accessing object incorrectly (wrong type).
499    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", "[0]"),
500                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
501    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", "[-1]"),
502                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
503    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", "int.nothing_here"),
504                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
505
506    delete_object("dict");
507}
508
509TEST_P(McdTestappTest, SubdocGet_DictSimpleRaw) {
510    test_subdoc_fetch_dict_simple(/*compressed*/false,
511                                  PROTOCOL_BINARY_CMD_SUBDOC_GET);
512}
513TEST_P(McdTestappTest, SubdocGet_DictSimpleCompressed) {
514    TESTAPP_SKIP_IF_NO_COMPRESSION();
515    test_subdoc_fetch_dict_simple(/*compressed*/true,
516                                  PROTOCOL_BINARY_CMD_SUBDOC_GET);
517}
518
519TEST_P(McdTestappTest, SubdocExists_DictSimpleRaw) {
520    test_subdoc_fetch_dict_simple(/*compressed*/false,
521                                  PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
522}
523TEST_P(McdTestappTest, SubdocExists_DictSimpleCompressed) {
524    TESTAPP_SKIP_IF_NO_COMPRESSION();
525    test_subdoc_fetch_dict_simple(/*compressed*/true,
526                                  PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
527}
528
529// JSON document containing nested dictionary.
530void test_subdoc_fetch_dict_nested(bool compressed,
531                                   protocol_binary_command cmd) {
532
533    ASSERT_TRUE((cmd == PROTOCOL_BINARY_CMD_SUBDOC_GET) ||
534                (cmd == PROTOCOL_BINARY_CMD_SUBDOC_EXISTS));
535
536    // Getting a bit complex to do raw (with all the quote escaping so use
537    // cJSON API.
538    unique_cJSON_ptr dict(cJSON_CreateObject());
539    cJSON* name = cJSON_CreateObject();
540    cJSON_AddStringToObject(name, "title", "Mr");
541    cJSON_AddStringToObject(name, "first", "Joseph");
542    cJSON_AddStringToObject(name, "last", "Bloggs");
543    cJSON_AddItemToObject(dict.get(), "name", name);
544
545    cJSON* orders = cJSON_CreateArray();
546    for (int i = 0; i < 10; i++) {
547        cJSON* order = cJSON_CreateObject();
548        std::string order_name("order_" + std::to_string(i));
549        cJSON_AddStringToObject(order, "date", "2020-04-04T18:17:04Z");
550        cJSON_AddNumberToObject(order, "count", i * 3);
551        std::string desc("Cool project #" + std::to_string(i));
552        cJSON_AddStringToObject(order, "description", desc.c_str());
553        cJSON_AddItemToArray(orders, order);
554    }
555    cJSON_AddItemToObject(dict.get(), "orders", orders);
556
557    char* dict_str = cJSON_PrintUnformatted(dict.get());
558
559    // Store to Couchbase, optionally compressing first.
560    store_object("dict2", dict_str, /*JSON*/true, compressed);
561    cJSON_Free(dict_str);
562
563    // a). Check successful access to individual nested components.
564    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict2", "name.title"), "\"Mr\"");
565    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict2", "name.first"), "\"Joseph\"");
566    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict2", "name.last"), "\"Bloggs\"");
567
568    // b). Check successful access to a whole sub-dictionary.
569    char* name_str = cJSON_PrintUnformatted(name);
570    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict2", "name"), name_str);
571    cJSON_Free(name_str);
572
573    // c). Check successful access to a whole sub-array.
574    char* orders_str = cJSON_PrintUnformatted(orders);
575    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict2", "orders"), orders_str);
576    cJSON_Free(orders_str);
577
578    // d). Check access to dict in array.
579    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "dict2", "orders[0].date"),
580                    "\"2020-04-04T18:17:04Z\"");
581
582    delete_object("dict2");
583}
584
585TEST_P(McdTestappTest, SubdocGet_DictNestedRaw) {
586    test_subdoc_fetch_dict_nested(/*compressed*/false,
587                                  PROTOCOL_BINARY_CMD_SUBDOC_GET);
588}
589TEST_P(McdTestappTest, SubdocGet_DictNestedCompressed) {
590    TESTAPP_SKIP_IF_NO_COMPRESSION();
591    test_subdoc_fetch_dict_nested(/*compressed*/true,
592                                  PROTOCOL_BINARY_CMD_SUBDOC_GET);
593}
594TEST_P(McdTestappTest, SubdocExists_DictNestedRaw) {
595    test_subdoc_fetch_dict_nested(/*compressed*/false,
596                                  PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
597}
598TEST_P(McdTestappTest, SubdocExists_DictNestedCompressed) {
599    TESTAPP_SKIP_IF_NO_COMPRESSION();
600    test_subdoc_fetch_dict_nested(/*compressed*/true,
601                                  PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
602}
603
604// Creates a nested dictionary with the specified number of levels.
605// Caller is responsible for calling cJSON_Free() on the result when finished
606//with.
607static cJSON* make_nested_dict(int nlevels) {
608    cJSON* child = cJSON_CreateObject();
609    cJSON* parent = nullptr;
610    for (int depth = nlevels-1; depth > 0; depth--) {
611        std::string name(std::to_string(depth));
612        parent = cJSON_CreateObject();
613        cJSON_AddItemToObject(parent, name.c_str(), child);
614        child = parent;
615    }
616    return parent;
617}
618
619// Deeply nested JSON dictionary; verify limits on how deep documents can be.
620void test_subdoc_fetch_dict_deep(protocol_binary_command cmd) {
621
622    // a). Should be able to access a deeply nested document as long as the
623    // path we ask for is no longer than MAX_SUBDOC_PATH_COMPONENTS.
624    unique_cJSON_ptr max_dict(make_nested_dict(MAX_SUBDOC_PATH_COMPONENTS));
625    char* max_dict_str = cJSON_PrintUnformatted(max_dict.get());
626    store_object("max_dict", max_dict_str);
627
628    cJSON_Free(max_dict_str);
629
630    std::string valid_max_path(std::to_string(1));
631    for (int depth = 2; depth < MAX_SUBDOC_PATH_COMPONENTS; depth++) {
632        valid_max_path += std::string(".") + std::to_string(depth);
633    }
634    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "max_dict", valid_max_path), "{}");
635
636    delete_object("max_dict");
637
638    // b). Accessing a deeper document should fail.
639    unique_cJSON_ptr too_deep_dict(make_nested_dict(MAX_SUBDOC_PATH_COMPONENTS + 1));
640    char* too_deep_dict_str = cJSON_PrintUnformatted(too_deep_dict.get());
641    store_object("too_deep_dict", too_deep_dict_str);
642    cJSON_Free(too_deep_dict_str);
643
644    std::string too_long_path(std::to_string(1));
645    for (int depth = 2; depth < MAX_SUBDOC_PATH_COMPONENTS + 1; depth++) {
646        too_long_path += std::string(".") + std::to_string(depth);
647    }
648    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "too_deep_dict", too_long_path),
649                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG);
650
651    delete_object("too_deep_dict");
652}
653
654TEST_P(McdTestappTest, SubdocGet_DictDeep) {
655    test_subdoc_fetch_dict_deep(PROTOCOL_BINARY_CMD_SUBDOC_GET);
656}
657TEST_P(McdTestappTest, SubdocExists_DictDeep) {
658    test_subdoc_fetch_dict_deep(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
659}
660
661// Creates a nested array with the specified number of levels.
662// Caller is responsible for calling cJSON_Free() on the result when finished
663//with.
664static cJSON* make_nested_array(int nlevels) {
665    cJSON* child = cJSON_CreateArray();
666    cJSON* parent = nullptr;
667    for (int depth = nlevels-1; depth > 0; depth--) {
668        parent = cJSON_CreateArray();
669        cJSON_AddItemToArray(parent, child);
670        child = parent;
671    }
672    return parent;
673}
674
675std::string make_nested_array_path(int nlevels) {
676    std::string path;
677    for (int depth = 1; depth < nlevels; depth++) {
678        path += "[0]";
679    }
680    return path;
681}
682
683// Deeply nested JSON array; verify limits on how deep documents can be.
684void test_subdoc_fetch_array_deep(protocol_binary_command cmd) {
685
686    // a). Should be able to access a deeply nested document as long as the
687    // path we ask for is no longer than MAX_SUBDOC_PATH_COMPONENTS.
688
689    unique_cJSON_ptr max_array(make_nested_array(MAX_SUBDOC_PATH_COMPONENTS));
690    char* max_array_str = cJSON_PrintUnformatted(max_array.get());
691    store_object("max_array", max_array_str);
692    cJSON_Free(max_array_str);
693
694    std::string valid_max_path(make_nested_array_path(MAX_SUBDOC_PATH_COMPONENTS));
695
696    EXPECT_SD_VALEQ(BinprotSubdocCommand(cmd, "max_array", valid_max_path), "[]");
697    delete_object("max_array");
698
699    // b). Accessing a deeper array should fail.
700    unique_cJSON_ptr too_deep_array(make_nested_array(MAX_SUBDOC_PATH_COMPONENTS + 1));
701    char* too_deep_array_str = cJSON_PrintUnformatted(too_deep_array.get());
702    store_object("too_deep_array", too_deep_array_str);
703    cJSON_Free(too_deep_array_str);
704
705    std::string too_long_path(make_nested_array_path(MAX_SUBDOC_PATH_COMPONENTS + 1));
706
707    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "too_deep_array", too_long_path),
708                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_E2BIG);
709    delete_object("too_deep_array");
710}
711
712TEST_P(McdTestappTest, SubdocGet_ArrayDeep) {
713    test_subdoc_fetch_array_deep(PROTOCOL_BINARY_CMD_SUBDOC_GET);
714}
715TEST_P(McdTestappTest, SubdocExists_ArrayDeep) {
716    test_subdoc_fetch_array_deep(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS);
717}
718
719/* Test adding to a JSON dictionary.
720 * @param compress If true operate on compressed JSON documents.
721 * @param cmd The binary protocol command to test. Permitted values are:
722 *            - PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD
723 *            - PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT
724 */
725void test_subdoc_dict_add_simple(bool compress, protocol_binary_command cmd) {
726    ASSERT_TRUE((cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD) ||
727                (cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT));
728
729    const std::vector<std::pair<std::string, std::string>> key_vals({
730            {"int", "2"},
731            {"float", "2.0"},
732            {"object", "{ \"foo\": \"bar\" }"},
733            {"array", "[ \"a\", \"b\", \"c\"]"},
734            {"true", "true"},
735            {"false", "false"},
736            {"null", "null"}});
737
738    // a). Attempt to add to non-existent document should fail.
739    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", "int", "2"),
740                      PROTOCOL_BINARY_RESPONSE_KEY_ENOENT);
741
742    // b). Attempt to add to non-JSON document should return ENOT_JSON
743    const char not_JSON[] = "not; valid, JSON";
744    store_object("binary", not_JSON, /*JSON*/false, compress);
745    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "binary", "int", "2"),
746                      PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON);
747    delete_object("binary");
748
749    // Store a simple JSON document to work on.
750    const char dict[] = "{ \"key1\": 1 }";
751    store_object("dict", dict, /*JSON*/true, compress);
752
753    // c). Addition of primitive types to the dict.
754    for (const auto& kv : key_vals) {
755        EXPECT_SD_OK(BinprotSubdocCommand(cmd, "dict", kv.first, kv.second));
756        EXPECT_SD_GET("dict", kv.first, kv.second);
757    }
758
759    // d). Check that attempts to add keys which already exist fail for DICT_ADD,
760    // and are permitted for DICT_UPSERT.
761    for (const auto& kv : key_vals) {
762        BinprotSubdocCommand sd_cmd(cmd, "dict", kv.first, kv.second);
763        if (cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD) {
764            EXPECT_SD_ERR(sd_cmd, PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS);
765        } else { // DICT_UPSERT
766            EXPECT_SD_OK(sd_cmd);
767            EXPECT_SD_GET("dict", kv.first, kv.second);
768        }
769    }
770
771    // e). Check that attempts to add keys with a missing intermediate
772    // dict path fail.
773    for (const auto& kv : key_vals) {
774        auto key = "intermediate." + kv.first;
775        EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", key, kv.second),
776                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
777    }
778
779    // f). Check that attempts to add keys with missing intermediate
780    // array path fail.
781    for (const auto& kv : key_vals) {
782        auto key = "intermediate_array[0]." + kv.first;
783        EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", key, kv.second),
784                          PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
785    }
786
787    // g). ... and they still fail even if MKDIR_P flag is specified (as
788    // intermediate array paths are never automatically created).
789    for (const auto& kv : key_vals) {
790        auto key = "intermediate_array[0]." + kv.first;
791        EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", key, kv.second, SUBDOC_FLAG_MKDIR_P),
792                          PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
793    }
794
795    // h) However attempts to add keys with _dict_ intermediate paths should
796    // succeed if the MKDIR_P flag is set.
797    for (const auto& kv : key_vals) {
798        auto key = "intermediate." + kv.first;
799        EXPECT_SD_OK(BinprotSubdocCommand(cmd, "dict", key, kv.second, SUBDOC_FLAG_MKDIR_P));
800        EXPECT_SD_GET("dict", key, kv.second);
801    }
802
803    // i). Check that attempts to add various invalid JSON fragments all fail.
804    const std::vector<std::pair<std::string, std::string>> invalid_key_vals({
805            {"bad_int", "\"2"},
806            {"bad_int2", "2a"},
807            {"bad_int3", "0x2"},
808            {"bad_int4", "2."},
809            {"bad_float", "2.0a"},
810            {"bad_float2", "2.0.0"},
811            {"bad_object", "{ \"foo\": }"},
812            {"bad_array", "[ \"a\" "},
813            {"bad_array2", "[ \"a\" }"},
814            {"bad_array3", "[ \"a\", }"},
815            {"bad_true", "TRUE"},
816            {"bad_false", "FALSE"},
817            {"bad_null", "nul"},
818    });
819    for (const auto& kv : invalid_key_vals) {
820        EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", kv.first, kv.second),
821                      PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT);
822    }
823
824    // j). Check CAS support - cmd with correct CAS should succeed.
825    // Get the current CAS.
826    BinprotSubdocResponse resp;
827    EXPECT_SUBDOC_CMD_RESP(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, "dict", "int"),
828        PROTOCOL_BINARY_RESPONSE_SUCCESS, "", resp);
829    uint64_t cas = resp.getCas();
830    EXPECT_NE(0, cas);
831    EXPECT_SUBDOC_CMD_RESP(
832            BinprotSubdocCommand(cmd,
833                                 "dict",
834                                 "new_int",
835                                 "3",
836                                 SUBDOC_FLAG_NONE,
837                                 mcbp::subdoc::doc_flag::None,
838                                 cas),
839            PROTOCOL_BINARY_RESPONSE_SUCCESS,
840            "",
841            resp);
842
843    uint64_t new_cas = resp.getCas();
844    EXPECT_NE(cas, new_cas);
845
846    // k). CAS - cmd with old cas should fail.
847    EXPECT_SD_ERR(BinprotSubdocCommand(cmd,
848                                       "dict",
849                                       "new_int2",
850                                       "4",
851                                       SUBDOC_FLAG_NONE,
852                                       mcbp::subdoc::doc_flag::None,
853                                       cas),
854                  PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
855
856    // l). CAS - manually corrupted (off by one) cas should fail.
857    EXPECT_SD_ERR(BinprotSubdocCommand(cmd,
858                                       "dict",
859                                       "new_int2",
860                                       "4",
861                                       SUBDOC_FLAG_NONE,
862                                       mcbp::subdoc::doc_flag::None,
863                                       new_cas + 1),
864                  PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
865
866    delete_object("dict");
867
868    // m). Attempt to perform dict command on array should fail.
869    store_object("array", "[1,2]", /*JSON*/true, compress);
870    EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "array","foo", "\"bar\""),
871                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
872    delete_object("array");
873
874    // n). Check that attempts to add keys to a valid JSON fragment which is
875    // not in a container fail. (We cannot operate on non-dict or array JSON
876    // objects).
877    store_object("dict", "\"string\"", /*JSON*/true, compress);
878    for (const auto& kv : key_vals) {
879        EXPECT_SD_ERR(BinprotSubdocCommand(cmd, "dict", kv.first, kv.second),
880                      PROTOCOL_BINARY_RESPONSE_SUBDOC_DOC_NOTJSON);
881    }
882    delete_object("dict");
883}
884
885TEST_P(McdTestappTest, SubdocDictAdd_SimpleRaw) {
886    test_subdoc_dict_add_simple(/*compress*/false,
887                                PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD);
888}
889
890TEST_P(McdTestappTest, SubdocDictAdd_SimpleCompressed) {
891    TESTAPP_SKIP_IF_NO_COMPRESSION();
892    test_subdoc_dict_add_simple(/*compress*/true,
893                                PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD);
894}
895
896TEST_P(McdTestappTest, SubdocDictUpsert_SimpleRaw) {
897    test_subdoc_dict_add_simple(/*compress*/false,
898                                PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT);
899}
900
901TEST_P(McdTestappTest, SubdocDictUpsert_SimpleCompressed) {
902    TESTAPP_SKIP_IF_NO_COMPRESSION();
903    test_subdoc_dict_add_simple(/*compress*/true,
904                                PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT);
905}
906
907// Test FEATURE_MUTATION_SEQNO support.
908TEST_P(McdTestappTest, SubdocDictAdd_SimpleRaw_MutationSeqno) {
909    set_mutation_seqno_feature(true);
910    test_subdoc_dict_add_simple(/*compress*/false,
911                                PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD);
912    set_mutation_seqno_feature(false);
913}
914
915void McdTestappTest::test_subdoc_dict_add_cas(bool compress,
916                                              protocol_binary_command cmd) {
917    ASSERT_TRUE((cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD) ||
918                (cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT));
919
920    // Store a simple JSON document to work on.
921    store_object("dict", "{}", /*JSON*/true, compress);
922
923    // a). Check that a CAS mismatch internally (between reading the JSON
924    // (doc to operate on and storing it), is correctly retried.
925    // (Note: the auto-retry only occurs when there is no CAS specified by the
926    // user).
927
928    // Configure the ewouldblock_engine to inject fake CAS failure for the
929    // 3rd call (i.e. the 1st engine->store() attempt). We only expect 6 calls
930    // total, so also make anything after that fail.
931    ewouldblock_engine_configure(ENGINE_KEY_EEXISTS, EWBEngineMode::Sequence,
932                                 0xffffffc4 /* <3 MSBytes all-ones>, 0b11,000,100 */);
933
934    // .. Yet a client request should succeed, as internal CAS failure should
935    // be retried.
936    BinprotSubdocResponse resp;
937    EXPECT_SUBDOC_CMD_RESP(BinprotSubdocCommand(cmd, "dict","new_int3", "3"),
938        PROTOCOL_BINARY_RESPONSE_SUCCESS, "", resp);
939    uint64_t new_cas = resp.getCas();
940
941    // b). Check that if the user specifies an explicit CAS, then a mismatch
942    // isn't retried and EEXISTS is returned back to the user.
943
944    // Setup ewouldblock_engine - first two calls succeed, 3rd (engine->store)
945    // fails. Do not expect more than 3 calls so make any further calls error.
946    ewouldblock_engine_configure(ENGINE_KEY_EEXISTS, EWBEngineMode::Sequence,
947                                 0xfffffffc /* <3 MSBytes all-ones>, 0b11,111,100 */);
948
949    EXPECT_SD_ERR(BinprotSubdocCommand(cmd,
950                                       "dict",
951                                       "new_int4",
952                                       "4",
953                                       SUBDOC_FLAG_NONE,
954                                       mcbp::subdoc::doc_flag::None,
955                                       new_cas),
956                  PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
957
958    // Cleanup.
959    ewouldblock_engine_disable();
960    delete_object("dict");
961}
962
963TEST_P(McdTestappTest, SubdocDictAdd_CasRaw) {
964    test_subdoc_dict_add_cas(/*compress*/false,
965                             PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD);
966}
967TEST_P(McdTestappTest, SubdocDictAdd_CasCompressed) {
968    TESTAPP_SKIP_IF_NO_COMPRESSION();
969    test_subdoc_dict_add_cas(/*compress*/true,
970                             PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD);
971}
972TEST_P(McdTestappTest, SubdocDictUpsert_CasRaw) {
973    test_subdoc_dict_add_cas(/*compress*/false,
974                             PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT);
975}
976TEST_P(McdTestappTest, SubdocDictUpsert_CasCompressed) {
977    TESTAPP_SKIP_IF_NO_COMPRESSION();
978    test_subdoc_dict_add_cas(/*compress*/true,
979                             PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT);
980}
981
982TEST_P(McdTestappTest, SubdocAddFlag_BucketStoreCas) {
983    // Check that if an item is set by another client after the fetch but
984    // before the store then we return EEXISTS.
985
986    // Setup ewouldblock_engine - first two calls succeed, 3rd (engine->store)
987    // fails. Any further calls should return EEXISTS.
988    ewouldblock_engine_configure(
989            ENGINE_KEY_EEXISTS,
990            EWBEngineMode::Sequence,
991            0xfffffffc /* <3 MSBytes all-ones>, 0b11,111,100 */);
992    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT,
993                                       "AddCasTest",
994                                       "element",
995                                       "4",
996                                       SUBDOC_FLAG_NONE,
997                                       mcbp::subdoc::doc_flag::Add,
998                                       0),
999                  PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
1000}
1001
1002void test_subdoc_dict_add_upsert_deep(protocol_binary_command cmd) {
1003
1004    ASSERT_TRUE((cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD) ||
1005                (cmd == PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT));
1006
1007    // a). Check that we can add elements to a document at the maximum nested
1008    // level.
1009    unique_cJSON_ptr one_less_max_dict(
1010            make_nested_dict(MAX_SUBDOC_PATH_COMPONENTS - 1));
1011    char* one_less_max_dict_str = cJSON_PrintUnformatted(one_less_max_dict.get());
1012    store_object("dict", one_less_max_dict_str);
1013    cJSON_Free(one_less_max_dict_str);
1014
1015    std::string one_less_max_path(std::to_string(1));
1016    for (int depth = 2; depth < MAX_SUBDOC_PATH_COMPONENTS - 1; depth++) {
1017        one_less_max_path += std::string(".") + std::to_string(depth);
1018    }
1019    // Check precondition - should have an empty dict we can access.
1020    EXPECT_SD_GET("dict", one_less_max_path, "{}");
1021
1022    // a). Check we can add primitive elements to this path.
1023    const std::vector<std::pair<std::string, std::string>> primitive_key_vals({
1024            {"int", "2"},
1025            {"float", "2.0"},
1026            {"true", "true"},
1027            {"false", "false"},
1028            {"null", "null"}});
1029    for (const auto& kv : primitive_key_vals) {
1030        const auto key = one_less_max_path + "." + kv.first;
1031        EXPECT_SD_OK(BinprotSubdocCommand(cmd, "dict", key, kv.second));
1032        EXPECT_SD_GET("dict", key, kv.second);
1033    }
1034
1035    delete_object("dict");
1036}
1037
1038TEST_P(McdTestappTest, SubdocDictAdd_Deep) {
1039    test_subdoc_dict_add_upsert_deep(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD);
1040}
1041
1042TEST_P(McdTestappTest, SubdocDictUpsert_Deep) {
1043    test_subdoc_dict_add_upsert_deep(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT);
1044}
1045
1046void test_subdoc_delete_simple(bool compress) {
1047
1048    // a). Create a document containing each of the primitive types, and then
1049    // ensure we can successfully delete each type.
1050    const char dict[] = "{"
1051            "\"0\": 1,"
1052            "\"1\": 2.0,"
1053            "\"2\": 3.141e3,"
1054            "\"3\": \"four\","
1055            "\"4\": {\"foo\": \"bar\"},"
1056            "\"5\": [1, 1, 1, 1],"
1057            "\"6\": true,"
1058            "\"7\": false"
1059            "}";
1060    store_object("dict", dict, /*JSON*/true, compress);
1061
1062    // Attempts to delete non-existent elements should fail.
1063    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "dict", "bad_key"),
1064                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
1065
1066    for (unsigned int ii = 0; ii < 8; ii++) {
1067        // Assert we can access it initially:
1068        std::string path(std::to_string(ii));
1069        BinprotSubdocResponse resp;
1070        EXPECT_SUBDOC_CMD_RESP(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, "dict", path),
1071                                         PROTOCOL_BINARY_RESPONSE_SUCCESS, "",
1072                                         resp);
1073        uint64_t cas = resp.getCas();
1074
1075        // Deleting with the wrong CAS should fail:
1076        EXPECT_SD_ERR(
1077                BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE,
1078                                     "dict",
1079                                     path,
1080                                     "",
1081                                     SUBDOC_FLAG_NONE,
1082                                     mcbp::subdoc::doc_flag::None,
1083                                     cas + 1),
1084                PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
1085
1086        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, "dict",path));
1087
1088        // Should be able to delete with no CAS specified.
1089        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "dict", path));
1090
1091        // ... and should no longer exist:
1092        EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, "dict", path),
1093                          PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
1094    }
1095
1096    // After deleting everything the dictionary should be empty.
1097    validate_object("dict", "{}");
1098    delete_object("dict");
1099}
1100
1101TEST_P(McdTestappTest, SubdocDelete_SimpleRaw) {
1102    test_subdoc_delete_simple(/*compress*/false);
1103}
1104
1105TEST_P(McdTestappTest, SubdocDelete_SimpleCompressed) {
1106    TESTAPP_SKIP_IF_NO_COMPRESSION();
1107    test_subdoc_delete_simple(/*compress*/true);
1108}
1109
1110TEST_P(McdTestappTest, SubdocDelete_Array) {
1111
1112    // Create an array, then test deleting elements.
1113    store_object("a", "[0,1,2,3,4]", /*JSON*/true, /*compress*/false);
1114
1115    // Sanity check - 3rd element should be 2
1116    EXPECT_SD_GET("a", "[2]", "2");
1117
1118    // a). Attempts to delete out of range elements should fail.
1119    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "a", "[5]"),
1120                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
1121
1122    // b). Test deleting at end of array.
1123    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "a", "[4]"));
1124    //     3rd element should still be 2; last element should now be 3.
1125    EXPECT_SD_GET("a", "[2]", "2");
1126    EXPECT_SD_GET("a", "[-1]", "3");
1127    validate_object("a", "[0,1,2,3]");
1128
1129    // c). Test deleting at start of array; elements are shuffled down.
1130    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "a", "[0]"));
1131    //     3rd element should now be 3; last element should still be 3.
1132    EXPECT_SD_GET("a", "[2]", "3");
1133    EXPECT_SD_GET("a", "[-1]", "3");
1134    validate_object("a", "[1,2,3]");
1135
1136    // d). Test deleting of last element using [-1].
1137    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "a", "[-1]"));
1138    //     Last element should now be 2.
1139    EXPECT_SD_GET("a", "[-1]", "2");
1140    validate_object("a", "[1,2]");
1141
1142    // e). Delete remaining elements.
1143    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "a", "[0]"));
1144    validate_object("a", "[2]");
1145    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "a", "[0]"));
1146    // Should have an empty array.
1147    validate_object("a", "[]");
1148
1149    delete_object("a");
1150}
1151
1152TEST_P(McdTestappTest, SubdocDelete_ArrayNested) {
1153    // Nested array containing different objects.
1154    store_object("b", "[0,[10,20,[100]],{\"key\":\"value\"}]",
1155                 /*JSON*/true, /*compress*/false);
1156
1157    // Sanity check - 2nd element should be "[10,20,[100]]"
1158    EXPECT_SD_GET("b", "[1]", "[10,20,[100]]");
1159
1160    // a). Delete nested array element
1161    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "b", "[1][2][0]"));
1162    EXPECT_SD_GET("b", "[1]", "[10,20,[]]");
1163
1164    // b). Delete the (now empty) nested array.
1165    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "b", "[1][2]"));
1166    EXPECT_SD_GET("b", "[1]", "[10,20]");
1167
1168    // c). Delete the next level up array.
1169    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "b", "[1]"));
1170    // element [1] should now be the dict.
1171    EXPECT_SD_GET("b", "[1]", "{\"key\":\"value\"}");
1172
1173    delete_object("b");
1174}
1175
1176const std::vector<std::string> JSON_VALUES({
1177    "1.1",
1178    "\"value\"",
1179    "{\"inner\":\"dict\"}",
1180    "[1,2]",
1181    "true",
1182    "false",
1183    "null"});
1184
1185TEST_P(McdTestappTest, SubdocReplace_SimpleDict)
1186{
1187    // Simple dictionary, replace first element with various types.
1188    store_object("a", "{\"key\":0,\"key2\":1}", /*JSON*/true, /*compress*/false);
1189
1190    // Sanity check - 'key' should be "0"
1191    EXPECT_SD_GET("a", "key", "0");
1192
1193    // Replace the initial key with each primitive type:
1194    for (const auto& replace : JSON_VALUES) {
1195        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE,
1196                               "a", "key", replace));
1197        EXPECT_SD_GET("a", "key", replace);
1198    }
1199    // Sanity-check the final document
1200    validate_object("a", "{\"key\":null,\"key2\":1}");
1201
1202    delete_object("a");
1203}
1204
1205TEST_P(McdTestappTest, SubdocReplace_SimpleArray)
1206{
1207    // Simple array, replace first element with various types.
1208    store_object("a", "[0,1]", /*JSON*/true, /*compress*/false);
1209
1210    // Sanity check - [0] should be "0"
1211    EXPECT_SD_GET("a", "[0]", "0");
1212
1213    // Replace the first element with each primitive type:
1214    for (const auto& replace : JSON_VALUES) {
1215        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "a", "[0]",
1216                                    replace));
1217        EXPECT_SD_GET("a", "[0]", replace);
1218    }
1219    // Sanity-check the final document
1220    validate_object("a", "[null,1]");
1221
1222    delete_object("a");
1223}
1224
1225TEST_P(McdTestappTest, SubdocReplace_ArrayDeep)
1226{
1227    // Test replacing in deeply nested arrays.
1228
1229    // Create an array at one less than the maximum depth and an associated path.
1230    unique_cJSON_ptr one_less_max(make_nested_array(MAX_SUBDOC_PATH_COMPONENTS));
1231    char* one_less_max_str = cJSON_PrintUnformatted(one_less_max.get());
1232    store_object("a", one_less_max_str);
1233    cJSON_Free(one_less_max_str);
1234
1235    std::string valid_max_path(make_nested_array_path(MAX_SUBDOC_PATH_COMPONENTS));
1236    EXPECT_SD_GET("a", valid_max_path, "[]");
1237
1238    // a). Should be able to replace an element at the max depth.
1239    std::string new_value("\"deep\"");
1240    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "a",
1241        valid_max_path, new_value));
1242    EXPECT_SD_GET("a", valid_max_path, new_value);
1243
1244    // b). But adding a nested array (taking the document over the maximum
1245    // depth) should fail.
1246    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "a",
1247                           valid_max_path, "[0]"),
1248                  PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_ETOODEEP);
1249
1250
1251    // c). Replace the whole deep array with a single toplevel element.
1252    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "a", "[0]", "[]"));
1253    EXPECT_SD_GET("a", "[0]", "[]");
1254    delete_object("a");
1255}
1256
1257TEST_P(McdTestappTest, SubdocArrayPushLast_Simple)
1258{
1259    // a). Empty array, append to it.
1260    store_object("a", "[]", /*JSON*/true, /*compress*/false);
1261    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST)
1262        .setKey("a").setPath("").setValue("0"));
1263    EXPECT_SD_GET("a", "[0]", "0");
1264    validate_object("a", "[0]");
1265
1266    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST, "a",
1267                           "", "1"));
1268    EXPECT_SD_GET("a", "[1]", "1");
1269    validate_object("a", "[0,1]");
1270
1271    BinprotSubdocResponse resp;
1272    EXPECT_SUBDOC_CMD_RESP(
1273        BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST)
1274        .setKey("a").setValue("2"), PROTOCOL_BINARY_RESPONSE_SUCCESS, "", resp);
1275    EXPECT_SD_GET("a", "[2]", "2");
1276    validate_object("a", "[0,1,2]");
1277
1278    // b). Check that using the correct CAS succeeds.
1279    EXPECT_SD_OK(
1280            BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1281                                 "a",
1282                                 "",
1283                                 "3",
1284                                 SUBDOC_FLAG_NONE,
1285                                 mcbp::subdoc::doc_flag::None,
1286                                 resp.getCas()));
1287    EXPECT_SD_GET("a", "[3]", "3"),
1288    validate_object("a", "[0,1,2,3]");
1289
1290    // c). But using the wrong one fails.
1291    EXPECT_SD_ERR(
1292            BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1293                                 "a",
1294                                 "",
1295                                 "4",
1296                                 SUBDOC_FLAG_NONE,
1297                                 mcbp::subdoc::doc_flag::None,
1298                                 resp.getCas()),
1299            PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS);
1300    validate_object("a", "[0,1,2,3]");
1301    delete_object("a");
1302
1303    // d). Check various other object types append successfully.
1304    store_object("b", "[]", /*JSON*/true, /*compress*/false);
1305    int index = 0;
1306    for (const auto& value : JSON_VALUES) {
1307        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1308                                    "b", "", value));
1309        std::string path("[" + std::to_string(index) + "]");
1310        EXPECT_SD_GET("b", path, value);
1311        index++;
1312    }
1313    delete_object("b");
1314
1315    // e). Check we can append multiple values at once.
1316    store_object("c", "[]", /*JSON*/true, /*compress*/false);
1317    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1318                                "c", "", "0,1"));
1319    validate_object("c", "[0,1]");
1320    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1321                                "c", "", "\"two\",3.141,{\"four\":4}"));
1322    validate_object("c", "[0,1,\"two\",3.141,{\"four\":4}]");
1323
1324    delete_object("c");
1325
1326    // f). Check MKDIR_P flag works.
1327    store_object("d", "{}", /*JSON*/true, /*compress*/false);
1328    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1329                                "d", "foo", "0", SUBDOC_FLAG_MKDIR_P));
1330    delete_object("d");
1331}
1332
1333TEST_P(McdTestappTest, SubdocArrayPushLast_Nested)
1334{
1335    // Operations on a nested array,
1336    // a). Begin with an empty nested array, append to it.
1337    store_object("a", "[[]]", /*JSON*/true, /*compress*/false);
1338    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST, "a",
1339                 "", "1"));
1340    EXPECT_SD_GET("a", "[0]", "[]");
1341    EXPECT_SD_GET("a", "[1]", "1");
1342    validate_object("a", "[[],1]");
1343
1344    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST, "a",
1345                 "", "2"));
1346    EXPECT_SD_GET("a", "[2]", "2");
1347    validate_object("a", "[[],1,2]");
1348
1349    delete_object("a");
1350}
1351
1352TEST_P(McdTestappTest, SubdocArrayPushFirst_Simple)
1353{
1354    // a). Empty array, prepend to it.
1355    store_object("a", "[]", /*JSON*/true, /*compress*/false);
1356    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST, "a", "", "0"));
1357    EXPECT_SD_GET("a", "[0]", "0");
1358    validate_object("a", "[0]");
1359
1360    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST, "a", "", "1"));
1361    EXPECT_SD_GET("a", "[0]", "1");
1362    validate_object("a", "[1,0]");
1363
1364    BinprotSubdocResponse resp;
1365    EXPECT_SUBDOC_CMD_RESP(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1366                                               "a", "", "2"),
1367                                     PROTOCOL_BINARY_RESPONSE_SUCCESS, "", resp);
1368    EXPECT_SD_GET("a", "[0]", "2");
1369    validate_object("a", "[2,1,0]");
1370
1371    // b). Check that using the correct CAS succeeds.
1372    EXPECT_SD_OK(
1373            BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1374                                 "a",
1375                                 "",
1376                                 "3",
1377                                 SUBDOC_FLAG_NONE,
1378                                 mcbp::subdoc::doc_flag::None,
1379                                 resp.getCas()));
1380    EXPECT_SD_GET("a", "[0]", "3");
1381    validate_object("a", "[3,2,1,0]");
1382
1383    // c). But using the wrong one fails.
1384    EXPECT_SUBDOC_CMD(
1385            BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1386                                 "a",
1387                                 "",
1388                                 "4",
1389                                 SUBDOC_FLAG_NONE,
1390                                 mcbp::subdoc::doc_flag::None,
1391                                 resp.getCas()),
1392            PROTOCOL_BINARY_RESPONSE_KEY_EEXISTS,
1393            "");
1394    validate_object("a", "[3,2,1,0]");
1395    delete_object("a");
1396
1397    // d). Check various other object types prepend successfully.
1398    store_object("b", "[]", /*JSON*/true, /*compress*/false);
1399    for (const auto& value : JSON_VALUES) {
1400        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1401                                    "b", "", value));
1402        EXPECT_SD_GET("b", "[0]", value);
1403    }
1404    delete_object("b");
1405
1406    // e). Check we can prepend multiple values at once.
1407    store_object("c", "[]", /*JSON*/true, /*compress*/false);
1408    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1409                           "c", "", "0,1"));
1410    validate_object("c", "[0,1]");
1411    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1412                           "c", "", "\"two\",3.141,{\"four\":4}"));
1413    validate_object("c", "[\"two\",3.141,{\"four\":4},0,1]");
1414    delete_object("c");
1415
1416    // f). Check MKDIR_P flag works.
1417    store_object("d", "{}", /*JSON*/true, /*compress*/false);
1418    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1419                           "d", "foo", "0", SUBDOC_FLAG_MKDIR_P));
1420    delete_object("d");
1421}
1422
1423TEST_P(McdTestappTest, SubdocArrayPushFirst_Nested)
1424{
1425    // Operations on a nested array.
1426    // a). Begin with an empty nested array, prepend to it.
1427    store_object("a", "[[]]", /*JSON*/true, /*compress*/false);
1428    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST, "a",
1429                           "", "1"));
1430    EXPECT_SD_GET("a", "[0]", "1");
1431    EXPECT_SD_GET("a", "[1]", "[]");
1432    validate_object("a", "[1,[]]");
1433
1434    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1435                 "a","", "2"));
1436    EXPECT_SD_GET("a", "[0]", "2");
1437    validate_object("a", "[2,1,[]]");
1438
1439    delete_object("a");
1440}
1441
1442TEST_P(McdTestappTest, SubdocArrayAddUnique_Simple)
1443{
1444    // Start with an array with a single element.
1445    store_object("a", "[]", /*JSON*/true, /*compress*/false);
1446
1447    // a). Add an element which doesn't already exist.
1448    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1449                           "a", "", "0"));
1450    validate_object("a", "[0]");
1451
1452    // b). Add an element which does already exist.
1453    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1454                            "a", "", "0"),
1455                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS);
1456    validate_object("a", "[0]");
1457    delete_object("a");
1458
1459    // c). Larger array, add an element which already exists.
1460    std::string array("[0,1,2,3,4,5,6,7,8,9]");
1461    store_object("b", array, /*JSON*/true, /*compress*/false);
1462    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1463                            "b", "", "6"),
1464                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS);
1465    validate_object("b", array.c_str());
1466
1467    // d). Check that all permitted types of values can be added:
1468    const std::vector<std::string> valid_unique_values({
1469        "\"string\"",
1470        "10",
1471        "1.0",
1472        "true",
1473        "false",
1474        "null"});
1475    for (const auto& v : valid_unique_values) {
1476        EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1477                               "b", "", v));
1478    }
1479    // ... and attempting to add a second time returns EEXISTS
1480    for (const auto& v : valid_unique_values) {
1481        EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1482                                "b", "", v),
1483                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EEXISTS);
1484    }
1485
1486#if 0 // TODO: According to the spec this shouldn't be permitted, however it
1487      // currently works...
1488    // f). Check it is not permitted to add non-primitive types (arrays, objects).
1489    const std::vector<std::string> invalid_unique_values({
1490        "{\"foo\": \"bar\"}",
1491        "[0,1,2]"});
1492    for (const auto& v : invalid_unique_values) {
1493        EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1494                                    "b", "", v),
1495                          PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH, "");
1496    }
1497#endif
1498    delete_object("b");
1499
1500    // g). Attempts to add_unique to a array with non-primitive values should
1501    // fail.
1502    store_object("c", "[{\"a\":\"b\"}]", /*JSON*/true, /*compress*/false);
1503    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1504                            "c", "", "1"),
1505                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
1506    delete_object("c");
1507
1508    store_object("d", "[[1,2]]", /*JSON*/true, /*compress*/false);
1509    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
1510                            "d", "", "3"),
1511                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
1512    delete_object("d");
1513}
1514
1515TEST_P(McdTestappTest, SubdocArrayInsert_Simple)
1516{
1517    // Start with an empty array.
1518    store_object("a", "[]", /*JSON*/true, /*compress*/false);
1519
1520    // a). Attempt to insert at position 0 should succeed.
1521    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1522                                "a", "[0]", "2"));
1523    validate_object("a", "[2]");
1524
1525    // b). Second insert at zero should succeed and shuffle existing element
1526    // down.
1527    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1528                                "a", "[0]", "0"));
1529    validate_object("a", "[0,2]");
1530
1531    // c). Insert at position 1 should shuffle down elements after, leave alone
1532    // elements before.
1533    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1534                           "a", "[1]", "1"));
1535    validate_object("a", "[0,1,2]");
1536
1537    // d). Insert at len(array) should add to the end, without moving existing
1538    // elements.
1539    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1540                                "a", "[3]", "3"));
1541    validate_object("a", "[0,1,2,3]");
1542
1543    delete_object("a");
1544}
1545
1546TEST_P(McdTestappTest, SubdocArrayInsert_Invalid)
1547{
1548    // Start with an empty array.
1549    store_object("a", "[]", /*JSON*/true, /*compress*/false);
1550
1551    // a). Attempt to insert past the end of the (empty) array should fail.
1552    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1553                                "a", "[1]", "0"),
1554                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT, "");
1555    validate_object("a", "[]");
1556
1557    // b). Insert at position '-1' is invalid.
1558    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1559                                "a", "[-1]", "3"),
1560                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL, "");
1561    reconnect_to_server();
1562    validate_object("a", "[]");
1563
1564    // c). MKDIR_P flag is not valid for ARRAY_INSERT
1565    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1566                                "a", "[0]", "1", SUBDOC_FLAG_MKDIR_P),
1567                      PROTOCOL_BINARY_RESPONSE_EINVAL, "");
1568    reconnect_to_server();
1569    validate_object("a", "[]");
1570
1571    // d). A path larger than len(array) should fail.
1572    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1573                                "a", "[1]", "1"),
1574                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT, "");
1575    validate_object("a", "[]");
1576
1577    // e). A path whose has component isn't an array subscript should fail.
1578    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1579                                "a", "[0].foo", "1"),
1580                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_EINVAL, "");
1581    reconnect_to_server();
1582    validate_object("a", "[]");
1583
1584    delete_object("a");
1585
1586    // f). Attempt to insert to a dict should fail.
1587    store_object("b", "{}", /*JSON*/true, /*compress*/false);
1588    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
1589                                "b", "[0]", "0"),
1590                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH, "");
1591    validate_object("b", "{}");
1592    delete_object("b");
1593}
1594
1595TEST_P(McdTestappTest, SubdocGetCount)
1596{
1597    // Start with an empty array.
1598    store_object("a", "[]", /*JSON*/true, /*compress*/false);
1599
1600    // Get size. Should be 0
1601    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT, "a", ""), "0");
1602
1603    // Store it again, giving it some size
1604    store_object("a", "[1,2,3]", true, false);
1605    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT, "a", ""), "3");
1606
1607    // Check for mismatch
1608    store_object("a", "{\"k\":\"v\"}", true, false);
1609    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT, "a", "k"),
1610                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
1611
1612    // Check for non-found
1613    EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_GET_COUNT, "a", "n"),
1614                  PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_ENOENT);
1615    delete_object("a");
1616}
1617
1618void test_subdoc_counter_simple() {
1619    store_object("a", "{}", /*JSON*/true, /*compress*/false);
1620
1621    // a). Check that empty document, empty path creates a new element.
1622    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1623                              "a", "key", "1"), "1");
1624    auto result = fetch_value("a");
1625    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1626    EXPECT_EQ("{\"key\":1}", result.second);
1627
1628    // b). Check we can now increment it further.
1629    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1630                              "a", "key", "1"), "2");
1631    result = fetch_value("a");
1632    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1633    EXPECT_EQ("{\"key\":2}", result.second);
1634
1635    // c). Decrement by 2; should go back to zero.
1636    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1637                                "a", "key", "-2"), "0");
1638    result = fetch_value("a");
1639    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1640    EXPECT_EQ("{\"key\":0}", result.second);
1641
1642    // d). Decrement by 1; should go negative.
1643    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1644                                "a", "key", "-1"), "-1");
1645    result = fetch_value("a");
1646    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1647    EXPECT_EQ("{\"key\":-1}", result.second);
1648
1649    delete_object("a");
1650}
1651
1652TEST_P(McdTestappTest, SubdocCounter_Simple) {
1653    test_subdoc_counter_simple();
1654}
1655
1656TEST_P(McdTestappTest, SubdocCounter_Simple_MutationSeqno) {
1657    set_mutation_seqno_feature(true);
1658    test_subdoc_counter_simple();
1659    set_mutation_seqno_feature(false);
1660}
1661
1662static const std::vector<std::string> NOT_INTEGER({
1663    "true",
1664    "false",
1665    "null",
1666    "\"string\"",
1667    "[0]",
1668    "{\"foo\": \"bar\"}",
1669    "1.1"
1670});
1671
1672TEST_P(McdTestappTest, SubdocCounter_InvalidNotInt)
1673{
1674    // Cannot increment things which are not integers.
1675    for (auto& val : NOT_INTEGER) {
1676        const std::string doc("{\"key\":" + val + "}");
1677        store_object("a", doc, /*JSON*/true, /*compress*/false);
1678        EXPECT_SD_ERR(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1679                                    "a", "key", "1"),
1680                      PROTOCOL_BINARY_RESPONSE_SUBDOC_PATH_MISMATCH);
1681        auto result = fetch_value("a");
1682        EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1683        EXPECT_EQ(doc, result.second);
1684        delete_object("a");
1685    }
1686}
1687
1688TEST_P(McdTestappTest, SubdocCounter_InvalidERange)
1689{
1690    // Cannot increment things which are not representable as int64_t.
1691    const auto int64_max = std::numeric_limits<int64_t>::max();
1692
1693    const std::vector<std::string> unrepresentable({
1694        std::to_string(uint64_t(int64_max) + 1),
1695        "-" + std::to_string(uint64_t(int64_max) + 2),
1696    });
1697    for (auto& val : unrepresentable) {
1698        const std::string doc("{\"key\":" + val + "}");
1699        store_object("b", doc, /*JSON*/true, /*compress*/false);
1700        EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1701                                    "b", "key", "1"),
1702                          PROTOCOL_BINARY_RESPONSE_SUBDOC_NUM_ERANGE, "");
1703        auto result = fetch_value("b");
1704        EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1705        EXPECT_EQ(doc, result.second);
1706        delete_object("b");
1707    }
1708}
1709
1710TEST_P(McdTestappTest, SubdocCounter_Limits)
1711{
1712    // a). Attempting to increment value one less than int64_t::MAX by one
1713    //     should succeed.
1714    const int64_t max = std::numeric_limits<int64_t>::max();
1715
1716    store_object("a", "{\"key\":" + std::to_string(max - 1) + "}",
1717                 /*JSON*/true, /*compress*/false);
1718    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1719                                "a", "key", "1"), std::to_string(max));
1720
1721    auto result = fetch_value("a");
1722    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1723    EXPECT_EQ("{\"key\":" + std::to_string(max) + "}", result.second);
1724
1725    // b). A further increment by one should fail.
1726    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1727                                "a", "key", "1"),
1728                      PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT, "");
1729
1730    delete_object("a");
1731
1732    // c). Same with int64_t::min() and decrement.
1733    const int64_t min = std::numeric_limits<int64_t>::min();
1734
1735    store_object("b", "{\"key\":" + std::to_string(min + 1) + "}",
1736                 /*JSON*/true, /*compress*/false);
1737    EXPECT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1738                              "b", "key", "-1"), std::to_string(min));
1739
1740    result = fetch_value("b");
1741    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1742    EXPECT_EQ("{\"key\":" + std::to_string(min) + "}", result.second);
1743
1744    // b). A further decrement by one should fail.
1745    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1746                                "b", "key", "-1"),
1747                      PROTOCOL_BINARY_RESPONSE_SUBDOC_VALUE_CANTINSERT, "");
1748
1749    delete_object("b");
1750}
1751
1752TEST_P(McdTestappTest, SubdocCounter_InvalidIncr)
1753{
1754    // Cannot increment by a non-numeric value.
1755    const std::string doc("{\"key\":10}");
1756    store_object("a", doc, /*JSON*/true, /*compress*/false);
1757
1758    for (auto& incr : NOT_INTEGER) {
1759        EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1760                                    "a", "key", incr),
1761                          PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_EINVAL, "");
1762        auto result = fetch_value("a");
1763        EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first)
1764            << " using increment '" << incr << "'";
1765        EXPECT_EQ(doc, result.second);
1766    }
1767
1768    // Cannot increment by zero.
1769    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1770                                "a", "key", "0"),
1771                      PROTOCOL_BINARY_RESPONSE_SUBDOC_DELTA_EINVAL, "");
1772    auto result = fetch_value("a");
1773    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1774    EXPECT_EQ(doc, result.second);
1775
1776    delete_object("a");
1777}
1778
1779// Test handling of the internal auto-retry when a CAS mismatch occurs due
1780// to the underlying document changing between subdoc reading the initial value
1781// and trying to write the new value (after applying the subdoc modification).
1782TEST_P(McdTestappTest, SubdocCASAutoRetry)
1783{
1784    // Store a simple dict value to operate on.
1785    store_object("a", "{}");
1786
1787    // 1. Setup ewouldblock_engine - make the first three store commands return
1788    // EXISTS.
1789    ewouldblock_engine_configure(ENGINE_SUCCESS, // not used for this mode
1790                                 EWBEngineMode::CasMismatch,
1791                                 3);
1792
1793    // Issue a DICT_ADD without an explicit CAS. We should have an auto-retry
1794    // occur (and the command succeed).
1795    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD,
1796                                "a", "key1", "1"));
1797
1798    // 2. Now retry with MAXIMUM_ATTEMPTS-1 CAS mismatches - this should still
1799    // succeed.
1800    ewouldblock_engine_configure(ENGINE_SUCCESS, // not used for this mode
1801                                 EWBEngineMode::CasMismatch, 99);
1802    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD,
1803                           "a", "key2", "2"));
1804
1805    // 3. Now with MAXIMUM_ATTEMPTS CAS mismatches - this should return TMPFAIL.
1806    ewouldblock_engine_configure(ENGINE_SUCCESS, // not used for this mode
1807                                 EWBEngineMode::CasMismatch,
1808                                 100);
1809    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD,
1810                                "a", "key3", "3"),
1811                      PROTOCOL_BINARY_RESPONSE_ETMPFAIL, "");
1812    delete_object("a");
1813}
1814
1815TEST_P(McdTestappTest, SubdocMkdoc_Array)
1816{
1817    // Create new document (array)
1818    ASSERT_SD_OK(
1819            BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
1820                                 "a",
1821                                 "",
1822                                 "0",
1823                                 SUBDOC_FLAG_NONE,
1824                                 mcbp::subdoc::doc_flag::Mkdoc,
1825                                 0));
1826    EXPECT_SD_GET("a", "[0]", "0");
1827
1828    // Flag doesn't do anything if doc already exists
1829    ASSERT_SD_OK(
1830            BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1831                                 "a",
1832                                 "",
1833                                 "1",
1834                                 SUBDOC_FLAG_NONE,
1835                                 mcbp::subdoc::doc_flag::Mkdoc,
1836                                 0));
1837    EXPECT_SD_GET("a", "[1]", "1");
1838
1839    delete_object("a");
1840}
1841
1842TEST_P(McdTestappTest, SubdocMkdoc_Dict) {
1843    // Create new document (dictionary)
1844    ASSERT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT,
1845                                      "a",
1846                                      "foo",
1847                                      "1",
1848                                      SUBDOC_FLAG_NONE,
1849                                      mcbp::subdoc::doc_flag::Mkdoc,
1850                                      0));
1851    EXPECT_SD_GET("a", "foo", "1");
1852
1853    // Flag doesn't do anything if doc already exists
1854    ASSERT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT,
1855                                      "a",
1856                                      "bar",
1857                                      "2",
1858                                      SUBDOC_FLAG_NONE,
1859                                      mcbp::subdoc::doc_flag::Mkdoc,
1860                                      0));
1861    EXPECT_SD_GET("a", "bar", "2");
1862
1863    // Flag still has MKDIR_P semantics if needed
1864    ASSERT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT,
1865                                      "a",
1866                                      "nested.path",
1867                                      "3",
1868                                      SUBDOC_FLAG_NONE,
1869                                      mcbp::subdoc::doc_flag::Mkdoc,
1870                                      0));
1871    EXPECT_SD_GET("a", "nested.path", "3");
1872
1873    delete_object("a");
1874}
1875
1876TEST_P(McdTestappTest, SubdocMkdoc_Counter) {
1877    // Counter should also work (with a path) + MKDIR_P
1878    ASSERT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1879                                         "a",
1880                                         "counter.path",
1881                                         "42",
1882                                         SUBDOC_FLAG_NONE,
1883                                         mcbp::subdoc::doc_flag::Mkdoc,
1884                                         0),
1885                    "42");
1886    // Repeat should be OK
1887    ASSERT_SD_VALEQ(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
1888                                         "a",
1889                                         "counter.path",
1890                                         "42",
1891                                         SUBDOC_FLAG_NONE,
1892                                         mcbp::subdoc::doc_flag::Mkdoc,
1893                                         0),
1894                    "84");
1895    delete_object("a");
1896}
1897
1898// Test operation of setting document expiry for single-path commands.
1899TEST_P(McdTestappTest, SubdocExpiry_Single)
1900{
1901    // Create two documents; one to be used for an exlicit 1s expiry and one
1902    // for an explicit 0s (i.e. never) expiry.
1903    store_object("ephemeral", "[\"a\"]");
1904    store_object("permanent", "[\"a\"]");
1905
1906    // Expiry not permitted for SUBDOC_GET operations.
1907    BinprotSubdocCommand get(PROTOCOL_BINARY_CMD_SUBDOC_GET, "ephemeral", "[0]");
1908    get.setExpiry(666);
1909    EXPECT_SD_ERR(get, PROTOCOL_BINARY_RESPONSE_EINVAL);
1910    reconnect_to_server();
1911
1912    // Perform a REPLACE operation, setting a expiry of 1s.
1913    BinprotSubdocCommand replace(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "ephemeral", "[0]",
1914                      "\"b\"");
1915    EXPECT_SD_OK(replace.setExpiry(1));
1916
1917    // Try to read the document immediately - should exist.
1918    auto result = fetch_value("ephemeral");
1919    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1920    EXPECT_EQ("[\"b\"]", result.second);
1921
1922    // Perform a REPLACE, explicitly encoding an expiry of 0s.
1923    BinprotSubdocCommand replace2(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "permanent", "[0]",
1924                      "\"b\"");
1925    EXPECT_SD_OK(replace2.setExpiry(0));
1926
1927    // Try to read the second document immediately - should exist.
1928    result = fetch_value("permanent");
1929    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1930    EXPECT_EQ("[\"b\"]", result.second);
1931
1932    // Sleep for 2s seconds.
1933    // TODO: it would be great if we could somehow accelerate time from the
1934    // harness, and not add 2s to the runtime of the test...
1935    usleep(2 * 1000 * 1000);
1936
1937    // Try to read the ephemeral document - shouldn't exist.
1938    result = fetch_value("ephemeral");
1939    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_KEY_ENOENT, result.first);
1940
1941    // Try to read the permanent document - should still exist.
1942    result = fetch_value("permanent");
1943    EXPECT_EQ(PROTOCOL_BINARY_RESPONSE_SUCCESS, result.first);
1944    EXPECT_EQ("[\"b\"]", result.second);
1945}
1946
1947// Test handling of not-my-vbucket for a SUBDOC_GET
1948TEST_P(McdTestappTest, SubdocGet_NotMyVbucket)
1949{
1950    const char array[] = "[0]";
1951    store_object("array", array);
1952
1953    // Make the next engine operation (get) return NOT_MY_VBUCKET.
1954    ewouldblock_engine_configure(ENGINE_NOT_MY_VBUCKET, EWBEngineMode::Next_N,
1955                                 1);
1956
1957    // Should fail with NOT-MY-VBUCKET, and a non-zero length body including the
1958    // cluster config.
1959    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_GET, "array", "[0]"),
1960                      PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, "");
1961
1962    // Second attempt should succced (as only next 1 engine op was set to fail).
1963    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_GET, "array", "[0]"),
1964                      PROTOCOL_BINARY_RESPONSE_SUCCESS, "0");
1965
1966    delete_object("array");
1967}
1968
1969// Test handling of not-my-vbucket for a SUBDOC_DICT_ADD
1970TEST_P(McdTestappTest, SubdocArrayPushLast_NotMyVbucket)
1971{
1972    const char array[] = "[0]";
1973    store_object("array", array);
1974
1975    // Configure the ewouldblock_engine to inject fake NOT-MY-VBUCKET failure
1976    // for the 3rd call (i.e. the 1st engine->store() attempt). We only expect 6 calls
1977    // total, so also make anything after that fail.
1978    ewouldblock_engine_configure(ENGINE_NOT_MY_VBUCKET, EWBEngineMode::Sequence,
1979                                 0xffffffc4 /* <3 MSBytes all-ones>, 0b11,000,100 */);
1980
1981    // Should fail with NOT-MY-VBUCKET, and a non-zero length body including the
1982    // cluster config.
1983    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1984                                "array", "", "1"),
1985                      PROTOCOL_BINARY_RESPONSE_NOT_MY_VBUCKET, "");
1986
1987    // Second attempt should succced (as only next 1 engine op was set to fail).
1988    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
1989                                "array", "", "1"),
1990                      PROTOCOL_BINARY_RESPONSE_SUCCESS, "");
1991
1992    // Cleanup.
1993    ewouldblock_engine_disable();
1994    delete_object("array");
1995}
1996
1997// Test that flags are preserved by subdoc mutation operations.
1998TEST_P(McdTestappTest, SubdocFlags)
1999{
2000    const char array[] = "[0]";
2001    const uint32_t flags = 0xcafebabe;
2002    store_object_with_flags("array", array, flags);
2003
2004    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE, "array",
2005                           "[0]", "1"));
2006
2007    validate_object("array", "[1]");
2008    validate_flags("array", 0xcafebabe);
2009
2010    delete_object("array");
2011}
2012
2013// Test that locked items are properly handled
2014TEST_P(McdTestappTest, SubdocLockedItem)
2015{
2016    store_object("item", "{}", true);
2017
2018    // Lock the object
2019    auto doc = getConnection().get_and_lock("item", 0, 10);
2020
2021    // Construct the subdoc command
2022    BinprotSubdocCommand sd_cmd(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT,
2023                     "item", "p", "true");
2024    sd_cmd.setCas(0);
2025    EXPECT_SUBDOC_CMD(sd_cmd, PROTOCOL_BINARY_RESPONSE_ETMPFAIL, "");
2026
2027    // Set the CAS to -1
2028    sd_cmd.setCas(-1);
2029    EXPECT_SUBDOC_CMD(sd_cmd, PROTOCOL_BINARY_RESPONSE_ETMPFAIL, "");
2030
2031    // Set our "normal" CAS back (
2032    sd_cmd.setCas(doc.info.cas);
2033    EXPECT_SUBDOC_CMD(sd_cmd, PROTOCOL_BINARY_RESPONSE_SUCCESS, "");
2034
2035    validate_object("item", "{\"p\":true}");
2036
2037    delete_object("item");
2038}
2039
2040
2041enum class SubdocCmdType {
2042    Lookup,
2043    Mutation
2044};
2045
2046struct SubdocStatTraits {
2047    const char* count_name;
2048    const char* bytes_total_name;
2049    const char* bytes_extracted_subset;
2050};
2051
2052static const SubdocStatTraits LOOKUP_TRAITS { "cmd_subdoc_lookup",
2053                                              "bytes_subdoc_lookup_total",
2054                                              "bytes_subdoc_lookup_extracted" };
2055
2056static const SubdocStatTraits MUTATION_TRAITS { "cmd_subdoc_mutation",
2057                                                "bytes_subdoc_mutation_total",
2058                                                "bytes_subdoc_mutation_inserted" };
2059
2060static void test_subdoc_stats_command(protocol_binary_command cmd,
2061                                      SubdocStatTraits traits,
2062                                      const std::string& doc,
2063                                      const std::string& path,
2064                                      const std::string& value,
2065                                      const std::string& fragment,
2066                                      size_t expected_total_len,
2067                                      size_t expected_subset_len) {
2068    store_object("doc", doc.c_str());
2069
2070    // Get initial stats
2071    auto stats = request_stats();
2072    auto count_before = extract_single_stat(stats, traits.count_name);
2073    auto bytes_before_total = extract_single_stat(stats, traits.bytes_total_name);
2074    auto bytes_before_subset = extract_single_stat(stats, traits.bytes_extracted_subset);
2075
2076    // Perform the operation
2077    EXPECT_SUBDOC_CMD(BinprotSubdocCommand(cmd, "doc", path, value),
2078                      PROTOCOL_BINARY_RESPONSE_SUCCESS, fragment);
2079
2080    // Get subsequent stats, check stat increased by one.
2081    stats = request_stats();
2082    auto count_after = extract_single_stat(stats, traits.count_name);
2083    auto bytes_after_total = extract_single_stat(stats, traits.bytes_total_name);
2084    auto bytes_after_subset = extract_single_stat(stats, traits.bytes_extracted_subset);
2085
2086    EXPECT_EQ(1, count_after - count_before);
2087    EXPECT_EQ(expected_total_len, bytes_after_total - bytes_before_total);
2088    EXPECT_EQ(expected_subset_len, bytes_after_subset - bytes_before_subset);
2089
2090    delete_object("doc");
2091}
2092
2093TEST_P(McdTestappTest, SubdocStatsLookupGet) {
2094    std::string doc("[10,11,12,13,14,15,16,17,18,19]");
2095    std::string response("10");
2096    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_GET, LOOKUP_TRAITS,
2097                              doc, "[0]", "", response,
2098                              doc.size(), response.size());
2099}
2100TEST_P(McdTestappTest, SubdocStatsLookupExists) {
2101    std::string doc("[10,11,12,13,14,15,16,17,18,19]");
2102    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_EXISTS, LOOKUP_TRAITS,
2103                              doc, "[0]", "", "", doc.size(), 0);
2104}
2105TEST_P(McdTestappTest, SubdocStatsDictAdd) {
2106    std::string input("{\"foo\":1,\"bar\":2}");
2107    std::string path("baz");
2108    std::string fragment("3");
2109    std::string result("{\"foo\":1,\"bar\":2,\"baz\":3}");
2110    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD,
2111                              MUTATION_TRAITS, input, path, fragment, "",
2112                              result.size(), fragment.size());
2113}
2114TEST_P(McdTestappTest, SubdocStatsDictUpsert) {
2115    std::string input("{\"foo\":1,\"bar\":2}");
2116    std::string path("bar");
2117    std::string fragment("3");
2118    std::string result("{\"foo\":1,\"bar\":3}");
2119    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT,
2120                              MUTATION_TRAITS, input, path, fragment, "",
2121                              result.size(), fragment.size());
2122}
2123TEST_P(McdTestappTest, SubdocStatsDelete) {
2124    std::string input("{\"foo\":1,\"bar\":2,\"baz\":3}");
2125    std::string path("baz");
2126    std::string result("{\"foo\":1,\"bar\":2}");
2127    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_DELETE,
2128                               MUTATION_TRAITS, input, path, "", "",
2129                               result.size(), 0);
2130}
2131TEST_P(McdTestappTest, SubdocStatsReplace) {
2132    std::string input("{\"foo\":1,\"bar\":2}");
2133    std::string path("bar");
2134    std::string fragment("3");
2135    std::string result("{\"foo\":1,\"bar\":3}");
2136    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_REPLACE,
2137                              MUTATION_TRAITS, input, path, fragment, "",
2138                              result.size(), fragment.size());
2139}
2140TEST_P(McdTestappTest, SubdocStatsArrayPushLast) {
2141    std::string input("[10,11,12,13,14,15,16,17,18,19]");
2142    std::string fragment("20");
2143    std::string result("[10,11,12,13,14,15,16,17,18,19,20]");
2144    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
2145                               MUTATION_TRAITS, input, "", fragment, "",
2146                               result.size(), fragment.size());
2147}
2148TEST_P(McdTestappTest, SubdocStatsArrayPushFirst) {
2149    std::string input("[10,11,12,13,14,15,16,17,18,19]");
2150    std::string fragment("9");
2151    std::string result("[9,10,11,12,13,14,15,16,17,18,19]");
2152    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_FIRST,
2153                               MUTATION_TRAITS, input, "", fragment, "",
2154                               result.size(), fragment.size());
2155}
2156TEST_P(McdTestappTest, SubdocStatsArrayInsert) {
2157    std::string input("[9,11,12,13,14,15,16,17,18,19]");
2158    std::string path("[0]");
2159    std::string fragment("10");
2160    std::string result("[9,10,11,12,13,14,15,16,17,18,19]");
2161    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_INSERT,
2162                               MUTATION_TRAITS, input, path, fragment, "",
2163                               result.size(), fragment.size());
2164}
2165TEST_P(McdTestappTest, SubdocStatsArrayAddUnique) {
2166    std::string input("[10,11,12,13,14,15,16,17,18,19]");
2167    std::string fragment("20");
2168    std::string result("[10,11,12,13,14,15,16,17,18,19,20]");
2169    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_ADD_UNIQUE,
2170                               MUTATION_TRAITS, input, "", fragment, "",
2171                               result.size(), fragment.size());
2172}
2173TEST_P(McdTestappTest, SubdocStatsCounter) {
2174    std::string input("{\"foo\":1,\"bar\":2}");
2175    std::string path("bar");
2176    std::string fragment("1");
2177    std::string result("{\"foo\":1,\"bar\":3}");
2178    test_subdoc_stats_command(PROTOCOL_BINARY_CMD_SUBDOC_COUNTER,
2179                               MUTATION_TRAITS, input, path, fragment, "3",
2180                               result.size(), fragment.size());
2181}
2182
2183TEST_P(McdTestappTest, SubdocUTF8PathTest) {
2184    // Check that using UTF8 characters in the path works, which it should
2185
2186    const char dict[] = "{ \"kéy1\": 1 }";
2187    store_object("dict", dict, /*JSON*/true, /*compress*/false);
2188    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD, "dict", "kéy2�", "56"));
2189    EXPECT_SD_GET("dict", "kéy2�", "56");
2190    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "dict", "kéy2�", ""));
2191    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT, "dict", "kéy2�", "44"));
2192    EXPECT_SD_GET("dict", "kéy2�", "44");
2193    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_UPSERT, "dict", "kéy2�", "99"));
2194    EXPECT_SD_GET("dict", "kéy2�", "99");
2195    validate_object("dict", "{ \"kéy1\": 1 ,\"kéy2�\":99}");
2196}
2197
2198TEST_P(McdTestappTest, SubdocUTF8ValTest) {
2199    // Check that using UTF8 characters in the value works, which it should
2200
2201    const char dict[] = "{ \"key1\": \"Ẇ̴̷̦͔͖͚̝̟̋̽ͪ̾ͤ̈́ͯͮͮ̀͗̌ͭ̾͜h̨̥̞͖̬̠͍͖̘̹͎͌̇̂̃ͯͣ͗̆̌̑ͨ̍̊ͪ̆̾̆̚͟͞ȧ̛̰̞̗̞̬̣̹͎̰̝͍͈̮̖̘̫̤̟͆̈́̒͗ͦ̋̓̌̊̋͝ͅţ͒ͮ͋̋̔̽ͥ̂ͭ̒̉̔̃ͫ̌̆̆҉̹͙̟̩̖̩̹̳̜͚̜̜ ͎̲͕̺̔̿̀͒̈́̏̌ͬͫ͒͂ͩͦ̀͝â̢̡̘̫̮̞̩̰̎ͨ̾ͤ̈́͑̉̈ͧ͆̃ͩ͆̚͡ ̧̢̛̙͔̰̹̲̱͔̤̝͖̥͚͓̲̪̯̟̖̏͒̽ͬ̂ͫͩͭ͋̏͊̽͗͊̀ͭ̋͘c̵̴͐̉̇͂̋ͬ̇̃͊ͨ͗̆̄̊́͏̡̡̫̦̦͉̼̙̜͉̯̮̪̫͍̩̼̘̫̻͍o̸̷͕̭̼̺̤͖͚̯̪̥̘̪̼̝̩̮͕̥̟̐͒̏ͭͦͮ̒ͧ̔̉̅̂͜͢͡o̷̢̡͕̟͓̺͚̟̱̜̻͇̘͍̤͓̲ͣͫ̾͛͗̅̐̏͑͆͌̀͜ͅͅͅl̴̡̙̹͖̈̄̌͒ͣ͒̅̏̕ ͛̐̿͋ͦ͛͌̄ͫ̒ͪ͊̀ͤ̀̿͏̶̡҉҉̤͎͖v̸̵̱͇̲͎̩͚̩͈̙̜̳̞̭̯̩̻̮̪ͯ̋̔͗̃̊ͬͮ̄̃͛̂̒̍͘͘a̦̝͇̙̬̬̰̪͙̗̟͙̝̬͛͂͑ͣ̓͑̏ͤ̑̀̚̚͘l͊̔́͋̋ͫ̈́̿̈̉̀͏̡͍͇̲̙̺̮͠uͬ̄̋̔ͪͧͥ͛ͭ̏̅ͫ͊̚͏̧͈̠̱͇͉̦̫͎͠ę̸͔̯̭̤͕̱͈̖͖̯̭̞͈͖ͨ̑̌̓̈ͮ͂̆̀͟ͅ\" }";
2202    store_object("dict", dict, /*JSON*/true, /*compress*/false);
2203    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD, "dict", "key2", "\"Ẇ̴̷̦͔͖͚̝̟̋̽ͪ̾ͤ̈́ͯͮͮ̀͗̌ͭ̾͜h̨̥̞͖̬̠͍͖̘̹͎͌̇̂̃ͯͣ͗̆̌̑ͨ̍̊ͪ̆̾̆̚͟͞ȧ̛̰̞̗̞̬̣̹͎̰̝͍͈̮̖̘̫̤̟͆̈́̒͗ͦ̋̓̌̊̋͝ͅţ͒ͮ͋̋̔̽ͥ̂ͭ̒̉̔̃ͫ̌̆̆҉̹͙̟̩̖̩̹̳̜͚̜̜ ͎̲͕̺̔̿̀͒̈́̏̌ͬͫ͒͂ͩͦ̀͝â̢̡̘̫̮̞̩̰̎ͨ̾ͤ̈́͑̉̈ͧ͆̃ͩ͆̚͡ ̧̢̛̙͔̰̹̲̱͔̤̝͖̥͚͓̲̪̯̟̖̏͒̽ͬ̂ͫͩͭ͋̏͊̽͗͊̀ͭ̋͘c̵̴͐̉̇͂̋ͬ̇̃͊ͨ͗̆̄̊́͏̡̡̫̦̦͉̼̙̜͉̯̮̪̫͍̩̼̘̫̻͍o̸̷͕̭̼̺̤͖͚̯̪̥̘̪̼̝̩̮͕̥̟̐͒̏ͭͦͮ̒ͧ̔̉̅̂͜͢͡o̷̢̡͕̟͓̺͚̟̱̜̻͇̘͍̤͓̲ͣͫ̾͛͗̅̐̏͑͆͌̀͜ͅͅͅl̴̡̙̹͖̈̄̌͒ͣ͒̅̏̕ ͛̐̿͋ͦ͛͌̄ͫ̒ͪ͊̀ͤ̀̿͏̶̡҉҉̤͎͖v̸̵̱͇̲͎̩͚̩͈̙̜̳̞̭̯̩̻̮̪ͯ̋̔͗̃̊ͬͮ̄̃͛̂̒̍͘͘a̦̝͇̙̬̬̰̪͙̗̟͙̝̬͛͂͑ͣ̓͑̏ͤ̑̀̚̚͘l͊̔́͋̋ͫ̈́̿̈̉̀͏̡͍͇̲̙̺̮͠uͬ̄̋̔ͪͧͥ͛ͭ̏̅ͫ͊̚͏̧͈̠̱͇͉̦̫͎͠ę̸͔̯̭̤͕̱͈̖͖̯̭̞͈͖ͨ̑̌̓̈ͮ͂̆̀͟ͅ\""));
2204    EXPECT_SD_GET("dict", "key2", "\"Ẇ̴̷̦͔͖͚̝̟̋̽ͪ̾ͤ̈́ͯͮͮ̀͗̌ͭ̾͜h̨̥̞͖̬̠͍͖̘̹͎͌̇̂̃ͯͣ͗̆̌̑ͨ̍̊ͪ̆̾̆̚͟͞ȧ̛̰̞̗̞̬̣̹͎̰̝͍͈̮̖̘̫̤̟͆̈́̒͗ͦ̋̓̌̊̋͝ͅţ͒ͮ͋̋̔̽ͥ̂ͭ̒̉̔̃ͫ̌̆̆҉̹͙̟̩̖̩̹̳̜͚̜̜ ͎̲͕̺̔̿̀͒̈́̏̌ͬͫ͒͂ͩͦ̀͝â̢̡̘̫̮̞̩̰̎ͨ̾ͤ̈́͑̉̈ͧ͆̃ͩ͆̚͡ ̧̢̛̙͔̰̹̲̱͔̤̝͖̥͚͓̲̪̯̟̖̏͒̽ͬ̂ͫͩͭ͋̏͊̽͗͊̀ͭ̋͘c̵̴͐̉̇͂̋ͬ̇̃͊ͨ͗̆̄̊́͏̡̡̫̦̦͉̼̙̜͉̯̮̪̫͍̩̼̘̫̻͍o̸̷͕̭̼̺̤͖͚̯̪̥̘̪̼̝̩̮͕̥̟̐͒̏ͭͦͮ̒ͧ̔̉̅̂͜͢͡o̷̢̡͕̟͓̺͚̟̱̜̻͇̘͍̤͓̲ͣͫ̾͛͗̅̐̏͑͆͌̀͜ͅͅͅl̴̡̙̹͖̈̄̌͒ͣ͒̅̏̕ ͛̐̿͋ͦ͛͌̄ͫ̒ͪ͊̀ͤ̀̿͏̶̡҉҉̤͎͖v̸̵̱͇̲͎̩͚̩͈̙̜̳̞̭̯̩̻̮̪ͯ̋̔͗̃̊ͬͮ̄̃͛̂̒̍͘͘a̦̝͇̙̬̬̰̪͙̗̟͙̝̬͛͂͑ͣ̓͑̏ͤ̑̀̚̚͘l͊̔́͋̋ͫ̈́̿̈̉̀͏̡͍͇̲̙̺̮͠uͬ̄̋̔ͪͧͥ͛ͭ̏̅ͫ͊̚͏̧͈̠̱͇͉̦̫͎͠ę̸͔̯̭̤͕̱͈̖͖̯̭̞͈͖ͨ̑̌̓̈ͮ͂̆̀͟ͅ\"");
2205    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DELETE, "dict", "key2", ""));
2206    validate_object("dict", "{ \"key1\": \"Ẇ̴̷̦͔͖͚̝̟̋̽ͪ̾ͤ̈́ͯͮͮ̀͗̌ͭ̾͜h̨̥̞͖̬̠͍͖̘̹͎͌̇̂̃ͯͣ͗̆̌̑ͨ̍̊ͪ̆̾̆̚͟͞ȧ̛̰̞̗̞̬̣̹͎̰̝͍͈̮̖̘̫̤̟͆̈́̒͗ͦ̋̓̌̊̋͝ͅţ͒ͮ͋̋̔̽ͥ̂ͭ̒̉̔̃ͫ̌̆̆҉̹͙̟̩̖̩̹̳̜͚̜̜ ͎̲͕̺̔̿̀͒̈́̏̌ͬͫ͒͂ͩͦ̀͝â̢̡̘̫̮̞̩̰̎ͨ̾ͤ̈́͑̉̈ͧ͆̃ͩ͆̚͡ ̧̢̛̙͔̰̹̲̱͔̤̝͖̥͚͓̲̪̯̟̖̏͒̽ͬ̂ͫͩͭ͋̏͊̽͗͊̀ͭ̋͘c̵̴͐̉̇͂̋ͬ̇̃͊ͨ͗̆̄̊́͏̡̡̫̦̦͉̼̙̜͉̯̮̪̫͍̩̼̘̫̻͍o̸̷͕̭̼̺̤͖͚̯̪̥̘̪̼̝̩̮͕̥̟̐͒̏ͭͦͮ̒ͧ̔̉̅̂͜͢͡o̷̢̡͕̟͓̺͚̟̱̜̻͇̘͍̤͓̲ͣͫ̾͛͗̅̐̏͑͆͌̀͜ͅͅͅl̴̡̙̹͖̈̄̌͒ͣ͒̅̏̕ ͛̐̿͋ͦ͛͌̄ͫ̒ͪ͊̀ͤ̀̿͏̶̡҉҉̤͎͖v̸̵̱͇̲͎̩͚̩͈̙̜̳̞̭̯̩̻̮̪ͯ̋̔͗̃̊ͬͮ̄̃͛̂̒̍͘͘a̦̝͇̙̬̬̰̪͙̗̟͙̝̬͛͂͑ͣ̓͑̏ͤ̑̀̚̚͘l͊̔́͋̋ͫ̈́̿̈̉̀͏̡͍͇̲̙̺̮͠uͬ̄̋̔ͪͧͥ͛ͭ̏̅ͫ͊̚͏̧͈̠̱͇͉̦̫͎͠ę̸͔̯̭̤͕̱͈̖͖̯̭̞͈͖ͨ̑̌̓̈ͮ͂̆̀͟ͅ\" }");
2207}
2208
2209// MB-30278: Perform a DICT_ADD followed by SD_GET to a path with backticks in
2210// them; to verify the path caching of un-escaped components is correctly reset
2211// between calls.
2212TEST_P(McdTestappTest, MB_30278_SubdocBacktickLookup) {
2213    // Test DICT_ADD where the element key contains a literal backtick.
2214    store_object("doc", "{}", /*JSON*/ true, /*compress*/ false);
2215    // Add a key with literal backtick (which we escape by passing
2216    // double-backtick)
2217    EXPECT_SD_OK(BinprotSubdocCommand(
2218            PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD, "doc", "key``", "\"value\""));
2219    EXPECT_SD_GET("doc", "key``", "\"value\"");
2220    validate_object("doc", R"({"key`":"value"})");
2221}
2222
2223// MB-30278: Perform two DICT_ADD calls to paths with backticks in them;
2224// to verify the path caching of un-escaped components is correctly reset
2225// between calls.
2226TEST_P(McdTestappTest, MB_30278_SubdocBacktickDictAdd) {
2227    store_object("doc", "{}", /*JSON*/ true, /*compress*/ false);
2228    EXPECT_SD_OK(BinprotSubdocCommand(
2229            PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD, "doc", "key``", "\"value\""));
2230    EXPECT_SD_OK(BinprotSubdocCommand(PROTOCOL_BINARY_CMD_SUBDOC_DICT_ADD,
2231                                      "doc",
2232                                      "key2``",
2233                                      "\"value2\""));
2234    validate_object("doc", R"({"key`":"value","key2`":"value2"})");
2235}
2236
2237// Tests how a single worker handles multiple "concurrent" connections
2238// performing operations.
2239class WorkerConcurrencyTest : public TestappTest {
2240public:
2241    static void SetUpTestCase() {
2242        memcached_cfg.reset(generate_config(0));
2243        // Change the number of worker threads to one so we guarantee that
2244        // multiple connections are handled by a single worker.
2245        cJSON_AddNumberToObject(memcached_cfg.get(), "threads", 1);
2246        start_memcached_server(memcached_cfg.get());
2247
2248        if (HasFailure()) {
2249            server_pid = reinterpret_cast<pid_t>(-1);
2250        } else {
2251            CreateTestBucket();
2252        }
2253    }
2254
2255    static unique_cJSON_ptr memcached_cfg;
2256};
2257
2258unique_cJSON_ptr WorkerConcurrencyTest::memcached_cfg;
2259
2260
2261TEST_F(WorkerConcurrencyTest, SubdocArrayPushLast_Concurrent) {
2262    // Concurrently add to two different array documents, using two connections.
2263
2264    // Setup the initial empty objects.
2265    store_object("a", "[]", /*JSON*/true, /*compress*/false);
2266    store_object("b", "[]", /*JSON*/true, /*compress*/false);
2267
2268    // Create an additional second connection to memcached.
2269    SOCKET* current_sock = &sock;
2270    SOCKET sock1 = *current_sock;
2271    SOCKET sock2 = connect_to_server_plain(port);
2272    ASSERT_NE(sock2, INVALID_SOCKET);
2273    sock = sock1;
2274
2275    const size_t push_count = 100;
2276    std::vector<uint8_t> send_buf;
2277
2278    // Build pipeline for the even commands.
2279    std::string expected_a;
2280    for (unsigned int i = 0; i < push_count; i += 2) {
2281        expected_a += std::to_string(i) + ",";
2282        BinprotSubdocCommand cmd(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST, "a", "", std::to_string(i));
2283        cmd.encode(send_buf);
2284    }
2285    *current_sock = sock1;
2286    safe_send(send_buf.data(), send_buf.size(), false);
2287
2288    // .. and the odd commands.
2289    send_buf.clear();
2290    std::string expected_b;
2291    for (unsigned int i = 1; i < push_count; i += 2) {
2292        expected_b += std::to_string(i) + ",";
2293        BinprotSubdocCommand cmd(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
2294                             "b", "", std::to_string(i));
2295        cmd.encode(send_buf);
2296    }
2297    *current_sock = sock2;
2298    safe_send(send_buf.data(), send_buf.size(), false);
2299
2300    // Fixup the expected values - remove the trailing comma and bookend with
2301    // [ ].
2302    expected_a.insert(0, "[");
2303    expected_a.replace(expected_a.size() - 1, 1, "]");
2304    expected_b.insert(0, "[");
2305    expected_b.replace(expected_b.size() - 1, 1, "]");
2306
2307    // Consume all the responses we should be expecting back.
2308    for (unsigned int i = 0; i < push_count; i++) {
2309        sock = (i % 2) ? sock1 : sock2;
2310        recv_subdoc_response(PROTOCOL_BINARY_CMD_SUBDOC_ARRAY_PUSH_LAST,
2311                PROTOCOL_BINARY_RESPONSE_SUCCESS, "");
2312    }
2313
2314    // Validate correct data was written.
2315    validate_object("a", expected_a);
2316    validate_object("b", expected_b);
2317
2318    // Restore original socket; free second one.
2319    *current_sock = sock1;
2320    closesocket(sock2);
2321
2322    delete_object("a");
2323    delete_object("b");
2324}
2325