From 79f33ca4424fab075b2c9d0a077d8b8d2067d1b0 Mon Sep 17 00:00:00 2001 From: Matt Broadstone Date: Sun, 24 Feb 2019 17:00:16 -0500 Subject: [PATCH] feat(unified-sdam): backport unified SDAM to master for v3.2.0 --- lib/operations/mongo_client_ops.js | 21 ++++- lib/topologies/native_topology.js | 51 +++++++++++ lib/utils.js | 4 +- test/config.js | 27 ++++-- test/examples/aggregate.js | 3 +- test/examples/array_filters.js | 3 +- test/examples/causal_consistency.js | 3 +- test/examples/change_streams.js | 3 +- test/examples/create_index.js | 3 +- test/examples/insert.js | 3 +- .../project_fields_from_query_results.js | 3 +- test/examples/query.js | 3 +- test/examples/query_array_of_documents.js | 3 +- test/examples/query_arrays.js | 3 +- test/examples/query_embedded_documents.js | 3 +- test/examples/query_for_null_fields.js | 3 +- test/examples/remove_documents.js | 3 +- test/examples/run_command.js | 3 +- test/examples/transactions.js | 3 +- test/examples/update_documents.js | 3 +- test/functional/authentication_tests.js | 12 +-- test/functional/collations_tests.js | 4 +- test/functional/connection_tests.js | 90 ++++++++++++------- test/functional/cursor_tests.js | 8 +- test/functional/db_tests.js | 14 ++- test/functional/domain_tests.js | 10 +++ test/functional/index_tests.js | 8 ++ test/functional/mongo_client_options_tests.js | 32 ++++--- test/functional/mongo_client_tests.js | 52 +++++++++-- test/functional/operation_example_tests.js | 10 ++- .../operation_generators_example_tests.js | 11 ++- .../operation_promises_example_tests.js | 10 ++- test/functional/readpreference_tests.js | 2 +- test/functional/replicaset_mock_tests.js | 7 +- test/functional/scram_sha_256_tests.js | 10 +-- test/functional/shared.js | 8 +- test/functional/uri_tests.js | 7 +- 37 files changed, 325 insertions(+), 121 deletions(-) create mode 100644 lib/topologies/native_topology.js diff --git a/lib/operations/mongo_client_ops.js b/lib/operations/mongo_client_ops.js index ecd33234c9..85bf5a6698 100644 --- a/lib/operations/mongo_client_ops.js +++ b/lib/operations/mongo_client_ops.js @@ -11,6 +11,7 @@ const ReadPreference = require('mongodb-core').ReadPreference; const ReplSet = require('../topologies/replset'); const Server = require('../topologies/server'); const ServerSessionPool = require('mongodb-core').Sessions.ServerSessionPool; +const NativeTopology = require('../topologies/native_topology'); let client; function loadClient() { @@ -109,7 +110,9 @@ const validOptionNames = [ 'minSize', 'monitorCommands', 'retryWrites', - 'useNewUrlParser' + 'useNewUrlParser', + 'useUnifiedTopology', + 'serverSelectionTimeoutMS' ]; function addListeners(mongoClient, topology) { @@ -126,7 +129,10 @@ function addListeners(mongoClient, topology) { function assignTopology(client, topology) { client.topology = topology; - topology.s.sessionPool = new ServerSessionPool(topology.s.coreTopology); + topology.s.sessionPool = + topology instanceof NativeTopology + ? new ServerSessionPool(topology) + : new ServerSessionPool(topology.s.coreTopology); } // Clear out all events @@ -225,6 +231,15 @@ function connect(mongoClient, url, options, callback) { } } + if (_finalOptions.useUnifiedTopology) { + return createTopology( + mongoClient, + 'unified', + _finalOptions, + connectHandler(mongoClient, _finalOptions, connectCallback) + ); + } + // Do we have a replicaset then skip discovery and go straight to connectivity if (_finalOptions.replicaSet || _finalOptions.rs_name) { return createTopology( @@ -434,6 +449,8 @@ function createTopology(mongoClient, topologyType, options, callback) { topology = new Mongos(servers, options); } else if (topologyType === 'replicaset') { topology = new ReplSet(servers, options); + } else if (topologyType === 'unified') { + topology = new NativeTopology(options.servers, options); } // Add listeners diff --git a/lib/topologies/native_topology.js b/lib/topologies/native_topology.js new file mode 100644 index 0000000000..13e33c714d --- /dev/null +++ b/lib/topologies/native_topology.js @@ -0,0 +1,51 @@ +'use strict'; + +const Topology = require('mongodb-core').Topology; +const ServerCapabilities = require('./topology_base').ServerCapabilities; +const Cursor = require('../cursor'); +const translateOptions = require('../utils').translateOptions; + +class NativeTopology extends Topology { + constructor(servers, options) { + options = options || {}; + + let clonedOptions = Object.assign( + {}, + { + cursorFactory: Cursor, + reconnect: false, + emitError: options.emitError, + size: options.poolSize, + monitorCommands: options.monitorCommands + } + ); + + // Translate any SSL options and other connectivity options + clonedOptions = translateOptions(clonedOptions, options); + + // Socket options + var socketOptions = + options.socketOptions && Object.keys(options.socketOptions).length > 0 + ? options.socketOptions + : options; + + // Translate all the options to the mongodb-core ones + clonedOptions = translateOptions(clonedOptions, socketOptions); + + super(servers, clonedOptions); + + // Do we have an application specific string + if (options.appname) { + this.s.clientInfo.application = { name: options.appname }; + } + } + + capabilities() { + if (this.s.sCapabilities) return this.s.sCapabilities; + if (this.lastIsMaster() == null) return null; + this.s.sCapabilities = new ServerCapabilities(this.lastIsMaster()); + return this.s.sCapabilities; + } +} + +module.exports = NativeTopology; diff --git a/lib/utils.js b/lib/utils.js index 90e6b7fda3..5cad073d78 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -588,12 +588,12 @@ function decorateWithCollation(command, target, options) { throw new TypeError('parameter "target" is missing a topology'); } - const capabilities = target.s.topology.capabilities(); + const capabilities = topology.capabilities(); if (options.collation && typeof options.collation === 'object') { if (capabilities && capabilities.commandsTakeCollation) { command.collation = options.collation; } else { - throw new MongoError(`server ${topology.s.coreTopology.name} does not support collation`); + throw new MongoError(`Current topology does not support collation`); } } } diff --git a/test/config.js b/test/config.js index 68e8bd53bd..bc4270cad8 100644 --- a/test/config.js +++ b/test/config.js @@ -4,18 +4,26 @@ const f = require('util').format; const url = require('url'); const qs = require('querystring'); class NativeConfiguration extends ConfigurationBase { - constructor(options) { - super(options); + constructor(environment) { + super(environment); this.type = 'native'; - this.topology = options.topology || this.defaultTopology; - this.replicasetName = options.replicasetName || 'rs'; + this.topology = environment.topology || this.defaultTopology; + this.environment = environment; + + if (environment.setName) { + this.replicasetName = environment.setName || 'rs'; + } } defaultTopology(serverHost, serverPort, serverOpts, _mongo) { return new _mongo.Server(serverHost, serverPort, serverOpts || {}); } + usingUnifiedTopology() { + return !!process.env.MONGODB_UNIFIED_TOPOLOGY; + } + start(callback) { const self = this; if (this.skipStart) return callback(); @@ -50,11 +58,20 @@ class NativeConfiguration extends ConfigurationBase { newClient(dbOptions, serverOptions) { // support MongoClient contructor form (url, options) for `newClient` if (typeof dbOptions === 'string') { - return new this.mongo.MongoClient(dbOptions, serverOptions); + return new this.mongo.MongoClient( + dbOptions, + this.usingUnifiedTopology() + ? Object.assign( + { useUnifiedTopology: true, serverSelectionTimeoutMS: 1000 }, + serverOptions + ) + : serverOptions + ); } dbOptions = dbOptions || {}; serverOptions = Object.assign({}, { haInterval: 100 }, serverOptions); + if (this.usingUnifiedTopology()) serverOptions.useUnifiedTopology = true; // Override implementation if (this.options.newDbInstance) { diff --git a/test/examples/aggregate.js b/test/examples/aggregate.js index 3d2b8cbf06..5f655b011f 100644 --- a/test/examples/aggregate.js +++ b/test/examples/aggregate.js @@ -2,7 +2,6 @@ /* eslint-disable no-unused-vars */ const setupDatabase = require('../functional/shared').setupDatabase; -const MongoClient = require('../../lib/mongo_client'); describe('examples.aggregaton:', function() { let client; @@ -13,7 +12,7 @@ describe('examples.aggregaton:', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); collection = client.db(this.configuration.db).collection('aggregateExample'); }); diff --git a/test/examples/array_filters.js b/test/examples/array_filters.js index 79f320352e..332c81fbd0 100644 --- a/test/examples/array_filters.js +++ b/test/examples/array_filters.js @@ -1,7 +1,6 @@ 'use strict'; const setupDatabase = require('../functional/shared').setupDatabase; -const MongoClient = require('../../lib/mongo_client'); describe('examples(project-fields-from-query):', function() { let client; @@ -12,7 +11,7 @@ describe('examples(project-fields-from-query):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); collection = client.db(this.configuration.db).collection('arrayFilterUpdateExample'); }); diff --git a/test/examples/causal_consistency.js b/test/examples/causal_consistency.js index 494ddce890..db06356e65 100644 --- a/test/examples/causal_consistency.js +++ b/test/examples/causal_consistency.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(causal-consistency):', function() { let client; @@ -14,7 +13,7 @@ describe('examples(causal-consistency):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); collection = client.db(this.configuration.db).collection('arrayFilterUpdateExample'); }); diff --git a/test/examples/change_streams.js b/test/examples/change_streams.js index 795d4bbe84..798bdcecc2 100644 --- a/test/examples/change_streams.js +++ b/test/examples/change_streams.js @@ -3,7 +3,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(change-stream):', function() { let client; @@ -14,7 +13,7 @@ describe('examples(change-stream):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/create_index.js b/test/examples/create_index.js index a5b4e5552a..d8651f4252 100644 --- a/test/examples/create_index.js +++ b/test/examples/create_index.js @@ -1,7 +1,6 @@ 'use strict'; const setupDatabase = require('../functional/shared').setupDatabase; -const MongoClient = require('../../lib/mongo_client'); describe('examples.createIndex:', function() { let client; @@ -12,7 +11,7 @@ describe('examples.createIndex:', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); collection = client.db(this.configuration.db).collection('createIndexExample'); }); diff --git a/test/examples/insert.js b/test/examples/insert.js index 293284a14f..62f4cdceec 100644 --- a/test/examples/insert.js +++ b/test/examples/insert.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(insert):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(insert):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/project_fields_from_query_results.js b/test/examples/project_fields_from_query_results.js index ae4781530f..5228a4c4fd 100644 --- a/test/examples/project_fields_from_query_results.js +++ b/test/examples/project_fields_from_query_results.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(project-fields-from-query):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(project-fields-from-query):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/query.js b/test/examples/query.js index f2dcff53ba..5ed8d0b1d2 100644 --- a/test/examples/query.js +++ b/test/examples/query.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(query):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(query):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/query_array_of_documents.js b/test/examples/query_array_of_documents.js index 8e703d5678..f2efbac93e 100644 --- a/test/examples/query_array_of_documents.js +++ b/test/examples/query_array_of_documents.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(query-array-of-documents):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(query-array-of-documents):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/query_arrays.js b/test/examples/query_arrays.js index 683ef43605..587a03eb39 100644 --- a/test/examples/query_arrays.js +++ b/test/examples/query_arrays.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(query-arrays):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(query-arrays):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/query_embedded_documents.js b/test/examples/query_embedded_documents.js index 512565306f..a3e8a592b4 100644 --- a/test/examples/query_embedded_documents.js +++ b/test/examples/query_embedded_documents.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(query-embedded-documents):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(query-embedded-documents):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/query_for_null_fields.js b/test/examples/query_for_null_fields.js index e0efb54c93..9db1a25dd2 100644 --- a/test/examples/query_for_null_fields.js +++ b/test/examples/query_for_null_fields.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(query-for-null-fields):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(query-for-null-fields):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/remove_documents.js b/test/examples/remove_documents.js index 875b3e6e42..496605dce8 100644 --- a/test/examples/remove_documents.js +++ b/test/examples/remove_documents.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(remove-documents):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(remove-documents):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/examples/run_command.js b/test/examples/run_command.js index 38e7638896..72329768af 100644 --- a/test/examples/run_command.js +++ b/test/examples/run_command.js @@ -1,7 +1,6 @@ 'use strict'; const setupDatabase = require('../functional/shared').setupDatabase; -const MongoClient = require('../../lib/mongo_client'); describe('examples.runCommand:', function() { let client; @@ -12,7 +11,7 @@ describe('examples.runCommand:', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); // Done to ensure existence of collection diff --git a/test/examples/transactions.js b/test/examples/transactions.js index 715de91e74..2ea1751a1d 100644 --- a/test/examples/transactions.js +++ b/test/examples/transactions.js @@ -1,7 +1,6 @@ 'use strict'; const setupDatabase = require('../functional/shared').setupDatabase; -const MongoClient = require('../../lib/mongo_client'); describe('examples(transactions):', function() { let client; @@ -19,7 +18,7 @@ describe('examples(transactions):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); await client.db('hr').dropDatabase(); await client.db('hr').createCollection('employees'); await client.db('reporting').dropDatabase(); diff --git a/test/examples/update_documents.js b/test/examples/update_documents.js index a5b7779a46..cb0dd1d5c7 100644 --- a/test/examples/update_documents.js +++ b/test/examples/update_documents.js @@ -2,7 +2,6 @@ const setupDatabase = require('../functional/shared').setupDatabase; const expect = require('chai').expect; -const MongoClient = require('../../lib/mongo_client'); describe('examples(update-documents):', function() { let client; @@ -13,7 +12,7 @@ describe('examples(update-documents):', function() { }); beforeEach(async function() { - client = await MongoClient.connect(this.configuration.url()); + client = await this.configuration.newClient().connect(); db = client.db(this.configuration.db); await db.collection('inventory').deleteMany({}); diff --git a/test/functional/authentication_tests.js b/test/functional/authentication_tests.js index b77123d55f..27944569f0 100644 --- a/test/functional/authentication_tests.js +++ b/test/functional/authentication_tests.js @@ -148,9 +148,8 @@ describe('Authentication', function() { // The actual test we wish to run test: function(done) { var configuration = this.configuration; - var MongoClient = configuration.require.MongoClient; - var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); + const client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); client.connect(function(err, client) { var db = client.db(configuration.db); var collection = db.collection( @@ -162,7 +161,11 @@ describe('Authentication', function() { adminDb.addUser('admin', 'admin', configuration.writeConcernMax(), function(err) { test.equal(null, err); - MongoClient.connect('mongodb://admin:admin@localhost:27017/admin', function(err) { + const validationClient = configuration.newClient( + 'mongodb://admin:admin@localhost:27017/admin' + ); + + validationClient.connect(function(err) { test.equal(null, err); adminDb.validateCollection( @@ -195,7 +198,6 @@ describe('Authentication', function() { // The actual test we wish to run test: function(done) { var configuration = this.configuration; - var MongoClient = configuration.require.MongoClient; var client = configuration.newClient({ w: 1 }, { poolSize: 1 }); // DOC_LINE var client = new MongoClient(new Server('localhost', 27017)); @@ -219,7 +221,7 @@ describe('Authentication', function() { test.ok(result != null); client.close(); - client = new MongoClient('mongodb://admin15:admin15@localhost:27017/admin'); + client = configuration.newClient('mongodb://admin15:admin15@localhost:27017/admin'); client.once('authenticated', function() { done(); }); diff --git a/test/functional/collations_tests.js b/test/functional/collations_tests.js index f28061eb94..9ef5f318ac 100644 --- a/test/functional/collations_tests.js +++ b/test/functional/collations_tests.js @@ -507,9 +507,7 @@ describe('Collation', function() { .then(() => Promise.reject('this test should fail')) .catch(err => { expect(err).to.exist; - expect(err.message).to.equal( - `server ${testContext.server.uri()} does not support collation` - ); + expect(err.message).to.match(/topology does not support collation/); return client.close(); }); }); diff --git a/test/functional/connection_tests.js b/test/functional/connection_tests.js index b1864ce023..959a5b877f 100644 --- a/test/functional/connection_tests.js +++ b/test/functional/connection_tests.js @@ -41,7 +41,12 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + var client = configuration.newClient( { w: 1 }, { poolSize: 1, host: '/tmp/mongodb-27017.sock', monitoring: false } @@ -253,11 +258,10 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var connect = configuration.require; + const configuration = this.configuration; + const client = configuration.newClient(); - connect( - configuration.url(), + client.connect( connectionTester(configuration, 'testConnectNoOptions', function(client) { client.close(); done(); @@ -274,12 +278,18 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var connect = configuration.require; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } - connect( - configuration.url(), - { auto_reconnect: true, poolSize: 4 }, + const client = configuration.newClient(configuration.url(), { + auto_reconnect: true, + poolSize: 4 + }); + + client.connect( connectionTester(configuration, 'testConnectServerOptions', function(client) { test.ok(client.topology.poolSize >= 1); test.equal(4, client.topology.s.coreTopology.s.pool.size); @@ -299,16 +309,19 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var connect = configuration.require; - - connect( - configuration.url(), - { - auto_reconnect: true, - poolSize: 4, - native_parser: process.env['TEST_NATIVE'] != null - }, + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + + const client = configuration.newClient(configuration.url(), { + auto_reconnect: true, + poolSize: 4, + native_parser: process.env['TEST_NATIVE'] != null + }); + + client.connect( connectionTester(configuration, 'testConnectAllOptions', function(client) { test.ok(client.topology.poolSize >= 1); test.equal(4, client.topology.s.coreTopology.s.pool.size); @@ -329,12 +342,13 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { var configuration = this.configuration; - var connect = configuration.require; var user = 'testConnectGoodAuth', password = 'password'; + const setupClient = configuration.newClient(); + // First add a user. - connect(configuration.url(), function(err, client) { + setupClient.connect(function(err, client) { test.equal(err, null); var db = client.db(configuration.db); @@ -346,8 +360,8 @@ describe('Connection', function() { }); function restOfTest() { - connect( - configuration.url(user, password), + const testClient = configuration.newClient(configuration.url(user, password)); + testClient.connect( connectionTester(configuration, 'testConnectGoodAuth', function(client) { client.close(); done(); @@ -366,12 +380,12 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { var configuration = this.configuration; - var connect = configuration.require; var user = 'testConnectGoodAuthAsOption', password = 'password'; // First add a user. - connect(configuration.url(), function(err, client) { + const setupClient = configuration.newClient(); + setupClient.connect(function(err, client) { test.equal(err, null); var db = client.db(configuration.db); @@ -384,9 +398,13 @@ describe('Connection', function() { function restOfTest() { var opts = { auth: { user: user, password: password } }; - connect( + + const testClient = configuration.newClient( configuration.url('baduser', 'badpassword'), - opts, + opts + ); + + testClient.connect( connectionTester(configuration, 'testConnectGoodAuthAsOption', function(client) { client.close(); done(); @@ -405,9 +423,9 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { var configuration = this.configuration; - var connect = configuration.require; + const client = configuration.newClient(configuration.url('slithy', 'toves')); - connect(configuration.url('slithy', 'toves'), function(err, client) { + client.connect(function(err, client) { expect(err).to.exist; expect(client).to.not.exist; done(); @@ -423,11 +441,11 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var connect = configuration.require; + const configuration = this.configuration; + const client = configuration.newClient('mangodb://localhost:27017/test?safe=false'); test.throws(function() { - connect('mangodb://localhost:27017/test?safe=false', function() { + client.connect(function() { test.ok(false, 'Bad URL!'); }); }); @@ -459,7 +477,11 @@ describe('Connection', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The unified topology deprecates autoReconnect, this test depends on the `reconnect` event + return this.skip(); + } var client = configuration.newClient({ w: 1 }, { poolSize: 1, auto_reconnect: true }); client.connect(function(err, client) { diff --git a/test/functional/cursor_tests.js b/test/functional/cursor_tests.js index de79182c3b..32b05606fb 100644 --- a/test/functional/cursor_tests.js +++ b/test/functional/cursor_tests.js @@ -297,7 +297,13 @@ describe('Cursor', function() { client.connect((err, client) => { const db = client.db(configuration.db); - const internalClientCursor = sinon.spy(client.topology.s.coreTopology, 'cursor'); + let internalClientCursor; + if (configuration.usingUnifiedTopology()) { + internalClientCursor = sinon.spy(client.topology, 'cursor'); + } else { + internalClientCursor = sinon.spy(client.topology.s.coreTopology, 'cursor'); + } + const expectedReadPreference = new ReadPreference(ReadPreference.SECONDARY); const cursor = db.collection('countTEST').find({ qty: { $gt: 4 } }); diff --git a/test/functional/db_tests.js b/test/functional/db_tests.js index d03edc1259..ec070a376d 100644 --- a/test/functional/db_tests.js +++ b/test/functional/db_tests.js @@ -154,7 +154,12 @@ describe('Db', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The unified topology deprecates autoReconnect + return this.skip(); + } + var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1, auto_reconnect: true @@ -194,7 +199,12 @@ describe('Db', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The unified topology does not use a store + return this.skip(); + } + var client = configuration.newClient( { w: 1 }, { poolSize: 1, auto_reconnect: true, bufferMaxEntries: 0 } diff --git a/test/functional/domain_tests.js b/test/functional/domain_tests.js index 02cc1eaeec..868f86e214 100644 --- a/test/functional/domain_tests.js +++ b/test/functional/domain_tests.js @@ -122,6 +122,11 @@ describe('Decimal128', function() { var Domain = require('domain'); var domainInstance = Domain.create(); var configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The unified topology does not use a store + return this.skip(); + } + var client = configuration.newClient( { w: 0 }, { poolSize: 1, auto_reconnect: true, domainsEnabled: true, bufferMaxEntries: 0 } @@ -161,6 +166,11 @@ describe('Decimal128', function() { var Domain = require('domain'); var domainInstance = Domain.create(); var configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The unified topology does not use a store + return this.skip(); + } + var client = configuration.newClient( { w: 1 }, { poolSize: 1, auto_reconnect: true, domainsEnabled: true, bufferMaxEntries: 0 } diff --git a/test/functional/index_tests.js b/test/functional/index_tests.js index 54d7c7f9a4..2bf5472674 100644 --- a/test/functional/index_tests.js +++ b/test/functional/index_tests.js @@ -1061,6 +1061,14 @@ describe('Indexes', function() { // The actual test we wish to run test: function(done) { var configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The new topology type has loose concepts of 'closing' and 'opening' a client. It will + // simply attempt here to retry the connection and reconnect, so this is a bad test for + // the driver in that configuration. + + return this.skip(); + } + var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); client.connect(function(err, client) { var db = client.db(configuration.db); diff --git a/test/functional/mongo_client_options_tests.js b/test/functional/mongo_client_options_tests.js index f3e4a49107..a898a288cd 100644 --- a/test/functional/mongo_client_options_tests.js +++ b/test/functional/mongo_client_options_tests.js @@ -16,12 +16,18 @@ describe('MongoClient Options', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var connect = configuration.require; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } - connect( - configuration.url(), - { autoReconnect: true, poolSize: 4 }, + const client = configuration.newClient(configuration.url(), { + autoReconnect: true, + poolSize: 4 + }); + + client.connect( connectionTester(configuration, 'testConnectServerOptions', function(client) { test.ok(client.topology.poolSize >= 1); test.equal(4, client.topology.s.coreTopology.s.pool.size); @@ -41,12 +47,18 @@ describe('MongoClient Options', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var connect = configuration.require; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } - connect( - configuration.url(), - { autoReconnect: true, poolSize: 4 }, + const client = configuration.newClient(configuration.url(), { + autoReconnect: true, + poolSize: 4 + }); + + client.connect( connectionTester(configuration, 'testConnectServerOptions', function(client) { test.ok(client.topology.poolSize >= 1); test.equal(4, client.topology.s.coreTopology.s.pool.size); diff --git a/test/functional/mongo_client_tests.js b/test/functional/mongo_client_tests.js index 5de99c9c11..9c20595257 100644 --- a/test/functional/mongo_client_tests.js +++ b/test/functional/mongo_client_tests.js @@ -18,7 +18,13 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // the new topology is far more resilient in these scenarios, making very difficult + // to reproduce the issues tested here. + return this.skip(); + } + const client = configuration.newClient({}, { bufferMaxEntries: 0, sslValidate: false }); client.connect(function(err, client) { @@ -58,7 +64,13 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // the new topology is far more resilient in these scenarios, making very difficult + // to reproduce the issues tested here. + return this.skip(); + } + const client = configuration.newClient({}, { bufferMaxEntries: 0, sslValidate: false }); client.connect(function(err, client) { @@ -157,7 +169,12 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + const client = configuration.newClient( {}, { @@ -195,7 +212,12 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + var url = configuration.url().replace('rs_name=rs', 'rs_name=rs1'); const client = configuration.newClient(url, { replSet: { @@ -245,7 +267,12 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + const client = configuration.newClient( {}, { @@ -291,7 +318,12 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + var url = configuration.url(); url = url.indexOf('?') !== -1 @@ -686,9 +718,13 @@ describe('MongoClient', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; - var uri = f('%s?socketTimeoutMS=120000&connectTimeoutMS=15000', configuration.url()); + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + var uri = f('%s?socketTimeoutMS=120000&connectTimeoutMS=15000', configuration.url()); const client = configuration.newClient(uri); client.connect(function(err, client) { test.equal(null, err); diff --git a/test/functional/operation_example_tests.js b/test/functional/operation_example_tests.js index f1577bd1cc..66ba7c6cf8 100644 --- a/test/functional/operation_example_tests.js +++ b/test/functional/operation_example_tests.js @@ -3579,7 +3579,15 @@ describe('Operation Examples', function() { // The actual test we wish to run test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The new topology type has loose concepts of 'closing' and 'opening' a client. It will + // simply attempt here to retry the connection and reconnect, so this is a bad test for + // the driver in that configuration. + + return this.skip(); + } + var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1 }); client.connect(function(err, client) { // LINE var MongoClient = require('mongodb').MongoClient, diff --git a/test/functional/operation_generators_example_tests.js b/test/functional/operation_generators_example_tests.js index 45224ad65e..97a1903384 100644 --- a/test/functional/operation_generators_example_tests.js +++ b/test/functional/operation_generators_example_tests.js @@ -2725,9 +2725,16 @@ describe('Operation (Generators)', function() { // The actual test we wish to run test: function() { - var configuration = this.configuration; - var co = require('co'); + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The new topology type has loose concepts of 'closing' and 'opening' a client. It will + // simply attempt here to retry the connection and reconnect, so this is a bad test for + // the driver in that configuration. + return this.skip(); + } + + var co = require('co'); return co(function*() { // Connect var client = yield configuration diff --git a/test/functional/operation_promises_example_tests.js b/test/functional/operation_promises_example_tests.js index 2ad950836d..576f4077d7 100644 --- a/test/functional/operation_promises_example_tests.js +++ b/test/functional/operation_promises_example_tests.js @@ -2787,7 +2787,15 @@ describe('Operation (Promises)', function() { // The actual test we wish to run test: function() { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // The new topology type has loose concepts of 'closing' and 'opening' a client. It will + // simply attempt here to retry the connection and reconnect, so this is a bad test for + // the driver in that configuration. + + return this.skip(); + } + var client = configuration.newClient(configuration.writeConcernMax(), { poolSize: 1, auto_reconnect: false diff --git a/test/functional/readpreference_tests.js b/test/functional/readpreference_tests.js index eea1697e9d..7ea3b7b5f4 100644 --- a/test/functional/readpreference_tests.js +++ b/test/functional/readpreference_tests.js @@ -55,7 +55,7 @@ describe('ReadPreference', function() { * @ignore */ it('Should correctly apply collection level read Preference to group', { - metadata: { requires: { mongodb: '>=2.6.0', topology: ['single', 'ssl'] } }, + metadata: { requires: { mongodb: '>=2.6.0,<=4.1.1', topology: ['single', 'ssl'] } }, // The actual test we wish to run test: function(done) { diff --git a/test/functional/replicaset_mock_tests.js b/test/functional/replicaset_mock_tests.js index ef42f36076..00162feffb 100644 --- a/test/functional/replicaset_mock_tests.js +++ b/test/functional/replicaset_mock_tests.js @@ -151,7 +151,12 @@ describe('ReplSet (mocks)', function() { }, test: function(done) { - var configuration = this.configuration; + const configuration = this.configuration; + if (configuration.usingUnifiedTopology()) { + // skipped for direct legacy variable inspection + return this.skip(); + } + const client = configuration.newClient( `mongodb://${test.mongos1.uri()},${test.mongos2.uri()}/test?socketTimeoutMS=120000&connectTimeoutMS=15000` ); diff --git a/test/functional/scram_sha_256_tests.js b/test/functional/scram_sha_256_tests.js index 348f9b20d6..11b3f51462 100644 --- a/test/functional/scram_sha_256_tests.js +++ b/test/functional/scram_sha_256_tests.js @@ -3,10 +3,8 @@ const expect = require('chai').expect; const sinon = require('sinon'); const ScramSHA256 = require('mongodb-core').ScramSHA256; -const MongoError = require('mongodb-core').MongoError; const setupDatabase = require('./shared').setupDatabase; const withClient = require('./shared').withClient; -const MongoClient = require('../../lib/mongo_client'); describe('SCRAM-SHA-256 auth', function() { const test = {}; @@ -107,7 +105,7 @@ describe('SCRAM-SHA-256 auth', function() { password )}authMechanism=${mechanism}`; - const client = new MongoClient(url); + const client = this.configuration.newClient(url); return withClient(client, client => { return client.db(this.configuration.db).stats(); @@ -140,7 +138,7 @@ describe('SCRAM-SHA-256 auth', function() { const password = encodeURIComponent(user.password); const url = makeConnectionString(this.configuration, username, password); - const client = new MongoClient(url); + const client = this.configuration.newClient(url); return withClient(client, client => { return client.db(this.configuration.db).stats(); @@ -187,7 +185,7 @@ describe('SCRAM-SHA-256 auth', function() { return withClient( this.configuration.newClient({}, options), () => Promise.reject(new Error('This request should have failed to authenticate')), - err => expect(err).to.not.be.null + err => expect(err).to.match(/Authentication failed/) ); } }); @@ -219,7 +217,7 @@ describe('SCRAM-SHA-256 auth', function() { withClient( this.configuration.newClient({}, options), () => Promise.reject(new Error('This request should have failed to authenticate')), - err => expect(err).to.be.an.instanceof(MongoError) + err => expect(err).to.match(/Authentication failed/) ); return Promise.all([getErrorMsg(noUsernameOptions), getErrorMsg(badPasswordOptions)]); diff --git a/test/functional/shared.js b/test/functional/shared.js index ca21418279..0432c90238 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -101,9 +101,9 @@ var delay = function(timeout) { }; module.exports = { - connectToDb: connectToDb, - setupDatabase: setupDatabase, - assert: assert, - delay: delay, + connectToDb, + setupDatabase, + assert, + delay, withClient }; diff --git a/test/functional/uri_tests.js b/test/functional/uri_tests.js index 96f0e0c470..497eca0b0a 100644 --- a/test/functional/uri_tests.js +++ b/test/functional/uri_tests.js @@ -3,6 +3,7 @@ const expect = require('chai').expect; const sinon = require('sinon'); const ReplSet = require('../../lib/topologies/replset'); +const NativeTopology = require('../../lib/topologies/native_topology'); describe('URI', function() { /** @@ -196,7 +197,11 @@ describe('URI', function() { done(); } - const connectStub = sinon.stub(ReplSet.prototype, 'connect').callsFake(validateConnect); + const topologyPrototype = this.configuration.usingUnifiedTopology() + ? NativeTopology.prototype + : ReplSet.prototpe; + + const connectStub = sinon.stub(topologyPrototype, 'connect').callsFake(validateConnect); const uri = 'mongodb://some-hostname/test?ssl=true&authMechanism=MONGODB-X509&replicaSet=rs0'; const client = this.configuration.newClient(uri, { useNewUrlParser: true }); client.connect();