1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2012-2020 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 #include "config.h"
18 #include <libcouchbase/couchbase.h>
19 #include <libcouchbase/utils.h>
20 #include <map>
21 #include "iotests.h"
22 #include "logging.h"
23 #include "internal.h"
24 #include "testutil.h"
25 
26 #define LOGARGS(instance, lvl) instance->settings, "tests-GET", LCB_LOG_##lvl, __FILE__, __LINE__
27 
28 class GetUnitTest : public MockUnitTest
29 {
30 };
31 
32 extern "C" {
testGetMissGetCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)33 static void testGetMissGetCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)
34 {
35     int *counter;
36     lcb_respget_cookie(resp, (void **)&counter);
37     EXPECT_EQ(LCB_ERR_DOCUMENT_NOT_FOUND, lcb_respget_status(resp));
38     const char *key;
39     size_t nkey;
40     lcb_respget_key(resp, &key, &nkey);
41     std::string val(key, nkey);
42     EXPECT_TRUE(val == "testGetMiss1" || val == "testGetMiss2");
43     ++(*counter);
44 }
45 }
46 
47 /**
48  * @test
49  * Get Miss
50  *
51  * @pre
52  * Request two non-existent keys
53  *
54  * @post
55  * Responses for both keys are received with error code
56  * @c KEY_ENOENT; response structure is not nullptr, and the keys match their
57  * expected value
58  *
59  * @todo (maybe check the values too?)
60  */
TEST_F(GetUnitTest, testGetMiss)61 TEST_F(GetUnitTest, testGetMiss)
62 {
63     HandleWrap hw;
64     lcb_INSTANCE *instance;
65     createConnection(hw, &instance);
66 
67     (void)lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)testGetMissGetCallback);
68     int numcallbacks = 0;
69     std::string key1("testGetMiss1"), key2("testGetMiss2");
70 
71     removeKey(instance, key1);
72     removeKey(instance, key2);
73 
74     lcb_CMDGET *cmd;
75 
76     lcb_cmdget_create(&cmd);
77     lcb_cmdget_key(cmd, key1.c_str(), key1.size());
78     EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, cmd));
79 
80     lcb_cmdget_key(cmd, key2.c_str(), key2.size());
81     EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, cmd));
82     lcb_cmdget_destroy(cmd);
83 
84     lcb_wait(instance, LCB_WAIT_DEFAULT);
85     EXPECT_EQ(2, numcallbacks);
86 }
87 
88 extern "C" {
testGetHitGetCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)89 static void testGetHitGetCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)
90 {
91     int *counter;
92     lcb_respget_cookie(resp, (void **)&counter);
93     EXPECT_EQ(LCB_SUCCESS, lcb_respget_status(resp));
94     ++(*counter);
95 }
96 }
97 
98 /**
99  * @test
100  * Get Hit
101  *
102  * @pre
103  * Store two keys, and retrieve them
104  *
105  * @post
106  * Both keys exist, and their return code is successul
107  */
TEST_F(GetUnitTest, testGetHit)108 TEST_F(GetUnitTest, testGetHit)
109 {
110     MockEnvironment *mock = MockEnvironment::getInstance();
111     tracing_guard use_tracing;
112     metrics_guard use_metrics;
113     HandleWrap hw;
114     lcb_INSTANCE *instance;
115     createConnection(hw, &instance);
116 
117     (void)lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)testGetHitGetCallback);
118     int numcallbacks = 0;
119     std::string key1("testGetKey1"), key2("testGetKey2");
120 
121     storeKey(instance, key1, "foo");
122     storeKey(instance, key2, "foo");
123 
124     lcb_CMDGET *cmd;
125 
126     lcb_cmdget_create(&cmd);
127     lcb_cmdget_key(cmd, key1.c_str(), key1.size());
128     EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, cmd));
129 
130     lcb_cmdget_key(cmd, key2.c_str(), key2.size());
131     EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, cmd));
132     lcb_cmdget_destroy(cmd);
133 
134     lcb_wait(instance, LCB_WAIT_DEFAULT);
135     EXPECT_EQ(2, numcallbacks);
136 
137     auto spans = mock->getTracer().spans;
138     ASSERT_EQ(4, spans.size());
139     auto span = spans[0];
140     assert_kv_span(span, "upsert", {});
141     span = spans[2];
142     assert_kv_span(span, "get", {});
143 
144     assert_kv_metrics(METRICS_OPS_METER_NAME, "get", 2, false);
145     assert_kv_metrics(METRICS_OPS_METER_NAME, "upsert", 2, false);
146 }
147 
148 extern "C" {
testTouchMissCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPTOUCH *resp)149 static void testTouchMissCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPTOUCH *resp)
150 {
151     int *counter;
152     lcb_resptouch_cookie(resp, (void **)&counter);
153     EXPECT_EQ(LCB_ERR_DOCUMENT_NOT_FOUND, lcb_resptouch_status(resp));
154     ++(*counter);
155 }
156 }
157 
158 /**
159  * @test Touch (Miss)
160  * @pre Schedule a touch for a non existent key with an expiry @c 666
161  * @post Touch fails with @c KEY_ENOENT
162  */
TEST_F(GetUnitTest, testTouchMiss)163 TEST_F(GetUnitTest, testTouchMiss)
164 {
165     std::string key("testTouchMissKey");
166     HandleWrap hw;
167     lcb_INSTANCE *instance;
168     createConnection(hw, &instance);
169 
170     (void)lcb_install_callback(instance, LCB_CALLBACK_TOUCH, (lcb_RESPCALLBACK)testTouchMissCallback);
171     removeKey(instance, key);
172 
173     int numcallbacks = 0;
174 
175     lcb_CMDTOUCH *cmd;
176     lcb_cmdtouch_create(&cmd);
177     lcb_cmdtouch_key(cmd, key.c_str(), key.size());
178     lcb_cmdtouch_expiry(cmd, 666);
179     lcb_touch(instance, &numcallbacks, cmd);
180     lcb_cmdtouch_destroy(cmd);
181     lcb_wait(instance, LCB_WAIT_DEFAULT);
182     EXPECT_EQ(1, numcallbacks);
183 }
184 
185 extern "C" {
testTouchHitCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPTOUCH *resp)186 static void testTouchHitCallback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPTOUCH *resp)
187 {
188     int *counter;
189     lcb_resptouch_cookie(resp, (void **)&counter);
190     EXPECT_EQ(LCB_SUCCESS, lcb_resptouch_status(resp));
191     ++(*counter);
192 }
193 }
194 
195 /**
196  * @test Touch (Hit)
197  * @pre Store a key, and schedule a touch operation with an expiry of @c 666
198  * @post Touch succeeds.
199  */
TEST_F(GetUnitTest, testTouchHit)200 TEST_F(GetUnitTest, testTouchHit)
201 {
202     std::string key("testTouchHitKey");
203     HandleWrap hw;
204     lcb_INSTANCE *instance;
205     createConnection(hw, &instance);
206 
207     (void)lcb_install_callback(instance, LCB_CALLBACK_TOUCH, (lcb_RESPCALLBACK)testTouchHitCallback);
208     storeKey(instance, key, "foo");
209 
210     int numcallbacks = 0;
211     lcb_CMDTOUCH *cmd;
212     lcb_cmdtouch_create(&cmd);
213     lcb_cmdtouch_key(cmd, key.c_str(), key.size());
214     lcb_cmdtouch_expiry(cmd, 666);
215     lcb_touch(instance, &numcallbacks, cmd);
216     lcb_cmdtouch_destroy(cmd);
217 
218     lcb_wait(instance, LCB_WAIT_DEFAULT);
219     EXPECT_EQ(1, numcallbacks);
220 }
221 
222 extern "C" {
flags_store_callback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)223 static void flags_store_callback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)
224 {
225     int *counter;
226     lcb_respstore_cookie(resp, (void **)&counter);
227     ASSERT_EQ(LCB_SUCCESS, lcb_respstore_status(resp));
228 
229     const char *key;
230     size_t nkey;
231     lcb_respstore_key(resp, &key, &nkey);
232     ASSERT_EQ(5, nkey);
233     ASSERT_EQ(0, memcmp(key, "flags", 5));
234 
235     lcb_STORE_OPERATION op;
236     lcb_respstore_operation(resp, &op);
237     ASSERT_EQ(LCB_STORE_UPSERT, op);
238     ++(*counter);
239 }
240 
flags_get_callback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)241 static void flags_get_callback(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)
242 {
243     int *counter;
244     lcb_respget_cookie(resp, (void **)&counter);
245     EXPECT_EQ(LCB_SUCCESS, lcb_respget_status(resp));
246 
247     const char *key;
248     size_t nkey;
249     lcb_respget_key(resp, &key, &nkey);
250     ASSERT_EQ(5, nkey);
251     ASSERT_EQ(0, memcmp(key, "flags", 5));
252 
253     const char *value;
254     size_t nvalue;
255     lcb_respget_value(resp, &value, &nvalue);
256     ASSERT_EQ(1, nvalue);
257     ASSERT_EQ(0, memcmp(value, "x", 1));
258 
259     uint32_t flags;
260     lcb_respget_flags(resp, &flags);
261     ASSERT_EQ(0xdeadbeef, flags);
262     ++(*counter);
263 }
264 }
265 
TEST_F(GetUnitTest, testFlags)266 TEST_F(GetUnitTest, testFlags)
267 {
268     lcb_INSTANCE *instance;
269     HandleWrap hw;
270 
271     createConnection(hw, &instance);
272 
273     (void)lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)flags_get_callback);
274     (void)lcb_install_callback(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)flags_store_callback);
275 
276     int numcallbacks = 0;
277 
278     lcb_CMDSTORE *scmd;
279     lcb_cmdstore_create(&scmd, LCB_STORE_UPSERT);
280     lcb_cmdstore_key(scmd, "flags", 5);
281     lcb_cmdstore_value(scmd, "x", 1);
282     lcb_cmdstore_flags(scmd, 0xdeadbeef);
283 
284     ASSERT_EQ(LCB_SUCCESS, lcb_store(instance, &numcallbacks, scmd));
285     lcb_cmdstore_destroy(scmd);
286 
287     // Wait for it to be persisted
288     lcb_wait(instance, LCB_WAIT_DEFAULT);
289 
290     lcb_CMDGET *gcmd;
291     lcb_cmdget_create(&gcmd);
292     lcb_cmdget_key(gcmd, "flags", 5);
293     ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, gcmd));
294     lcb_cmdget_destroy(gcmd);
295 
296     /* Wait for it to be received */
297     lcb_wait(instance, LCB_WAIT_DEFAULT);
298     EXPECT_EQ(2, numcallbacks);
299 }
300 
301 struct RGetCookie {
302     unsigned remaining{};
303     lcb_STATUS expectrc{};
304     std::string value;
305     lcb_U64 cas{};
306     int hits_active{0};
307     int hits_replicas{0};
308 };
309 
310 extern "C" {
rget_callback(lcb_INSTANCE *instance, int, const lcb_RESPGETREPLICA *resp)311 static void rget_callback(lcb_INSTANCE *instance, int, const lcb_RESPGETREPLICA *resp)
312 {
313     RGetCookie *rck;
314     lcb_respgetreplica_cookie(resp, (void **)&rck);
315 
316     if (lcb_respgetreplica_is_active(resp)) {
317         rck->hits_active++;
318     } else {
319         rck->hits_replicas++;
320     }
321 
322     lcb_STATUS rc = lcb_respgetreplica_status(resp);
323     ASSERT_EQ(rck->expectrc, rc);
324     ASSERT_NE(0, rck->remaining);
325     rck->remaining--;
326 
327     if (rc == LCB_SUCCESS) {
328         const char *v;
329         size_t n;
330         lcb_respgetreplica_value(resp, &v, &n);
331         std::string value(v, n);
332         ASSERT_STREQ(rck->value.c_str(), value.c_str());
333 
334         uint64_t cas;
335         lcb_respgetreplica_cas(resp, &cas);
336         ASSERT_EQ(rck->cas, cas);
337     }
338 }
rget_noop_callback(lcb_INSTANCE *, int, const lcb_RESPGETREPLICA *)339 static void rget_noop_callback(lcb_INSTANCE *, int, const lcb_RESPGETREPLICA *) {}
340 }
341 
TEST_F(GetUnitTest, testGetReplica)342 TEST_F(GetUnitTest, testGetReplica)
343 {
344     SKIP_UNLESS_MOCK()
345     MockEnvironment *mock = MockEnvironment::getInstance();
346     HandleWrap hw;
347     lcb_INSTANCE *instance;
348     createConnection(hw, &instance);
349     std::string key("a_key_GETREPLICA");
350     std::string val("a_value");
351 
352     lcb_CMDGETREPLICA *rcmd;
353 
354     lcb_install_callback(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPCALLBACK)rget_callback);
355     RGetCookie rck;
356     rck.remaining = 1;
357     rck.expectrc = LCB_SUCCESS;
358     int nreplicas = lcb_get_num_replicas(instance);
359 
360     for (int ii = 0; ii < nreplicas; ii++) {
361         MockMutationCommand mcCmd(MockCommand::CACHE, key);
362         mcCmd.cas = static_cast<uint64_t>(ii) + 100;
363         rck.cas = mcCmd.cas;
364         mcCmd.replicaList.clear();
365         mcCmd.replicaList.push_back(ii);
366 
367         mock->sendCommand(mcCmd);
368         mock->getResponse();
369 
370         lcb_STATUS err;
371         lcb_REPLICA_MODE mode;
372         switch (ii) {
373             case 0:
374                 mode = LCB_REPLICA_MODE_IDX0;
375                 break;
376             case 1:
377                 mode = LCB_REPLICA_MODE_IDX1;
378                 break;
379             case 2:
380                 mode = LCB_REPLICA_MODE_IDX2;
381                 break;
382             default:
383                 ASSERT_FALSE("Unexpected replica index");
384                 break;
385         }
386         lcb_cmdgetreplica_create(&rcmd, mode);
387         lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
388 
389         rck.remaining = 1;
390         lcb_sched_enter(instance);
391         err = lcb_getreplica(instance, &rck, rcmd);
392         ASSERT_EQ(LCB_SUCCESS, err);
393         lcb_cmdgetreplica_destroy(rcmd);
394 
395         lcb_sched_leave(instance);
396         lcb_wait(instance, LCB_WAIT_DEFAULT);
397         ASSERT_EQ(0, rck.remaining);
398     }
399 
400     // Test with the "All" mode
401     MockMutationCommand mcCmd(MockCommand::CACHE, key);
402     mcCmd.cas = 999;
403     mcCmd.onMaster = true;
404     mcCmd.replicaCount = nreplicas;
405     mock->sendCommand(mcCmd);
406     mock->getResponse();
407 
408     rck.remaining = nreplicas + 1 /* active */;
409     rck.cas = mcCmd.cas;
410     rck.expectrc = LCB_SUCCESS;
411     rck.hits_active = rck.hits_replicas = 0;
412 
413     lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_ALL);
414     lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
415     lcb_sched_enter(instance);
416     lcb_STATUS err = lcb_getreplica(instance, &rck, rcmd);
417     lcb_cmdgetreplica_destroy(rcmd);
418     ASSERT_EQ(LCB_SUCCESS, err);
419     lcb_sched_leave(instance);
420 
421     lcb_wait(instance, LCB_WAIT_DEFAULT);
422     ASSERT_EQ(0, rck.remaining);
423     ASSERT_EQ(1, rck.hits_active);
424     ASSERT_EQ(nreplicas, rck.hits_replicas);
425 
426     MockMutationCommand purgeCmd(MockCommand::PURGE, key);
427     purgeCmd.onMaster = true;
428     purgeCmd.replicaCount = nreplicas;
429     mock->sendCommand(purgeCmd);
430     mock->getResponse();
431 
432     // Test with the "First" mode. Ensure that only the _last_ replica
433     // contains the item
434     mcCmd.onMaster = false;
435     mcCmd.replicaCount = 0;
436     mcCmd.replicaList.clear();
437     mcCmd.replicaList.push_back(nreplicas - 1);
438     mcCmd.cas = 42;
439     rck.cas = mcCmd.cas;
440 
441     // Set the timeout to something higher, since we have more than one packet
442     // to send.
443     lcb_cntl_setu32(instance, LCB_CNTL_OP_TIMEOUT, 10000000);
444 
445     // The first replica should respond with ENOENT, the second should succeed
446     // though
447     mock->sendCommand(mcCmd);
448     mock->getResponse();
449     lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_ANY);
450     lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
451     rck.remaining = 1;
452     lcb_sched_enter(instance);
453     err = lcb_getreplica(instance, &rck, rcmd);
454     lcb_cmdgetreplica_destroy(rcmd);
455     ASSERT_EQ(LCB_SUCCESS, err);
456     lcb_sched_leave(instance);
457     lcb_wait(instance, LCB_WAIT_DEFAULT);
458     ASSERT_EQ(0, rck.remaining);
459 
460     // Test with an invalid index
461     rcmd = nullptr;
462     ASSERT_EQ(LCB_ERR_INVALID_ARGUMENT, lcb_cmdgetreplica_create(&rcmd, (lcb_REPLICA_MODE)42));
463     ASSERT_EQ((lcb_CMDGETREPLICA *)nullptr, rcmd);
464 
465     // If no crash, it's good.
466     if (lcb_get_num_replicas(instance) > 1) {
467         // Use the 'first' mode, but make the second replica index be -1, so
468         // that in the retry we need to skip over an index.
469 
470         lcbvb_CONFIG *vbc;
471         err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBCONFIG, (void *)&vbc);
472         int vbid = lcbvb_k2vb(vbc, key.c_str(), key.size());
473         int oldix;
474 
475         lcbvb_VBUCKET *vb = &vbc->vbuckets[vbid];
476         oldix = vb->servers[2];
477         vb->servers[2] = -1;
478 
479         rck.expectrc = LCB_ERR_DOCUMENT_NOT_FOUND;
480         rck.remaining = 1;
481         lcb_sched_enter(instance);
482         lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_ANY);
483         lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
484         err = lcb_getreplica(instance, &rck, rcmd);
485         lcb_cmdgetreplica_destroy(rcmd);
486         ASSERT_EQ(LCB_SUCCESS, err);
487         lcb_sched_leave(instance);
488         lcb_wait(instance, LCB_WAIT_DEFAULT);
489         ASSERT_EQ(0, rck.remaining);
490 
491         // Try with ALL again (should give an error)
492         lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_ALL);
493         lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
494         lcb_sched_enter(instance);
495         err = lcb_getreplica(instance, nullptr, rcmd);
496         lcb_cmdgetreplica_destroy(rcmd);
497         ASSERT_EQ(LCB_ERR_NO_MATCHING_SERVER, err);
498         lcb_sched_leave(instance);
499 
500         vb->servers[2] = oldix;
501     } else {
502         printf("Not enough replicas for get-with-replica test\n");
503     }
504 
505     // Test rget with a missing key. Fixes a potential bug
506     lcb_install_callback(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPCALLBACK)rget_noop_callback);
507     removeKey(instance, key);
508     lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_ANY);
509     lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
510     lcb_sched_enter(instance);
511     err = lcb_getreplica(instance, nullptr, rcmd);
512     lcb_cmdgetreplica_destroy(rcmd);
513     lcb_sched_leave(instance);
514     lcb_wait(instance, LCB_WAIT_DEFAULT);
515 }
516 
517 extern "C" {
store_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)518 static void store_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)
519 {
520     size_t *counter;
521     lcb_respstore_cookie(resp, (void **)&counter);
522     lcb_STATUS rc = lcb_respstore_status(resp);
523     EXPECT_EQ(LCB_SUCCESS, rc);
524     ++(*counter);
525 }
526 
store_error_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)527 static void store_error_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)
528 {
529     size_t *counter;
530     lcb_respstore_cookie(resp, (void **)&counter);
531     lcb_STATUS rc = lcb_respstore_status(resp);
532     EXPECT_TRUE(rc == LCB_SUCCESS || rc == LCB_ERR_AUTHENTICATION_FAILURE);
533     // EXPECT_EQ(LCB_ERR_AUTHENTICATION_FAILURE, rc);
534     ++(*counter);
535 }
536 }
537 
get_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)538 static void get_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)
539 {
540     size_t *counter;
541     lcb_respget_cookie(resp, (void **)&counter);
542     lcb_STATUS rc = lcb_respget_status(resp);
543     const char *key;
544     size_t nkey;
545     lcb_respget_key(resp, &key, &nkey);
546     std::string keystr(key, nkey);
547     ++(*counter);
548     lcb_log(LOGARGS(instance, DEBUG), "receive '%s' on get callback %lu, status: %s", keystr.c_str(), size_t(*counter),
549             lcb_strerror_short(rc));
550 }
551 
552 struct ReplicaGetCookie {
553     unsigned remaining{};
554     std::set<lcb_STATUS> expectrc;
555 };
556 
replicaget_callback(lcb_INSTANCE *instance, int, const lcb_RESPGETREPLICA *resp)557 static void replicaget_callback(lcb_INSTANCE *instance, int, const lcb_RESPGETREPLICA *resp)
558 {
559     ReplicaGetCookie *rck;
560     lcb_respgetreplica_cookie(resp, (void **)&rck);
561 
562     lcb_STATUS rc = lcb_respgetreplica_status(resp);
563     EXPECT_EQ(1, rck->expectrc.count(rc));
564     EXPECT_NE(0, rck->remaining);
565     rck->remaining--;
566 }
567 
TEST_F(GetUnitTest, testFailoverAndGetReplica)568 TEST_F(GetUnitTest, testFailoverAndGetReplica)
569 {
570     SKIP_UNLESS_MOCK()
571     const char *argv[] = {"--replicas", "3", "--nodes", "4", nullptr};
572     MockEnvironment mock_o(argv), *mock = &mock_o;
573     HandleWrap hw;
574     lcb_INSTANCE *instance;
575     mock->createConnection(hw, &instance);
576     lcb_connect(instance);
577     lcb_wait(instance, LCB_WAIT_DEFAULT);
578     ASSERT_EQ(3, lcb_get_num_replicas(instance));
579     ASSERT_EQ(4, lcb_get_num_nodes(instance));
580 
581     // Set the timeout for 100 ms
582     lcb_uint32_t tmoval = 100000;
583     lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmoval);
584     // Reduce configuration poll interval to get new configuration sooner
585     lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_CONFIG_POLL_INTERVAL, &tmoval);
586 
587     // store keys
588     size_t counter = 0;
589     lcb_CMDSTORE *scmd;
590     lcb_cmdstore_create(&scmd, LCB_STORE_UPSERT);
591     std::string key("key");
592     lcb_cmdstore_key(scmd, key.c_str(), strlen(key.c_str()));
593     lcb_cmdstore_value(scmd, "val", 3);
594     ASSERT_EQ(LCB_SUCCESS, lcb_store(instance, &counter, scmd));
595     lcb_cmdstore_destroy(scmd);
596     lcb_install_callback(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_callback);
597     lcb_wait(instance, LCB_WAIT_NOCHECK);
598     ASSERT_EQ(1U, counter);
599 
600     // check server index
601     int nodeFirstReplica = mock->getKeyIndex(instance, key, "default", 1);
602     // failover node first replica
603     mock->failoverNode(nodeFirstReplica, "default", false);
604     lcb_log(LOGARGS(instance, INFO), "Failover node %d (1st replica)...", nodeFirstReplica);
605 
606     counter = 0;
607     {
608         lcb_CMDGET *gcmd;
609         lcb_cmdget_create(&gcmd);
610         lcb_cmdget_key(gcmd, key.c_str(), strlen(key.c_str()));
611         ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, &counter, gcmd));
612         lcb_cmdget_destroy(gcmd);
613         lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
614         lcb_log(LOGARGS(instance, INFO), "get master");
615         lcb_wait(instance, LCB_WAIT_DEFAULT);
616         ASSERT_EQ(1U, counter);
617     }
618 
619     // check server second replica
620     int nodeSecondReplica = mock->getKeyIndex(instance, key, "default", 2);
621     // failover node second replica
622     mock->failoverNode(nodeSecondReplica, "default", false);
623     lcb_log(LOGARGS(instance, INFO), "Failover node %d (2nd replica)...", nodeSecondReplica);
624 
625     // check server third replica
626     int nodeThirdReplica = mock->getKeyIndex(instance, key, "default", 3);
627     // failover node third replica
628     mock->failoverNode(nodeThirdReplica, "default", false);
629     lcb_log(LOGARGS(instance, INFO), "Failover node %d (3rd replica)...", nodeThirdReplica);
630 
631     usleep(LCB_MS2US(300));
632     {
633         lcb_CMDGETREPLICA *rcmd;
634         lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_IDX2); // third replica
635         lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
636         ReplicaGetCookie rck;
637         rck.remaining = 1;
638         rck.expectrc.insert(LCB_ERR_MAP_CHANGED);
639         rck.expectrc.insert(LCB_ERR_TIMEOUT);
640         EXPECT_EQ(LCB_SUCCESS, lcb_getreplica(instance, &rck, rcmd));
641         lcb_cmdgetreplica_destroy(rcmd);
642         lcb_install_callback(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPCALLBACK)replicaget_callback);
643         lcb_log(LOGARGS(instance, INFO), "get third replica");
644         lcb_wait(instance, LCB_WAIT_DEFAULT);
645         EXPECT_EQ(0, rck.remaining);
646     }
647     {
648         lcb_CMDGETREPLICA *rcmd;
649         lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_IDX1); // second replica
650         lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
651         ReplicaGetCookie rck;
652         rck.remaining = 1;
653         rck.expectrc.insert(LCB_ERR_MAP_CHANGED);
654         rck.expectrc.insert(LCB_ERR_TIMEOUT);
655         lcb_STATUS rc = lcb_getreplica(instance, &rck, rcmd);
656         EXPECT_TRUE(rc == LCB_SUCCESS || rc == LCB_ERR_NO_MATCHING_SERVER);
657         lcb_cmdgetreplica_destroy(rcmd);
658         if (rc == LCB_SUCCESS) {
659             lcb_install_callback(instance, LCB_CALLBACK_GETREPLICA, (lcb_RESPCALLBACK)replicaget_callback);
660             lcb_log(LOGARGS(instance, INFO), "get second replica");
661             lcb_wait(instance, LCB_WAIT_DEFAULT);
662             EXPECT_EQ(0, rck.remaining);
663         }
664     }
665     lcb_tick_nowait(instance);
666     {
667         lcb_CMDGETREPLICA *rcmd;
668         lcb_cmdgetreplica_create(&rcmd, LCB_REPLICA_MODE_IDX0); // first replica
669         lcb_cmdgetreplica_key(rcmd, key.c_str(), key.size());
670         /* here we definitely have new configuration already and the library will reject get_with_replica request */
671         EXPECT_EQ(LCB_ERR_NO_MATCHING_SERVER, lcb_getreplica(instance, nullptr, rcmd));
672         lcb_cmdgetreplica_destroy(rcmd);
673     }
674 
675     counter = 0;
676     {
677         lcb_CMDGET *gcmd;
678         lcb_cmdget_create(&gcmd);
679         lcb_cmdget_key(gcmd, key.c_str(), strlen(key.c_str()));
680         EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &counter, gcmd));
681         lcb_cmdget_destroy(gcmd);
682         lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
683         lcb_log(LOGARGS(instance, INFO), "get master");
684         lcb_wait(instance, LCB_WAIT_NOCHECK);
685         EXPECT_EQ(1U, counter);
686     }
687 }
688 
689 // FIXME: revisit the test, re-enable it after migration to caves
TEST_F(GetUnitTest, DISABLED_testFailoverAndMultiGet)690 TEST_F(GetUnitTest, DISABLED_testFailoverAndMultiGet)
691 {
692     SKIP_UNLESS_MOCK()
693     MockEnvironment *mock = MockEnvironment::getInstance();
694     HandleWrap hw;
695     lcb_INSTANCE *instance;
696     createConnection(hw, &instance);
697     size_t nbCallbacks = 50;
698     std::vector<std::string> keys;
699     keys.resize(nbCallbacks);
700 
701     // Set the timeout for 100 ms
702     lcb_uint32_t tmoval = 100000;
703     lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_OP_TIMEOUT, &tmoval);
704 
705     // store keys
706     lcb_sched_enter(instance);
707 
708     size_t counter = 0;
709     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
710         lcb_CMDSTORE *scmd;
711         lcb_cmdstore_create(&scmd, LCB_STORE_UPSERT);
712         char key[6];
713         sprintf(key, "key%zu", ii);
714         keys[ii] = std::string(key);
715         lcb_cmdstore_key(scmd, key, strlen(key));
716         lcb_cmdstore_value(scmd, "val", 3);
717         ASSERT_EQ(LCB_SUCCESS, lcb_store(instance, &counter, scmd));
718         lcb_cmdstore_destroy(scmd);
719     }
720 
721     lcb_sched_leave(instance);
722     lcb_install_callback(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_callback);
723     lcb_wait(instance, LCB_WAIT_NOCHECK);
724     ASSERT_EQ(nbCallbacks, counter);
725 
726     // check server index
727     size_t nbKeyinNode0 = 0;
728     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
729         if (mock->getKeyIndex(instance, keys[ii]) == 0) { // master copy of key in node 0
730             ++nbKeyinNode0;
731         }
732     }
733     EXPECT_GE(nbKeyinNode0, 2); // at least 2 keys with master in node 0
734 
735     // multiget OK
736     lcb_sched_enter(instance);
737 
738     counter = 0;
739     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
740         lcb_CMDGET *gcmd;
741         lcb_cmdget_create(&gcmd);
742         lcb_cmdget_key(gcmd, keys[ii].c_str(), strlen(keys[ii].c_str()));
743         ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, &counter, gcmd));
744         lcb_cmdget_destroy(gcmd);
745     }
746 
747     lcb_sched_leave(instance);
748     lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
749     lcb_wait(instance, LCB_WAIT_NOCHECK);
750     ASSERT_EQ(nbCallbacks, counter);
751 
752     // failover index 0
753     mock->failoverNode(0, "default", false);
754     lcb_log(LOGARGS(instance, INFO), "Failover node 0 ...");
755 
756     // multiget KO
757     lcb_sched_enter(instance);
758 
759     counter = 0;
760     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
761         lcb_CMDGET *gcmd;
762         lcb_cmdget_create(&gcmd);
763         lcb_cmdget_key(gcmd, keys[ii].c_str(), strlen(keys[ii].c_str()));
764         ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, &counter, gcmd));
765         lcb_cmdget_destroy(gcmd);
766     }
767 
768     lcb_sched_leave(instance);
769     lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
770     lcb_wait(instance, LCB_WAIT_NOCHECK);
771     ASSERT_EQ(nbCallbacks, counter);
772 
773     // multiget KO
774     lcb_sched_enter(instance);
775 
776     counter = 0;
777     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
778         lcb_CMDGET *gcmd;
779         lcb_cmdget_create(&gcmd);
780         lcb_cmdget_key(gcmd, keys[ii].c_str(), strlen(keys[ii].c_str()));
781         ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, &counter, gcmd));
782         lcb_cmdget_destroy(gcmd);
783     }
784 
785     lcb_sched_leave(instance);
786     lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
787     lcb_wait(instance, LCB_WAIT_NOCHECK);
788     ASSERT_EQ(nbCallbacks, counter);
789 
790     // multiget KO
791     lcb_sched_enter(instance);
792 
793     counter = 0;
794     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
795         lcb_CMDGET *gcmd;
796         lcb_cmdget_create(&gcmd);
797         lcb_cmdget_key(gcmd, keys[ii].c_str(), strlen(keys[ii].c_str()));
798         ASSERT_EQ(LCB_SUCCESS, lcb_get(instance, &counter, gcmd));
799         lcb_cmdget_destroy(gcmd);
800     }
801 
802     lcb_sched_leave(instance);
803     lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)get_callback);
804     lcb_wait(instance, LCB_WAIT_NOCHECK);
805     ASSERT_EQ(nbCallbacks, counter);
806 }
807 
808 extern "C" {
809 struct pl_result {
810     lcb_STATUS status{LCB_ERR_GENERIC};
811     bool invoked{false};
812     uint64_t cas{0};
813 };
814 
pl_store_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)815 static void pl_store_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPSTORE *resp)
816 {
817     pl_result *res = nullptr;
818     lcb_respstore_cookie(resp, (void **)&res);
819     res->invoked = true;
820     res->status = lcb_respstore_status(resp);
821     if (res->status == LCB_SUCCESS) {
822         lcb_respstore_cas(resp, &res->cas);
823     }
824 }
825 
pl_get_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)826 static void pl_get_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)
827 {
828     pl_result *res = nullptr;
829     lcb_respget_cookie(resp, (void **)&res);
830     res->invoked = true;
831     res->status = lcb_respget_status(resp);
832     if (res->status == LCB_SUCCESS) {
833         lcb_respget_cas(resp, &res->cas);
834     }
835 }
836 
pl_unlock_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPUNLOCK *resp)837 static void pl_unlock_callback(lcb_INSTANCE *instance, lcb_CALLBACK_TYPE, const lcb_RESPUNLOCK *resp)
838 {
839     pl_result *res = nullptr;
840     lcb_respunlock_cookie(resp, (void **)&res);
841     res->invoked = true;
842     res->status = lcb_respunlock_status(resp);
843     if (res->status == LCB_SUCCESS) {
844         lcb_respunlock_cas(resp, &res->cas);
845     }
846 }
847 }
848 
TEST_F(GetUnitTest, testPessimisticLock)849 TEST_F(GetUnitTest, testPessimisticLock)
850 {
851     SKIP_IF_MOCK()
852     MockEnvironment *mock = MockEnvironment::getInstance();
853     HandleWrap hw;
854     lcb_INSTANCE *instance;
855     createConnection(hw, &instance);
856 
857     lcb_install_callback(instance, LCB_CALLBACK_GET, reinterpret_cast<lcb_RESPCALLBACK>(pl_get_callback));
858     lcb_install_callback(instance, LCB_CALLBACK_STORE, reinterpret_cast<lcb_RESPCALLBACK>(pl_store_callback));
859     lcb_install_callback(instance, LCB_CALLBACK_UNLOCK, reinterpret_cast<lcb_RESPCALLBACK>(pl_unlock_callback));
860 
861     std::string key(unique_name("testPessimisticLock"));
862     std::uint32_t lock_time_s = 10;
863 
864     std::uint64_t cas{0};
865     {
866         pl_result res{};
867 
868         std::string value{"foo"};
869         lcb_CMDSTORE *cmd = nullptr;
870         lcb_cmdstore_create(&cmd, LCB_STORE_UPSERT);
871         lcb_cmdstore_key(cmd, key.c_str(), key.size());
872         lcb_cmdstore_value(cmd, value.c_str(), value.size());
873         lcb_store(instance, &res, cmd);
874         lcb_cmdstore_destroy(cmd);
875         lcb_wait(instance, LCB_WAIT_DEFAULT);
876 
877         ASSERT_TRUE(res.invoked);
878         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
879         cas = res.cas;
880     }
881     {
882         // lock and record CAS of the locked document
883         pl_result res{};
884 
885         lcb_CMDGET *cmd = nullptr;
886         lcb_cmdget_create(&cmd);
887         lcb_cmdget_key(cmd, key.c_str(), key.size());
888         lcb_cmdget_locktime(cmd, lock_time_s);
889         lcb_get(instance, &res, cmd);
890         lcb_cmdget_destroy(cmd);
891         lcb_wait(instance, LCB_WAIT_DEFAULT);
892 
893         ASSERT_TRUE(res.invoked);
894         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
895         ASSERT_NE(cas, res.cas);
896         cas = res.cas;
897     }
898     {
899         // real CAS is masked now and not visible by regular GET
900         pl_result res{};
901 
902         lcb_CMDGET *cmd = nullptr;
903         lcb_cmdget_create(&cmd);
904         lcb_cmdget_key(cmd, key.c_str(), key.size());
905         lcb_get(instance, &res, cmd);
906         lcb_cmdget_destroy(cmd);
907         lcb_wait(instance, LCB_WAIT_DEFAULT);
908 
909         ASSERT_TRUE(res.invoked);
910         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
911         ASSERT_NE(cas, res.cas);
912     }
913     {
914         // it is not allowed to lock the same key twice
915         pl_result res{};
916 
917         lcb_CMDGET *cmd = nullptr;
918         lcb_cmdget_create(&cmd);
919         lcb_cmdget_key(cmd, key.c_str(), key.size());
920         lcb_cmdget_locktime(cmd, lock_time_s);
921         lcb_get(instance, &res, cmd);
922         lcb_cmdget_destroy(cmd);
923         lcb_wait(instance, LCB_WAIT_DEFAULT);
924 
925         ASSERT_TRUE(res.invoked);
926         ASSERT_STATUS_EQ(LCB_ERR_DOCUMENT_LOCKED, res.status);
927     }
928     {
929         // it is not allowed to mutate the locked key
930         pl_result res{};
931 
932         std::string value{"foo"};
933         lcb_CMDSTORE *cmd = nullptr;
934         lcb_cmdstore_create(&cmd, LCB_STORE_UPSERT);
935         lcb_cmdstore_key(cmd, key.c_str(), key.size());
936         lcb_cmdstore_value(cmd, value.c_str(), value.size());
937         lcb_store(instance, &res, cmd);
938         lcb_cmdstore_destroy(cmd);
939         lcb_wait(instance, LCB_WAIT_DEFAULT);
940 
941         ASSERT_TRUE(res.invoked);
942         ASSERT_STATUS_EQ(LCB_ERR_DOCUMENT_LOCKED, res.status);
943     }
944     {
945         // but mutating the locked key is allowed with known cas
946         pl_result res{};
947 
948         std::string value{"foo"};
949         lcb_CMDSTORE *cmd = nullptr;
950         lcb_cmdstore_create(&cmd, LCB_STORE_REPLACE);
951         lcb_cmdstore_key(cmd, key.c_str(), key.size());
952         lcb_cmdstore_value(cmd, value.c_str(), value.size());
953         lcb_cmdstore_cas(cmd, cas);
954         lcb_store(instance, &res, cmd);
955         lcb_cmdstore_destroy(cmd);
956         lcb_wait(instance, LCB_WAIT_DEFAULT);
957 
958         ASSERT_TRUE(res.invoked);
959         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
960     }
961     {
962         pl_result res{};
963 
964         lcb_CMDGET *cmd = nullptr;
965         lcb_cmdget_create(&cmd);
966         lcb_cmdget_key(cmd, key.c_str(), key.size());
967         lcb_cmdget_locktime(cmd, lock_time_s);
968         lcb_get(instance, &res, cmd);
969         lcb_cmdget_destroy(cmd);
970         lcb_wait(instance, LCB_WAIT_DEFAULT);
971 
972         ASSERT_TRUE(res.invoked);
973         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
974         ASSERT_NE(cas, res.cas);
975         cas = res.cas;
976     }
977     {
978         // to unlock key without mutation, lcb_unlock might be used
979         pl_result res{};
980 
981         std::string value{"foo"};
982         lcb_CMDUNLOCK *cmd = nullptr;
983         lcb_cmdunlock_create(&cmd);
984         lcb_cmdunlock_key(cmd, key.c_str(), key.size());
985         lcb_cmdunlock_cas(cmd, cas);
986         lcb_unlock(instance, &res, cmd);
987         lcb_cmdunlock_destroy(cmd);
988         lcb_wait(instance, LCB_WAIT_DEFAULT);
989 
990         ASSERT_TRUE(res.invoked);
991         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
992     }
993     {
994         // now the key is not locked
995         pl_result res{};
996 
997         std::string value{"foo"};
998         lcb_CMDSTORE *cmd = nullptr;
999         lcb_cmdstore_create(&cmd, LCB_STORE_UPSERT);
1000         lcb_cmdstore_key(cmd, key.c_str(), key.size());
1001         lcb_cmdstore_value(cmd, value.c_str(), value.size());
1002         lcb_store(instance, &res, cmd);
1003         lcb_cmdstore_destroy(cmd);
1004         lcb_wait(instance, LCB_WAIT_DEFAULT);
1005 
1006         ASSERT_TRUE(res.invoked);
1007         ASSERT_STATUS_EQ(LCB_SUCCESS, res.status);
1008     }
1009 }
1010 
1011 extern "C" {
test_canceled_callback(lcb_INSTANCE *, int, const lcb_RESPGET *resp)1012 static void test_canceled_callback(lcb_INSTANCE *, int, const lcb_RESPGET *resp)
1013 {
1014     int *counter;
1015     ASSERT_EQ(LCB_ERR_REQUEST_CANCELED, lcb_respget_status(resp));
1016     lcb_respget_cookie(resp, (void **)&counter);
1017     ++(*counter);
1018 }
1019 }
1020 
TEST_F(GetUnitTest, testGetCanceled)1021 TEST_F(GetUnitTest, testGetCanceled)
1022 {
1023     SKIP_UNLESS_MOCK()
1024     HandleWrap hw;
1025     lcb_INSTANCE *instance;
1026     std::string key("keyGetCanceled");
1027     MockEnvironment *mock = MockEnvironment::getInstance();
1028     createConnection(hw, &instance);
1029     lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)test_canceled_callback);
1030     mock->hiccupNodes(2000, 1);
1031     int numcallbacks = 0;
1032     {
1033         lcb_CMDGET *cmd = nullptr;
1034         lcb_cmdget_create(&cmd);
1035         lcb_cmdget_key(cmd, key.c_str(), key.size());
1036         EXPECT_EQ(LCB_SUCCESS, lcb_get(instance, &numcallbacks, cmd));
1037         lcb_cmdget_destroy(cmd);
1038     }
1039     hw.destroy();
1040 
1041     auto *io_plugin = getenv("LCB_IOPS_NAME");
1042     if (io_plugin != nullptr && strcmp(io_plugin, "libuv") == 0) {
1043         /* for libuv it is acceptable that the IO loop might outlive the instance,
1044          * in this case the callback will not be invoked
1045          * TODO: update this condition once KV operations will accept callbacks directly without lookup through instance
1046          */
1047         EXPECT_TRUE(numcallbacks == 1 || numcallbacks == 0);
1048     } else {
1049         EXPECT_EQ(1, numcallbacks);
1050     }
1051 }
1052 
1053 extern "C" {
change_password_http_callback(lcb_INSTANCE *instance, int cbtype, const lcb_RESPHTTP *resp)1054 static void change_password_http_callback(lcb_INSTANCE *instance, int cbtype, const lcb_RESPHTTP *resp)
1055 {
1056     const char *body = nullptr;
1057     size_t nbody = 0;
1058     lcb_resphttp_body(resp, &body, &nbody);
1059     uint16_t status;
1060     lcb_resphttp_http_status(resp, &status);
1061     EXPECT_EQ(200, status) << std::string(body, nbody);
1062     const char *const *headers;
1063     EXPECT_EQ(LCB_SUCCESS, lcb_resphttp_headers(resp, &headers));
1064     EXPECT_EQ(LCB_SUCCESS, lcb_resphttp_status(resp));
1065 }
1066 
bootstrap_callback(lcb_INSTANCE *instance, lcb_STATUS err)1067 static void bootstrap_callback(lcb_INSTANCE *instance, lcb_STATUS err)
1068 {
1069     EXPECT_TRUE(err == LCB_SUCCESS || err == LCB_ERR_BUCKET_NOT_FOUND || err == LCB_ERR_AUTHENTICATION_FAILURE);
1070     EXPECT_NE(err, LCB_ERR_NO_MATCHING_SERVER);
1071 }
1072 }
1073 
change_password(lcb_INSTANCE *instance, const std::string &current_password, const std::string &new_password)1074 static void change_password(lcb_INSTANCE *instance, const std::string &current_password,
1075                             const std::string &new_password)
1076 {
1077     lcb_CMDHTTP *cmd;
1078     lcb_STATUS err;
1079     std::string username = MockEnvironment::getInstance()->getUsername();
1080     std::string path = "/controller/changePassword";
1081     std::string body = "password=" + new_password;
1082     std::string content_type = "application/x-www-form-urlencoded";
1083 
1084     lcb_cmdhttp_create(&cmd, LCB_HTTP_TYPE_MANAGEMENT);
1085     lcb_cmdhttp_method(cmd, LCB_HTTP_METHOD_POST);
1086     lcb_cmdhttp_content_type(cmd, content_type.c_str(), content_type.size());
1087     lcb_cmdhttp_path(cmd, path.c_str(), path.size());
1088     lcb_cmdhttp_body(cmd, body.c_str(), body.size());
1089     lcb_cmdhttp_username(cmd, username.c_str(), username.size());
1090     lcb_cmdhttp_password(cmd, current_password.c_str(), current_password.size());
1091 
1092     err = lcb_http(instance, nullptr, cmd);
1093     lcb_cmdhttp_destroy(cmd);
1094     EXPECT_EQ(LCB_SUCCESS, err);
1095     err = lcb_wait(instance, LCB_WAIT_DEFAULT);
1096     EXPECT_EQ(LCB_SUCCESS, err);
1097 }
1098 
TEST_F(GetUnitTest, testChangePassword)1099 TEST_F(GetUnitTest, testChangePassword)
1100 {
1101     SKIP_IF_MOCK();
1102     MockEnvironment *mock = MockEnvironment::getInstance();
1103     HandleWrap hw;
1104     lcb_INSTANCE *instance;
1105     size_t nbCallbacks = 1;
1106     std::vector<std::string> keys;
1107     std::string key = "key";
1108     createConnection(hw, &instance);
1109     HandleWrap hwHttp;
1110     lcb_INSTANCE *instanceHttp;
1111     createConnection(hwHttp, &instanceHttp);
1112     (void)lcb_install_callback(instanceHttp, LCB_CALLBACK_HTTP, (lcb_RESPCALLBACK)change_password_http_callback);
1113 
1114     // store keys
1115     // run one set to connect to only one node
1116     // the goal is to change the password then try to connect others nodes
1117     keys.resize(nbCallbacks);
1118     lcb_install_callback(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_callback);
1119     size_t counter = 0;
1120     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
1121         lcb_log(LOGARGS(instance, INFO), "*************** running a set (OK) ***************");
1122         lcb_CMDSTORE *scmd;
1123         lcb_cmdstore_create(&scmd, LCB_STORE_UPSERT);
1124         char akey[6];
1125         sprintf(akey, "key%lu", ii);
1126         keys[ii] = std::string(akey);
1127         lcb_cmdstore_key(scmd, akey, strlen(akey));
1128         lcb_cmdstore_value(scmd, "val", 3);
1129         EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &counter, scmd));
1130         lcb_cmdstore_destroy(scmd);
1131 
1132         lcb_wait(instance, LCB_WAIT_DEFAULT);
1133     }
1134     EXPECT_EQ(nbCallbacks, counter);
1135 
1136     // change password
1137     std::string wrong_password = "wrong_password";
1138     std::string correct_password = mock->getPassword();
1139     lcb_log(LOGARGS(instanceHttp, INFO),
1140             "*************** Change RBAC correct_password '%s' for user 'myuser' ***************",
1141             wrong_password.c_str());
1142     change_password(instanceHttp, correct_password, wrong_password);
1143     sleep(3);
1144 
1145     // store keys
1146     // after changing password, run multiget trying to connect others nodes not already connected
1147     nbCallbacks = 30;
1148     lcb_install_callback(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_error_callback);
1149     keys.resize(nbCallbacks);
1150     counter = 0;
1151     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
1152         lcb_log(LOGARGS(instance, INFO), "*************** running a set (sometimes KO) ***************");
1153         lcb_CMDSTORE *scmd;
1154         lcb_cmdstore_create(&scmd, LCB_STORE_UPSERT);
1155         char akey[6];
1156         sprintf(akey, "key%lu", ii);
1157         keys[ii] = std::string(akey);
1158         lcb_cmdstore_key(scmd, akey, strlen(akey));
1159         lcb_cmdstore_value(scmd, "val", 3);
1160         EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &counter, scmd));
1161         lcb_cmdstore_destroy(scmd);
1162 
1163         lcb_wait(instance, LCB_WAIT_DEFAULT);
1164     }
1165     EXPECT_EQ(nbCallbacks, counter);
1166 
1167     // reinit password to myuser
1168     lcb_log(LOGARGS(instanceHttp, INFO), "*************** Reinit Rbac password '%s' for user 'myuser' ***************",
1169             correct_password.c_str());
1170     change_password(instanceHttp, wrong_password, correct_password);
1171     sleep(3);
1172 
1173     // rollback password (to the correct one) and run multiget
1174     nbCallbacks = 30;
1175     lcb_install_callback(instance, LCB_CALLBACK_STORE, (lcb_RESPCALLBACK)store_callback);
1176     counter = 0;
1177     for (size_t ii = 0; ii < nbCallbacks; ++ii) {
1178         lcb_log(LOGARGS(instance, INFO), "*************** running a set (OK) ***************");
1179         lcb_CMDSTORE *scmd;
1180         lcb_cmdstore_create(&scmd, LCB_STORE_UPSERT);
1181         char akey[6];
1182         sprintf(akey, "key%lu", ii);
1183         keys[ii] = std::string(akey);
1184         lcb_cmdstore_key(scmd, akey, strlen(akey));
1185         lcb_cmdstore_value(scmd, "val", 3);
1186         EXPECT_EQ(LCB_SUCCESS, lcb_store(instance, &counter, scmd));
1187         lcb_cmdstore_destroy(scmd);
1188 
1189         lcb_wait(instance, LCB_WAIT_DEFAULT);
1190     }
1191     EXPECT_EQ(nbCallbacks, counter);
1192 }
1193 
1194 struct touch_result {
1195     bool called{false};
1196     lcb_STATUS rc{LCB_ERR_GENERIC};
1197 };
1198 
1199 struct gat_result {
1200     bool called{false};
1201     lcb_STATUS rc{LCB_ERR_GENERIC};
1202     std::string value{};
1203 };
1204 
1205 extern "C" {
test_touch_zero_reset(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPTOUCH *resp)1206 static void test_touch_zero_reset(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPTOUCH *resp)
1207 {
1208     touch_result *result;
1209     lcb_resptouch_cookie(resp, (void **)&result);
1210     result->called = true;
1211     result->rc = lcb_resptouch_status(resp);
1212 }
1213 
test_get_and_touch_zero_reset(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)1214 static void test_get_and_touch_zero_reset(lcb_INSTANCE *, lcb_CALLBACK_TYPE, const lcb_RESPGET *resp)
1215 {
1216     gat_result *result;
1217     lcb_respget_cookie(resp, (void **)&result);
1218     result->called = true;
1219     result->rc = lcb_respget_status(resp);
1220     const char *value = nullptr;
1221     std::size_t value_len = 0;
1222     lcb_respget_value(resp, &value, &value_len);
1223     if (value_len > 0 && value != nullptr) {
1224         result->value.assign(value, value_len);
1225     }
1226 }
1227 }
1228 
TEST_F(GetUnitTest, testTouchWithZeroExpiryResetsExpiry)1229 TEST_F(GetUnitTest, testTouchWithZeroExpiryResetsExpiry)
1230 {
1231     std::string key = unique_name("gat_reset_expiry_key");
1232     std::string value = unique_name("gat_reset_expiry_value");
1233 
1234     HandleWrap hw;
1235     lcb_INSTANCE *instance;
1236     createConnection(hw, &instance);
1237 
1238     (void)lcb_install_callback(instance, LCB_CALLBACK_TOUCH, (lcb_RESPCALLBACK)test_touch_zero_reset);
1239     (void)lcb_install_callback(instance, LCB_CALLBACK_GET, (lcb_RESPCALLBACK)test_get_and_touch_zero_reset);
1240     storeKey(instance, key, value);
1241 
1242     {
1243         touch_result res{};
1244         lcb_CMDTOUCH *cmd;
1245         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdtouch_create(&cmd));
1246         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdtouch_key(cmd, key.c_str(), key.size()));
1247         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdtouch_expiry(cmd, 1));
1248         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_touch(instance, &res, cmd));
1249         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdtouch_destroy(cmd));
1250         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_wait(instance, LCB_WAIT_DEFAULT));
1251         ASSERT_TRUE(res.called);
1252         ASSERT_STATUS_EQ(LCB_SUCCESS, res.rc);
1253     }
1254 
1255     {
1256         gat_result res{};
1257         lcb_CMDGET *cmd;
1258         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_create(&cmd));
1259         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_key(cmd, key.c_str(), key.size()));
1260         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_expiry(cmd, 0));
1261         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_get(instance, &res, cmd));
1262         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_destroy(cmd));
1263         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_wait(instance, LCB_WAIT_DEFAULT));
1264         ASSERT_TRUE(res.called);
1265         ASSERT_STATUS_EQ(LCB_SUCCESS, res.rc);
1266         ASSERT_EQ(value, res.value);
1267     }
1268 
1269     sleep(2);
1270 
1271     {
1272         gat_result res{};
1273         lcb_CMDGET *cmd;
1274         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_create(&cmd));
1275         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_key(cmd, key.c_str(), key.size()));
1276         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_get(instance, &res, cmd));
1277         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_cmdget_destroy(cmd));
1278         ASSERT_STATUS_EQ(LCB_SUCCESS, lcb_wait(instance, LCB_WAIT_DEFAULT));
1279         ASSERT_TRUE(res.called);
1280         ASSERT_STATUS_EQ(LCB_SUCCESS, res.rc);
1281         ASSERT_EQ(value, res.value);
1282     }
1283 }
1284