summaryrefslogtreecommitdiff
path: root/src/mongo/client/mongo_uri_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/client/mongo_uri_test.cpp')
-rw-r--r--src/mongo/client/mongo_uri_test.cpp292
1 files changed, 269 insertions, 23 deletions
diff --git a/src/mongo/client/mongo_uri_test.cpp b/src/mongo/client/mongo_uri_test.cpp
index 6a5f6c9882a..c32b1c0b5c5 100644
--- a/src/mongo/client/mongo_uri_test.cpp
+++ b/src/mongo/client/mongo_uri_test.cpp
@@ -28,11 +28,17 @@
#include "mongo/platform/basic.h"
-#include "mongo/client/mongo_uri.h"
+#include <fstream>
#include "mongo/base/string_data.h"
+#include "mongo/bson/bsonobj.h"
+#include "mongo/bson/bsontypes.h"
+#include "mongo/bson/json.h"
+#include "mongo/client/mongo_uri.h"
#include "mongo/unittest/unittest.h"
+#include <boost/filesystem/operations.hpp>
+
namespace {
using mongo::MongoURI;
@@ -60,16 +66,77 @@ const URITestCase validCases[] = {
{"mongodb://user@127.0.0.1", "user", "", kMaster, "", 1, 0, ""},
- {"mongodb://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"},
-
{"mongodb://localhost/?foo=bar", "", "", kMaster, "", 1, 1, ""},
+ {"mongodb://localhost,/?foo=bar", "", "", kMaster, "", 1, 1, ""},
+
{"mongodb://user:pwd@127.0.0.1:1234", "user", "pwd", kMaster, "", 1, 0, ""},
{"mongodb://user@127.0.0.1:1234", "user", "", kMaster, "", 1, 0, ""},
{"mongodb://127.0.0.1:1234/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"},
+ {"mongodb://127.0.0.1/dbName?foo=a&c=b", "", "", kMaster, "", 1, 2, "dbName"},
+
+ {"mongodb://user:pwd@127.0.0.1,/dbName?foo=a&c=b", "user", "pwd", kMaster, "", 1, 2, "dbName"},
+
+ {"mongodb://user:pwd@127.0.0.1,127.0.0.2/dbname?a=b&replicaSet=replName",
+ "user",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://needs%20encoding%25%23!%3C%3E:pwd@127.0.0.1,127.0.0.2/"
+ "dbname?a=b&replicaSet=replName",
+ "needs encoding%#!<>",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://needs%20encoding%25%23!%3C%3E:pwd@127.0.0.1,127.0.0.2/"
+ "db@name?a=b&replicaSet=replName",
+ "needs encoding%#!<>",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "db@name"},
+
+ {"mongodb://user:needs%20encoding%25%23!%3C%3E@127.0.0.1,127.0.0.2/"
+ "dbname?a=b&replicaSet=replName",
+ "user",
+ "needs encoding%#!<>",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://user:pwd@127.0.0.1,127.0.0.2/dbname?a=b&replicaSet=needs%20encoding%25%23!%3C%3E",
+ "user",
+ "pwd",
+ kSet,
+ "needs encoding%#!<>",
+ 2,
+ 2,
+ "dbname"},
+
+ {"mongodb://user:pwd@127.0.0.1,127.0.0.2/needsencoding%40hello?a=b&replicaSet=replName",
+ "user",
+ "pwd",
+ kSet,
+ "replName",
+ 2,
+ 2,
+ "needsencoding@hello"},
+
{"mongodb://user:pwd@127.0.0.1,127.0.0.2/?replicaSet=replName",
"user",
"pwd",
@@ -259,21 +326,27 @@ const URITestCase validCases[] = {
1,
2,
""},
- {"mongodb:///tmp/mongodb-27017.sock", "", "", kMaster, "", 1, 0, ""},
- {"mongodb:///tmp/mongodb-27017.sock,/tmp/mongodb-27018.sock/?replicaSet=replName",
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock", "", "", kMaster, "", 1, 0, ""},
+
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/?replicaSet=replName",
"",
"",
kSet,
"replName",
2,
1,
- ""}};
+ ""},
+};
const InvalidURITestCase invalidCases[] = {
// No host.
{"mongodb://"},
+ {"mongodb://usr:pwd@/dbname?a=b"},
+
+ // Username and password must be encoded (cannot have ':' or '@')
+ {"mongodb://usr:pwd:@127.0.0.1/dbName?foo=a&c=b"},
// Needs a "/" after the hosts and before the options.
{"mongodb://localhost:27017,localhost:27018?replicaSet=missingSlash"},
@@ -282,32 +355,109 @@ const InvalidURITestCase invalidCases[] = {
{"mongodb://localhost:27017localhost:27018"},
// Domain sockets have to end in ".sock".
- {"mongodb:///notareal/domainsock"},
+ {"mongodb://%2Fnotareal%2Fdomainsock"},
+
+ // Database name cannot contain slash ("/"), backslash ("\"), space (" "), double-quote ("""),
+ // or dollar sign ("$")
+ {"mongodb://usr:pwd@localhost:27017/db$name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db/name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db\\name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db name?a=b"},
+ {"mongodb://usr:pwd@localhost:27017/db\"name?a=b"},
+
+ // Options must have a key
+ {"mongodb://usr:pwd@localhost:27017/dbname?=b"},
+
+ // Cannot skip a key value pair
+ {"mongodb://usr:pwd@localhost:27017/dbname?a=b&&b=c"},
+
+ // Multiple Unix domain sockets and auth DB resembling a socket (relative path)
+ {"mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.sock?replicaSet=replName"},
+
+ // Multiple Unix domain sockets with auth DB resembling a path (relative path)
+ {"mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.shoe?replicaSet=replName"},
+
+ // Multiple Unix domain sockets and auth DB resembling a socket (absolute path)
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/"
+ "admin.sock?replicaSet=replName"},
+
+ // Multiple Unix domain sockets with auth DB resembling a path (absolute path)
+ {"mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/"
+ "admin.shoe?replicaSet=replName"},
+
+ // Missing value in key value pair for options
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b&d"},
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&c=b&d="},
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&h=&c=b&d=6"},
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&h&c=b&d=6"},
+
+ // Missing a hostname, or unparsable hostname(s)
+ {"mongodb://,/dbName"},
+ {"mongodb://user:pwd@,/dbName"},
+ {"mongodb://localhost:1234:5678/dbName"},
// Options can't have multiple question marks. Only one.
{"mongodb://localhost:27017/?foo=a?c=b&d=e?asdf=foo"},
+
+ // Missing a key in key value pair for options
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&=d&c=b"},
+
+ // Missing an entire key-value pair
+ {"mongodb://127.0.0.1:1234/dbName?foo=a&&c=b"},
};
+// Helper Method to take a filename for a json file and return the array of tests inside of it
+mongo::BSONObj getBsonFromJsonFile(std::string fileName) {
+ boost::filesystem::path directoryPath = boost::filesystem::current_path();
+ boost::filesystem::path filePath(directoryPath / "src" / "mongo" / "client" /
+ "mongo_uri_tests" / fileName);
+ std::string filename(filePath.string());
+ std::ifstream infile(filename.c_str());
+ std::string data((std::istreambuf_iterator<char>(infile)), std::istreambuf_iterator<char>());
+ if (data.empty()) {
+ // v3.4 test infra is unable to see these extra files.
+ return mongo::BSONObj();
+ }
+ mongo::BSONObj obj = mongo::fromjson(data);
+ ASSERT_TRUE(obj.valid(mongo::BSONVersion::kLatest));
+ ASSERT_TRUE(obj.hasField("tests"));
+ mongo::BSONObj arr = obj.getField("tests").embeddedObject().getOwned();
+ ASSERT_TRUE(arr.couldBeArray());
+ return arr;
+}
+
+// Helper method to take a BSONElement and either extract its string or return an empty string
+std::string returnStringFromElementOrNull(mongo::BSONElement element) {
+ ASSERT_TRUE(!element.eoo());
+ if (element.type() == mongo::jstNULL) {
+ return std::string();
+ }
+ ASSERT_EQ(element.type(), mongo::String);
+ return element.String();
+}
+
+// Helper method to take a valid test case, parse() it, and assure the output is correct
+void testValidURIFormat(URITestCase testCase) {
+ mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n';
+ std::string errMsg;
+ const auto cs_status = MongoURI::parse(testCase.URI);
+ ASSERT_OK(cs_status);
+ auto result = cs_status.getValue();
+ ASSERT_EQ(testCase.uname, result.getUser());
+ ASSERT_EQ(testCase.password, result.getPassword());
+ ASSERT_EQ(testCase.type, result.type());
+ ASSERT_EQ(testCase.setname, result.getSetName());
+ ASSERT_EQ(testCase.numservers, result.getServers().size());
+ ASSERT_EQ(testCase.numOptions, result.getOptions().size());
+ ASSERT_EQ(testCase.database, result.getDatabase());
+}
+
TEST(MongoURI, GoodTrickyURIs) {
const size_t numCases = sizeof(validCases) / sizeof(validCases[0]);
for (size_t i = 0; i != numCases; ++i) {
const URITestCase testCase = validCases[i];
- mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n';
- std::string errMsg;
- auto cs_status = MongoURI::parse(testCase.URI);
- if (!cs_status.getStatus().toString().empty()) {
- mongo::unittest::log() << "error with uri: " << cs_status.getStatus().toString();
- }
- ASSERT_TRUE(cs_status.isOK());
- auto result = cs_status.getValue();
- ASSERT_EQ(testCase.uname, result.getUser());
- ASSERT_EQ(testCase.password, result.getPassword());
- ASSERT_EQ(testCase.type, result.type());
- ASSERT_EQ(testCase.setname, result.getSetName());
- ASSERT_EQ(testCase.numservers, result.getServers().size());
- ASSERT_EQ(testCase.numOptions, result.getOptions().size());
- ASSERT_EQ(testCase.database, result.getDatabase());
+ testValidURIFormat(testCase);
}
}
@@ -318,7 +468,7 @@ TEST(MongoURI, InvalidURIs) {
const InvalidURITestCase testCase = invalidCases[i];
mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n';
auto cs_status = MongoURI::parse(testCase.URI);
- ASSERT_FALSE(cs_status.isOK());
+ ASSERT_NOT_OK(cs_status);
}
}
@@ -357,4 +507,100 @@ TEST(MongoURI, CloneURIForServer) {
ASSERT_EQ(clonedURIOptions.at("ssl"), "true");
}
+/**
+ * These tests come from the Mongo Uri Specifications for the drivers found at:
+ * https://github.com/mongodb/specifications/tree/master/source/connection-string/tests
+ * They have been slighly altered as the Drivers specification is slighly different
+ * from the server specification.
+ */
+TEST(MongoURI, specTests) {
+ const std::string files[] = {
+ "mongo-uri-valid-auth.json",
+ "mongo-uri-options.json",
+ "mongo-uri-unix-sockets-absolute.json",
+ "mongo-uri-unix-sockets-relative.json",
+ "mongo-uri-warnings.json",
+ "mongo-uri-host-identifiers.json",
+ "mongo-uri-invalid.json",
+ };
+
+ for (const auto& file : files) {
+ const auto testBson = getBsonFromJsonFile(file);
+
+ for (const auto& testElement : testBson) {
+ ASSERT_EQ(testElement.type(), mongo::Object);
+ const auto test = testElement.Obj();
+
+ // First extract the valid field and the uri field
+ const auto validDoc = test.getField("valid");
+ ASSERT_FALSE(validDoc.eoo());
+ ASSERT_TRUE(validDoc.isBoolean());
+ const auto valid = validDoc.Bool();
+
+ const auto uriDoc = test.getField("uri");
+ ASSERT_FALSE(uriDoc.eoo());
+ ASSERT_EQ(uriDoc.type(), mongo::String);
+ const auto uri = uriDoc.String();
+
+ if (!valid) {
+ // This uri string is invalid --> parse the uri and ensure it fails
+ const InvalidURITestCase testCase = {uri};
+ mongo::unittest::log() << "Testing URI: " << testCase.URI << '\n';
+ auto cs_status = MongoURI::parse(testCase.URI);
+ ASSERT_NOT_OK(cs_status);
+ } else {
+ // This uri is valid -- > parse the remaining necessary fields
+
+ // parse the auth options
+ std::string database, username, password;
+
+ const auto auth = test.getField("auth");
+ ASSERT_FALSE(auth.eoo());
+ if (auth.type() != mongo::jstNULL) {
+ ASSERT_EQ(auth.type(), mongo::Object);
+ const auto authObj = auth.embeddedObject();
+ database = returnStringFromElementOrNull(authObj.getField("db"));
+ username = returnStringFromElementOrNull(authObj.getField("username"));
+ password = returnStringFromElementOrNull(authObj.getField("password"));
+ }
+
+ // parse the hosts
+ const auto hosts = test.getField("hosts");
+ ASSERT_FALSE(hosts.eoo());
+ ASSERT_EQ(hosts.type(), mongo::Array);
+ const auto numHosts = static_cast<size_t>(hosts.Obj().nFields());
+
+ // parse the options
+ mongo::ConnectionString::ConnectionType connectionType = kMaster;
+ size_t numOptions = 0;
+ std::string setName;
+ const auto optionsElement = test.getField("options");
+ ASSERT_FALSE(optionsElement.eoo());
+ if (optionsElement.type() != mongo::jstNULL) {
+ ASSERT_EQ(optionsElement.type(), mongo::Object);
+ const auto optionsObj = optionsElement.Obj();
+ numOptions = optionsObj.nFields();
+ const auto replsetElement = optionsObj.getField("replicaSet");
+ if (!replsetElement.eoo()) {
+ ASSERT_EQ(replsetElement.type(), mongo::String);
+ setName = replsetElement.String();
+ connectionType = kSet;
+ }
+ }
+
+ // Create the URITestCase abnd
+ const URITestCase testCase = {uri,
+ username,
+ password,
+ connectionType,
+ setName,
+ numHosts,
+ numOptions,
+ database};
+ testValidURIFormat(testCase);
+ }
+ }
+ }
+}
+
} // namespace