475f694c1333756bf981f898be5bdf45a8fb57c5
[ep-engine.git] / tests / module_tests / kvstore_test.cc
1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2016 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 "config.h"
19
20 #include <platform/dirutils.h>
21
22 #include "callbacks.h"
23 #include "couch-kvstore/couch-kvstore.h"
24 #include "kvstore.h"
25 #include "src/internal.h"
26 #include "tests/module_tests/test_helpers.h"
27 #include "tests/test_fileops.h"
28
29 #include <gmock/gmock.h>
30 #include <gtest/gtest.h>
31 #include <kvstore.h>
32 #include <unordered_map>
33 #include <vector>
34
35 class WriteCallback : public Callback<mutation_result> {
36 public:
37     WriteCallback() {}
38
39     void callback(mutation_result &result) {
40
41     }
42
43 };
44
45 class StatsCallback : public Callback<kvstats_ctx> {
46 public:
47     StatsCallback() {}
48
49     void callback(kvstats_ctx &result) {
50
51     }
52
53 };
54
55 class KVStoreTestCacheCallback : public Callback<CacheLookup> {
56 public:
57     KVStoreTestCacheCallback(int64_t s, int64_t e, uint16_t vbid) :
58         start(s), end(e), vb(vbid) { }
59
60     void callback(CacheLookup &lookup) {
61         EXPECT_EQ(vb, lookup.getVBucketId());
62         EXPECT_LE(start, lookup.getBySeqno());
63         EXPECT_LE(lookup.getBySeqno(), end);
64     }
65
66 private:
67     int64_t start;
68     int64_t end;
69     uint16_t vb;
70 };
71
72 class GetCallback : public Callback<GetValue> {
73 public:
74     GetCallback(ENGINE_ERROR_CODE _expectedErrorCode = ENGINE_SUCCESS) :
75         expectCompressed(false),
76         expectedErrorCode(_expectedErrorCode) { }
77
78     GetCallback(bool expect_compressed,
79                 ENGINE_ERROR_CODE _expectedErrorCode = ENGINE_SUCCESS) :
80         expectCompressed(expect_compressed),
81         expectedErrorCode(_expectedErrorCode) { }
82
83     void callback(GetValue &result) {
84         EXPECT_EQ(expectedErrorCode, result.getStatus());
85         if (result.getStatus() == ENGINE_SUCCESS) {
86             if (expectCompressed) {
87                 EXPECT_EQ(PROTOCOL_BINARY_DATATYPE_SNAPPY,
88                           result.getValue()->getDataType());
89                 result.getValue()->decompressValue();
90             }
91
92             EXPECT_EQ(0,
93                       strncmp("value",
94                               result.getValue()->getData(),
95                               result.getValue()->getNBytes()));
96             delete result.getValue();
97         }
98     }
99
100 private:
101     bool expectCompressed;
102     ENGINE_ERROR_CODE expectedErrorCode;
103 };
104
105 class BloomFilterCallback : public Callback<std::string&, bool&> {
106 public:
107     BloomFilterCallback() {}
108     void callback(std::string& ra, bool& rb) override {}
109 };
110
111 class ExpiryCallback : public Callback<std::string&, uint64_t&> {
112 public:
113     ExpiryCallback() {}
114     void callback(std::string& ra, uint64_t& rb) override {}
115 };
116
117 /**
118  * Utility template for generating callbacks for various
119  * KVStore functions from a lambda/std::function
120  */
121 template <typename... RV>
122 class CustomCallback : public Callback<RV...> {
123 public:
124     CustomCallback(std::function<void(RV...)> _cb)
125         : cb(_cb) {}
126     CustomCallback()
127         : cb([](RV... val){}) {}
128
129     void callback(RV&...result) {
130         cb(result...);
131     }
132
133 protected:
134     std::function<void(RV...)> cb;
135 };
136
137 /**
138  * Callback that can be given a lambda to use, specifically
139  * for the Rollback callback
140  */
141 class CustomRBCallback : public RollbackCB {
142 public:
143     CustomRBCallback(std::function<void(GetValue)> _cb)
144         : cb(_cb) {}
145     CustomRBCallback()
146         : cb([](GetValue val){}) {}
147
148     void callback(GetValue &result) {
149         cb(result);
150         delete result.getValue();
151     }
152
153 protected:
154     std::function<void(GetValue)> cb;
155 };
156
157 // Initializes a KVStore
158 static void initialize_kv_store(KVStore* kvstore) {
159     std::string failoverLog("");
160     // simulate the setVbState by incrementing the rev
161     kvstore->incrementRevision(0);
162     vbucket_state state(vbucket_state_active, 0, 0, 0, 0, 0, 0, 0, failoverLog);
163     // simulate the setVbState by incrementing the rev
164     kvstore->incrementRevision(0);
165     kvstore->snapshotVBucket(0, state,
166                             VBStatePersist::VBSTATE_PERSIST_WITHOUT_COMMIT);
167 }
168
169 // Creates and initializes a KVStore with the given config
170 static std::unique_ptr<KVStore> setup_kv_store(KVStoreConfig& config) {
171     auto kvstore = KVStoreFactory::create(config);
172     initialize_kv_store(kvstore.rw.get());
173     return std::move(kvstore.rw);
174 }
175
176 /* Test callback for stats handling.
177  * 'cookie' is a std::unordered_map<std::string, std::string) which stats
178  * are accumulated in.
179  */
180 static void add_stat_callback(const char *key, const uint16_t klen,
181                               const char *val, const uint32_t vlen,
182                               const void *cookie) {
183     auto* map = reinterpret_cast<std::map<std::string, std::string>*>(
184             const_cast<void*>(cookie));
185     ASSERT_NE(nullptr, map);
186     map->insert(std::make_pair(std::string(key, klen),
187                                std::string(val, vlen)));
188 }
189
190 /**
191  * Test fixture for KVStore tests. Inherited by both ForestDB and
192  * Couchstore test fixtures.
193  **/
194 class KVStoreTest : public ::testing::Test {
195 protected:
196     void SetUp() override {
197         auto* info = ::testing::UnitTest::GetInstance()->current_test_info();
198         data_dir = std::string(info->test_case_name()) + "_" + info->name() +
199             ".db";
200     }
201
202     void TearDown() override {
203         cb::io::rmrf(data_dir);
204     }
205
206     std::string data_dir;
207 };
208
209 /// Test fixture for tests which run on both Couchstore and ForestDB.
210 class CouchAndForestTest : public KVStoreTest,
211                            public ::testing::WithParamInterface<std::string> {
212 };
213
214 /// Test fixture for tests which run only on Couchstore.
215 class CouchKVStoreTest : public KVStoreTest {
216 };
217
218 /* Test basic set / get of a document */
219 TEST_P(CouchAndForestTest, BasicTest) {
220     KVStoreConfig config(
221             1024, 4, data_dir, GetParam(), 0, false /*persistnamespace*/);
222     auto kvstore = setup_kv_store(config);
223
224     kvstore->begin();
225     StoredDocKey key = makeStoredDocKey("key");
226     Item item(key, 0, 0, "value", 5);
227     WriteCallback wc;
228     kvstore->set(item, wc);
229
230     EXPECT_TRUE(kvstore->commit(nullptr /*no collections manifest*/));
231
232     GetCallback gc;
233     kvstore->get(key, 0, gc);
234 }
235
236 TEST_F(CouchKVStoreTest, CompressedTest) {
237     KVStoreConfig config(
238             1024, 4, data_dir, "couchdb", 0, false /*persistnamespace*/);
239     auto kvstore = setup_kv_store(config);
240
241     kvstore->begin();
242
243     uint8_t datatype = PROTOCOL_BINARY_RAW_BYTES;
244     WriteCallback wc;
245     for (int i = 1; i <= 5; i++) {
246         std::string key("key" + std::to_string(i));
247         Item item(makeStoredDocKey(key),
248                   0, 0, "value", 5, &datatype, 1, 0, i);
249         kvstore->set(item, wc);
250     }
251
252     StatsCallback sc;
253     kvstore->commit(nullptr /*no collections manifest*/);
254
255     std::shared_ptr<Callback<GetValue> > cb(new GetCallback(true/*expectcompressed*/));
256     std::shared_ptr<Callback<CacheLookup> > cl(new KVStoreTestCacheCallback(1, 5, 0));
257     ScanContext* scanCtx;
258     scanCtx = kvstore->initScanContext(cb, cl, 0, 1,
259                                        DocumentFilter::ALL_ITEMS,
260                                        ValueFilter::VALUES_COMPRESSED);
261
262     ASSERT_NE(nullptr, scanCtx);
263     EXPECT_EQ(scan_success, kvstore->scan(scanCtx));
264     kvstore->destroyScanContext(scanCtx);
265 }
266
267 // Verify the stats returned from operations are accurate.
268 TEST_F(CouchKVStoreTest, StatsTest) {
269     KVStoreConfig config(
270             1024, 4, data_dir, "couchdb", 0, false /*persistnamespace*/);
271     auto kvstore = setup_kv_store(config);
272
273     // Perform a transaction with a single mutation (set) in it.
274     kvstore->begin();
275     const std::string key{"key"};
276     const std::string value{"value"};
277     Item item(makeStoredDocKey(key), 0, 0, value.c_str(), value.size());
278     WriteCallback wc;
279     kvstore->set(item, wc);
280
281     StatsCallback sc;
282     EXPECT_TRUE(kvstore->commit(nullptr /*no collections manifest*/));
283     // Check statistics are correct.
284     std::map<std::string, std::string> stats;
285     kvstore->addStats(add_stat_callback, &stats);
286     EXPECT_EQ("1", stats["rw_0:io_num_write"]);
287     const size_t io_write_bytes = stoul(stats["rw_0:io_write_bytes"]);
288     EXPECT_EQ(key.size() + value.size() +
289               MetaData::getMetaDataSize(MetaData::Version::V1),
290               io_write_bytes);
291
292     // Hard to determine exactly how many bytes should have been written, but
293     // expect non-zero, and least as many as the actual documents.
294     const size_t io_total_write_bytes = stoul(stats["rw_0:io_total_write_bytes"]);
295     EXPECT_GT(io_total_write_bytes, 0);
296     EXPECT_GE(io_total_write_bytes, io_write_bytes);
297 }
298
299 // Verify the compaction stats returned from operations are accurate.
300 TEST_F(CouchKVStoreTest, CompactStatsTest) {
301     KVStoreConfig config(
302             1, 4, data_dir, "couchdb", 0, false /*persistnamespace*/);
303     auto kvstore = setup_kv_store(config);
304
305     // Perform a transaction with a single mutation (set) in it.
306     kvstore->begin();
307     const std::string key{"key"};
308     const std::string value{"value"};
309     Item item(makeStoredDocKey(key), 0, 0, value.c_str(), value.size());
310     WriteCallback wc;
311     kvstore->set(item, wc);
312
313     EXPECT_TRUE(kvstore->commit(nullptr /*no collections manifest*/));
314
315     std::shared_ptr<Callback<std::string&, bool&> >
316         filter(new BloomFilterCallback());
317     std::shared_ptr<Callback<std::string&, uint64_t&> >
318         expiry(new ExpiryCallback());
319
320     compaction_ctx cctx;
321     cctx.purge_before_seq = 0;
322     cctx.purge_before_ts = 0;
323     cctx.curr_time = 0;
324     cctx.drop_deletes = 0;
325     cctx.db_file_id = 0;
326
327     EXPECT_TRUE(kvstore->compactDB(&cctx));
328     // Check statistics are correct.
329     std::map<std::string, std::string> stats;
330     kvstore->addStats(add_stat_callback, &stats);
331     EXPECT_EQ("1", stats["rw_0:io_num_write"]);
332     const size_t io_write_bytes = stoul(stats["rw_0:io_write_bytes"]);
333
334     // Hard to determine exactly how many bytes should have been written, but
335     // expect non-zero, and at least twice as many as the actual documents for
336     // the total and once as many for compaction alone.
337     const size_t io_total_write_bytes = stoul(stats["rw_0:io_total_write_bytes"]);
338     const size_t io_compaction_write_bytes = stoul(stats["rw_0:io_compaction_write_bytes"]);
339     EXPECT_GT(io_total_write_bytes, 0);
340     EXPECT_GT(io_compaction_write_bytes, 0);
341     EXPECT_GT(io_total_write_bytes, io_compaction_write_bytes);
342     EXPECT_GE(io_total_write_bytes, io_write_bytes * 2);
343     EXPECT_GE(io_compaction_write_bytes, io_write_bytes);
344 }
345
346 // Regression test for MB-17517 - ensure that if a couchstore file has a max
347 // CAS of -1, it is detected and reset to zero when file is loaded.
348 TEST_F(CouchKVStoreTest, MB_17517MaxCasOfMinus1) {
349     KVStoreConfig config(
350             1024, 4, data_dir, "couchdb", 0, false /*persistnamespace*/);
351     auto kvstore = KVStoreFactory::create(config);
352     ASSERT_NE(nullptr, kvstore.rw);
353
354     // Activate vBucket.
355     std::string failoverLog("[]");
356     vbucket_state state(vbucket_state_active, /*ckid*/0, /*maxDelSeqNum*/0,
357                         /*highSeqno*/0, /*purgeSeqno*/0, /*lastSnapStart*/0,
358                         /*lastSnapEnd*/0, /*maxCas*/-1, failoverLog);
359     EXPECT_TRUE(kvstore.rw->snapshotVBucket(
360             /*vbid*/ 0, state, VBStatePersist::VBSTATE_PERSIST_WITHOUT_COMMIT));
361     EXPECT_EQ(~0ull, kvstore.rw->listPersistedVbuckets()[0]->maxCas);
362
363     // Close the file, then re-open.
364     kvstore = KVStoreFactory::create(config);
365     EXPECT_NE(nullptr, kvstore.rw);
366
367     // Check that our max CAS was repaired on startup.
368     EXPECT_EQ(0u, kvstore.rw->listPersistedVbuckets()[0]->maxCas);
369 }
370
371 // Regression test for MB-19430 - ensure that an attempt to get the
372 // item count from a file which doesn't exist yet propagates the
373 // error so the caller can detect (and retry as necessary).
374 TEST_F(CouchKVStoreTest, MB_18580_ENOENT) {
375     KVStoreConfig config(
376             1024, 4, data_dir, "couchdb", 0, false /*persistnamespace*/);
377     // Create a read-only kvstore (which disables item count caching), then
378     // attempt to get the count from a non-existent vbucket.
379     auto kvstore = KVStoreFactory::create(config);
380     ASSERT_NE(nullptr, kvstore.ro);
381
382     // Expect to get a system_error (ENOENT)
383     EXPECT_THROW(kvstore.ro->getDbFileInfo(0), std::system_error);
384 }
385
386 /**
387  * The CouchKVStoreErrorInjectionTest cases utilise GoogleMock to inject
388  * errors into couchstore as if they come from the filesystem in order
389  * to observe how CouchKVStore handles the error and logs it.
390  *
391  * The GoogleMock framework allows expectations to be set on how an object
392  * will be called and how it will respond. Generally we will set a Couchstore
393  * FileOps instance to return an error code on the 'nth' call as follows:
394  *
395  *      EXPECT_CALL(ops, open(_, _, _, _)).Times(AnyNumber());
396  *      EXPECT_CALL(ops, open(_, _, _, _))
397  *          .WillOnce(Return(COUCHSTORE_ERROR_OPEN_FILE)).RetiresOnSaturation();
398  *      EXPECT_CALL(ops, open(_, _, _, _)).Times(n).RetiresOnSaturation();
399  *
400  * We will additionally set an expectation on the LoggerMock regarding how it
401  * will be called by CouchKVStore. In this instance we have set an expectation
402  * that the logger will be called with a logging level greater than or equal
403  * to info, and the log message will contain the error string that corresponds
404  * to `COUCHSTORE_ERROR_OPEN_FILE`.
405  *
406  *      EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
407  *      EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
408  *                               VCE(COUCHSTORE_ERROR_OPEN_FILE))
409  *      ).Times(1).RetiresOnSaturation();
410  */
411
412 using namespace testing;
413
414 /**
415  * The MockLogger is used to verify that the logger is called with certain
416  * parameters / messages.
417  *
418  * The MockLogger is slightly misleading in that it mocks a function that
419  * is not on the API of the logger, instead mocking a function that is
420  * called with the preformatted log message.
421  */
422 class MockLogger : public Logger {
423 public:
424     MockLogger() {
425         ON_CALL(*this, mlog(_, _)).WillByDefault(Invoke([](EXTENSION_LOG_LEVEL sev,
426                                                            const std::string& msg){
427         }));
428     }
429
430     void vlog(EXTENSION_LOG_LEVEL severity, const char* fmt, va_list va) const override {
431         mlog(severity, vatos(fmt, va));
432     }
433
434     MOCK_CONST_METHOD2(mlog, void(EXTENSION_LOG_LEVEL severity,
435                                   const std::string& message));
436
437 private:
438     /**
439      * Convert fmt cstring and a variadic arguments list to a string
440      */
441     static std::string vatos(const char* fmt, va_list va) {
442         std::vector<char> buffer;
443         va_list cpy;
444
445         // Calculate Size
446         va_copy(cpy, va);
447         buffer.resize(vsnprintf(nullptr, 0, fmt, cpy) + 1);
448         va_end(cpy);
449
450         // Write to vector and return as string
451         vsnprintf(buffer.data(), buffer.size(), fmt, va);
452         return std::string(buffer.data());
453     }
454
455 };
456
457 /**
458  * VCE: Verify Couchstore Error
459  *
460  * This is a GoogleMock matcher which will match against a string
461  * which has the corresponding message for the passed couchstore
462  * error code in it. e.g.
463  *
464  *     VCE(COUCHSTORE_ERROR_WRITE)
465  *
466  * will match against a string which contains 'error writing to file'.
467  */
468 MATCHER_P(VCE, value, "is string of %(value)") {
469     return arg.find(couchstore_strerror(value)) != std::string::npos;
470 }
471
472 /**
473  * CouchKVStoreErrorInjectionTest is used for tests which verify
474  * log messages from error injection in couchstore.
475  */
476 class CouchKVStoreErrorInjectionTest : public ::testing::Test {
477 public:
478     CouchKVStoreErrorInjectionTest()
479         : data_dir("CouchKVStoreErrorInjectionTest.db"),
480           ops(create_default_file_ops()),
481           config(KVStoreConfig(1024,
482                                4,
483                                data_dir,
484                                "couchdb",
485                                0,
486                                false /*persistnamespace*/)
487                          .setLogger(logger)
488                          .setBuffered(false)) {
489         cb::io::rmrf(data_dir.c_str());
490         kvstore.reset(new CouchKVStore(config, ops));
491         initialize_kv_store(kvstore.get());
492     }
493     ~CouchKVStoreErrorInjectionTest() {
494         cb::io::rmrf(data_dir.c_str());
495     }
496
497 protected:
498     void generate_items(size_t count) {
499         for(unsigned i(0); i < count; i++) {
500             std::string key("key" + std::to_string(i));
501             items.push_back(Item(makeStoredDocKey(key), 0, 0, "value", 5,
502                                  nullptr, 0, 0, i + 1));
503         }
504     }
505
506     void populate_items(size_t count) {
507         generate_items(count);
508         CustomCallback<mutation_result> set_callback;
509         kvstore->begin();
510         for(const auto& item: items) {
511             kvstore->set(item, set_callback);
512         }
513         kvstore->commit(nullptr /*no collections manifest*/);
514     }
515
516     vb_bgfetch_queue_t make_bgfetch_queue() {
517         vb_bgfetch_queue_t itms;
518         for(const auto& item: items) {
519             vb_bgfetch_item_ctx_t ctx;
520             ctx.isMetaOnly = false;
521             itms[item.getKey()] = std::move(ctx);
522         }
523         return itms;
524     }
525
526
527     const std::string data_dir;
528
529     ::testing::NiceMock<MockOps> ops;
530     ::testing::NiceMock<MockLogger> logger;
531
532     KVStoreConfig config;
533     std::unique_ptr<CouchKVStore> kvstore;
534     std::vector<Item> items;
535 };
536
537
538 /**
539  * Injects error during CouchKVStore::openDB_retry/couchstore_open_db_ex
540  */
541 TEST_F(CouchKVStoreErrorInjectionTest, openDB_retry_open_db_ex) {
542     generate_items(1);
543     CustomCallback<mutation_result> set_callback;
544
545     kvstore->begin();
546     kvstore->set(items.front(), set_callback);
547     {
548         /* Establish Logger expectation */
549         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
550         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_NOTICE),
551                                  VCE(COUCHSTORE_ERROR_OPEN_FILE))
552         ).Times(1).RetiresOnSaturation();
553
554         /* Establish FileOps expectation */
555         EXPECT_CALL(ops, open(_, _, _, _)).Times(AnyNumber());
556         EXPECT_CALL(ops, open(_, _, _, _))
557             .WillOnce(Return(COUCHSTORE_ERROR_OPEN_FILE)).RetiresOnSaturation();
558
559         kvstore->commit(nullptr /*no collections manifest*/);
560     }
561 }
562
563 /**
564  * Injects error during CouchKVStore::openDB/couchstore_open_db_ex
565  */
566 TEST_F(CouchKVStoreErrorInjectionTest, openDB_open_db_ex) {
567     generate_items(1);
568     CustomCallback<mutation_result> set_callback;
569
570     kvstore->begin();
571     kvstore->set(items.front(), set_callback);
572     {
573         /* Establish Logger expectation */
574         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
575         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
576                                  VCE(COUCHSTORE_ERROR_OPEN_FILE))
577                    ).Times(1).RetiresOnSaturation();
578
579         /* Establish FileOps expectation */
580         EXPECT_CALL(ops, open(_, _, _, _))
581             .WillRepeatedly(Return(COUCHSTORE_ERROR_OPEN_FILE)).RetiresOnSaturation();
582
583         kvstore->commit(nullptr /*no collections manifest*/);
584     }
585 }
586
587 /**
588  * Injects error during CouchKVStore::commit/couchstore_save_documents
589  */
590 TEST_F(CouchKVStoreErrorInjectionTest, commit_save_documents) {
591     generate_items(1);
592     CustomCallback<mutation_result> set_callback;
593
594     kvstore->begin();
595     kvstore->set(items.front(), set_callback);
596     {
597         /* Establish Logger expectation */
598         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
599         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
600                                  VCE(COUCHSTORE_ERROR_WRITE))
601                    ).Times(1).RetiresOnSaturation();
602
603         /* Establish FileOps expectation */
604         EXPECT_CALL(ops, pwrite(_, _, _, _, _))
605             .WillOnce(Return(COUCHSTORE_ERROR_WRITE)).RetiresOnSaturation();
606
607         kvstore->commit(nullptr /*no collections manifest*/);
608     }
609
610 }
611
612 /**
613  * Injects error during CouchKVStore::commit/couchstore_save_local_document
614  */
615 TEST_F(CouchKVStoreErrorInjectionTest, commit_save_local_document) {
616     generate_items(1);
617     CustomCallback<mutation_result> set_callback;
618
619     kvstore->begin();
620     kvstore->set(items.front(), set_callback);
621     {
622         /* Establish Logger expectation */
623         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
624         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
625                                  VCE(COUCHSTORE_ERROR_WRITE))
626                    ).Times(1).RetiresOnSaturation();
627
628         /* Establish FileOps expectation */
629         EXPECT_CALL(ops, pwrite(_, _, _, _, _))
630             .WillOnce(Return(COUCHSTORE_ERROR_WRITE)).RetiresOnSaturation();
631         EXPECT_CALL(ops, pwrite(_, _, _, _, _)).Times(6).RetiresOnSaturation();
632
633         kvstore->commit(nullptr /*no collections manifest*/);
634     }
635
636 }
637
638 /**
639  * Injects error during CouchKVStore::commit/couchstore_commit
640  */
641 TEST_F(CouchKVStoreErrorInjectionTest, commit_commit) {
642     generate_items(1);
643     CustomCallback<mutation_result> set_callback;
644
645     kvstore->begin();
646     kvstore->set(items.front(), set_callback);
647     {
648         /* Establish Logger expectation */
649         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
650         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
651                                  VCE(COUCHSTORE_ERROR_WRITE))
652                    ).Times(1).RetiresOnSaturation();
653
654         /* Establish FileOps expectation */
655         EXPECT_CALL(ops, pwrite(_, _, _, _, _))
656             .WillOnce(Return(COUCHSTORE_ERROR_WRITE)).RetiresOnSaturation();
657         EXPECT_CALL(ops, pwrite(_, _, _, _, _)).Times(8).RetiresOnSaturation();
658
659         kvstore->commit(nullptr /*no collections manifest*/);
660     }
661 }
662
663 /**
664  * Injects error during CouchKVStore::get/couchstore_docinfo_by_id
665  */
666 TEST_F(CouchKVStoreErrorInjectionTest, get_docinfo_by_id) {
667     populate_items(1);
668     CustomCallback<GetValue> get_callback;
669     {
670         /* Establish Logger expectation */
671         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
672         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
673                                  VCE(COUCHSTORE_ERROR_READ))
674                    ).Times(1).RetiresOnSaturation();
675
676         /* Establish FileOps expectation */
677         EXPECT_CALL(ops, pread(_, _, _, _, _))
678             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
679         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(3).RetiresOnSaturation();
680         kvstore->get(items.front().getKey(), 0, get_callback);
681
682     }
683 }
684
685 /**
686  * Injects error during CouchKVStore::get/couchstore_open_doc_with_docinfo
687  */
688 TEST_F(CouchKVStoreErrorInjectionTest, get_open_doc_with_docinfo) {
689     populate_items(1);
690     CustomCallback<GetValue> get_callback;
691     {
692         /* Establish Logger expectation */
693         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
694         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
695                                  VCE(COUCHSTORE_ERROR_READ))
696                    ).Times(1).RetiresOnSaturation();
697
698         /* Establish FileOps expectation */
699         EXPECT_CALL(ops, pread(_, _, _, _, _))
700             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
701         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(5).RetiresOnSaturation();
702         kvstore->get(items.front().getKey(), 0, get_callback);
703
704     }
705 }
706
707 /**
708  * Injects error during CouchKVStore::getMulti/couchstore_docinfos_by_id
709  */
710 TEST_F(CouchKVStoreErrorInjectionTest, getMulti_docinfos_by_id) {
711     populate_items(1);
712     vb_bgfetch_queue_t itms(make_bgfetch_queue());
713     {
714
715         /* Establish Logger expectation */
716         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
717         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
718                                  VCE(COUCHSTORE_ERROR_READ))
719                    ).Times(1).RetiresOnSaturation();
720
721         /* Establish FileOps expectation */
722         EXPECT_CALL(ops, pread(_, _, _, _, _))
723             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
724         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(3).RetiresOnSaturation();
725         kvstore->getMulti(0, itms);
726
727     }
728 }
729
730
731 /**
732  * Injects error during CouchKVStore::getMulti/couchstore_open_doc_with_docinfo
733  */
734 TEST_F(CouchKVStoreErrorInjectionTest, getMulti_open_doc_with_docinfo) {
735     populate_items(1);
736     vb_bgfetch_queue_t itms(make_bgfetch_queue());
737     {
738         /* Check preconditions */
739         ASSERT_EQ(0, kvstore->getKVStoreStat().numGetFailure);
740
741         /* Establish FileOps expectation */
742         EXPECT_CALL(ops, pread(_, _, _, _, _))
743             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
744         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(5).RetiresOnSaturation();
745         kvstore->getMulti(0, itms);
746
747         EXPECT_EQ(1, kvstore->getKVStoreStat().numGetFailure);
748     }
749 }
750
751 /**
752  * Injects error during CouchKVStore::compactDB/couchstore_compact_db_ex
753  */
754 TEST_F(CouchKVStoreErrorInjectionTest, compactDB_compact_db_ex) {
755     populate_items(1);
756
757     compaction_ctx cctx;
758     cctx.purge_before_seq = 0;
759     cctx.purge_before_ts = 0;
760     cctx.curr_time = 0;
761     cctx.drop_deletes = 0;
762     cctx.db_file_id = 0;
763
764     {
765         /* Establish Logger expectation */
766         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
767         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
768                                  VCE(COUCHSTORE_ERROR_OPEN_FILE))
769                    ).Times(1).RetiresOnSaturation();
770
771         /* Establish FileOps expectation */
772         EXPECT_CALL(ops, open(_, _, _, _))
773             .WillOnce(Return(COUCHSTORE_ERROR_OPEN_FILE)).RetiresOnSaturation();
774         EXPECT_CALL(ops, open(_, _, _, _)).Times(1).RetiresOnSaturation();
775         kvstore->compactDB(&cctx);
776     }
777 }
778
779 /**
780  * Injects error during CouchKVStore::getNumItems/couchstore_changes_count
781  */
782 TEST_F(CouchKVStoreErrorInjectionTest, getNumItems_changes_count) {
783     populate_items(1);
784     {
785         /* Establish FileOps expectation */
786         EXPECT_CALL(ops, pread(_, _, _, _, _))
787             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
788         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(3).RetiresOnSaturation();
789         try {
790             kvstore->getNumItems(0, 0, 100000);
791             EXPECT_TRUE(false) << "kvstore->getNumItems(0, 0, 100000); should "
792                                   "have thrown a runtime_error";
793         } catch (const std::runtime_error& e) {
794             EXPECT_THAT(std::string(e.what()), VCE(COUCHSTORE_ERROR_READ));
795         }
796
797     }
798 }
799
800 /**
801  * Injects error during CouchKVStore::reset/couchstore_commit
802  */
803 TEST_F(CouchKVStoreErrorInjectionTest, reset_commit) {
804     populate_items(1);
805     {
806         /* Establish Logger expectation */
807         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
808         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
809                                  VCE(COUCHSTORE_ERROR_READ))
810                    ).Times(1).RetiresOnSaturation();
811
812         /* Establish FileOps expectation */
813         EXPECT_CALL(ops, sync(_, _))
814             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
815
816         kvstore->reset(0);
817     }
818 }
819
820 /**
821  * Injects error during CouchKVStore::initScanContext/couchstore_changes_count
822  */
823 TEST_F(CouchKVStoreErrorInjectionTest, initScanContext_changes_count) {
824     populate_items(1);
825     auto cb(std::make_shared<CustomCallback<GetValue>>());
826     auto cl(std::make_shared<CustomCallback<CacheLookup>>());
827     {
828         /* Establish FileOps expectation */
829         EXPECT_CALL(ops, pread(_, _, _, _, _))
830             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
831         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(3).RetiresOnSaturation();
832
833         ScanContext* scanCtx = nullptr;
834         scanCtx = kvstore->initScanContext(cb, cl, 0, 0,
835                                            DocumentFilter::ALL_ITEMS,
836                                            ValueFilter::VALUES_DECOMPRESSED);
837         EXPECT_EQ(nullptr, scanCtx)
838                 << "kvstore->initScanContext(cb, cl, 0, 0, "
839                    "DocumentFilter::ALL_ITEMS, "
840                    "ValueFilter::VALUES_DECOMPRESSED); should "
841                    "have returned NULL";
842
843         kvstore->destroyScanContext(scanCtx);
844     }
845 }
846
847 /**
848  * Injects error during CouchKVStore::scan/couchstore_changes_since
849  */
850 TEST_F(CouchKVStoreErrorInjectionTest, scan_changes_since) {
851     populate_items(1);
852     auto cb(std::make_shared<CustomCallback<GetValue>>());
853     auto cl(std::make_shared<CustomCallback<CacheLookup>>());
854     auto scan_context = kvstore->initScanContext(cb, cl, 0, 0,
855                                               DocumentFilter::ALL_ITEMS,
856                                               ValueFilter::VALUES_DECOMPRESSED);
857     {
858         /* Establish Logger expectation */
859         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
860         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
861                                  VCE(COUCHSTORE_ERROR_READ))
862                    ).Times(1).RetiresOnSaturation();
863
864         /* Establish FileOps expectation */
865         EXPECT_CALL(ops, pread(_, _, _, _, _))
866             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
867
868         kvstore->scan(scan_context);
869     }
870
871     kvstore->destroyScanContext(scan_context);
872 }
873
874 /**
875  * Injects error during CouchKVStore::recordDbDump/couchstore_open_doc_with_docinfo
876  */
877 TEST_F(CouchKVStoreErrorInjectionTest, recordDbDump_open_doc_with_docinfo) {
878     populate_items(1);
879     auto cb(std::make_shared<CustomCallback<GetValue>>());
880     auto cl(std::make_shared<CustomCallback<CacheLookup>>());
881     auto scan_context = kvstore->initScanContext(cb, cl, 0, 0,
882                                                  DocumentFilter::ALL_ITEMS,
883                                                  ValueFilter::VALUES_DECOMPRESSED);
884     {
885         /* Establish Logger expectation */
886         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
887         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
888                                  VCE(COUCHSTORE_ERROR_READ))
889                    ).Times(1).RetiresOnSaturation();
890
891         /* Establish FileOps expectation */
892         EXPECT_CALL(ops, pread(_, _, _, _, _))
893             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
894         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(2).RetiresOnSaturation();
895
896         kvstore->scan(scan_context);
897     }
898
899     kvstore->destroyScanContext(scan_context);
900 }
901
902 /**
903  * Injects error during CouchKVStore::rollback/couchstore_changes_count/1
904  */
905 TEST_F(CouchKVStoreErrorInjectionTest, rollback_changes_count1) {
906     generate_items(6);
907     CustomCallback<mutation_result> set_callback;
908
909     for(const auto item: items) {
910         kvstore->begin();
911         kvstore->set(item, set_callback);
912         kvstore->commit(nullptr /*no collections manifest*/);
913     }
914
915     auto rcb(std::make_shared<CustomRBCallback>());
916     {
917         /* Establish Logger expectation */
918         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
919         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
920                                  VCE(COUCHSTORE_ERROR_READ))
921                    ).Times(1).RetiresOnSaturation();
922
923         /* Establish FileOps expectation */
924         EXPECT_CALL(ops, pread(_, _, _, _, _))
925             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
926         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(3).RetiresOnSaturation();
927
928         kvstore->rollback(0, 5, rcb);
929     }
930 }
931
932 /**
933  * Injects error during CouchKVStore::rollback/couchstore_rewind_header
934  */
935 TEST_F(CouchKVStoreErrorInjectionTest, rollback_rewind_header) {
936     generate_items(6);
937     CustomCallback<mutation_result> set_callback;
938
939     for(const auto item: items) {
940         kvstore->begin();
941         kvstore->set(item, set_callback);
942         kvstore->commit(nullptr /*no collections manifest*/);
943     }
944
945     auto rcb(std::make_shared<CustomRBCallback>());
946     {
947         /* Establish Logger expectation */
948         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
949         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
950                                  VCE(COUCHSTORE_ERROR_DB_NO_LONGER_VALID))
951                    ).Times(1).RetiresOnSaturation();
952
953         /* Establish FileOps expectation */
954         EXPECT_CALL(ops, pread(_, _, _, _, _))
955             /* Doing an ALLOC_FAIL as Couchstore will just
956              * keep rolling back otherwise */
957             .WillOnce(Return(COUCHSTORE_ERROR_ALLOC_FAIL)).RetiresOnSaturation();
958         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(9).RetiresOnSaturation();
959
960         kvstore->rollback(0, 5, rcb);
961     }
962 }
963
964 /**
965  * Injects error during CouchKVStore::rollback/couchstore_changes_count/2
966  */
967 TEST_F(CouchKVStoreErrorInjectionTest, rollback_changes_count2) {
968     generate_items(6);
969     CustomCallback<mutation_result> set_callback;
970
971     for(const auto item: items) {
972         kvstore->begin();
973         kvstore->set(item, set_callback);
974         kvstore->commit(nullptr /*no collections manifest*/);
975     }
976
977     auto rcb(std::make_shared<CustomRBCallback>());
978     {
979         /* Establish Logger expectation */
980         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
981         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
982                                  VCE(COUCHSTORE_ERROR_READ))
983                    ).Times(1).RetiresOnSaturation();
984
985         /* Establish FileOps expectation */
986         EXPECT_CALL(ops, pread(_, _, _, _, _))
987             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
988         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(11).RetiresOnSaturation();
989
990         kvstore->rollback(0, 5, rcb);
991     }
992 }
993
994 /**
995  * Injects error during CouchKVStore::readVBState/couchstore_open_local_document
996  */
997 TEST_F(CouchKVStoreErrorInjectionTest, readVBState_open_local_document) {
998     generate_items(6);
999     CustomCallback<mutation_result> set_callback;
1000
1001     for(const auto item: items) {
1002         kvstore->begin();
1003         kvstore->set(item, set_callback);
1004         kvstore->commit(nullptr /*no collections manifest*/);
1005     }
1006
1007     auto rcb(std::make_shared<CustomRBCallback>());
1008     {
1009         /* Establish Logger expectation */
1010         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
1011         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
1012                                  VCE(COUCHSTORE_ERROR_READ))
1013                    ).Times(1).RetiresOnSaturation();
1014
1015         /* Establish FileOps expectation */
1016         EXPECT_CALL(ops, pread(_, _, _, _, _))
1017             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
1018         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(20).RetiresOnSaturation();
1019
1020         kvstore->rollback(0, 5, rcb);
1021     }
1022 }
1023
1024 /**
1025  * Injects error during CouchKVStore::getAllKeys/couchstore_all_docs
1026  */
1027 TEST_F(CouchKVStoreErrorInjectionTest, getAllKeys_all_docs) {
1028     populate_items(1);
1029
1030     auto adcb(std::make_shared<CustomCallback<const DocKey&>>());
1031     StoredDocKey start = makeStoredDocKey("");
1032     {
1033         /* Establish Logger expectation */
1034         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
1035         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
1036                                  VCE(COUCHSTORE_ERROR_READ))
1037                    ).Times(1).RetiresOnSaturation();
1038
1039         /* Establish FileOps expectation */
1040         EXPECT_CALL(ops, pread(_, _, _, _, _))
1041             .WillOnce(Return(COUCHSTORE_ERROR_READ)).RetiresOnSaturation();
1042         EXPECT_CALL(ops, pread(_, _, _, _, _)).Times(3).RetiresOnSaturation();
1043
1044
1045         kvstore->getAllKeys(0, start, 1, adcb);
1046     }
1047 }
1048
1049 /**
1050  * Injects error during CouchKVStore::closeDB/couchstore_close_file
1051  */
1052 TEST_F(CouchKVStoreErrorInjectionTest, closeDB_close_file) {
1053     {
1054         /* Establish Logger expectation */
1055         EXPECT_CALL(logger, mlog(_, _)).Times(AnyNumber());
1056         EXPECT_CALL(logger, mlog(Ge(EXTENSION_LOG_WARNING),
1057                                  VCE(COUCHSTORE_ERROR_FILE_CLOSE))
1058                    ).Times(1).RetiresOnSaturation();
1059
1060         /* Establish FileOps expectation */
1061         EXPECT_CALL(ops, close(_, _)).Times(AnyNumber());
1062         EXPECT_CALL(ops, close(_, _))
1063                 .WillOnce(DoAll(IgnoreResult(Invoke(ops.get_wrapped(),
1064                                                     &FileOpsInterface::close)),
1065                                 Return(COUCHSTORE_ERROR_FILE_CLOSE)))
1066                 .RetiresOnSaturation();
1067
1068         populate_items(1);
1069     }
1070 }
1071
1072 class MockCouchRequest : public CouchRequest {
1073 public:
1074     class MetaData {
1075     public:
1076         MetaData()
1077             : cas(0),
1078               expiry(0),
1079               flags(0),
1080               ext1(0),
1081               ext2(0),
1082               legacyDeleted(0) {
1083         }
1084
1085         uint64_t cas;
1086         uint32_t expiry;
1087         uint32_t flags;
1088         uint8_t ext1;
1089         uint8_t ext2;
1090         uint8_t legacyDeleted; // allow testing via 19byte meta document
1091
1092         static const size_t sizeofV0 = 16;
1093         static const size_t sizeofV1 = 18;
1094         static const size_t sizeofV2 = 19;
1095     };
1096
1097     MockCouchRequest(const Item& it,
1098                      uint64_t rev,
1099                      MutationRequestCallback& cb,
1100                      bool del)
1101         : CouchRequest(it, rev, cb, del, false /*persist namespace*/) {
1102     }
1103
1104     ~MockCouchRequest() {}
1105
1106     // Update what will be written as 'metadata'
1107     void writeMetaData(MetaData& meta, size_t size) {
1108         std::memcpy(dbDocInfo.rev_meta.buf, &meta, size);
1109         dbDocInfo.rev_meta.size = size;
1110     }
1111 };
1112
1113 class MockCouchKVStore : public CouchKVStore {
1114 public:
1115     MockCouchKVStore(KVStoreConfig& config) : CouchKVStore(config) {
1116     }
1117
1118     // Mocks original code but returns the IORequest for fuzzing
1119     MockCouchRequest* setAndReturnRequest(const Item &itm, Callback<mutation_result> &cb) {
1120         if (isReadOnly()) {
1121             throw std::logic_error("MockCouchKVStore::set: Not valid on a read-only "
1122                             "object.");
1123         }
1124         if (!intransaction) {
1125             throw std::invalid_argument("MockCouchKVStore::set: intransaction must be "
1126                             "true to perform a set operation.");
1127         }
1128
1129         bool deleteItem = false;
1130         MutationRequestCallback requestcb;
1131         uint64_t fileRev = dbFileRevMap[itm.getVBucketId()];
1132
1133         // each req will be de-allocated after commit
1134         requestcb.setCb = &cb;
1135         MockCouchRequest *req = new MockCouchRequest(itm, fileRev, requestcb, deleteItem);
1136         pendingReqsQ.push_back(req);
1137         return req;
1138     }
1139 };
1140
1141 //
1142 // Explicitly test couchstore (not valid for ForestDB)
1143 // Intended to ensure we can read and write couchstore files and
1144 // parse metadata we store in them.
1145 //
1146 class CouchstoreTest : public ::testing::Test {
1147 public:
1148     CouchstoreTest()
1149         : data_dir("CouchstoreTest.db"),
1150           vbid(0),
1151           config(KVStoreConfig(1024,
1152                                4,
1153                                data_dir,
1154                                "couchdb",
1155                                0,
1156                                false /*persistnamespace*/)
1157                          .setBuffered(false)) {
1158         cb::io::rmrf(data_dir.c_str());
1159         kvstore.reset(new MockCouchKVStore(config));
1160         StatsCallback sc;
1161         std::string failoverLog("");
1162         // simulate a setVBState - increment the rev and then persist the
1163         // state
1164         kvstore->incrementRevision(0);
1165         vbucket_state state(vbucket_state_active, 0, 0, 0, 0, 0, 0, 0,
1166                             failoverLog);
1167         // simulate a setVBState - increment the dbFile revision
1168         kvstore->incrementRevision(0);
1169         kvstore->snapshotVBucket(0, state,
1170                                  VBStatePersist::VBSTATE_PERSIST_WITHOUT_COMMIT);
1171     }
1172
1173     ~CouchstoreTest() {
1174         cb::io::rmrf(data_dir.c_str());
1175     }
1176
1177 protected:
1178     std::string data_dir;
1179     std::unique_ptr<MockCouchKVStore> kvstore;
1180     uint16_t vbid;
1181     KVStoreConfig config;
1182 };
1183
1184 template<class T>
1185 class MockedGetCallback : public Callback<T> {
1186     public:
1187         MockedGetCallback() {}
1188
1189         ~MockedGetCallback() {
1190             delete savedValue.getValue();
1191         }
1192
1193         void callback(GetValue& value){
1194             status(value.getStatus());
1195             if (value.getStatus() == ENGINE_SUCCESS) {
1196                 EXPECT_CALL(*this, value("value"));
1197                 cas(value.getValue()->getCas());
1198                 expTime(value.getValue()->getExptime());
1199                 flags(value.getValue()->getFlags());
1200                 datatype(protocol_binary_datatype_t(value.getValue()->getDataType()));
1201                 this->value(std::string(value.getValue()->getData(),
1202                                         value.getValue()->getNBytes()));
1203                 savedValue = value;
1204             }
1205         }
1206
1207         Item* getValue() {
1208             return savedValue.getValue();
1209         }
1210
1211         /*
1212          * Define a number of mock methods that will be invoked by the
1213          * callback method. Functions can then setup expectations of the
1214          * value of each method e.g. expect cas to be -1
1215          */
1216         MOCK_METHOD1_T(status, void(ENGINE_ERROR_CODE));
1217         MOCK_METHOD1_T(cas, void(uint64_t));
1218         MOCK_METHOD1_T(expTime, void(uint32_t));
1219         MOCK_METHOD1_T(flags, void(uint32_t));
1220         MOCK_METHOD1_T(datatype, void(protocol_binary_datatype_t));
1221         MOCK_METHOD1_T(value, void(std::string));
1222     private:
1223         GetValue savedValue;
1224 };
1225
1226 /*
1227  * The overall aim of these tests is to create an Item, write it to disk
1228  * then read it back from disk and look at various fields which are
1229  * built from the couchstore rev_meta feature.
1230  *
1231  * Validation of the Item read from disk is performed by the GetCallback.
1232  * A number of validators can be called upon which compare the disk Item
1233  * against an expected Item.
1234  *
1235  * The MockCouchKVStore exposes some of the internals of the class so we
1236  * can inject custom metadata by using ::setAndReturnRequest instead of ::set
1237  *
1238  */
1239 TEST_F(CouchstoreTest, noMeta) {
1240     StoredDocKey key = makeStoredDocKey("key");
1241     Item item(key, 0, 0, "value", 5);
1242     WriteCallback wc;
1243     kvstore->begin();
1244     auto request = kvstore->setAndReturnRequest(item, wc);
1245
1246     // Now directly mess with the metadata of the value which will be written
1247     MockCouchRequest::MetaData meta;
1248     request->writeMetaData(meta, 0); // no meta!
1249
1250     kvstore->commit(nullptr /*no collections manifest*/);
1251
1252     GetCallback gc(ENGINE_TMPFAIL);
1253     kvstore->get(key, 0, gc);
1254 }
1255
1256 TEST_F(CouchstoreTest, shortMeta) {
1257     StoredDocKey key = makeStoredDocKey("key");
1258     Item item(key, 0, 0, "value", 5);
1259     WriteCallback wc;
1260     kvstore->begin();
1261     auto request = kvstore->setAndReturnRequest(item, wc);
1262
1263     // Now directly mess with the metadata of the value which will be written
1264     MockCouchRequest::MetaData meta;
1265     request->writeMetaData(meta, 4); // not enough meta!
1266     kvstore->commit(nullptr /*no collections manifest*/);
1267
1268     GetCallback gc(ENGINE_TMPFAIL);
1269     kvstore->get(key, 0, gc);
1270 }
1271
1272 TEST_F(CouchstoreTest, testV0MetaThings) {
1273     StoredDocKey key = makeStoredDocKey("key");
1274     // Baseline test, just writes meta things and reads them
1275     // via standard interfaces
1276     // Ensure CAS, exptime and flags are set to something.
1277     Item item(key,
1278               0x01020304/*flags*/, 0xaa00bb11/*expiry*/,
1279               "value", 5,
1280               nullptr, 0,
1281               0xf00fcafe11225566ull);
1282
1283     WriteCallback wc;
1284     kvstore->begin();
1285     kvstore->set(item, wc);
1286     kvstore->commit(nullptr /*no collections manifest*/);
1287
1288     MockedGetCallback<GetValue> gc;
1289     EXPECT_CALL(gc, status(ENGINE_SUCCESS));
1290     EXPECT_CALL(gc, cas(0xf00fcafe11225566ull));
1291     EXPECT_CALL(gc, expTime(0xaa00bb11));
1292     EXPECT_CALL(gc, flags(0x01020304));
1293     EXPECT_CALL(gc, datatype(PROTOCOL_BINARY_RAW_BYTES));
1294     kvstore->get(key, 0, gc);
1295 }
1296
1297 TEST_F(CouchstoreTest, testV1MetaThings) {
1298     // Baseline test, just writes meta things and reads them
1299     // via standard interfaces
1300     // Ensure CAS, exptime and flags are set to something.
1301     uint8_t datatype = PROTOCOL_BINARY_DATATYPE_JSON; //lies, but non-zero
1302     StoredDocKey key = makeStoredDocKey("key");
1303     Item item(key,
1304               0x01020304/*flags*/, 0xaa00bb11,/*expiry*/
1305               "value", 5,
1306               &datatype, 1, /*ext_meta is v1 extension*/
1307               0xf00fcafe11225566ull);
1308     EXPECT_NE(0, datatype); // make sure we writing non-zero
1309     WriteCallback wc;
1310     kvstore->begin();
1311     kvstore->set(item, wc);
1312     kvstore->commit(nullptr /*no collections manifest*/);
1313
1314     MockedGetCallback<GetValue> gc;
1315     EXPECT_CALL(gc, status(ENGINE_SUCCESS));
1316     EXPECT_CALL(gc, cas(0xf00fcafe11225566ull));
1317     EXPECT_CALL(gc, expTime(0xaa00bb11));
1318     EXPECT_CALL(gc, flags(0x01020304));
1319     EXPECT_CALL(gc, datatype(PROTOCOL_BINARY_DATATYPE_JSON));
1320
1321     kvstore->get(key, 0, gc);
1322 }
1323
1324 TEST_F(CouchstoreTest, fuzzV0) {
1325     StoredDocKey key = makeStoredDocKey("key");
1326     Item item(key, 0, 0, "value", 5);
1327     WriteCallback wc;
1328     kvstore->begin();
1329     auto request = kvstore->setAndReturnRequest(item, wc);
1330
1331     // Now directly mess with the metadata of the value which will be written
1332     MockCouchRequest::MetaData meta;
1333     meta.cas = 0xf00fcafe11225566ull;
1334     meta.expiry = 0xaa00bb11;
1335     meta.flags = 0x01020304;
1336     request->writeMetaData(meta, MockCouchRequest::MetaData::sizeofV0);
1337     kvstore->commit(nullptr /*no collections manifest*/);
1338
1339     // CAS is byteswapped when read back
1340     MockedGetCallback<GetValue> gc;
1341     EXPECT_CALL(gc, status(ENGINE_SUCCESS));
1342     EXPECT_CALL(gc, cas(htonll(0xf00fcafe11225566ull)));
1343     EXPECT_CALL(gc, expTime(htonl(0xaa00bb11)));
1344     EXPECT_CALL(gc, flags(0x01020304));
1345     EXPECT_CALL(gc, datatype(PROTOCOL_BINARY_RAW_BYTES));
1346     kvstore->get(key, 0, gc);
1347 }
1348
1349 TEST_F(CouchstoreTest, fuzzV1) {
1350     StoredDocKey key = makeStoredDocKey("key");
1351     Item item(key, 0, 0, "value", 5);
1352     WriteCallback wc;
1353     kvstore->begin();
1354     auto request = kvstore->setAndReturnRequest(item, wc);
1355
1356     // Now directly mess with the metadata of the value which will be written
1357     MockCouchRequest::MetaData meta;
1358     meta.cas = 0xf00fcafe11225566ull;
1359     meta.expiry = 0xaa00bb11;
1360     meta.flags = 0x01020304;
1361     meta.ext1 = 2;
1362     meta.ext2 = 33;
1363     request->writeMetaData(meta, MockCouchRequest::MetaData::sizeofV1);
1364     kvstore->commit(nullptr /*no collections manifest*/);
1365     MockedGetCallback<GetValue> gc;
1366     uint8_t expectedDataType = 33;
1367     EXPECT_CALL(gc, status(ENGINE_SUCCESS));
1368     EXPECT_CALL(gc, cas(htonll(0xf00fcafe11225566ull)));
1369     EXPECT_CALL(gc, expTime(htonl(0xaa00bb11)));
1370     EXPECT_CALL(gc, flags(0x01020304));
1371     EXPECT_CALL(gc, datatype(protocol_binary_datatype_t(expectedDataType)));
1372     kvstore->get(key, 0, gc);
1373 }
1374
1375 TEST_F(CouchstoreTest, testV0WriteReadWriteRead) {
1376     // Ensure CAS, exptime and flags are set to something.
1377     uint8_t datatype = PROTOCOL_BINARY_DATATYPE_JSON; //lies, but non-zero
1378     StoredDocKey key = makeStoredDocKey("key");
1379     Item item(key,
1380               0x01020304/*flags*/, 0xaa00bb11,/*expiry*/
1381               "value", 5,
1382               &datatype, 1, /*ext_meta is v1 extension*/
1383               0xf00fcafe11225566ull);
1384
1385     EXPECT_NE(0, datatype); // make sure we writing non-zero values
1386
1387     // Write an item with forced (valid) V0 meta
1388     MockCouchRequest::MetaData meta;
1389     meta.cas = 0xf00fcafe11225566ull;
1390     meta.expiry = 0xaa00bb11;
1391     meta.flags = 0x01020304;
1392
1393     WriteCallback wc;
1394     kvstore->begin();
1395     auto request = kvstore->setAndReturnRequest(item, wc);
1396
1397     // Force the meta to be V0
1398     request->writeMetaData(meta, MockCouchRequest::MetaData::sizeofV0);
1399
1400     // Commit it
1401     kvstore->commit(nullptr /*no collections manifest*/);
1402
1403     // Read back, are V1 fields sane?
1404     MockedGetCallback<GetValue> gc;
1405     EXPECT_CALL(gc, status(ENGINE_SUCCESS));
1406     EXPECT_CALL(gc, cas(htonll(0xf00fcafe11225566ull)));
1407     EXPECT_CALL(gc, expTime(htonl(0xaa00bb11)));
1408     EXPECT_CALL(gc, flags(0x01020304));
1409     EXPECT_CALL(gc, datatype(protocol_binary_datatype_t(meta.ext2)));
1410     kvstore->get(key, 0, gc);
1411
1412     // Write back the item we read (this will write out V1 meta)
1413     kvstore->begin();
1414     kvstore->set(*gc.getValue(), wc);
1415     kvstore->commit(nullptr /*no collections manifest*/);
1416
1417     // Read back, is conf_res_mode sane?
1418     MockedGetCallback<GetValue> gc2;
1419     EXPECT_CALL(gc2, status(ENGINE_SUCCESS));
1420     EXPECT_CALL(gc2, cas(htonll(0xf00fcafe11225566ull)));
1421     EXPECT_CALL(gc2, expTime(htonl(0xaa00bb11)));
1422     EXPECT_CALL(gc2, flags(0x01020304));
1423     EXPECT_CALL(gc2, datatype(protocol_binary_datatype_t(meta.ext2)));
1424     kvstore->get(key, 0, gc2);
1425 }
1426
1427 TEST_F(CouchstoreTest, testV2WriteRead) {
1428     // Ensure CAS, exptime and flags are set to something.
1429     uint8_t datatype = PROTOCOL_BINARY_DATATYPE_JSON; //lies, but non-zero
1430     StoredDocKey key = makeStoredDocKey("key");
1431     Item item(key,
1432               0x01020304/*flags*/, 0xaa00bb11,/*expiry*/
1433               "value", 5,
1434               &datatype, 1, /*ext_meta is v1 extension*/
1435               0xf00fcafe11225566ull);
1436
1437     EXPECT_NE(0, datatype); // make sure we writing non-zero values
1438
1439     // Write an item with forced (valid) V2 meta
1440     // In 4.6 we removed the extra conflict resolution byte, so be sure we
1441     // operate correctly if a document has V2 meta.
1442     MockCouchRequest::MetaData meta;
1443     meta.cas = 0xf00fcafe11225566ull;
1444     meta.expiry = 0xaa00bb11;
1445     meta.flags = 0x01020304;
1446     meta.ext1 = FLEX_META_CODE;
1447     meta.ext2 = datatype;
1448     meta.legacyDeleted = 0x01;
1449
1450     WriteCallback wc;
1451     kvstore->begin();
1452     auto request = kvstore->setAndReturnRequest(item, wc);
1453
1454     // Force the meta to be V2 (19 bytes)
1455     request->writeMetaData(meta, MockCouchRequest::MetaData::sizeofV2);
1456
1457     // Commit it
1458     kvstore->commit(nullptr /*no collections manifest*/);
1459
1460     // Read back successful, the extra byte will of been dropped.
1461     MockedGetCallback<GetValue> gc;
1462     EXPECT_CALL(gc, status(ENGINE_SUCCESS));
1463     EXPECT_CALL(gc, cas(htonll(0xf00fcafe11225566ull)));
1464     EXPECT_CALL(gc, expTime(htonl(0xaa00bb11)));
1465     EXPECT_CALL(gc, flags(0x01020304));
1466     EXPECT_CALL(gc, datatype(protocol_binary_datatype_t(meta.ext2)));
1467     kvstore->get(key, 0, gc);
1468 }
1469
1470 class CouchKVStoreMetaData : public ::testing::Test {
1471 };
1472
1473 TEST_F(CouchKVStoreMetaData, basic) {
1474     // Lock down the size assumptions.
1475     EXPECT_EQ(16, MetaData::getMetaDataSize(MetaData::Version::V0));
1476     EXPECT_EQ(16 + 2, MetaData::getMetaDataSize(MetaData::Version::V1));
1477     EXPECT_EQ(16 + 2 + 1, MetaData::getMetaDataSize(MetaData::Version::V2));
1478 }
1479
1480 TEST_F(CouchKVStoreMetaData, overlay) {
1481     std::vector<char> data(16);
1482     sized_buf meta;
1483     meta.buf = data.data();
1484     meta.size = data.size();
1485     auto metadata = MetaDataFactory::createMetaData(meta);
1486     EXPECT_EQ(MetaData::Version::V0, metadata->getVersionInitialisedFrom());
1487
1488     data.resize(16 + 2);
1489     meta.buf = data.data();
1490     meta.size = data.size();
1491     metadata = MetaDataFactory::createMetaData(meta);
1492     EXPECT_EQ(MetaData::Version::V1, metadata->getVersionInitialisedFrom());
1493
1494     // Even with a 19 byte (v2) meta, the expectation is we become V1
1495     data.resize(16 + 2 + 1);
1496     meta.buf = data.data();
1497     meta.size = data.size();
1498     metadata = MetaDataFactory::createMetaData(meta);
1499     EXPECT_EQ(MetaData::Version::V1, metadata->getVersionInitialisedFrom());
1500
1501     // Buffers too large and small
1502     data.resize(16 + 2 + 1 + 1);
1503     meta.buf = data.data();
1504     meta.size = data.size();
1505     EXPECT_THROW(MetaDataFactory::createMetaData(meta), std::logic_error);
1506
1507     data.resize(15);
1508     meta.buf = data.data();
1509     meta.size = data.size();
1510     EXPECT_THROW(MetaDataFactory::createMetaData(meta), std::logic_error);
1511 }
1512
1513 TEST_F(CouchKVStoreMetaData, overlayExpands1) {
1514     std::vector<char> data(16);
1515     sized_buf meta;
1516     sized_buf out;
1517     meta.buf = data.data();
1518     meta.size = data.size();
1519
1520     // V0 in yet V1 "moved out"
1521     auto metadata = MetaDataFactory::createMetaData(meta);
1522     EXPECT_EQ(MetaData::Version::V0, metadata->getVersionInitialisedFrom());
1523     out.size = MetaData::getMetaDataSize(MetaData::Version::V1);
1524     out.buf = new char[out.size];
1525     metadata->copyToBuf(out);
1526     EXPECT_EQ(out.size, MetaData::getMetaDataSize(MetaData::Version::V1));
1527
1528     // We created a copy of the metadata so we must cleanup
1529     delete [] out.buf;
1530 }
1531
1532 TEST_F(CouchKVStoreMetaData, overlayExpands2) {
1533     std::vector<char> data(16 + 2);
1534     sized_buf meta;
1535     sized_buf out;
1536     meta.buf = data.data();
1537     meta.size = data.size();
1538
1539     // V1 in V1 "moved out"
1540     auto metadata = MetaDataFactory::createMetaData(meta);
1541     EXPECT_EQ(MetaData::Version::V1, metadata->getVersionInitialisedFrom());
1542     out.size = MetaData::getMetaDataSize(MetaData::Version::V1);
1543     out.buf = new char[out.size];
1544     metadata->copyToBuf(out);
1545     EXPECT_EQ(out.size, MetaData::getMetaDataSize(MetaData::Version::V1));
1546
1547     // We created a copy of the metadata so we must cleanup
1548     delete [] out.buf;
1549 }
1550
1551 TEST_F(CouchKVStoreMetaData, writeToOverlay) {
1552     std::vector<char> data(16);
1553     sized_buf meta;
1554     sized_buf out;
1555     meta.buf = data.data();
1556     meta.size = data.size();
1557
1558     // Test that we can initialise from V0 but still set
1559     // all fields of all versions
1560     auto metadata = MetaDataFactory::createMetaData(meta);
1561     EXPECT_EQ(MetaData::Version::V0, metadata->getVersionInitialisedFrom());
1562
1563     uint64_t cas = 0xf00f00ull;
1564     uint32_t exp = 0xcafe1234;
1565     uint32_t flags = 0xc0115511;
1566     metadata->setCas(cas);
1567     metadata->setExptime(exp);
1568     metadata->setFlags(flags);
1569     metadata->setDataType(PROTOCOL_BINARY_DATATYPE_JSON);
1570
1571     // Check they all read back
1572     EXPECT_EQ(cas, metadata->getCas());
1573     EXPECT_EQ(exp, metadata->getExptime());
1574     EXPECT_EQ(flags, metadata->getFlags());
1575     EXPECT_EQ(FLEX_META_CODE, metadata->getFlexCode());
1576     EXPECT_EQ(PROTOCOL_BINARY_DATATYPE_JSON, metadata->getDataType());
1577
1578     // Now we move the metadata out, this will give back a V1 structure
1579     out.size = MetaData::getMetaDataSize(MetaData::Version::V1);
1580     out.buf = new char[out.size];
1581     metadata->copyToBuf(out);
1582     metadata = MetaDataFactory::createMetaData(out);
1583     EXPECT_EQ(MetaData::Version::V1, metadata->getVersionInitialisedFrom()); // Is it V1?
1584
1585     // All the written fields should be the same
1586     // Check they all read back
1587     EXPECT_EQ(cas, metadata->getCas());
1588     EXPECT_EQ(exp, metadata->getExptime());
1589     EXPECT_EQ(flags, metadata->getFlags());
1590     EXPECT_EQ(FLEX_META_CODE, metadata->getFlexCode());
1591     EXPECT_EQ(PROTOCOL_BINARY_DATATYPE_JSON, metadata->getDataType());
1592     EXPECT_EQ(out.size, MetaData::getMetaDataSize(MetaData::Version::V1));
1593
1594     // We moved the metadata so we must cleanup
1595     delete [] out.buf;
1596 }
1597
1598 //
1599 // Test that assignment operates as expected (we use this in edit_docinfo_hook)
1600 //
1601 TEST_F(CouchKVStoreMetaData, assignment) {
1602     std::vector<char> data(16);
1603     sized_buf meta;
1604     meta.buf = data.data();
1605     meta.size = data.size();
1606     auto metadata = MetaDataFactory::createMetaData(meta);
1607     uint64_t cas = 0xf00f00ull;
1608     uint32_t exp = 0xcafe1234;
1609     uint32_t flags = 0xc0115511;
1610     metadata->setCas(cas);
1611     metadata->setExptime(exp);
1612     metadata->setFlags(flags);
1613     metadata->setDataType( PROTOCOL_BINARY_DATATYPE_JSON);
1614
1615     // Create a second metadata to write into
1616     auto copy = MetaDataFactory::createMetaData();
1617
1618     // Copy overlaid into managed
1619     *copy = *metadata;
1620
1621     // Test that the copy doesn't write to metadata
1622     copy->setExptime(100);
1623     EXPECT_EQ(exp, metadata->getExptime());
1624
1625     EXPECT_EQ(cas, copy->getCas());
1626     EXPECT_EQ(100, copy->getExptime());
1627     EXPECT_EQ(flags, copy->getFlags());
1628     EXPECT_EQ(FLEX_META_CODE, copy->getFlexCode());
1629     EXPECT_EQ(PROTOCOL_BINARY_DATATYPE_JSON, copy->getDataType());
1630
1631     // And a final assignment
1632     auto copy2 = MetaDataFactory::createMetaData();
1633     *copy2 = *copy;
1634
1635     // test that copy2 doesn't update copy
1636     copy2->setCas(99);
1637     EXPECT_NE(99, copy->getCas());
1638
1639     // Yet copy2 did
1640     EXPECT_EQ(99, copy2->getCas());
1641     EXPECT_EQ(100, copy2->getExptime());
1642     EXPECT_EQ(flags, copy2->getFlags());
1643     EXPECT_EQ(FLEX_META_CODE, copy2->getFlexCode());
1644     EXPECT_EQ(PROTOCOL_BINARY_DATATYPE_JSON, copy2->getDataType());
1645 }
1646
1647 #ifdef EP_USE_FORESTDB
1648 // Test cases which run on both Couchstore and ForestDB
1649 INSTANTIATE_TEST_CASE_P(CouchstoreAndForestDB,
1650                         CouchAndForestTest,
1651                         ::testing::Values("couchdb", "forestdb"),
1652                         [] (const ::testing::TestParamInfo<std::string>& info) {
1653                             return info.param;
1654                         });
1655 #else
1656 INSTANTIATE_TEST_CASE_P(CouchstoreAndForestDB,
1657                         CouchAndForestTest,
1658                         ::testing::Values("couchdb"),
1659                         [] (const ::testing::TestParamInfo<std::string>& info) {
1660                             return info.param;
1661                         });
1662 #endif