summaryrefslogtreecommitdiff
path: root/src/mongo/db/repl/database_cloner_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/repl/database_cloner_test.cpp')
-rw-r--r--src/mongo/db/repl/database_cloner_test.cpp913
1 files changed, 472 insertions, 441 deletions
diff --git a/src/mongo/db/repl/database_cloner_test.cpp b/src/mongo/db/repl/database_cloner_test.cpp
index 1cb772bc898..3683e24eae3 100644
--- a/src/mongo/db/repl/database_cloner_test.cpp
+++ b/src/mongo/db/repl/database_cloner_test.cpp
@@ -39,463 +39,494 @@
namespace {
- using namespace mongo;
- using namespace mongo::repl;
-
- const std::string dbname("db");
-
- class DatabaseClonerTest : public BaseClonerTest {
- public:
-
- DatabaseClonerTest();
- void collectionWork(const Status& status, const NamespaceString& sourceNss);
- void clear() override;
- BaseCloner* getCloner() const override;
-
- protected:
-
- void setUp() override;
- void tearDown() override;
-
- std::list<std::pair<Status, NamespaceString> > collectionWorkResults;
- std::unique_ptr<DatabaseCloner> databaseCloner;
- };
-
- DatabaseClonerTest::DatabaseClonerTest()
- : collectionWorkResults(),
- databaseCloner() { }
-
- void DatabaseClonerTest::collectionWork(const Status& status, const NamespaceString& srcNss) {
- collectionWorkResults.emplace_back(status, srcNss);
- }
-
- void DatabaseClonerTest::setUp() {
- BaseClonerTest::setUp();
- collectionWorkResults.clear();
- databaseCloner.reset(new DatabaseCloner(&getExecutor(),
- target,
- dbname,
- BSONObj(),
- DatabaseCloner::ListCollectionsPredicateFn(),
- storageInterface.get(),
- stdx::bind(&DatabaseClonerTest::collectionWork,
- this,
- stdx::placeholders::_1,
- stdx::placeholders::_2),
- stdx::bind(&DatabaseClonerTest::setStatus,
- this,
- stdx::placeholders::_1)));
- }
-
- void DatabaseClonerTest::tearDown() {
- BaseClonerTest::tearDown();
- databaseCloner.reset();
- collectionWorkResults.clear();
- }
-
- void DatabaseClonerTest::clear() {
- }
-
- BaseCloner* DatabaseClonerTest::getCloner() const {
- return databaseCloner.get();
- }
-
- TEST_F(DatabaseClonerTest, InvalidConstruction) {
- ReplicationExecutor& executor = getExecutor();
-
- const BSONObj filter;
- DatabaseCloner::ListCollectionsPredicateFn pred;
- CollectionCloner::StorageInterface* si = storageInterface.get();
- namespace stdxph = stdx::placeholders;
- const DatabaseCloner::CollectionCallbackFn ccb =
- stdx::bind(&DatabaseClonerTest::collectionWork, this, stdxph::_1, stdxph::_2);
-
- const auto& cb = [](const Status&) { FAIL("should not reach here"); };
-
- // Null executor.
- ASSERT_THROWS(DatabaseCloner(nullptr, target, dbname, filter, pred, si, ccb, cb),
+using namespace mongo;
+using namespace mongo::repl;
+
+const std::string dbname("db");
+
+class DatabaseClonerTest : public BaseClonerTest {
+public:
+ DatabaseClonerTest();
+ void collectionWork(const Status& status, const NamespaceString& sourceNss);
+ void clear() override;
+ BaseCloner* getCloner() const override;
+
+protected:
+ void setUp() override;
+ void tearDown() override;
+
+ std::list<std::pair<Status, NamespaceString>> collectionWorkResults;
+ std::unique_ptr<DatabaseCloner> databaseCloner;
+};
+
+DatabaseClonerTest::DatabaseClonerTest() : collectionWorkResults(), databaseCloner() {}
+
+void DatabaseClonerTest::collectionWork(const Status& status, const NamespaceString& srcNss) {
+ collectionWorkResults.emplace_back(status, srcNss);
+}
+
+void DatabaseClonerTest::setUp() {
+ BaseClonerTest::setUp();
+ collectionWorkResults.clear();
+ databaseCloner.reset(new DatabaseCloner(
+ &getExecutor(),
+ target,
+ dbname,
+ BSONObj(),
+ DatabaseCloner::ListCollectionsPredicateFn(),
+ storageInterface.get(),
+ stdx::bind(&DatabaseClonerTest::collectionWork,
+ this,
+ stdx::placeholders::_1,
+ stdx::placeholders::_2),
+ stdx::bind(&DatabaseClonerTest::setStatus, this, stdx::placeholders::_1)));
+}
+
+void DatabaseClonerTest::tearDown() {
+ BaseClonerTest::tearDown();
+ databaseCloner.reset();
+ collectionWorkResults.clear();
+}
+
+void DatabaseClonerTest::clear() {}
+
+BaseCloner* DatabaseClonerTest::getCloner() const {
+ return databaseCloner.get();
+}
+
+TEST_F(DatabaseClonerTest, InvalidConstruction) {
+ ReplicationExecutor& executor = getExecutor();
+
+ const BSONObj filter;
+ DatabaseCloner::ListCollectionsPredicateFn pred;
+ CollectionCloner::StorageInterface* si = storageInterface.get();
+ namespace stdxph = stdx::placeholders;
+ const DatabaseCloner::CollectionCallbackFn ccb =
+ stdx::bind(&DatabaseClonerTest::collectionWork, this, stdxph::_1, stdxph::_2);
+
+ const auto& cb = [](const Status&) { FAIL("should not reach here"); };
+
+ // Null executor.
+ ASSERT_THROWS(DatabaseCloner(nullptr, target, dbname, filter, pred, si, ccb, cb),
+ UserException);
+
+ // Empty database name
+ ASSERT_THROWS(DatabaseCloner(&executor, target, "", filter, pred, si, ccb, cb), UserException);
+
+ // Callback function cannot be null.
+ {
+ DatabaseCloner::CallbackFn ncb;
+ ASSERT_THROWS(DatabaseCloner(&executor, target, dbname, filter, pred, si, ccb, ncb),
UserException);
-
- // Empty database name
- ASSERT_THROWS(DatabaseCloner(&executor, target, "", filter, pred, si, ccb, cb),
- UserException);
-
- // Callback function cannot be null.
- {
- DatabaseCloner::CallbackFn ncb;
- ASSERT_THROWS(DatabaseCloner(&executor, target, dbname, filter, pred, si, ccb, ncb),
- UserException);
- }
-
- // Storage interface cannot be null.
- {
- CollectionCloner::StorageInterface* nsi = nullptr;
- ASSERT_THROWS(DatabaseCloner(&executor, target, dbname, filter, pred, nsi, ccb, cb),
- UserException);
- }
-
- // CollectionCallbackFn function cannot be null.
- {
- DatabaseCloner::CollectionCallbackFn nccb;
- ASSERT_THROWS(DatabaseCloner(&executor, target, dbname, filter, pred, si, nccb, cb),
- UserException);
- }
- }
-
- TEST_F(DatabaseClonerTest, ClonerLifeCycle) {
- testLifeCycle();
}
- TEST_F(DatabaseClonerTest, FirstRemoteCommandWithoutFilter) {
- ASSERT_OK(databaseCloner->start());
-
- auto net = getNet();
- ASSERT_TRUE(net->hasReadyRequests());
- NetworkOperationIterator noi = net->getNextReadyRequest();
- auto&& noiRequest = noi->getRequest();
- ASSERT_EQUALS(nss.db().toString(), noiRequest.dbname);
- ASSERT_EQUALS("listCollections", std::string(noiRequest.cmdObj.firstElementFieldName()));
- ASSERT_EQUALS(1, noiRequest.cmdObj.firstElement().numberInt());
- ASSERT_FALSE(noiRequest.cmdObj.hasField("filter"));
- ASSERT_FALSE(net->hasReadyRequests());
- ASSERT_TRUE(databaseCloner->isActive());
+ // Storage interface cannot be null.
+ {
+ CollectionCloner::StorageInterface* nsi = nullptr;
+ ASSERT_THROWS(DatabaseCloner(&executor, target, dbname, filter, pred, nsi, ccb, cb),
+ UserException);
}
- TEST_F(DatabaseClonerTest, FirstRemoteCommandWithFilter) {
- const BSONObj listCollectionsFilter = BSON("name" << "coll");
- databaseCloner.reset(new DatabaseCloner(&getExecutor(),
- target,
- dbname,
- listCollectionsFilter,
- DatabaseCloner::ListCollectionsPredicateFn(),
- storageInterface.get(),
- stdx::bind(&DatabaseClonerTest::collectionWork,
- this,
- stdx::placeholders::_1,
- stdx::placeholders::_2),
- stdx::bind(&DatabaseClonerTest::setStatus,
- this,
- stdx::placeholders::_1)));
- ASSERT_OK(databaseCloner->start());
-
- auto net = getNet();
- ASSERT_TRUE(net->hasReadyRequests());
- NetworkOperationIterator noi = net->getNextReadyRequest();
- auto&& noiRequest = noi->getRequest();
- ASSERT_EQUALS(nss.db().toString(), noiRequest.dbname);
- ASSERT_EQUALS("listCollections", std::string(noiRequest.cmdObj.firstElementFieldName()));
- ASSERT_EQUALS(1, noiRequest.cmdObj.firstElement().numberInt());
- BSONElement filterElement = noiRequest.cmdObj.getField("filter");
- ASSERT_TRUE(filterElement.isABSONObj());
- ASSERT_EQUALS(listCollectionsFilter, filterElement.Obj());
- ASSERT_FALSE(net->hasReadyRequests());
- ASSERT_TRUE(databaseCloner->isActive());
+ // CollectionCallbackFn function cannot be null.
+ {
+ DatabaseCloner::CollectionCallbackFn nccb;
+ ASSERT_THROWS(DatabaseCloner(&executor, target, dbname, filter, pred, si, nccb, cb),
+ UserException);
}
-
- TEST_F(DatabaseClonerTest, InvalidListCollectionsFilter) {
- ASSERT_OK(databaseCloner->start());
-
- processNetworkResponse(
- BSON("ok" << 0 << "errmsg" << "unknown operator" << "code" << ErrorCodes::BadValue));
-
- ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
- ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, ClonerLifeCycle) {
+ testLifeCycle();
+}
+
+TEST_F(DatabaseClonerTest, FirstRemoteCommandWithoutFilter) {
+ ASSERT_OK(databaseCloner->start());
+
+ auto net = getNet();
+ ASSERT_TRUE(net->hasReadyRequests());
+ NetworkOperationIterator noi = net->getNextReadyRequest();
+ auto&& noiRequest = noi->getRequest();
+ ASSERT_EQUALS(nss.db().toString(), noiRequest.dbname);
+ ASSERT_EQUALS("listCollections", std::string(noiRequest.cmdObj.firstElementFieldName()));
+ ASSERT_EQUALS(1, noiRequest.cmdObj.firstElement().numberInt());
+ ASSERT_FALSE(noiRequest.cmdObj.hasField("filter"));
+ ASSERT_FALSE(net->hasReadyRequests());
+ ASSERT_TRUE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, FirstRemoteCommandWithFilter) {
+ const BSONObj listCollectionsFilter = BSON("name"
+ << "coll");
+ databaseCloner.reset(new DatabaseCloner(
+ &getExecutor(),
+ target,
+ dbname,
+ listCollectionsFilter,
+ DatabaseCloner::ListCollectionsPredicateFn(),
+ storageInterface.get(),
+ stdx::bind(&DatabaseClonerTest::collectionWork,
+ this,
+ stdx::placeholders::_1,
+ stdx::placeholders::_2),
+ stdx::bind(&DatabaseClonerTest::setStatus, this, stdx::placeholders::_1)));
+ ASSERT_OK(databaseCloner->start());
+
+ auto net = getNet();
+ ASSERT_TRUE(net->hasReadyRequests());
+ NetworkOperationIterator noi = net->getNextReadyRequest();
+ auto&& noiRequest = noi->getRequest();
+ ASSERT_EQUALS(nss.db().toString(), noiRequest.dbname);
+ ASSERT_EQUALS("listCollections", std::string(noiRequest.cmdObj.firstElementFieldName()));
+ ASSERT_EQUALS(1, noiRequest.cmdObj.firstElement().numberInt());
+ BSONElement filterElement = noiRequest.cmdObj.getField("filter");
+ ASSERT_TRUE(filterElement.isABSONObj());
+ ASSERT_EQUALS(listCollectionsFilter, filterElement.Obj());
+ ASSERT_FALSE(net->hasReadyRequests());
+ ASSERT_TRUE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, InvalidListCollectionsFilter) {
+ ASSERT_OK(databaseCloner->start());
+
+ processNetworkResponse(BSON("ok" << 0 << "errmsg"
+ << "unknown operator"
+ << "code" << ErrorCodes::BadValue));
+
+ ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+// A database may have no collections. Nothing to do for the database cloner.
+TEST_F(DatabaseClonerTest, ListCollectionsReturnedNoCollections) {
+ ASSERT_OK(databaseCloner->start());
+
+ // Keep going even if initial batch is empty.
+ processNetworkResponse(createListCollectionsResponse(1, BSONArray()));
+
+ ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
+ ASSERT_TRUE(databaseCloner->isActive());
+
+ // Final batch is also empty. Database cloner should stop and return a successful status.
+ processNetworkResponse(createListCollectionsResponse(0, BSONArray(), "nextBatch"));
+
+ ASSERT_OK(getStatus());
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, ListCollectionsPredicate) {
+ DatabaseCloner::ListCollectionsPredicateFn pred =
+ [](const BSONObj& info) { return info["name"].String() != "b"; };
+ databaseCloner.reset(new DatabaseCloner(
+ &getExecutor(),
+ target,
+ dbname,
+ BSONObj(),
+ pred,
+ storageInterface.get(),
+ stdx::bind(&DatabaseClonerTest::collectionWork,
+ this,
+ stdx::placeholders::_1,
+ stdx::placeholders::_2),
+ stdx::bind(&DatabaseClonerTest::setStatus, this, stdx::placeholders::_1)));
+ ASSERT_OK(databaseCloner->start());
+
+ const std::vector<BSONObj> sourceInfos = {BSON("name"
+ << "a"
+ << "options" << BSONObj()),
+ BSON("name"
+ << "b"
+ << "options" << BSONObj()),
+ BSON("name"
+ << "c"
+ << "options" << BSONObj())};
+ processNetworkResponse(createListCollectionsResponse(
+ 0, BSON_ARRAY(sourceInfos[0] << sourceInfos[1] << sourceInfos[2])));
+
+ ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
+ ASSERT_TRUE(databaseCloner->isActive());
+
+ const std::vector<BSONObj>& collectionInfos = databaseCloner->getCollectionInfos();
+ ASSERT_EQUALS(2U, collectionInfos.size());
+ ASSERT_EQUALS(sourceInfos[0], collectionInfos[0]);
+ ASSERT_EQUALS(sourceInfos[2], collectionInfos[1]);
+}
+
+TEST_F(DatabaseClonerTest, ListCollectionsMultipleBatches) {
+ ASSERT_OK(databaseCloner->start());
+
+ const std::vector<BSONObj> sourceInfos = {BSON("name"
+ << "a"
+ << "options" << BSONObj()),
+ BSON("name"
+ << "b"
+ << "options" << BSONObj())};
+ processNetworkResponse(createListCollectionsResponse(1, BSON_ARRAY(sourceInfos[0])));
+
+ ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
+ ASSERT_TRUE(databaseCloner->isActive());
+
+ {
+ const std::vector<BSONObj>& collectionInfos = databaseCloner->getCollectionInfos();
+ ASSERT_EQUALS(1U, collectionInfos.size());
+ ASSERT_EQUALS(sourceInfos[0], collectionInfos[0]);
}
- // A database may have no collections. Nothing to do for the database cloner.
- TEST_F(DatabaseClonerTest, ListCollectionsReturnedNoCollections) {
- ASSERT_OK(databaseCloner->start());
-
- // Keep going even if initial batch is empty.
- processNetworkResponse(createListCollectionsResponse(1, BSONArray()));
-
- ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
- ASSERT_TRUE(databaseCloner->isActive());
-
- // Final batch is also empty. Database cloner should stop and return a successful status.
- processNetworkResponse(createListCollectionsResponse(0, BSONArray(), "nextBatch"));
-
- ASSERT_OK(getStatus());
- ASSERT_FALSE(databaseCloner->isActive());
- }
+ processNetworkResponse(
+ createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[1]), "nextBatch"));
- TEST_F(DatabaseClonerTest, ListCollectionsPredicate) {
- DatabaseCloner::ListCollectionsPredicateFn pred = [](const BSONObj& info) {
- return info["name"].String() != "b";
- };
- databaseCloner.reset(new DatabaseCloner(&getExecutor(),
- target,
- dbname,
- BSONObj(),
- pred,
- storageInterface.get(),
- stdx::bind(&DatabaseClonerTest::collectionWork,
- this,
- stdx::placeholders::_1,
- stdx::placeholders::_2),
- stdx::bind(&DatabaseClonerTest::setStatus,
- this,
- stdx::placeholders::_1)));
- ASSERT_OK(databaseCloner->start());
-
- const std::vector<BSONObj> sourceInfos = {
- BSON("name" << "a" << "options" << BSONObj()),
- BSON("name" << "b" << "options" << BSONObj()),
- BSON("name" << "c" << "options" << BSONObj())};
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[0] <<
- sourceInfos[1] <<
- sourceInfos[2])));
-
- ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
- ASSERT_TRUE(databaseCloner->isActive());
+ ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
+ ASSERT_TRUE(databaseCloner->isActive());
+ {
const std::vector<BSONObj>& collectionInfos = databaseCloner->getCollectionInfos();
ASSERT_EQUALS(2U, collectionInfos.size());
ASSERT_EQUALS(sourceInfos[0], collectionInfos[0]);
- ASSERT_EQUALS(sourceInfos[2], collectionInfos[1]);
+ ASSERT_EQUALS(sourceInfos[1], collectionInfos[1]);
}
-
- TEST_F(DatabaseClonerTest, ListCollectionsMultipleBatches) {
- ASSERT_OK(databaseCloner->start());
-
- const std::vector<BSONObj> sourceInfos = {
- BSON("name" << "a" << "options" << BSONObj()),
- BSON("name" << "b" << "options" << BSONObj())};
- processNetworkResponse(createListCollectionsResponse(1, BSON_ARRAY(sourceInfos[0])));
-
- ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
- ASSERT_TRUE(databaseCloner->isActive());
-
- {
- const std::vector<BSONObj>& collectionInfos = databaseCloner->getCollectionInfos();
- ASSERT_EQUALS(1U, collectionInfos.size());
- ASSERT_EQUALS(sourceInfos[0], collectionInfos[0]);
- }
-
- processNetworkResponse(
- createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[1]), "nextBatch"));
-
- ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
- ASSERT_TRUE(databaseCloner->isActive());
-
- {
- const std::vector<BSONObj>& collectionInfos = databaseCloner->getCollectionInfos();
- ASSERT_EQUALS(2U, collectionInfos.size());
- ASSERT_EQUALS(sourceInfos[0], collectionInfos[0]);
- ASSERT_EQUALS(sourceInfos[1], collectionInfos[1]);
- }
- }
-
- TEST_F(DatabaseClonerTest, CollectionInfoNameFieldMissing) {
- ASSERT_OK(databaseCloner->start());
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("options" << BSONObj()))));
- ASSERT_EQUALS(ErrorCodes::FailedToParse, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "must contain 'name' field");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, CollectionInfoNameNotAString) {
- ASSERT_OK(databaseCloner->start());
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << 123 << "options" << BSONObj()))));
- ASSERT_EQUALS(ErrorCodes::TypeMismatch, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "'name' field must be a string");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, CollectionInfoNameEmpty) {
- ASSERT_OK(databaseCloner->start());
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "" << "options" << BSONObj()))));
- ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "invalid collection namespace: db.");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, CollectionInfoNameDuplicate) {
- ASSERT_OK(databaseCloner->start());
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "a" << "options" << BSONObj()) <<
- BSON("name" << "a" << "options" << BSONObj()))));
- ASSERT_EQUALS(ErrorCodes::DuplicateKey, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "duplicate collection name 'a'");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, CollectionInfoOptionsFieldMissing) {
- ASSERT_OK(databaseCloner->start());
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(BSON("name" << "a"))));
- ASSERT_EQUALS(ErrorCodes::FailedToParse, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "must contain 'options' field");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, CollectionInfoOptionsNotAnObject) {
- ASSERT_OK(databaseCloner->start());
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "a" << "options" << 123))));
- ASSERT_EQUALS(ErrorCodes::TypeMismatch, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "'options' field must be an object");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, InvalidCollectionOptions) {
- ASSERT_OK(databaseCloner->start());
-
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "a" << "options" << BSON("storageEngine" << 1)))));
-
- ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, ListCollectionsReturnsEmptyCollectionName) {
- databaseCloner.reset(new DatabaseCloner(&getExecutor(),
- target,
- dbname,
- BSONObj(),
- DatabaseCloner::ListCollectionsPredicateFn(),
- storageInterface.get(),
- stdx::bind(&DatabaseClonerTest::collectionWork,
- this,
- stdx::placeholders::_1,
- stdx::placeholders::_2),
- stdx::bind(&DatabaseClonerTest::setStatus,
- this,
- stdx::placeholders::_1)));
- ASSERT_OK(databaseCloner->start());
-
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "" << "options" << BSONObj()))));
-
- ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
- ASSERT_STRING_CONTAINS(getStatus().reason(), "invalid collection namespace: db.");
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, StartFirstCollectionClonerFailed) {
- ASSERT_OK(databaseCloner->start());
-
- databaseCloner->setStartCollectionClonerFn([](CollectionCloner& cloner) {
+}
+
+TEST_F(DatabaseClonerTest, CollectionInfoNameFieldMissing) {
+ ASSERT_OK(databaseCloner->start());
+ processNetworkResponse(
+ createListCollectionsResponse(0, BSON_ARRAY(BSON("options" << BSONObj()))));
+ ASSERT_EQUALS(ErrorCodes::FailedToParse, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "must contain 'name' field");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, CollectionInfoNameNotAString) {
+ ASSERT_OK(databaseCloner->start());
+ processNetworkResponse(createListCollectionsResponse(
+ 0, BSON_ARRAY(BSON("name" << 123 << "options" << BSONObj()))));
+ ASSERT_EQUALS(ErrorCodes::TypeMismatch, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "'name' field must be a string");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, CollectionInfoNameEmpty) {
+ ASSERT_OK(databaseCloner->start());
+ processNetworkResponse(
+ createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << ""
+ << "options" << BSONObj()))));
+ ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "invalid collection namespace: db.");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, CollectionInfoNameDuplicate) {
+ ASSERT_OK(databaseCloner->start());
+ processNetworkResponse(
+ createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << "a"
+ << "options" << BSONObj())
+ << BSON("name"
+ << "a"
+ << "options" << BSONObj()))));
+ ASSERT_EQUALS(ErrorCodes::DuplicateKey, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "duplicate collection name 'a'");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, CollectionInfoOptionsFieldMissing) {
+ ASSERT_OK(databaseCloner->start());
+ processNetworkResponse(createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << "a"))));
+ ASSERT_EQUALS(ErrorCodes::FailedToParse, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "must contain 'options' field");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, CollectionInfoOptionsNotAnObject) {
+ ASSERT_OK(databaseCloner->start());
+ processNetworkResponse(createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << "a"
+ << "options" << 123))));
+ ASSERT_EQUALS(ErrorCodes::TypeMismatch, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "'options' field must be an object");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, InvalidCollectionOptions) {
+ ASSERT_OK(databaseCloner->start());
+
+ processNetworkResponse(
+ createListCollectionsResponse(
+ 0,
+ BSON_ARRAY(BSON("name"
+ << "a"
+ << "options" << BSON("storageEngine" << 1)))));
+
+ ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, ListCollectionsReturnsEmptyCollectionName) {
+ databaseCloner.reset(new DatabaseCloner(
+ &getExecutor(),
+ target,
+ dbname,
+ BSONObj(),
+ DatabaseCloner::ListCollectionsPredicateFn(),
+ storageInterface.get(),
+ stdx::bind(&DatabaseClonerTest::collectionWork,
+ this,
+ stdx::placeholders::_1,
+ stdx::placeholders::_2),
+ stdx::bind(&DatabaseClonerTest::setStatus, this, stdx::placeholders::_1)));
+ ASSERT_OK(databaseCloner->start());
+
+ processNetworkResponse(
+ createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << ""
+ << "options" << BSONObj()))));
+
+ ASSERT_EQUALS(ErrorCodes::BadValue, getStatus().code());
+ ASSERT_STRING_CONTAINS(getStatus().reason(), "invalid collection namespace: db.");
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, StartFirstCollectionClonerFailed) {
+ ASSERT_OK(databaseCloner->start());
+
+ databaseCloner->setStartCollectionClonerFn(
+ [](CollectionCloner& cloner) { return Status(ErrorCodes::OperationFailed, ""); });
+
+ processNetworkResponse(
+ createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << "a"
+ << "options" << BSONObj()))));
+
+ ASSERT_EQUALS(ErrorCodes::OperationFailed, getStatus().code());
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, StartSecondCollectionClonerFailed) {
+ ASSERT_OK(databaseCloner->start());
+
+ // Replace scheduleDbWork function so that all callbacks (including exclusive tasks)
+ // will run through network interface.
+ auto&& executor = getExecutor();
+ databaseCloner->setScheduleDbWorkFn([&](const ReplicationExecutor::CallbackFn& workFn) {
+ return executor.scheduleWork(workFn);
+ });
+
+ databaseCloner->setStartCollectionClonerFn([](CollectionCloner& cloner) {
+ if (cloner.getSourceNamespace().coll() == "b") {
return Status(ErrorCodes::OperationFailed, "");
- });
-
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "a" << "options" << BSONObj()))));
-
- ASSERT_EQUALS(ErrorCodes::OperationFailed, getStatus().code());
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, StartSecondCollectionClonerFailed) {
- ASSERT_OK(databaseCloner->start());
-
- // Replace scheduleDbWork function so that all callbacks (including exclusive tasks)
- // will run through network interface.
- auto&& executor = getExecutor();
- databaseCloner->setScheduleDbWorkFn([&](const ReplicationExecutor::CallbackFn& workFn) {
- return executor.scheduleWork(workFn);
- });
-
- databaseCloner->setStartCollectionClonerFn([](CollectionCloner& cloner) {
- if (cloner.getSourceNamespace().coll() == "b") {
- return Status(ErrorCodes::OperationFailed, "");
- }
- return cloner.start();
- });
-
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(
- BSON("name" << "a" << "options" << BSONObj()) <<
- BSON("name" << "b" << "options" << BSONObj()))));
-
- processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
- processNetworkResponse(createCursorResponse(0, BSONArray()));
-
- ASSERT_EQUALS(ErrorCodes::OperationFailed, getStatus().code());
- ASSERT_FALSE(databaseCloner->isActive());
- }
-
- TEST_F(DatabaseClonerTest, FirstCollectionListIndexesFailed) {
- ASSERT_OK(databaseCloner->start());
-
- // Replace scheduleDbWork function so that all callbacks (including exclusive tasks)
- // will run through network interface.
- auto&& executor = getExecutor();
- databaseCloner->setScheduleDbWorkFn([&](const ReplicationExecutor::CallbackFn& workFn) {
- return executor.scheduleWork(workFn);
- });
-
- const std::vector<BSONObj> sourceInfos = {
- BSON("name" << "a" << "options" << BSONObj()),
- BSON("name" << "b" << "options" << BSONObj())};
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[0] <<
- sourceInfos[1])));
-
- ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
- ASSERT_TRUE(databaseCloner->isActive());
-
- // Collection cloners are run serially for now.
- // This affects the order of the network responses.
- processNetworkResponse(
- BSON("ok" << 0 << "errmsg" << "" << "code" << ErrorCodes::NamespaceNotFound));
-
- processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
- processNetworkResponse(createCursorResponse(0, BSONArray()));
-
- ASSERT_OK(getStatus());
- ASSERT_FALSE(databaseCloner->isActive());
-
- ASSERT_EQUALS(2U, collectionWorkResults.size());
- {
- auto i = collectionWorkResults.cbegin();
- ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, i->first.code());
- ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "a").ns());
- i++;
- ASSERT_OK(i->first);
- ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "b").ns());
}
+ return cloner.start();
+ });
+
+ processNetworkResponse(
+ createListCollectionsResponse(0,
+ BSON_ARRAY(BSON("name"
+ << "a"
+ << "options" << BSONObj())
+ << BSON("name"
+ << "b"
+ << "options" << BSONObj()))));
+
+ processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
+ processNetworkResponse(createCursorResponse(0, BSONArray()));
+
+ ASSERT_EQUALS(ErrorCodes::OperationFailed, getStatus().code());
+ ASSERT_FALSE(databaseCloner->isActive());
+}
+
+TEST_F(DatabaseClonerTest, FirstCollectionListIndexesFailed) {
+ ASSERT_OK(databaseCloner->start());
+
+ // Replace scheduleDbWork function so that all callbacks (including exclusive tasks)
+ // will run through network interface.
+ auto&& executor = getExecutor();
+ databaseCloner->setScheduleDbWorkFn([&](const ReplicationExecutor::CallbackFn& workFn) {
+ return executor.scheduleWork(workFn);
+ });
+
+ const std::vector<BSONObj> sourceInfos = {BSON("name"
+ << "a"
+ << "options" << BSONObj()),
+ BSON("name"
+ << "b"
+ << "options" << BSONObj())};
+ processNetworkResponse(
+ createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[0] << sourceInfos[1])));
+
+ ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
+ ASSERT_TRUE(databaseCloner->isActive());
+
+ // Collection cloners are run serially for now.
+ // This affects the order of the network responses.
+ processNetworkResponse(BSON("ok" << 0 << "errmsg"
+ << ""
+ << "code" << ErrorCodes::NamespaceNotFound));
+
+ processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
+ processNetworkResponse(createCursorResponse(0, BSONArray()));
+
+ ASSERT_OK(getStatus());
+ ASSERT_FALSE(databaseCloner->isActive());
+
+ ASSERT_EQUALS(2U, collectionWorkResults.size());
+ {
+ auto i = collectionWorkResults.cbegin();
+ ASSERT_EQUALS(ErrorCodes::NamespaceNotFound, i->first.code());
+ ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "a").ns());
+ i++;
+ ASSERT_OK(i->first);
+ ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "b").ns());
}
-
- TEST_F(DatabaseClonerTest, CreateCollections) {
- ASSERT_OK(databaseCloner->start());
-
- // Replace scheduleDbWork function so that all callbacks (including exclusive tasks)
- // will run through network interface.
- auto&& executor = getExecutor();
- databaseCloner->setScheduleDbWorkFn([&](const ReplicationExecutor::CallbackFn& workFn) {
- return executor.scheduleWork(workFn);
- });
-
- const std::vector<BSONObj> sourceInfos = {
- BSON("name" << "a" << "options" << BSONObj()),
- BSON("name" << "b" << "options" << BSONObj())};
- processNetworkResponse(createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[0] <<
- sourceInfos[1])));
-
- ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
- ASSERT_TRUE(databaseCloner->isActive());
-
- // Collection cloners are run serially for now.
- // This affects the order of the network responses.
- processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
- processNetworkResponse(createCursorResponse(0, BSONArray()));
-
- processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
- processNetworkResponse(createCursorResponse(0, BSONArray()));
-
- ASSERT_OK(getStatus());
- ASSERT_FALSE(databaseCloner->isActive());
-
- ASSERT_EQUALS(2U, collectionWorkResults.size());
- {
- auto i = collectionWorkResults.cbegin();
- ASSERT_OK(i->first);
- ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "a").ns());
- i++;
- ASSERT_OK(i->first);
- ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "b").ns());
- }
+}
+
+TEST_F(DatabaseClonerTest, CreateCollections) {
+ ASSERT_OK(databaseCloner->start());
+
+ // Replace scheduleDbWork function so that all callbacks (including exclusive tasks)
+ // will run through network interface.
+ auto&& executor = getExecutor();
+ databaseCloner->setScheduleDbWorkFn([&](const ReplicationExecutor::CallbackFn& workFn) {
+ return executor.scheduleWork(workFn);
+ });
+
+ const std::vector<BSONObj> sourceInfos = {BSON("name"
+ << "a"
+ << "options" << BSONObj()),
+ BSON("name"
+ << "b"
+ << "options" << BSONObj())};
+ processNetworkResponse(
+ createListCollectionsResponse(0, BSON_ARRAY(sourceInfos[0] << sourceInfos[1])));
+
+ ASSERT_EQUALS(getDetectableErrorStatus(), getStatus());
+ ASSERT_TRUE(databaseCloner->isActive());
+
+ // Collection cloners are run serially for now.
+ // This affects the order of the network responses.
+ processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
+ processNetworkResponse(createCursorResponse(0, BSONArray()));
+
+ processNetworkResponse(createListIndexesResponse(0, BSON_ARRAY(idIndexSpec)));
+ processNetworkResponse(createCursorResponse(0, BSONArray()));
+
+ ASSERT_OK(getStatus());
+ ASSERT_FALSE(databaseCloner->isActive());
+
+ ASSERT_EQUALS(2U, collectionWorkResults.size());
+ {
+ auto i = collectionWorkResults.cbegin();
+ ASSERT_OK(i->first);
+ ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "a").ns());
+ i++;
+ ASSERT_OK(i->first);
+ ASSERT_EQUALS(i->second.ns(), NamespaceString(dbname, "b").ns());
}
+}
-} // namespace
+} // namespace