#include #include "connspec.h" using namespace lcb; static size_t countHosts(const Connspec *spec) { return spec->hosts().size(); } class ConnstrTest : public ::testing::Test { protected: Connspec params; const char *errmsg; void reinit() { params = Connspec(); } void SetUp() { params = Connspec(); } }; const Spechost* findHost(const Connspec& params, const char* srch) { for (size_t ii = 0; ii < params.hosts().size(); ++ii) { const Spechost *cur = ¶ms.hosts()[ii]; if (srch == cur->hostname) { return cur; } } return NULL; } const Spechost* findHost(const Connspec* params, const char *srch) { return findHost(*params, srch); } struct OptionPair { std::string key; std::string value; }; bool findOption(const Connspec& params, const char *srch, OptionPair& op) { int iter = 0; Connspec::Options::const_iterator ii = params.options().begin(); for (; ii != params.options().end(); ++ii) { if (ii->first == srch) { op.key = ii->first; op.value = ii->second; return true; } } return false; } bool findOption(const Connspec* params, const char *srch, OptionPair& op) { return findOption(*params, srch, op); } size_t countHosts(const Connspec ¶ms) { return params.hosts().size(); } TEST_F(ConnstrTest, testParseBasic) { lcb_error_t err; err = params.parse("couchbase://1.2.3.4", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); const Spechost *tmphost; tmphost = findHost(params, "1.2.3.4"); ASSERT_EQ(1, countHosts(params)); ASSERT_FALSE(NULL == tmphost); ASSERT_EQ(0, tmphost->port); ASSERT_EQ(0, tmphost->type); // Nothing reinit(); // test with bad scheme err = params.parse("blah://foo.com", &errmsg); ASSERT_NE(LCB_SUCCESS, err) << "Error on bad scheme"; reinit(); err = params.parse("couchbase://", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok with scheme only"; reinit(); err = params.parse("couchbase://?", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok with only '?'"; reinit(); err = params.parse("couchbase://?&", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok with only '?&'"; reinit(); err = params.parse("1.2.3.4", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok without scheme"; ASSERT_EQ(LCB_CONFIG_HTTP_PORT, params.default_port()); reinit(); err = params.parse("1.2.3.4:999", &errmsg); ASSERT_EQ(1, countHosts(¶ms)); tmphost = findHost(¶ms, "1.2.3.4"); ASSERT_FALSE(tmphost == NULL); ASSERT_EQ(999, tmphost->port); ASSERT_TRUE(tmphost->isHTTP()); } TEST_F(ConnstrTest, testParseHosts) { lcb_error_t err; err = params.parse("couchbase://foo.com,bar.com,baz.com", &errmsg); ASSERT_EQ(3, countHosts(¶ms)); ASSERT_FALSE(NULL == findHost(¶ms, "foo.com")); ASSERT_FALSE(NULL == findHost(¶ms, "bar.com")); ASSERT_FALSE(NULL == findHost(¶ms, "baz.com")); // Parse with 'legacy' format reinit(); err = params.parse("couchbase://foo.com:8091", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); const Spechost *dh = findHost(¶ms, "foo.com"); ASSERT_FALSE(NULL == dh); ASSERT_EQ("foo.com", dh->hostname); // CCBC-599 ASSERT_EQ(0, dh->port); ASSERT_EQ(0, dh->type); // parse with invalid port, without specifying protocol reinit(); err = params.parse("couchbase://foo.com:4444", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); dh = findHost(¶ms, "foo.com"); ASSERT_EQ(4444, dh->port); ASSERT_TRUE(dh->isMCD()); reinit(); err = params.parse("couchbases://foo.com:4444", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); dh = findHost(¶ms, "foo.com"); ASSERT_EQ(LCB_SSL_ENABLED, params.sslopts()); ASSERT_EQ(4444, dh->port); ASSERT_TRUE(dh->isMCDS()); // Parse with recognized format reinit(); err = params.parse("couchbase://foo.com:4444=mcd", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); dh = findHost(¶ms, "foo.com"); ASSERT_EQ("foo.com", dh->hostname); ASSERT_EQ(4444, dh->port); ASSERT_TRUE(dh->isMCD()); //Parse multiple hosts with ports reinit(); err = params.parse("couchbase://foo.com:4444=mcd,bar.com:5555=mcd", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); dh = findHost(¶ms, "foo.com"); ASSERT_FALSE(dh == NULL); ASSERT_EQ("foo.com", dh->hostname); ASSERT_EQ(4444, dh->port); ASSERT_TRUE(dh->isMCD()); dh = findHost(¶ms, "bar.com"); ASSERT_FALSE(dh == NULL); ASSERT_EQ("bar.com", dh->hostname); ASSERT_EQ(5555, dh->port); ASSERT_TRUE(dh->isMCD()); reinit(); err = params.parse("couchbase://foo.com,bar.com:4444", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); dh = findHost(¶ms, "bar.com"); ASSERT_EQ(4444, dh->port); ASSERT_TRUE(dh->isMCD()); dh = findHost(¶ms, "foo.com"); ASSERT_TRUE(dh->isTypeless()); reinit(); err = params.parse("couchbase://foo.com;bar.com;baz.com", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Can parse old-style semicolons"; ASSERT_EQ(3, countHosts(¶ms)); ASSERT_FALSE(NULL == findHost(¶ms, "foo.com")); ASSERT_FALSE(NULL == findHost(¶ms, "bar.com")); ASSERT_FALSE(NULL == findHost(¶ms, "baz.com")); } TEST_F(ConnstrTest, testParseBucket) { lcb_error_t err; err = params.parse("couchbase://foo.com/user", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ("user", params.bucket()) << "Basic bucket parse"; reinit(); err = params.parse("couchbase://foo.com/user/", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Bucket can have a slash"; // We can have a bucket using a slash reinit(); err = params.parse("couchbase:///default", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Bucket without host OK"; ASSERT_EQ("default", params.bucket()); reinit(); err = params.parse("couchbase:///default?", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ("default", params.bucket()); reinit(); err = params.parse("couchbase:///%2FUsers%2F?", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ("/Users/", params.bucket()); } TEST_F(ConnstrTest, testOptionsPassthrough) { lcb_error_t err; err = params.parse("couchbase://?foo=bar", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Options only"; ASSERT_FALSE(params.options().empty()); ASSERT_NE(0, params.options().size()); OptionPair op; ASSERT_TRUE(findOption(¶ms, "foo", op)); ASSERT_EQ("bar", op.value); reinit(); err = params.parse("couchbase://?foo=bar", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_TRUE(findOption(¶ms, "foo", op)); ASSERT_EQ("bar", op.value); reinit(); err = params.parse("couchbase://?foo", &errmsg); ASSERT_NE(LCB_SUCCESS, err) << "Option without value"; // Multiple options reinit(); err = params.parse("couchbase://?foo=fooval&bar=barval", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_TRUE(findOption(¶ms, "foo", op)); ASSERT_EQ("fooval", op.value); ASSERT_TRUE(findOption(¶ms, "bar", op)); ASSERT_EQ("barval", op.value); reinit(); err = params.parse("couchbase:///protected?ssl=on&compression=off", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok with bucket and no hosts"; ASSERT_EQ(1, countHosts(¶ms)); ASSERT_FALSE(NULL == findHost(¶ms, "localhost")); ASSERT_TRUE(findOption(¶ms, "compression", op)); reinit(); err = params.parse("couchbase://?foo=foo&bar=bar&", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok with trailing '&'"; reinit(); err = params.parse("couchbase://?foo=foo&bootstrap_on=all&bar=bar", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "Ok with non-passthrough option"; ASSERT_TRUE(findOption(¶ms, "foo", op)); ASSERT_TRUE(findOption(¶ms, "bar", op)); ASSERT_FALSE(findOption(¶ms, "bootstrap_on", op)); } TEST_F(ConnstrTest, testRecognizedOptions) { lcb_error_t err; err = params.parse("couchbases://", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ(LCB_SSL_ENABLED, params.sslopts()); reinit(); err = params.parse("couchbase://?ssl=on", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ(LCB_SSL_ENABLED, params.sslopts()); reinit(); err = params.parse("couchbases://?ssl=no_verify", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ(LCB_SSL_ENABLED|LCB_SSL_NOVERIFY, params.sslopts()); reinit(); err = params.parse("couchbases://?ssl=off", &errmsg); ASSERT_NE(LCB_SUCCESS, err); // Loglevel reinit(); err = params.parse("couchbase://?console_log_level=5", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ(5, params.loglevel()); reinit(); err = params.parse("couchbase://?console_log_level=gah", &errmsg); ASSERT_NE(LCB_SUCCESS, err); } TEST_F(ConnstrTest, testTransportOptions) { lcb_error_t err; err = params.parse("couchbase://", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_FALSE(params.is_bs_udef()); reinit(); err = params.parse("couchbase://?bootstrap_on=cccp", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "bootstrap_on=cccp"; ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_CCCP)); ASSERT_FALSE(params.has_bsmode(LCB_CONFIG_TRANSPORT_HTTP)); reinit(); err = params.parse("couchbase://?bootstrap_on=http", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "bootstrap_on=http"; ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_HTTP)); ASSERT_FALSE(params.has_bsmode(LCB_CONFIG_TRANSPORT_CCCP)); reinit(); err = params.parse("couchbase://?bootstrap_on=all", &errmsg); ASSERT_EQ(LCB_SUCCESS, err) << "bootstrap_on=all"; ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_CCCP)); ASSERT_TRUE(params.has_bsmode(LCB_CONFIG_TRANSPORT_HTTP)); reinit(); err = params.parse("couchbase://?bootstrap_on=bleh", &errmsg); ASSERT_NE(LCB_SUCCESS, err) << "Error on bad bootstrap_on value"; } TEST_F(ConnstrTest, testCompatConversion) { lcb_error_t err; struct lcb_create_st cropts; memset(&cropts, 0, sizeof cropts); cropts.version = 0; cropts.v.v0.bucket = "users"; cropts.v.v0.host = "foo.com;bar.com;baz.com"; cropts.v.v0.passwd = "secret"; err = params.load(cropts); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_FALSE(NULL == findHost(¶ms, "foo.com")); ASSERT_FALSE(NULL == findHost(¶ms, "bar.com")); ASSERT_FALSE(NULL == findHost(¶ms, "baz.com")); ASSERT_EQ(3, countHosts(¶ms)); ASSERT_EQ("users", params.bucket()); ASSERT_EQ("secret", params.password()); // Ensure old-style port specifications are parsed and don't throw an // error. We'd also like to verify that these actually land within // the htport field, that's a TODO reinit(); memset(&cropts, 0, sizeof cropts); cropts.version = 2; cropts.v.v2.host = "foo.com:9030;bar.com:9040;baz.com:9050"; cropts.v.v2.mchosts = "foo.com:7030;bar.com:7040;baz.com:7050"; err = params.load(cropts); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ(6, countHosts(¶ms)); // Ensure struct fields override the URI string reinit(); memset(&cropts, 0, sizeof cropts); cropts.version = 3; cropts.v.v3.passwd = "secret"; cropts.v.v3.connstr = "couchbase:///fluffle?password=bleh"; err = params.load(cropts); ASSERT_EQ(LCB_SUCCESS, err); ASSERT_EQ("fluffle", params.bucket()); ASSERT_EQ(cropts.v.v3.passwd, params.password()); } TEST_F(ConnstrTest, testCertificateWithoutSSL) { // Ensure we get an invalid input error for certificate paths without // couchbases:// lcb_error_t err; err = params.parse( "couchbase://1.2.3.4/default?certpath=/foo/bar/baz", &errmsg); ASSERT_NE(LCB_SUCCESS, err); reinit(); err = params.parse( "couchbases://1.2.3.4/default?certpath=/foo/bar/baz", &errmsg); ASSERT_EQ(LCB_SUCCESS, err); } TEST_F(ConnstrTest, testDnsSrvExplicit) { // Test various things relating to DNS SRV lcb_error_t err; err = params.parse("couchbase+dnssrv://1.1.1.1", &errmsg); EXPECT_EQ(LCB_SUCCESS, err); EXPECT_TRUE(params.can_dnssrv()); EXPECT_TRUE(params.is_explicit_dnssrv()); reinit(); err = params.parse("couchbase+dnssrv://1.1.1.1,2.2.2.2", &errmsg); EXPECT_NE(LCB_SUCCESS, err); reinit(); err = params.parse("couchbases+dnssrv://1.1.1.1", &errmsg); EXPECT_EQ(LCB_SUCCESS, err); EXPECT_NE(0, params.sslopts()); EXPECT_TRUE(params.can_dnssrv()); EXPECT_TRUE(params.is_explicit_dnssrv()); } TEST_F(ConnstrTest, testDnsSrvImplicit) { EXPECT_EQ(LCB_SUCCESS, params.parse("couchbase://")); EXPECT_FALSE(params.can_dnssrv()); EXPECT_FALSE(params.is_explicit_dnssrv()); reinit(); EXPECT_EQ(LCB_SUCCESS, params.parse("couchbase://1.1.1.1")); EXPECT_TRUE(params.can_dnssrv()); EXPECT_FALSE(params.is_explicit_dnssrv()); reinit(); EXPECT_EQ(LCB_SUCCESS, params.parse("couchbase://1.1.1.1,2.2.2.2")); EXPECT_FALSE(params.can_dnssrv()) << "No implicit SRV on multiple hosts"; reinit(); EXPECT_EQ(LCB_SUCCESS, params.parse("couchbase://1.1.1.1:666")); EXPECT_FALSE(params.can_dnssrv()); reinit(); EXPECT_EQ(LCB_SUCCESS, params.parse("couchbase://1.1.1.1:11210")); EXPECT_TRUE(params.can_dnssrv()); reinit(); EXPECT_EQ(LCB_SUCCESS, params.parse("couchbases://1.1.1.1")); EXPECT_TRUE(params.can_dnssrv()); }