diff --git a/index.js b/index.js index badab9cfe..f3e3149a3 100644 --- a/index.js +++ b/index.js @@ -32,5 +32,7 @@ module.exports = { X509: require('./lib/auth/x509'), Plain: require('./lib/auth/plain'), GSSAPI: require('./lib/auth/gssapi'), - ScramSHA1: require('./lib/auth/scram') + ScramSHA1: require('./lib/auth/scram'), + // Utilities + parseConnectionString: require('./lib/uri_parser') }; diff --git a/lib/error.js b/lib/error.js index 5946e8e57..b0515b617 100644 --- a/lib/error.js +++ b/lib/error.js @@ -39,7 +39,7 @@ util.inherits(MongoError, Error); * @method * @param {Error|string|object} options The options used to create the error. * @return {MongoError} A MongoError instance - * @deprecated Use new MongoError() instead. + * @deprecated Use `new MongoError()` instead. */ MongoError.create = function(options) { return new MongoError(options); @@ -60,7 +60,23 @@ var MongoNetworkError = function(message) { }; util.inherits(MongoNetworkError, MongoError); +/** + * An error used when attempting to parse a value (like a connection string) + * + * @class + * @param {Error|string|object} message The error message + * @property {string} message The error message + * @return {MongoParseError} A MongoNetworkError instance + * @extends {MongoError} + */ +const MongoParseError = function(message) { + MongoError.call(this, message); + this.name = 'MongoParseError'; +}; +util.inherits(MongoParseError, MongoError); + module.exports = { MongoError: MongoError, - MongoNetworkError: MongoNetworkError + MongoNetworkError: MongoNetworkError, + MongoParseError: MongoParseError }; diff --git a/lib/uri_parser.js b/lib/uri_parser.js new file mode 100644 index 000000000..3ac310878 --- /dev/null +++ b/lib/uri_parser.js @@ -0,0 +1,297 @@ +'use strict'; +const URL = require('url'); +const qs = require('querystring'); +const dns = require('dns'); +const MongoParseError = require('./error').MongoParseError; + +/** + * The following regular expression validates a connection string and breaks the + * provide string into the following capture groups: [protocol, username, password, hosts] + */ +const HOSTS_RX = /(mongodb(?:\+srv|)):\/\/(?: (?:[^:]*) (?: : ([^@]*) )? @ )?([^/?]*)(?:\/|)(.*)/; + +/** + * Determines whether a provided address matches the provided parent domain in order + * to avoid certain attack vectors. + * + * @param {String} srvAddress The address to check against a domain + * @param {String} parentDomain The domain to check the provided address against + * @return {Boolean} Whether the provided address matches the parent domain + */ +function matchesParentDomain(srvAddress, parentDomain) { + const regex = /^.*?\./; + const srv = `.${srvAddress.replace(regex, '')}`; + const parent = `.${parentDomain.replace(regex, '')}`; + return srv.endsWith(parent); +} + +/** + * Lookup an `mongodb+srv` connection string, combine the parts and reparse it as a normal + * connection string. + * + * @param {string} uri The connection string to parse + * @param {object} options Optional user provided connection string options + * @param {function} callback + */ +function parseSrvConnectionString(uri, options, callback) { + const result = URL.parse(uri, true); + + if (result.hostname.split('.').length < 3) { + return callback(new MongoParseError('URI does not have hostname, domain name and tld')); + } + + result.domainLength = result.hostname.split('.').length; + if (result.pathname && result.pathname.match(',')) { + return callback(new MongoParseError('Invalid URI, cannot contain multiple hostnames')); + } + + if (result.port) { + return callback(new MongoParseError(`Ports not accepted with '${PROTOCOL_MONGODB_SRV}' URIs`)); + } + + let srvAddress = `_mongodb._tcp.${result.host}`; + dns.resolveSrv(srvAddress, (err, addresses) => { + if (err) return callback(err); + + if (addresses.length === 0) { + return callback(new MongoParseError('No addresses found at host')); + } + + for (let i = 0; i < addresses.length; i++) { + if (!matchesParentDomain(addresses[i].name, result.hostname, result.domainLength)) { + return callback( + new MongoParseError('Server record does not share hostname with parent URI') + ); + } + } + + let base = result.auth ? `mongodb://${result.auth}@` : `mongodb://`; + let connectionStrings = addresses.map( + (address, i) => + i === 0 ? `${base}${address.name}:${address.port}` : `${address.name}:${address.port}` + ); + + let connectionString = connectionStrings.join(',') + '/'; + let connectionStringOptions = []; + + // Default to SSL true + if (!options.ssl && (!result.search || !result.query.hasOwnProperty('ssl'))) { + connectionStringOptions.push('ssl=true'); + } + + // Keep original uri options + if (result.search) { + connectionStringOptions.push(result.search.replace('?', '')); + } + + dns.resolveTxt(result.host, (err, record) => { + if (err) { + if (err.code !== 'ENODATA') { + return callback(err); + } + record = null; + } + + if (record) { + if (record.length > 1) { + return callback(new MongoParseError('Multiple text records not allowed')); + } + + record = record[0]; + record = record.length > 1 ? record.join('') : record[0]; + if (!record.includes('authSource') && !record.includes('replicaSet')) { + return callback( + new MongoParseError('Text record must only set `authSource` or `replicaSet`') + ); + } + + connectionStringOptions.push(record); + } + + // Add any options to the connection string + if (connectionStringOptions.length) { + connectionString += `?${connectionStringOptions.join('&')}`; + } + + parseConnectionString(connectionString, callback); + }); + }); +} + +/** + * Parses a query string item according to the connection string spec + * + * @param {Array|String} value The value to parse + * @return {Array|Object|String} The parsed value + */ +function parseQueryStringItemValue(value) { + if (Array.isArray(value)) { + // deduplicate and simplify arrays + value = value.filter((value, idx) => value.indexOf(value) === idx); + if (value.length === 1) value = value[0]; + } else if (value.indexOf(':') > 0) { + value = value.split(',').reduce((result, pair) => { + const parts = pair.split(':'); + result[parts[0]] = parseQueryStringItemValue(parts[1]); + return result; + }, {}); + } else if (value.toLowerCase() === 'true' || value.toLowerCase() === 'false') { + value = value.toLowerCase() === 'true'; + } else if (!Number.isNaN(value)) { + const numericValue = parseFloat(value); + if (!Number.isNaN(numericValue)) { + value = parseFloat(value); + } + } + + return value; +} + +/** + * Parses a query string according the connection string spec. + * + * @param {String} query The query string to parse + * @return {Object|Error} The parsed query string as an object, or an error if one was encountered + */ +function parseQueryString(query) { + const result = {}; + let parsedQueryString = qs.parse(query); + + for (const key in parsedQueryString) { + const value = parsedQueryString[key]; + if (value === '' || value == null) { + return new MongoParseError('Incomplete key value pair for option'); + } + + result[key.toLowerCase()] = parseQueryStringItemValue(value); + } + + // special cases for known deprecated options + if (result.wtimeout && result.wtimeoutms) { + delete result.wtimeout; + console.warn('Unsupported option `wtimeout` specified'); + } + + return Object.keys(result).length ? result : null; +} + +const PROTOCOL_MONGODB = 'mongodb'; +const PROTOCOL_MONGODB_SRV = 'mongodb+srv'; +const SUPPORTED_PROTOCOLS = [PROTOCOL_MONGODB, PROTOCOL_MONGODB_SRV]; + +/** + * Parses a MongoDB connection string + * + * @param {*} uri the MongoDB connection string to parse + * @param {object} [options] Optional settings. + * @param {parseCallback} callback + */ +function parseConnectionString(uri, options, callback) { + if (typeof options === 'function') (callback = options), (options = {}); + options = options || {}; + + const cap = uri.match(HOSTS_RX); + if (!cap) { + return callback(new MongoParseError('Invalid connection string')); + } + + const protocol = cap[1]; + if (SUPPORTED_PROTOCOLS.indexOf(protocol) === -1) { + return callback(new MongoParseError('Invalid protocol provided')); + } + + if (protocol === PROTOCOL_MONGODB_SRV) { + return parseSrvConnectionString(uri, options, callback); + } + + const dbAndQuery = cap[4].split('?'); + const db = dbAndQuery.length > 0 ? dbAndQuery[0] : null; + const query = dbAndQuery.length > 1 ? dbAndQuery[1] : null; + let parsedOptions = parseQueryString(query); + if (parsedOptions instanceof MongoParseError) { + return callback(parsedOptions); + } + + parsedOptions = Object.assign({}, parsedOptions, options); + const auth = { username: null, password: null, db: db && db !== '' ? qs.unescape(db) : null }; + if (cap[4].split('?')[0].indexOf('@') !== -1) { + return callback(new MongoParseError('Unescaped slash in userinfo section')); + } + + const authorityParts = cap[3].split('@'); + if (authorityParts.length > 2) { + return callback(new MongoParseError('Unescaped at-sign in authority section')); + } + + if (authorityParts.length > 1) { + const authParts = authorityParts.shift().split(':'); + if (authParts.length > 2) { + return callback(new MongoParseError('Unescaped colon in authority section')); + } + + auth.username = qs.unescape(authParts[0]); + auth.password = authParts[1] ? qs.unescape(authParts[1]) : null; + } + + let hostParsingError = null; + const hosts = authorityParts + .shift() + .split(',') + .map(host => { + let parsedHost = URL.parse(`mongodb://${host}`); + if (parsedHost.path === '/:') { + hostParsingError = new MongoParseError('Double colon in host identifier'); + return null; + } + + // heuristically determine if we're working with a domain socket + if (host.match(/\.sock/)) { + parsedHost.hostname = qs.unescape(host); + parsedHost.port = null; + } + + if (Number.isNaN(parsedHost.port)) { + hostParsingError = new MongoParseError('Invalid port (non-numeric string)'); + return; + } + + const result = { + host: parsedHost.hostname, + port: parsedHost.port ? parseInt(parsedHost.port) : null + }; + + if (result.port === 0) { + hostParsingError = new MongoParseError('Invalid port (zero) with hostname'); + return; + } + + if (result.port > 65535) { + hostParsingError = new MongoParseError('Invalid port (larger than 65535) with hostname'); + return; + } + + if (result.port < 0) { + hostParsingError = new MongoParseError('Invalid port (negative number)'); + return; + } + + return result; + }) + .filter(host => !!host); + + if (hostParsingError) { + return callback(hostParsingError); + } + + if (hosts.length === 0 || hosts[0].host === '' || hosts[0].host === null) { + return callback(new MongoParseError('No hostname or hostnames provided in connection string')); + } + + callback(null, { + hosts: hosts, + auth: auth.db || auth.username ? auth : null, + options: Object.keys(parsedOptions).length ? parsedOptions : null + }); +} + +module.exports = parseConnectionString; diff --git a/test/tests/spec/connection-string/Makefile b/test/tests/spec/connection-string/Makefile new file mode 100644 index 000000000..71d6cf840 --- /dev/null +++ b/test/tests/spec/connection-string/Makefile @@ -0,0 +1,7 @@ +YAML_FILES=$(shell find . -iname '*.yml') +JSON_FILES=$(patsubst %.yml,%.json,$(YAML_FILES)) + +all: $(JSON_FILES) + +%.json : %.yml + jwc yaml2json $< > $@ diff --git a/test/tests/spec/connection-string/README.rst b/test/tests/spec/connection-string/README.rst new file mode 100644 index 000000000..faf767651 --- /dev/null +++ b/test/tests/spec/connection-string/README.rst @@ -0,0 +1,97 @@ +======================= +Connection String Tests +======================= + +The YAML and JSON files in this directory tree are platform-independent tests +that drivers can use to prove their conformance to the Connection String Spec. + +As the spec is primarily concerned with parsing the parts of a URI, these tests +do not focus on host and option validation. Where necessary, the tests use +options known to be (un)supported by drivers to assert behavior such as issuing +a warning on repeated option keys. As such these YAML tests are in no way a +replacement for more thorough testing. However, they can provide an initial +verification of your implementation. + +Converting to JSON +------------------ + +The tests are written in YAML because it is easier for humans to write and read, +and because YAML includes a standard comment format. A JSONified version of each +YAML file is included in this repository. Whenever you change the YAML, +re-convert to JSON. One method to convert to JSON is with +`jsonwidget-python `_:: + + pip install PyYAML urwid jsonwidget + make + +Or instead of "make":: + + for i in `find . -iname '*.yml'`; do + echo "${i%.*}" + jwc yaml2json $i > ${i%.*}.json + done + +Alternatively, you can use `yamljs `_:: + + npm install -g yamljs + yaml2json -s -p -r . + +Version +------- + +Files in the "specifications" repository have no version scheme. They are not +tied to a MongoDB server version. + +Format +------ + +Each YAML file contains an object with a single ``tests`` key. This key is an +array of test case objects, each of which have the following keys: + +- ``description``: A string describing the test. +- ``uri``: A string containing the URI to be parsed. +- ``valid:`` A boolean indicating if the URI should be considered valid. +- ``warning:`` A boolean indicating whether URI parsing should emit a warning + (independent of whether or not the URI is valid). +- ``hosts``: An array of host objects, each of which have the following keys: + + - ``type``: A string denoting the type of host. Possible values are "ipv4", + "ip_literal", "hostname", and "unix". Asserting the type is *optional*. + - ``host``: A string containing the parsed host. + - ``port``: An integer containing the parsed port number. +- ``auth``: An object containing the following keys: + + - ``username``: A string containing the parsed username. For auth mechanisms + that do not utilize a password, this may be the entire ``userinfo`` token + (as discussed in `RFC 2396 `_). + - ``password``: A string containing the parsed password. + - ``db``: A string containing the parsed authentication database. For legacy + implementations that support namespaces (databases and collections) this may + be the full namespace eg: ``.`` +- ``options``: An object containing key/value pairs for each parsed query string + option. + +If a test case includes a null value for one of these keys (e.g. ``auth: ~``, +``port: ~``), no assertion is necessary. This both simplifies parsing of the +test files (keys should always exist) and allows flexibility for drivers that +might substitute default values *during* parsing (e.g. omitted ``port`` could be +parsed as 27017). + +The ``valid`` and ``warning`` fields are boolean in order to keep the tests +flexible. We are not concerned with asserting the format of specific error or +warnings messages strings. + +Use as unit tests +================= + +Testing whether a URI is valid or not should simply be a matter of checking +whether URI parsing (or MongoClient construction) raises an error or exception. +Testing for emitted warnings may require more legwork (e.g. configuring a log +handler and watching for output). + +Not all drivers may be able to directly assert the hosts, auth credentials, and +options. Doing so may require exposing the driver's URI parsing component. + +The file valid-db-with-dotted-name.yml is a special case for testing drivers +that allow dotted namespaces, instead of only database names, in the Auth +Database portion of the URI. diff --git a/test/tests/spec/connection-string/invalid-uris.json b/test/tests/spec/connection-string/invalid-uris.json new file mode 100644 index 000000000..3cffddaf5 --- /dev/null +++ b/test/tests/spec/connection-string/invalid-uris.json @@ -0,0 +1,256 @@ +{ + "tests": [ + { + "auth": null, + "description": "Empty string", + "hosts": null, + "options": null, + "uri": "", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid scheme", + "hosts": null, + "options": null, + "uri": "mongo://localhost:27017", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Missing host", + "hosts": null, + "options": null, + "uri": "mongodb://", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Double colon in host identifier", + "hosts": null, + "options": null, + "uri": "mongodb://localhost::27017", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Double colon in host identifier and trailing slash", + "hosts": null, + "options": null, + "uri": "mongodb://localhost::27017/", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Double colon in host identifier with missing host and port", + "hosts": null, + "options": null, + "uri": "mongodb://::", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Double colon in host identifier with missing port", + "hosts": null, + "options": null, + "uri": "mongodb://localhost,localhost::", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Double colon in host identifier and second host", + "hosts": null, + "options": null, + "uri": "mongodb://localhost::27017,abc", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (negative number) with hostname", + "hosts": null, + "options": null, + "uri": "mongodb://localhost:-1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (zero) with hostname", + "hosts": null, + "options": null, + "uri": "mongodb://localhost:0/", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (positive number) with hostname", + "hosts": null, + "options": null, + "uri": "mongodb://localhost:65536", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (positive number) with hostname and trailing slash", + "hosts": null, + "options": null, + "uri": "mongodb://localhost:65536/", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (non-numeric string) with hostname", + "hosts": null, + "options": null, + "uri": "mongodb://localhost:foo", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (negative number) with IP literal", + "hosts": null, + "options": null, + "uri": "mongodb://[::1]:-1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (zero) with IP literal", + "hosts": null, + "options": null, + "uri": "mongodb://[::1]:0/", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (positive number) with IP literal", + "hosts": null, + "options": null, + "uri": "mongodb://[::1]:65536", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (positive number) with IP literal and trailing slash", + "hosts": null, + "options": null, + "uri": "mongodb://[::1]:65536/", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Invalid port (non-numeric string) with IP literal", + "hosts": null, + "options": null, + "uri": "mongodb://[::1]:foo", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Missing delimiting slash between hosts and options", + "hosts": null, + "options": null, + "uri": "mongodb://example.com?w=1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Incomplete key value pair for option", + "hosts": null, + "options": null, + "uri": "mongodb://example.com/?w", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username with password containing an unescaped colon", + "hosts": null, + "options": null, + "uri": "mongodb://alice:foo:bar@127.0.0.1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username with password containing an unescaped colon", + "hosts": null, + "options": null, + "uri": "mongodb://alice:foo:bar@127.0.0.1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username containing an unescaped at-sign", + "hosts": null, + "options": null, + "uri": "mongodb://alice@@127.0.0.1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username with password containing an unescaped at-sign", + "hosts": null, + "options": null, + "uri": "mongodb://alice@foo:bar@127.0.0.1", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username containing an unescaped slash", + "hosts": null, + "options": null, + "uri": "mongodb://alice/@127.0.0.1/db", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username containing unescaped slash with password", + "hosts": null, + "options": null, + "uri": "mongodb://alice/bob:foo@127.0.0.1/db", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Username with password containing an unescaped slash", + "hosts": null, + "options": null, + "uri": "mongodb://alice:foo/bar@127.0.0.1/db", + "valid": false, + "warning": null + }, + { + "auth": null, + "description": "Host with unescaped slash", + "hosts": null, + "options": null, + "uri": "mongodb:///tmp/mongodb-27017.sock/", + "valid": false, + "warning": null + } + ] +} diff --git a/test/tests/spec/connection-string/invalid-uris.yml b/test/tests/spec/connection-string/invalid-uris.yml new file mode 100644 index 000000000..7b25afd6a --- /dev/null +++ b/test/tests/spec/connection-string/invalid-uris.yml @@ -0,0 +1,225 @@ +tests: + - + description: "Empty string" + uri: "" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid scheme" + uri: "mongo://localhost:27017" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Missing host" + uri: "mongodb://" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Double colon in host identifier" + uri: "mongodb://localhost::27017" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Double colon in host identifier and trailing slash" + uri: "mongodb://localhost::27017/" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Double colon in host identifier with missing host and port" + uri: "mongodb://::" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Double colon in host identifier with missing port" + uri: "mongodb://localhost,localhost::" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Double colon in host identifier and second host" + uri: "mongodb://localhost::27017,abc" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (negative number) with hostname" + uri: "mongodb://localhost:-1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (zero) with hostname" + uri: "mongodb://localhost:0/" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (positive number) with hostname" + uri: "mongodb://localhost:65536" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (positive number) with hostname and trailing slash" + uri: "mongodb://localhost:65536/" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (non-numeric string) with hostname" + uri: "mongodb://localhost:foo" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (negative number) with IP literal" + uri: "mongodb://[::1]:-1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (zero) with IP literal" + uri: "mongodb://[::1]:0/" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (positive number) with IP literal" + uri: "mongodb://[::1]:65536" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (positive number) with IP literal and trailing slash" + uri: "mongodb://[::1]:65536/" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Invalid port (non-numeric string) with IP literal" + uri: "mongodb://[::1]:foo" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Missing delimiting slash between hosts and options" + uri: "mongodb://example.com?w=1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Incomplete key value pair for option" + uri: "mongodb://example.com/?w" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username with password containing an unescaped colon" + uri: "mongodb://alice:foo:bar@127.0.0.1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username with password containing an unescaped colon" + uri: "mongodb://alice:foo:bar@127.0.0.1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username containing an unescaped at-sign" + uri: "mongodb://alice@@127.0.0.1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username with password containing an unescaped at-sign" + uri: "mongodb://alice@foo:bar@127.0.0.1" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username containing an unescaped slash" + uri: "mongodb://alice/@127.0.0.1/db" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username containing unescaped slash with password" + uri: "mongodb://alice/bob:foo@127.0.0.1/db" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Username with password containing an unescaped slash" + uri: "mongodb://alice:foo/bar@127.0.0.1/db" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ + - + description: "Host with unescaped slash" + uri: "mongodb:///tmp/mongodb-27017.sock/" + valid: false + warning: ~ + hosts: ~ + auth: ~ + options: ~ diff --git a/test/tests/spec/connection-string/valid-auth.json b/test/tests/spec/connection-string/valid-auth.json new file mode 100644 index 000000000..9cb90c7d0 --- /dev/null +++ b/test/tests/spec/connection-string/valid-auth.json @@ -0,0 +1,311 @@ +{ + "tests": [ + { + "auth": { + "db": null, + "password": "foo", + "username": "alice" + }, + "description": "User info for single IPv4 host without database", + "hosts": [ + { + "host": "127.0.0.1", + "port": null, + "type": "ipv4" + } + ], + "options": null, + "uri": "mongodb://alice:foo@127.0.0.1", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "test", + "password": "foo", + "username": "alice" + }, + "description": "User info for single IPv4 host with database", + "hosts": [ + { + "host": "127.0.0.1", + "port": null, + "type": "ipv4" + } + ], + "options": null, + "uri": "mongodb://alice:foo@127.0.0.1/test", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": "bar", + "username": "bob" + }, + "description": "User info for single IP literal host without database", + "hosts": [ + { + "host": "::1", + "port": 27018, + "type": "ip_literal" + } + ], + "options": null, + "uri": "mongodb://bob:bar@[::1]:27018", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "bar", + "username": "bob" + }, + "description": "User info for single IP literal host with database", + "hosts": [ + { + "host": "::1", + "port": 27018, + "type": "ip_literal" + } + ], + "options": null, + "uri": "mongodb://bob:bar@[::1]:27018/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": "baz", + "username": "eve" + }, + "description": "User info for single hostname without database", + "hosts": [ + { + "host": "example.com", + "port": null, + "type": "hostname" + } + ], + "options": null, + "uri": "mongodb://eve:baz@example.com", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "db2", + "password": "baz", + "username": "eve" + }, + "description": "User info for single hostname with database", + "hosts": [ + { + "host": "example.com", + "port": null, + "type": "hostname" + } + ], + "options": null, + "uri": "mongodb://eve:baz@example.com/db2", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": "secret", + "username": "alice" + }, + "description": "User info for multiple hosts without database", + "hosts": [ + { + "host": "127.0.0.1", + "port": null, + "type": "ipv4" + }, + { + "host": "example.com", + "port": 27018, + "type": "hostname" + } + ], + "options": null, + "uri": "mongodb://alice:secret@127.0.0.1,example.com:27018", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "secret", + "username": "alice" + }, + "description": "User info for multiple hosts with database", + "hosts": [ + { + "host": "example.com", + "port": null, + "type": "hostname" + }, + { + "host": "::1", + "port": 27019, + "type": "ip_literal" + } + ], + "options": null, + "uri": "mongodb://alice:secret@example.com,[::1]:27019/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": null, + "username": "alice" + }, + "description": "Username without password", + "hosts": [ + { + "host": "127.0.0.1", + "port": null, + "type": "ipv4" + } + ], + "options": null, + "uri": "mongodb://alice@127.0.0.1", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": "", + "username": "alice" + }, + "description": "Username with empty password", + "hosts": [ + { + "host": "127.0.0.1", + "port": null, + "type": "ipv4" + } + ], + "options": null, + "uri": "mongodb://alice:@127.0.0.1", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "my=db", + "password": null, + "username": "@l:ce/=" + }, + "description": "Escaped username and database without password", + "hosts": [ + { + "host": "example.com", + "port": null, + "type": "hostname" + } + ], + "options": null, + "uri": "mongodb://%40l%3Ace%2F%3D@example.com/my%3Ddb", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin?", + "password": "f:zzb@z/z=", + "username": "$am" + }, + "description": "Escaped user info and database (MONGODB-CR)", + "hosts": [ + { + "host": "127.0.0.1", + "port": null, + "type": "ipv4" + } + ], + "options": { + "authmechanism": "MONGODB-CR" + }, + "uri": "mongodb://%24am:f%3Azzb%40z%2Fz%3D@127.0.0.1/admin%3F?authMechanism=MONGODB-CR", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": null, + "username": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" + }, + "description": "Escaped username (MONGODB-X509)", + "hosts": [ + { + "host": "localhost", + "port": null, + "type": "hostname" + } + ], + "options": { + "authmechanism": "MONGODB-X509" + }, + "uri": "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509", + "valid": true, + "warning": false + }, + { + "auth": { + "db": null, + "password": "secret", + "username": "user@EXAMPLE.COM" + }, + "description": "Escaped username (GSSAPI)", + "hosts": [ + { + "host": "localhost", + "port": null, + "type": "hostname" + } + ], + "options": { + "authmechanism": "GSSAPI", + "authmechanismproperties": { + "CANONICALIZE_HOST_NAME": true, + "SERVICE_NAME": "other" + } + }, + "uri": "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "secret", + "username": "alice" + }, + "description": "At-signs in options aren't part of the userinfo", + "hosts": [ + { + "host": "example.com", + "port": null, + "type": "hostname" + } + ], + "options": { + "replicaset": "my@replicaset" + }, + "uri": "mongodb://alice:secret@example.com/admin?replicaset=my@replicaset", + "valid": true, + "warning": false + } + ] +} diff --git a/test/tests/spec/connection-string/valid-auth.yml b/test/tests/spec/connection-string/valid-auth.yml new file mode 100644 index 000000000..1319a4188 --- /dev/null +++ b/test/tests/spec/connection-string/valid-auth.yml @@ -0,0 +1,241 @@ +tests: + - + description: "User info for single IPv4 host without database" + uri: "mongodb://alice:foo@127.0.0.1" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + auth: + username: "alice" + password: "foo" + db: ~ + options: ~ + - + description: "User info for single IPv4 host with database" + uri: "mongodb://alice:foo@127.0.0.1/test" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + auth: + username: "alice" + password: "foo" + db: "test" + options: ~ + - + description: "User info for single IP literal host without database" + uri: "mongodb://bob:bar@[::1]:27018" + valid: true + warning: false + hosts: + - + type: "ip_literal" + host: "::1" + port: 27018 + auth: + username: "bob" + password: "bar" + db: ~ + options: ~ + - + description: "User info for single IP literal host with database" + uri: "mongodb://bob:bar@[::1]:27018/admin" + valid: true + warning: false + hosts: + - + type: "ip_literal" + host: "::1" + port: 27018 + auth: + username: "bob" + password: "bar" + db: "admin" + options: ~ + - + description: "User info for single hostname without database" + uri: "mongodb://eve:baz@example.com" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: + username: "eve" + password: "baz" + db: ~ + options: ~ + - + description: "User info for single hostname with database" + uri: "mongodb://eve:baz@example.com/db2" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: + username: "eve" + password: "baz" + db: "db2" + options: ~ + - + description: "User info for multiple hosts without database" + uri: "mongodb://alice:secret@127.0.0.1,example.com:27018" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + - + type: "hostname" + host: "example.com" + port: 27018 + auth: + username: "alice" + password: "secret" + db: ~ + options: ~ + - + description: "User info for multiple hosts with database" + uri: "mongodb://alice:secret@example.com,[::1]:27019/admin" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + - + type: "ip_literal" + host: "::1" + port: 27019 + auth: + username: "alice" + password: "secret" + db: "admin" + options: ~ + - + description: "Username without password" + uri: "mongodb://alice@127.0.0.1" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + auth: + username: "alice" + password: ~ + db: ~ + options: ~ + - + description: "Username with empty password" + uri: "mongodb://alice:@127.0.0.1" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + auth: + username: "alice" + password: "" + db: ~ + options: ~ + - + description: "Escaped username and database without password" + uri: "mongodb://%40l%3Ace%2F%3D@example.com/my%3Ddb" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: + username: "@l:ce/=" + password: ~ + db: "my=db" + options: ~ + - + description: "Escaped user info and database (MONGODB-CR)" + uri: "mongodb://%24am:f%3Azzb%40z%2Fz%3D@127.0.0.1/admin%3F?authMechanism=MONGODB-CR" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + auth: + username: "$am" + password: "f:zzb@z/z=" + db: "admin?" + options: + authmechanism: "MONGODB-CR" + - + description: "Escaped username (MONGODB-X509)" + uri: "mongodb://CN%3DmyName%2COU%3DmyOrgUnit%2CO%3DmyOrg%2CL%3DmyLocality%2CST%3DmyState%2CC%3DmyCountry@localhost/?authMechanism=MONGODB-X509" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "localhost" + port: ~ + auth: + username: "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry" + password: ~ + db: ~ + options: + authmechanism: "MONGODB-X509" + - + description: "Escaped username (GSSAPI)" + uri: "mongodb://user%40EXAMPLE.COM:secret@localhost/?authMechanismProperties=SERVICE_NAME:other,CANONICALIZE_HOST_NAME:true&authMechanism=GSSAPI" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "localhost" + port: ~ + auth: + username: "user@EXAMPLE.COM" + password: "secret" + db: ~ + options: + authmechanism: "GSSAPI" + authmechanismproperties: + SERVICE_NAME: "other" + CANONICALIZE_HOST_NAME: true + - + description: "At-signs in options aren't part of the userinfo" + uri: "mongodb://alice:secret@example.com/admin?replicaset=my@replicaset" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: + username: "alice" + password: "secret" + db: "admin" + options: + replicaset: "my@replicaset" diff --git a/test/tests/spec/connection-string/valid-db-with-dotted-name.json b/test/tests/spec/connection-string/valid-db-with-dotted-name.json new file mode 100644 index 000000000..017dbc1da --- /dev/null +++ b/test/tests/spec/connection-string/valid-db-with-dotted-name.json @@ -0,0 +1,100 @@ +{ + "tests": [ + { + "auth": { + "db": "admin.sock", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets and auth DB resembling a socket (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin.shoe", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets with auth DB resembling a path (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.shoe", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin.sock", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets and auth DB resembling a socket (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin.shoe", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets with auth DB resembling a path (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.shoe", + "valid": true, + "warning": false + } + ] +} diff --git a/test/tests/spec/connection-string/valid-db-with-dotted-name.yml b/test/tests/spec/connection-string/valid-db-with-dotted-name.yml new file mode 100644 index 000000000..651e62e65 --- /dev/null +++ b/test/tests/spec/connection-string/valid-db-with-dotted-name.yml @@ -0,0 +1,77 @@ +tests: + - + description: "Multiple Unix domain sockets and auth DB resembling a socket (relative path)" + uri: "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin.sock" + options: ~ + - + description: "Multiple Unix domain sockets with auth DB resembling a path (relative path)" + uri: "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin.shoe" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin.shoe" + options: ~ + - + description: "Multiple Unix domain sockets and auth DB resembling a socket (absolute path)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin.sock" + options: ~ + - + description: "Multiple Unix domain sockets with auth DB resembling a path (absolute path)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin.shoe" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin.shoe" + options: ~ diff --git a/test/tests/spec/connection-string/valid-host_identifiers.json b/test/tests/spec/connection-string/valid-host_identifiers.json new file mode 100644 index 000000000..e8833b4af --- /dev/null +++ b/test/tests/spec/connection-string/valid-host_identifiers.json @@ -0,0 +1,154 @@ +{ + "tests": [ + { + "description": "Single IPv4 host without port", + "uri": "mongodb://127.0.0.1", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ipv4", + "host": "127.0.0.1", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Single IPv4 host with port", + "uri": "mongodb://127.0.0.1:27018", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ipv4", + "host": "127.0.0.1", + "port": 27018 + } + ], + "auth": null, + "options": null + }, + { + "description": "Single IP literal host without port", + "uri": "mongodb://[::1]", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ip_literal", + "host": "::1", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Single IP literal host with port", + "uri": "mongodb://[::1]:27019", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ip_literal", + "host": "::1", + "port": 27019 + } + ], + "auth": null, + "options": null + }, + { + "description": "Single hostname without port", + "uri": "mongodb://example.com", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Single hostname with port", + "uri": "mongodb://example.com:27020", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": 27020 + } + ], + "auth": null, + "options": null + }, + { + "description": "Single hostname (resembling IPv4) without port", + "uri": "mongodb://256.0.0.1", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "256.0.0.1", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Multiple hosts (mixed formats)", + "uri": "mongodb://127.0.0.1,[::1]:27018,example.com:27019", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "ipv4", + "host": "127.0.0.1", + "port": null + }, + { + "type": "ip_literal", + "host": "::1", + "port": 27018 + }, + { + "type": "hostname", + "host": "example.com", + "port": 27019 + } + ], + "auth": null, + "options": null + }, + { + "description": "UTF-8 hosts", + "uri": "mongodb://bücher.example.com,umläut.example.com/", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "bücher.example.com", + "port": null + }, + { + "type": "hostname", + "host": "umläut.example.com", + "port": null + } + ], + "auth": null, + "options": null + } + ] +} diff --git a/test/tests/spec/connection-string/valid-host_identifiers.yml b/test/tests/spec/connection-string/valid-host_identifiers.yml new file mode 100644 index 000000000..4d185c876 --- /dev/null +++ b/test/tests/spec/connection-string/valid-host_identifiers.yml @@ -0,0 +1,121 @@ +tests: + - + description: "Single IPv4 host without port" + uri: "mongodb://127.0.0.1" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + auth: ~ + options: ~ + - + description: "Single IPv4 host with port" + uri: "mongodb://127.0.0.1:27018" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: 27018 + auth: ~ + options: ~ + - + description: "Single IP literal host without port" + uri: "mongodb://[::1]" + valid: true + warning: false + hosts: + - + type: "ip_literal" + host: "::1" + port: ~ + auth: ~ + options: ~ + - + description: "Single IP literal host with port" + uri: "mongodb://[::1]:27019" + valid: true + warning: false + hosts: + - + type: "ip_literal" + host: "::1" + port: 27019 + auth: ~ + options: ~ + - + description: "Single hostname without port" + uri: "mongodb://example.com" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: ~ + options: ~ + - + description: "Single hostname with port" + uri: "mongodb://example.com:27020" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: 27020 + auth: ~ + options: ~ + - + description: "Single hostname (resembling IPv4) without port" + uri: "mongodb://256.0.0.1" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "256.0.0.1" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple hosts (mixed formats)" + uri: "mongodb://127.0.0.1,[::1]:27018,example.com:27019" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: ~ + - + type: "ip_literal" + host: "::1" + port: 27018 + - + type: "hostname" + host: "example.com" + port: 27019 + auth: ~ + options: ~ + - + description: "UTF-8 hosts" + uri: "mongodb://bücher.example.com,umläut.example.com/" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "bücher.example.com" + port: ~ + - + type: "hostname" + host: "umläut.example.com" + port: ~ + auth: ~ + options: ~ diff --git a/test/tests/spec/connection-string/valid-options.json b/test/tests/spec/connection-string/valid-options.json new file mode 100644 index 000000000..4c2bded9e --- /dev/null +++ b/test/tests/spec/connection-string/valid-options.json @@ -0,0 +1,25 @@ +{ + "tests": [ + { + "description": "Option names are normalized to lowercase", + "uri": "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR", + "valid": true, + "warning": false, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": { + "username": "alice", + "password": "secret", + "db": "admin" + }, + "options": { + "authmechanism": "MONGODB-CR" + } + } + ] +} diff --git a/test/tests/spec/connection-string/valid-options.yml b/test/tests/spec/connection-string/valid-options.yml new file mode 100644 index 000000000..e1b94039c --- /dev/null +++ b/test/tests/spec/connection-string/valid-options.yml @@ -0,0 +1,17 @@ +tests: + - + description: "Option names are normalized to lowercase" + uri: "mongodb://alice:secret@example.com/admin?AUTHMechanism=MONGODB-CR" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: + username: "alice" + password: "secret" + db: "admin" + options: + authmechanism: "MONGODB-CR" diff --git a/test/tests/spec/connection-string/valid-unix_socket-absolute.json b/test/tests/spec/connection-string/valid-unix_socket-absolute.json new file mode 100644 index 000000000..a5602c3bb --- /dev/null +++ b/test/tests/spec/connection-string/valid-unix_socket-absolute.json @@ -0,0 +1,251 @@ +{ + "tests": [ + { + "auth": null, + "description": "Unix domain socket (absolute path with trailing slash)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock/", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket (absolute path without trailing slash)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket (absolute path with spaces in path)", + "hosts": [ + { + "host": "/tmp/ /mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2F %2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple Unix domain sockets (absolute paths)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple hosts (absolute path and ipv4)", + "hosts": [ + { + "host": "127.0.0.1", + "port": 27017, + "type": "ipv4" + }, + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://127.0.0.1:27017,%2Ftmp%2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple hosts (absolute path and hostname resembling relative path)", + "hosts": [ + { + "host": "mongodb-27017.sock", + "port": null, + "type": "hostname" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://mongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "foo", + "username": "alice" + }, + "description": "Unix domain socket with auth database (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://alice:foo@%2Ftmp%2Fmongodb-27017.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket with path resembling socket file (absolute path with trailing slash)", + "hosts": [ + { + "host": "/tmp/path.to.sock/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock/", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket with path resembling socket file (absolute path without trailing slash)", + "hosts": [ + { + "host": "/tmp/path.to.sock/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "bar", + "username": "bob" + }, + "description": "Unix domain socket with path resembling socket file and auth (absolute path)", + "hosts": [ + { + "host": "/tmp/path.to.sock/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://bob:bar@%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets and auth DB (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets with auth DB (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "bar", + "username": "bob" + }, + "description": "Multiple Unix domain sockets with auth and query string (absolute path)", + "hosts": [ + { + "host": "/tmp/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": { + "w": 1 + }, + "uri": "mongodb://bob:bar@%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin?w=1", + "valid": true, + "warning": false + } + ] +} diff --git a/test/tests/spec/connection-string/valid-unix_socket-absolute.yml b/test/tests/spec/connection-string/valid-unix_socket-absolute.yml new file mode 100644 index 000000000..651e06fe3 --- /dev/null +++ b/test/tests/spec/connection-string/valid-unix_socket-absolute.yml @@ -0,0 +1,197 @@ +tests: + - + description: "Unix domain socket (absolute path with trailing slash)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock/" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket (absolute path without trailing slash)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket (absolute path with spaces in path)" + uri: "mongodb://%2Ftmp%2F %2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/ /mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple Unix domain sockets (absolute paths)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple hosts (absolute path and ipv4)" + uri: "mongodb://127.0.0.1:27017,%2Ftmp%2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: 27017 + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple hosts (absolute path and hostname resembling relative path)" + uri: "mongodb://mongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket with auth database (absolute path)" + uri: "mongodb://alice:foo@%2Ftmp%2Fmongodb-27017.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + auth: + username: "alice" + password: "foo" + db: "admin" + options: ~ + - + description: "Unix domain socket with path resembling socket file (absolute path with trailing slash)" + uri: "mongodb://%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock/" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/path.to.sock/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket with path resembling socket file (absolute path without trailing slash)" + uri: "mongodb://%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/path.to.sock/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket with path resembling socket file and auth (absolute path)" + uri: "mongodb://bob:bar@%2Ftmp%2Fpath.to.sock%2Fmongodb-27017.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/path.to.sock/mongodb-27017.sock" + port: ~ + auth: + username: "bob" + password: "bar" + db: "admin" + options: ~ + - + description: "Multiple Unix domain sockets and auth DB (absolute path)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin" + options: ~ + - + description: "Multiple Unix domain sockets with auth DB (absolute path)" + uri: "mongodb://%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin" + options: ~ + - + description: "Multiple Unix domain sockets with auth and query string (absolute path)" + uri: "mongodb://bob:bar@%2Ftmp%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock/admin?w=1" + valid: true + warning: false + hosts: + - + type: "unix" + host: "/tmp/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: + username: "bob" + password: "bar" + db: "admin" + options: + w: 1 diff --git a/test/tests/spec/connection-string/valid-unix_socket-relative.json b/test/tests/spec/connection-string/valid-unix_socket-relative.json new file mode 100644 index 000000000..81fbf212d --- /dev/null +++ b/test/tests/spec/connection-string/valid-unix_socket-relative.json @@ -0,0 +1,271 @@ +{ + "tests": [ + { + "auth": null, + "description": "Unix domain socket (relative path with trailing slash)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock/", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket (relative path without trailing slash)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket (relative path with spaces)", + "hosts": [ + { + "host": "rel/ /mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2F %2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple Unix domain sockets (relative paths)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple Unix domain sockets (relative and absolute paths)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "/tmp/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple hosts (relative path and ipv4)", + "hosts": [ + { + "host": "127.0.0.1", + "port": 27017, + "type": "ipv4" + }, + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://127.0.0.1:27017,rel%2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Multiple hosts (relative path and hostname resembling relative path)", + "hosts": [ + { + "host": "mongodb-27017.sock", + "port": null, + "type": "hostname" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://mongodb-27017.sock,rel%2Fmongodb-27018.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "foo", + "username": "alice" + }, + "description": "Unix domain socket with auth database (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://alice:foo@rel%2Fmongodb-27017.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket with path resembling socket file (relative path with trailing slash)", + "hosts": [ + { + "host": "rel/path.to.sock/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fpath.to.sock%2Fmongodb-27017.sock/", + "valid": true, + "warning": false + }, + { + "auth": null, + "description": "Unix domain socket with path resembling socket file (relative path without trailing slash)", + "hosts": [ + { + "host": "rel/path.to.sock/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fpath.to.sock%2Fmongodb-27017.sock", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "bar", + "username": "bob" + }, + "description": "Unix domain socket with path resembling socket file and auth (relative path)", + "hosts": [ + { + "host": "rel/path.to.sock/mongodb-27017.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://bob:bar@rel%2Fpath.to.sock%2Fmongodb-27017.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets and auth DB resembling a socket (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": null, + "username": null + }, + "description": "Multiple Unix domain sockets with auth DB resembling a path (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": null, + "uri": "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin", + "valid": true, + "warning": false + }, + { + "auth": { + "db": "admin", + "password": "bar", + "username": "bob" + }, + "description": "Multiple Unix domain sockets with auth and query string (relative path)", + "hosts": [ + { + "host": "rel/mongodb-27017.sock", + "port": null, + "type": "unix" + }, + { + "host": "rel/mongodb-27018.sock", + "port": null, + "type": "unix" + } + ], + "options": { + "w": 1 + }, + "uri": "mongodb://bob:bar@rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?w=1", + "valid": true, + "warning": false + } + ] +} diff --git a/test/tests/spec/connection-string/valid-unix_socket-relative.yml b/test/tests/spec/connection-string/valid-unix_socket-relative.yml new file mode 100644 index 000000000..7164188b3 --- /dev/null +++ b/test/tests/spec/connection-string/valid-unix_socket-relative.yml @@ -0,0 +1,213 @@ +tests: + - + description: "Unix domain socket (relative path with trailing slash)" + uri: "mongodb://rel%2Fmongodb-27017.sock/" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket (relative path without trailing slash)" + uri: "mongodb://rel%2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket (relative path with spaces)" + uri: "mongodb://rel%2F %2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/ /mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple Unix domain sockets (relative paths)" + uri: "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple Unix domain sockets (relative and absolute paths)" + uri: "mongodb://rel%2Fmongodb-27017.sock,%2Ftmp%2Fmongodb-27018.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "/tmp/mongodb-27018.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple hosts (relative path and ipv4)" + uri: "mongodb://127.0.0.1:27017,rel%2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "ipv4" + host: "127.0.0.1" + port: 27017 + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Multiple hosts (relative path and hostname resembling relative path)" + uri: "mongodb://mongodb-27017.sock,rel%2Fmongodb-27018.sock" + valid: true + warning: false + hosts: + - + type: "hostname" + host: "mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket with auth database (relative path)" + uri: "mongodb://alice:foo@rel%2Fmongodb-27017.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + auth: + username: "alice" + password: "foo" + db: "admin" + options: ~ + - + description: "Unix domain socket with path resembling socket file (relative path with trailing slash)" + uri: "mongodb://rel%2Fpath.to.sock%2Fmongodb-27017.sock/" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/path.to.sock/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket with path resembling socket file (relative path without trailing slash)" + uri: "mongodb://rel%2Fpath.to.sock%2Fmongodb-27017.sock" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/path.to.sock/mongodb-27017.sock" + port: ~ + auth: ~ + options: ~ + - + description: "Unix domain socket with path resembling socket file and auth (relative path)" + uri: "mongodb://bob:bar@rel%2Fpath.to.sock%2Fmongodb-27017.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/path.to.sock/mongodb-27017.sock" + port: ~ + auth: + username: "bob" + password: "bar" + db: "admin" + options: ~ + - + description: "Multiple Unix domain sockets and auth DB resembling a socket (relative path)" + uri: "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin" + options: ~ + - + description: "Multiple Unix domain sockets with auth DB resembling a path (relative path)" + uri: "mongodb://rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: + username: ~ + password: ~ + db: "admin" + options: ~ + - + description: "Multiple Unix domain sockets with auth and query string (relative path)" + uri: "mongodb://bob:bar@rel%2Fmongodb-27017.sock,rel%2Fmongodb-27018.sock/admin?w=1" + valid: true + warning: false + hosts: + - + type: "unix" + host: "rel/mongodb-27017.sock" + port: ~ + - + type: "unix" + host: "rel/mongodb-27018.sock" + port: ~ + auth: + username: "bob" + password: "bar" + db: "admin" + options: + w: 1 diff --git a/test/tests/spec/connection-string/valid-warnings.json b/test/tests/spec/connection-string/valid-warnings.json new file mode 100644 index 000000000..87f7248f2 --- /dev/null +++ b/test/tests/spec/connection-string/valid-warnings.json @@ -0,0 +1,68 @@ +{ + "tests": [ + { + "description": "Unrecognized option keys are ignored", + "uri": "mongodb://example.com/?foo=bar", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Unsupported option values are ignored", + "uri": "mongodb://example.com/?fsync=ifPossible", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": null + }, + { + "description": "Repeated option keys", + "uri": "mongodb://example.com/?replicaSet=test&replicaSet=test", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "replicaset": "test" + } + }, + { + "description": "Deprecated (or unknown) options are ignored if replacement exists", + "uri": "mongodb://example.com/?wtimeout=5&wtimeoutMS=10", + "valid": true, + "warning": true, + "hosts": [ + { + "type": "hostname", + "host": "example.com", + "port": null + } + ], + "auth": null, + "options": { + "wtimeoutms": 10 + } + } + ] +} diff --git a/test/tests/spec/connection-string/valid-warnings.yml b/test/tests/spec/connection-string/valid-warnings.yml new file mode 100644 index 000000000..4515c9a3b --- /dev/null +++ b/test/tests/spec/connection-string/valid-warnings.yml @@ -0,0 +1,51 @@ +tests: + - + description: "Unrecognized option keys are ignored" + uri: "mongodb://example.com/?foo=bar" + valid: true + warning: true + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: ~ + options: ~ + - + description: "Unsupported option values are ignored" + uri: "mongodb://example.com/?fsync=ifPossible" + valid: true + warning: true + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: ~ + options: ~ + - + description: "Repeated option keys" + uri: "mongodb://example.com/?replicaSet=test&replicaSet=test" + valid: true + warning: true + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: ~ + options: + replicaset: "test" + - + description: "Deprecated (or unknown) options are ignored if replacement exists" + uri: "mongodb://example.com/?wtimeout=5&wtimeoutMS=10" + valid: true + warning: true + hosts: + - + type: "hostname" + host: "example.com" + port: ~ + auth: ~ + options: + wtimeoutms: 10 diff --git a/test/tests/spec/initial-dns-seedlist-discovery/README.rst b/test/tests/spec/initial-dns-seedlist-discovery/README.rst new file mode 100644 index 000000000..5b093e19d --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/README.rst @@ -0,0 +1,87 @@ +==================================== +Initial DNS Seedlist Discovery tests +==================================== + +This directory contains platform-independent tests that drivers can use +to prove their conformance to the Initial DNS Seedlist Discovery spec. + +Test Setup +---------- + +Start a three-node replica set on localhost, on ports 27017, 27018, and 27019, +with replica set name "repl0". The replica set MUST be started with SSL +enabled. + +To run the tests that accompany this spec, you need to configure the SRV and +TXT records with a real name server. The following records are required for +these tests:: + + Record TTL Class Address + localhost.test.build.10gen.cc. 86400 IN A 127.0.0.1 + localhost.sub.test.build.10gen.cc. 86400 IN A 127.0.0.1 + + Record TTL Class Port Target + _mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test1.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. + _mongodb._tcp.test2.test.build.10gen.cc. 86400 IN SRV 27018 localhost.test.build.10gen.cc. + _mongodb._tcp.test2.test.build.10gen.cc. 86400 IN SRV 27019 localhost.test.build.10gen.cc. + _mongodb._tcp.test3.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test5.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test6.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test7.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test8.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test10.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test11.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + _mongodb._tcp.test12.test.build.10gen.cc. 86400 IN SRV 27017 localhost.build.10gen.cc. + _mongodb._tcp.test13.test.build.10gen.cc. 86400 IN SRV 27017 test.build.10gen.cc. + _mongodb._tcp.test14.test.build.10gen.cc. 86400 IN SRV 27017 localhost.not-test.build.10gen.cc. + _mongodb._tcp.test15.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.not-build.10gen.cc. + _mongodb._tcp.test16.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.not-10gen.cc. + _mongodb._tcp.test17.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.not-cc. + _mongodb._tcp.test18.test.build.10gen.cc. 86400 IN SRV 27017 localhost.sub.test.build.10gen.cc. + _mongodb._tcp.test19.test.build.10gen.cc. 86400 IN SRV 27017 localhost.evil.build.10gen.cc. + _mongodb._tcp.test19.test.build.10gen.cc. 86400 IN SRV 27017 localhost.test.build.10gen.cc. + + Record TTL Class Text + test5.test.build.10gen.cc. 86400 IN TXT "replicaSet=repl0&authSource=thisDB" + test6.test.build.10gen.cc. 86400 IN TXT "replicaSet=repl0" + test6.test.build.10gen.cc. 86400 IN TXT "authSource=otherDB" + test7.test.build.10gen.cc. 86400 IN TXT "ssl=false" + test8.test.build.10gen.cc. 86400 IN TXT "authSource" + test10.test.build.10gen.cc. 86400 IN TXT "socketTimeoutMS=500" + test11.test.build.10gen.cc. 86400 IN TXT "replicaS" "et=rep" "l0" + +Note that ``test4`` is omitted deliberately to test what happens with no SRV +record. ``test9`` is missing because it was deleted during the development of +the tests. The missing ``test.`` sub-domain in the SRV record target for +``test12`` is deliberate. + +In our tests we have used ``localhost.test.build.10gen.cc`` as the domain, and +then configured ``localhost.test.build.10gen.cc`` to resolve to 127.0.0.1. + +You need to adapt the records shown above to replace ``test.build.10gen.cc`` +with your own domain name, and update the "uri" field in the YAML or JSON files +in this directory with the actual domain. + +Test Format and Use +------------------- + +These YAML and JSON files contain the following fields: + +- ``uri``: a mongodb+srv connection string +- ``seeds``: the expected set of initial seeds discovered from the SRV record +- ``hosts``: the discovered topology's list of hosts once SDAM completes a scan +- ``options``: the parsed connection string options as discovered from URI and + TXT records +- ``error``: indicates that the parsing of the URI, or the resolving or + contents of the SRV or TXT records included errors. +- ``comment``: a comment to indicate why a test would fail. + +For each file, create MongoClient initialized with the mongodb+srv connection +string. You SHOULD verify that the client's initial seed list matches the list of +seeds. You MUST verify that the set of ServerDescriptions in the client's +TopologyDescription eventually matches the list of hosts. You MUST verify that +each of the values of the Connection String Options under ``options`` match the +Client's parsed value for that option. There may be other options parsed by +the Client as well, which a test does not verify. You MUST verify that an +error has been thrown if ``error`` is present. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/longer-parent-in-return.json b/test/tests/spec/initial-dns-seedlist-discovery/longer-parent-in-return.json new file mode 100644 index 000000000..9a8267eae --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/longer-parent-in-return.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test18.test.build.10gen.cc/?replicaSet=repl0", + "seeds": [ + "localhost.sub.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + }, + "comment": "Is correct, as returned host name shared the URI root \"test.build.10gen.cc\"." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/longer-parent-in-return.yml b/test/tests/spec/initial-dns-seedlist-discovery/longer-parent-in-return.yml new file mode 100644 index 000000000..e77c4570d --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/longer-parent-in-return.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test18.test.build.10gen.cc/?replicaSet=repl0" +seeds: + - localhost.sub.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true +comment: Is correct, as returned host name shared the URI root "test.build.10gen.cc". diff --git a/test/tests/spec/initial-dns-seedlist-discovery/misformatted-option.json b/test/tests/spec/initial-dns-seedlist-discovery/misformatted-option.json new file mode 100644 index 000000000..3c8c29ace --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/misformatted-option.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test8.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because the options in the TXT record are incorrectly formatted (misses value)." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/misformatted-option.yml b/test/tests/spec/initial-dns-seedlist-discovery/misformatted-option.yml new file mode 100644 index 000000000..9669772cb --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/misformatted-option.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test8.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because the options in the TXT record are incorrectly formatted (misses value). diff --git a/test/tests/spec/initial-dns-seedlist-discovery/no-results.json b/test/tests/spec/initial-dns-seedlist-discovery/no-results.json new file mode 100644 index 000000000..c1dc02d28 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/no-results.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test4.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because no SRV records are present for this URI." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/no-results.yml b/test/tests/spec/initial-dns-seedlist-discovery/no-results.yml new file mode 100644 index 000000000..e09bd060c --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/no-results.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test4.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because no SRV records are present for this URI. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/not-enough-parts.json b/test/tests/spec/initial-dns-seedlist-discovery/not-enough-parts.json new file mode 100644 index 000000000..7cfce2ec5 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/not-enough-parts.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because host in URI does not have {hostname}, {domainname} and {tld}." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/not-enough-parts.yml b/test/tests/spec/initial-dns-seedlist-discovery/not-enough-parts.yml new file mode 100644 index 000000000..b36fa4a5d --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/not-enough-parts.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because host in URI does not have {hostname}, {domainname} and {tld}. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/one-result-default-port.json b/test/tests/spec/initial-dns-seedlist-discovery/one-result-default-port.json new file mode 100644 index 000000000..cebb3b1ec --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/one-result-default-port.json @@ -0,0 +1,15 @@ +{ + "uri": "mongodb+srv://test3.test.build.10gen.cc/?replicaSet=repl0", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/one-result-default-port.yml b/test/tests/spec/initial-dns-seedlist-discovery/one-result-default-port.yml new file mode 100644 index 000000000..395bcdc96 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/one-result-default-port.yml @@ -0,0 +1,10 @@ +uri: "mongodb+srv://test3.test.build.10gen.cc/?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true diff --git a/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.json b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.json new file mode 100644 index 000000000..622668c35 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.json @@ -0,0 +1,15 @@ +{ + "uri": "mongodb+srv://test11.test.build.10gen.cc/", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.yml b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.yml new file mode 100644 index 000000000..90a702cdb --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record-multiple-strings.yml @@ -0,0 +1,10 @@ +uri: "mongodb+srv://test11.test.build.10gen.cc/" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true diff --git a/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record.json b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record.json new file mode 100644 index 000000000..2385021ad --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc/", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "authSource": "thisDB", + "ssl": true + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record.yml b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record.yml new file mode 100644 index 000000000..9356eaa2c --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/one-txt-record.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test5.test.build.10gen.cc/" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + authSource: thisDB + ssl: true diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch1.json b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch1.json new file mode 100644 index 000000000..8d0147a48 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch1.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test14.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because returned host name's part \"not-test\" mismatches URI parent part \"test\"." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch1.yml b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch1.yml new file mode 100644 index 000000000..e35dfdf6d --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch1.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test14.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because returned host name's part "not-test" mismatches URI parent part "test". diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch2.json b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch2.json new file mode 100644 index 000000000..996249eb9 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch2.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test15.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because returned host name's part \"not-build\" mismatches URI parent part \"build\"." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch2.yml b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch2.yml new file mode 100644 index 000000000..595e5493c --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch2.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test15.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because returned host name's part "not-build" mismatches URI parent part "build". diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch3.json b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch3.json new file mode 100644 index 000000000..69e724af6 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch3.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test16.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because returned host name's part \"not-10gen\" mismatches URI parent part \"10gen\"." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch3.yml b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch3.yml new file mode 100644 index 000000000..64ca2e708 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch3.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test16.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because returned host name's part "not-10gen" mismatches URI parent part "10gen". diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch4.json b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch4.json new file mode 100644 index 000000000..254168e34 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch4.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test17.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because returned host name's TLD \"not-cc\" mismatches URI TLD \"cc\"." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch4.yml b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch4.yml new file mode 100644 index 000000000..226d6fa3b --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch4.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test17.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because returned host name's TLD "not-cc" mismatches URI TLD "cc". diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch5.json b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch5.json new file mode 100644 index 000000000..92c024b4f --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch5.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test19.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because one of the returned host names' domain name parts \"evil\" mismatches \"test\"." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch5.yml b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch5.yml new file mode 100644 index 000000000..1ed2bda4e --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/parent-part-mismatch5.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test19.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because one of the returned host names' domain name parts "evil" mismatches "test". diff --git a/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-too-short.json b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-too-short.json new file mode 100644 index 000000000..676eb0c0d --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-too-short.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test13.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because returned host name's parent (build.10gen.cc) misses \"test.\"" +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-too-short.yml b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-too-short.yml new file mode 100644 index 000000000..397aec895 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-too-short.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test13.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because returned host name's parent (build.10gen.cc) misses "test." diff --git a/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-wrong.json b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-wrong.json new file mode 100644 index 000000000..3aabfd819 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-wrong.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test12.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because returned host name is too short and mismatches a parent." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-wrong.yml b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-wrong.yml new file mode 100644 index 000000000..1fc3867a0 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/returned-parent-wrong.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test12.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because returned host name is too short and mismatches a parent. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/two-results-default-port.json b/test/tests/spec/initial-dns-seedlist-discovery/two-results-default-port.json new file mode 100644 index 000000000..66028310a --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/two-results-default-port.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0", + "seeds": [ + "localhost.test.build.10gen.cc:27017", + "localhost.test.build.10gen.cc:27018" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/two-results-default-port.yml b/test/tests/spec/initial-dns-seedlist-discovery/two-results-default-port.yml new file mode 100644 index 000000000..61d38b5e8 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/two-results-default-port.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test1.test.build.10gen.cc/?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27017 + - localhost.test.build.10gen.cc:27018 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true diff --git a/test/tests/spec/initial-dns-seedlist-discovery/two-results-nonstandard-port.json b/test/tests/spec/initial-dns-seedlist-discovery/two-results-nonstandard-port.json new file mode 100644 index 000000000..4900f7cff --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/two-results-nonstandard-port.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test2.test.build.10gen.cc/?replicaSet=repl0", + "seeds": [ + "localhost.test.build.10gen.cc:27018", + "localhost.test.build.10gen.cc:27019" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "ssl": true + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/two-results-nonstandard-port.yml b/test/tests/spec/initial-dns-seedlist-discovery/two-results-nonstandard-port.yml new file mode 100644 index 000000000..7185f52cd --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/two-results-nonstandard-port.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test2.test.build.10gen.cc/?replicaSet=repl0" +seeds: + - localhost.test.build.10gen.cc:27018 + - localhost.test.build.10gen.cc:27019 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + ssl: true diff --git a/test/tests/spec/initial-dns-seedlist-discovery/two-txt-records.json b/test/tests/spec/initial-dns-seedlist-discovery/two-txt-records.json new file mode 100644 index 000000000..f0654ef6c --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/two-txt-records.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test6.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because there are two TXT records." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/two-txt-records.yml b/test/tests/spec/initial-dns-seedlist-discovery/two-txt-records.yml new file mode 100644 index 000000000..c6093613d --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/two-txt-records.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test6.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because there are two TXT records. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-not-allowed-option.json b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-not-allowed-option.json new file mode 100644 index 000000000..2a5cf2f00 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-not-allowed-option.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test10.test.build.10gen.cc/?replicaSet=repl0", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because socketTimeoutMS is not an allowed option." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-not-allowed-option.yml b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-not-allowed-option.yml new file mode 100644 index 000000000..f4ff1cfd1 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-not-allowed-option.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test10.test.build.10gen.cc/?replicaSet=repl0" +seeds: [] +hosts: [] +error: true +comment: Should fail because socketTimeoutMS is not an allowed option. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.json b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.json new file mode 100644 index 000000000..0ebc737bd --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc/?ssl=false", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "authSource": "thisDB", + "ssl": false + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.yml b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.yml new file mode 100644 index 000000000..2a922aa23 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-ssl-option.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test5.test.build.10gen.cc/?ssl=false" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + authSource: thisDB + ssl: false diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.json b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.json new file mode 100644 index 000000000..2626ba608 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.json @@ -0,0 +1,16 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc/?authSource=otherDB", + "seeds": [ + "localhost.test.build.10gen.cc:27017" + ], + "hosts": [ + "localhost:27017", + "localhost:27018", + "localhost:27019" + ], + "options": { + "replicaSet": "repl0", + "authSource": "otherDB", + "ssl": true + } +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.yml b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.yml new file mode 100644 index 000000000..a9015599e --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-overridden-uri-option.yml @@ -0,0 +1,11 @@ +uri: "mongodb+srv://test5.test.build.10gen.cc/?authSource=otherDB" +seeds: + - localhost.test.build.10gen.cc:27017 +hosts: + - localhost:27017 + - localhost:27018 + - localhost:27019 +options: + replicaSet: repl0 + authSource: otherDB + ssl: true diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.json b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.json new file mode 100644 index 000000000..0d333a459 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test7.test.build.10gen.cc/", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because \"ssl\" is not an allowed option." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.yml b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.yml new file mode 100644 index 000000000..ba3877ee9 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/txt-record-with-unallowed-option.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test7.test.build.10gen.cc/" +seeds: [] +hosts: [] +error: true +comment: Should fail because "ssl" is not an allowed option. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/uri-with-port.json b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-port.json new file mode 100644 index 000000000..b981e2a1b --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-port.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc:8123/?replicaSet=repl0", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because the mongodb+srv URI includes a port." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/uri-with-port.yml b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-port.yml new file mode 100644 index 000000000..f1944dcdd --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-port.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test5.test.build.10gen.cc:8123/?replicaSet=repl0" +seeds: [] +hosts: [] +error: true +comment: Should fail because the mongodb+srv URI includes a port. diff --git a/test/tests/spec/initial-dns-seedlist-discovery/uri-with-two-hosts.json b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-two-hosts.json new file mode 100644 index 000000000..5261a39cf --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-two-hosts.json @@ -0,0 +1,7 @@ +{ + "uri": "mongodb+srv://test5.test.build.10gen.cc,test6.test.build.10gen.cc/?replicaSet=repl0", + "seeds": [], + "hosts": [], + "error": true, + "comment": "Should fail because the mongodb+srv URI includes two host names." +} diff --git a/test/tests/spec/initial-dns-seedlist-discovery/uri-with-two-hosts.yml b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-two-hosts.yml new file mode 100644 index 000000000..3b2189d48 --- /dev/null +++ b/test/tests/spec/initial-dns-seedlist-discovery/uri-with-two-hosts.yml @@ -0,0 +1,5 @@ +uri: "mongodb+srv://test5.test.build.10gen.cc,test6.test.build.10gen.cc/?replicaSet=repl0" +seeds: [] +hosts: [] +error: true +comment: Should fail because the mongodb+srv URI includes two host names. diff --git a/test/tests/unit/connection_string_spec_tests.js b/test/tests/unit/connection_string_spec_tests.js new file mode 100644 index 000000000..95c923cc1 --- /dev/null +++ b/test/tests/unit/connection_string_spec_tests.js @@ -0,0 +1,77 @@ +'use strict'; + +const parseConnectionString = require('../../../lib/uri_parser'); +const fs = require('fs'); +const f = require('util').format; +const expect = require('chai').expect; +const punycode = require('punycode'); +const MongoParseError = require('../../../lib/error').MongoParseError; + +// NOTE: These are cases we could never check for unless we write out own +// url parser. The node parser simply won't let these through, so we +// are safe skipping them. +const skipTests = [ + 'Invalid port (negative number) with hostname', + 'Invalid port (non-numeric string) with hostname', + 'Missing delimiting slash between hosts and options', + + // These tests are only relevant to the native driver which + // cares about specific keys, and validating their values + 'Unrecognized option keys are ignored', + 'Unsupported option values are ignored' +]; + +describe('Connection String (spec)', function() { + const testFiles = fs + .readdirSync(f('%s/../spec/connection-string', __dirname)) + .filter(x => x.indexOf('.json') !== -1) + .map(x => JSON.parse(fs.readFileSync(f('%s/../spec/connection-string/%s', __dirname, x)))); + + // Execute the tests + for (let i = 0; i < testFiles.length; i++) { + const testFile = testFiles[i]; + + // Get each test + for (let j = 0; j < testFile.tests.length; j++) { + const test = testFile.tests[j]; + if (skipTests.indexOf(test.description) !== -1) { + continue; + } + + it(test.description, { + metadata: { requires: { topology: 'single' } }, + test: function(done) { + const valid = test.valid; + + parseConnectionString(test.uri, function(err, result) { + if (valid === false) { + expect(err).to.exist; + expect(err).to.be.instanceOf(MongoParseError); + expect(result).to.not.exist; + } else { + expect(err).to.not.exist; + expect(result).to.exist; + + // remove data we don't track + if (test.auth && test.auth.password === '') { + test.auth.password = null; + } + + test.hosts = test.hosts.map(host => { + delete host.type; + host.host = punycode.toASCII(host.host); + return host; + }); + + expect(result.hosts).to.eql(test.hosts); + expect(result.auth).to.eql(test.auth); + expect(result.options).to.eql(test.options); + } + + done(); + }); + } + }); + } + } +}); diff --git a/test/tests/unit/mongodb_srv_spec_tests.js b/test/tests/unit/mongodb_srv_spec_tests.js new file mode 100644 index 000000000..a6298fd68 --- /dev/null +++ b/test/tests/unit/mongodb_srv_spec_tests.js @@ -0,0 +1,54 @@ +'use strict'; +const fs = require('fs'); +const path = require('path'); +const parseConnectionString = require('../../../lib/uri_parser'); +const expect = require('chai').expect; + +describe('mongodb+srv (spec)', function() { + const specPath = path.join(__dirname, '../spec', 'initial-dns-seedlist-discovery'); + const testFiles = fs + .readdirSync(specPath) + .filter(x => x.indexOf('.json') !== -1) + .map(x => [x, fs.readFileSync(path.join(specPath, x), 'utf8')]) + .map(x => [path.basename(x[0], '.json'), JSON.parse(x[1])]); + + testFiles.forEach(test => { + if (!test[1].comment) { + test[1].comment = test[0]; + } + + it(test[1].comment, { + metadata: { requires: { topology: ['single'] } }, + test: function(done) { + parseConnectionString(test[1].uri, (err, result) => { + if (test[1].error) { + expect(err).to.exist; + expect(result).to.not.exist; + } else { + expect(err).to.not.exist; + expect(result).to.exist; + + if (test[1].options && test[1].options.replicaSet) { + expect(result.options.replicaset).to.equal(test[1].options.replicaSet); + } + + if (test[1].options && test[1].options.ssl) { + expect(result.options.ssl).to.equal(test[1].options.ssl); + } + + if ( + test[1].parsed_options && + test[1].parsed_options.user && + test[1].parsed_options.password + ) { + expect(result.auth.username).to.equal(test[1].parsed_options.user); + expect(result.auth.password).to.equal(test[1].parsed_options.password); + } + } + + done(); + }); + } + }); + }); +});