/**
* Copyright (C) 2015 MongoDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/platform/basic.h"
#include "mongo/client/replica_set_monitor.h"
#include "mongo/client/replica_set_monitor_internal.h"
#include "mongo/unittest/unittest.h"
namespace {
using namespace mongo;
using std::set;
// Pull nested types to top-level scope
typedef ReplicaSetMonitor::SetState SetState;
typedef SetState::Node Node;
typedef SetState::Nodes Nodes;
bool isCompatible(const Node& node, ReadPreference pref, const TagSet& tagSet) {
set seeds;
seeds.insert(node.host);
SetState set("name", seeds);
set.nodes.push_back(node);
ReadPreferenceSetting criteria(pref, tagSet);
return !set.getMatchingHost(criteria).empty();
}
const BSONObj SampleIsMasterDoc = BSON("tags" << BSON("dc"
<< "NYC"
<< "p"
<< "2"
<< "region"
<< "NA"));
const BSONObj SampleTags = SampleIsMasterDoc["tags"].Obj();
const BSONObj NoTags = BSONObj();
const BSONObj NoTagIsMasterDoc = BSON("isMaster" << true);
TEST(ReplSetMonitorNode, SimpleGoodMatch) {
Node node(((HostAndPort())));
node.tags = BSON("dc"
<< "sf");
ASSERT(node.matches(BSON("dc"
<< "sf")));
}
TEST(ReplSetMonitorNode, SimpleBadMatch) {
Node node((HostAndPort()));
node.tags = BSON("dc"
<< "nyc");
ASSERT(!node.matches(BSON("dc"
<< "sf")));
}
TEST(ReplSetMonitorNode, ExactMatch) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(node.matches(SampleIsMasterDoc["tags"].Obj()));
}
TEST(ReplSetMonitorNode, EmptyTag) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(node.matches(BSONObj()));
}
TEST(ReplSetMonitorNode, MemberNoTagMatchesEmptyTag) {
Node node((HostAndPort()));
node.tags = NoTags;
ASSERT(node.matches(BSONObj()));
}
TEST(ReplSetMonitorNode, MemberNoTagDoesNotMatch) {
Node node((HostAndPort()));
node.tags = NoTags;
ASSERT(!node.matches(BSON("dc"
<< "NYC")));
}
TEST(ReplSetMonitorNode, IncompleteMatch) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(!node.matches(BSON("dc"
<< "NYC"
<< "p"
<< "2"
<< "hello"
<< "world")));
}
TEST(ReplSetMonitorNode, PartialMatch) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(node.matches(BSON("dc"
<< "NYC"
<< "p"
<< "2")));
}
TEST(ReplSetMonitorNode, SingleTagCrit) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(node.matches(BSON("p"
<< "2")));
}
TEST(ReplSetMonitorNode, BadSingleTagCrit) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(!node.matches(BSON("dc"
<< "SF")));
}
TEST(ReplSetMonitorNode, NonExistingFieldTag) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(!node.matches(BSON("noSQL"
<< "Mongo")));
}
TEST(ReplSetMonitorNode, UnorederedMatching) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(node.matches(BSON("p"
<< "2"
<< "dc"
<< "NYC")));
}
TEST(ReplSetMonitorNode, SameValueDiffKey) {
Node node((HostAndPort()));
node.tags = SampleTags;
ASSERT(!node.matches(BSON("datacenter"
<< "NYC")));
}
TEST(ReplSetMonitorNode, PriNodeCompatibleTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = true;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "NYC"));
TagSet tags(BSONArray(builder.done()));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, SecNodeCompatibleTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = false;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "NYC"));
TagSet tags(BSONArray(builder.done()));
ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, PriNodeNotCompatibleTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = true;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "SF"));
TagSet tags(BSONArray(builder.done()));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, SecNodeNotCompatibleTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = false;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "SF"));
TagSet tags(BSONArray(builder.done()));
ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, PriNodeCompatiblMultiTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = true;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "RP"));
builder.append(BSON("dc"
<< "NYC"
<< "p"
<< "2"));
TagSet tags(BSONArray(builder.done()));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, SecNodeCompatibleMultiTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = false;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "RP"));
builder.append(BSON("dc"
<< "NYC"
<< "p"
<< "2"));
TagSet tags(BSONArray(builder.done()));
ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, PriNodeNotCompatibleMultiTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = true;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "sf"));
builder.append(BSON("dc"
<< "NYC"
<< "P"
<< "4"));
TagSet tags(BSONArray(builder.done()));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
TEST(ReplSetMonitorNode, SecNodeNotCompatibleMultiTag) {
Node node(HostAndPort("dummy", 3));
node.tags = SampleTags;
node.isUp = true;
node.isMaster = false;
BSONArrayBuilder builder;
builder.append(BSON("dc"
<< "sf"));
builder.append(BSON("dc"
<< "NYC"
<< "P"
<< "4"));
TagSet tags(BSONArray(builder.done()));
ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryOnly, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::PrimaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryPreferred, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::SecondaryOnly, tags));
ASSERT(!isCompatible(node, mongo::ReadPreference::Nearest, tags));
}
} // namespace