From 90642baede5ae4fbc2c8f29ec2d64fe966488941 Mon Sep 17 00:00:00 2001 From: emadum Date: Sat, 9 May 2020 21:42:45 -0400 Subject: [PATCH 01/17] refactor: improve shared helpers --- test/functional/change_stream.test.js | 77 +++++---- test/functional/index.test.js | 32 ++-- test/functional/logger.test.js | 152 ++++++++-------- test/functional/shared.js | 240 +++++++++++++------------- 4 files changed, 253 insertions(+), 248 deletions(-) diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index c60dd0044d7..bfc1ee64b03 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2,7 +2,8 @@ const assert = require('assert'); const { Transform } = require('stream'); const { MongoError, MongoNetworkError } = require('../../lib/error'); -const { setupDatabase, withTempDb, delay } = require('./shared'); +const shared = require('./shared'); +const { setupDatabase, delay } = shared; const co = require('co'); const mock = require('mongodb-mock-server'); const chai = require('chai'); @@ -2602,19 +2603,23 @@ describe('Change Streams', function() { it('should return null on single iteration of empty cursor', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, test: function() { - return withTempDb( - 'testTryNext', - { w: 'majority' }, - this.configuration.newClient(), - db => done => { - const changeStream = db.collection('test').watch(); - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + const withClient = shared.withClient.bind(this); + const withDb = shared.withDb.bind(this); + return withClient( + withDb( + 'testTryNext', + { w: 'majority' }, + function(db, done) { + const changeStream = db.collection('test').watch(); + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); - } + changeStream.close(done); + }); + }, + true + ) ); } }); @@ -2622,26 +2627,24 @@ describe('Change Streams', function() { it('should iterate a change stream until first empty batch', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, test: function() { - return withTempDb( - 'testTryNext', - { w: 'majority' }, - this.configuration.newClient(), - db => done => { - const collection = db.collection('test'); - const changeStream = collection.watch(); - waitForStarted(changeStream, () => { - collection.insertOne({ a: 42 }, err => { - expect(err).to.not.exist; - - collection.insertOne({ b: 24 }, err => { + const withClient = shared.withClient.bind(this); + const withDb = shared.withDb.bind(this); + return withClient( + withDb( + 'testTryNext', + { w: 'majority' }, + function(db, done) { + const collection = db.collection('test'); + const changeStream = collection.watch(); + waitForStarted(changeStream, () => { + collection.insertOne({ a: 42 }, err => { expect(err).to.not.exist; + + collection.insertOne({ b: 24 }, err => { + expect(err).to.not.exist; + }); }); }); - }); - - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.exist; tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; @@ -2649,13 +2652,19 @@ describe('Change Streams', function() { tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; - expect(doc).to.not.exist; + expect(doc).to.exist; - changeStream.close(done); + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.not.exist; + + changeStream.close(done); + }); }); }); - }); - } + }, + true + ) ); } }); diff --git a/test/functional/index.test.js b/test/functional/index.test.js index 83772ee665f..7f4512a473a 100644 --- a/test/functional/index.test.js +++ b/test/functional/index.test.js @@ -2,8 +2,7 @@ var test = require('./shared').assert; var setupDatabase = require('./shared').setupDatabase; const expect = require('chai').expect; -const withClient = require('./shared').withClient; -const withMonitoredClient = require('./shared').withMonitoredClient; +const shared = require('./shared'); describe('Indexes', function() { before(function() { @@ -1209,18 +1208,21 @@ describe('Indexes', function() { return { metadata: { requires: { mongodb: '<4.4' } }, test: function() { - return withClient(this.configuration.newClient(), client => done => { - const db = client.db('test'); - const collection = db.collection('commitQuorum'); - testCommand(db, collection, (err, result) => { - expect(err).to.exist; - expect(err.message).to.equal( - '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' - ); - expect(result).to.not.exist; - done(); - }); - }); + const withClient = shared.withClient.bind(this); + const withDb = shared.withDb.bind(this); + return withClient( + withDb('test', (db, done) => { + const collection = db.collection('commitQuorum'); + testCommand(db, collection, (err, result) => { + expect(err).to.exist; + expect(err.message).to.equal( + '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' + ); + expect(result).to.not.exist; + done(); + }); + }) + ); } }; } @@ -1250,7 +1252,7 @@ describe('Indexes', function() { function commitQuorumTest(testCommand) { return { metadata: { requires: { mongodb: '>=4.4', topology: ['replicaset', 'sharded'] } }, - test: withMonitoredClient('createIndexes', function(client, events, done) { + test: shared.withMonitoredClient('createIndexes', function(client, events, done) { const db = client.db('test'); const collection = db.collection('commitQuorum'); collection.insertOne({ a: 1 }, function(err) { diff --git a/test/functional/logger.test.js b/test/functional/logger.test.js index 079b4a15f81..4f54b1e4e25 100644 --- a/test/functional/logger.test.js +++ b/test/functional/logger.test.js @@ -1,6 +1,6 @@ 'use strict'; -var expect = require('chai').expect; -var connectToDb = require('./shared').connectToDb; +const expect = require('chai').expect; +const shared = require('./shared'); const Logger = require('../../lib/logger'); describe('Logger', function() { @@ -54,29 +54,26 @@ describe('Logger', function() { it('should not fail with undefined id', { metadata: { requires: { topology: ['single'] } }, - test: function(done) { - var self = this; - + test: function() { // set a custom logger per http://mongodb.github.io/node-mongodb-native/2.0/tutorials/logging/ Logger.setCurrentLogger(function() {}); Logger.setLevel('debug'); - connectToDb('mongodb://localhost:27017/test', self.configuration.db, function( - err, - db, - client - ) { - expect(err).to.not.exist; - - // perform any operation that gets logged - db.collection('foo').findOne({}, function(err) { - expect(err).to.not.exist; + const withClient = shared.withClient.bind(this); + const withDb = shared.withDb.bind(this); + return withClient( + this.configuration.newClient('mongodb://localhost:27017/test'), + withDb(this.configuration.db, function(db, done) { + // perform any operation that gets logged + db.collection('foo').findOne({}, function(err) { + expect(err).to.not.exist; - // Clean up - Logger.reset(); - client.close(done); - }); - }); + // Clean up + Logger.reset(); + done(); + }); + }) + ); } }); @@ -86,43 +83,40 @@ describe('Logger', function() { it('should correctly log cursor', { metadata: { requires: { topology: ['single'] } }, - test: function(done) { - var self = this; - - connectToDb('mongodb://localhost:27017/test', self.configuration.db, function( - err, - db, - client - ) { - expect(err).to.not.exist; - - // Status - var logged = false; - - // Set the current logger - Logger.setCurrentLogger(function(msg, context) { - expect(msg).to.exist; - expect(context.type).to.equal('debug'); - expect(context.className).to.equal('Cursor'); - logged = true; - }); - - // Set the filter - Logger.setLevel('debug'); - Logger.filter('class', ['Cursor']); + test: function() { + const withClient = shared.withClient.bind(this); + const withDb = shared.withDb.bind(this); + return withClient( + this.configuration.newClient('mongodb://localhost:27017/test'), + withDb(this.configuration.db, function(db, done) { + // Status + var logged = false; + + // Set the current logger + Logger.setCurrentLogger(function(msg, context) { + expect(msg).to.exist; + expect(context.type).to.equal('debug'); + expect(context.className).to.equal('Cursor'); + logged = true; + }); - // perform any operation that gets logged - db.collection('logging') - .find() - .toArray(function(err) { - expect(err).to.not.exist; - expect(logged).to.be.true; + // Set the filter + Logger.setLevel('debug'); + Logger.filter('class', ['Cursor']); - // Clean up - Logger.reset(); - client.close(done); - }); - }); + // perform any operation that gets logged + db.collection('logging') + .find() + .toArray(function(err) { + expect(err).to.not.exist; + expect(logged).to.be.true; + + // Clean up + Logger.reset(); + done(); + }); + }) + ); } }); @@ -132,34 +126,34 @@ describe('Logger', function() { it('should pass the logLevel down through the options', { metadata: { requires: { topology: ['single'] } }, - test: function(done) { - var self = this; - + test: function() { Logger.filter('class', ['Cursor']); var logged = false; - connectToDb( - 'mongodb://localhost:27017/test', - self.configuration.db, - { - loggerLevel: 'debug', - logger: function() { - logged = true; + const withClient = shared.withClient.bind(this); + const withDb = shared.withDb.bind(this); + return withClient( + this.configuration.newClient('mongodb://localhost:27017/test'), + withDb( + this.configuration.db, + { + loggerLevel: 'debug', + logger: function() { + logged = true; + } + }, + (db, done) => { + // perform any operation that gets logged + db.collection('foo').findOne({}, function(err) { + expect(err).to.not.exist; + expect(logged).to.be.true; + + // Clean up + Logger.reset(); + done(); + }); } - }, - function(err, db, client) { - expect(err).to.not.exist; - - // perform any operation that gets logged - db.collection('foo').findOne({}, function(err) { - expect(err).to.not.exist; - expect(logged).to.be.true; - - // Clean up - Logger.reset(); - client.close(done); - }); - } + ) ); } }); diff --git a/test/functional/shared.js b/test/functional/shared.js index 7b03073e43f..81b32a54e07 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -3,6 +3,45 @@ const MongoClient = require('../../').MongoClient; const expect = require('chai').expect; +// helpers for using chai.expect in the assert style +const assert = { + equal: function(a, b) { + expect(a).to.equal(b); + }, + + deepEqual: function(a, b) { + expect(a).to.eql(b); + }, + + strictEqual: function(a, b) { + expect(a).to.eql(b); + }, + + notEqual: function(a, b) { + expect(a).to.not.equal(b); + }, + + ok: function(a) { + expect(a).to.be.ok; + }, + + throws: function(func) { + expect(func).to.throw; + } +}; + +function delay(timeout) { + return new Promise(function(resolve) { + setTimeout(function() { + resolve(); + }, timeout); + }); +} + +function dropCollection(dbObj, collectionName) { + return dbObj.dropCollection(collectionName).catch(ignoreNsNotFound); +} + function filterForCommands(commands, bag) { commands = Array.isArray(commands) ? commands : [commands]; return function(event) { @@ -17,16 +56,8 @@ function filterOutCommands(commands, bag) { }; } -function connectToDb(url, db, options, callback) { - if (typeof options === 'function') { - callback = options; - options = {}; - } - - MongoClient.connect(url, options || {}, function(err, client) { - if (err) return callback(err); - callback(null, client.db(db), client); - }); +function ignoreNsNotFound(err) { + if (!err.message.match(/ns not found/)) throw err; } function setupDatabase(configuration, dbsToClean) { @@ -57,8 +88,21 @@ function setupDatabase(configuration, dbsToClean) { ); } -function makeCleanupFn(client) { - return function(err) { +/** + * Safely perform a test with provided MongoClient, ensuring client won't leak. + * + * @param {MongoClient} [client] + * @param {Function|Promise} operation + * @param {Function|Promise} [errorHandler] + */ +function withClient(client, operation, errorHandler) { + if (!(client instanceof MongoClient)) { + client = this.configuration.newClient(); + operation = client; + errorHandler = operation; + } + + function cleanup(err) { return new Promise((resolve, reject) => { try { client.close(closeErr => { @@ -72,29 +116,7 @@ function makeCleanupFn(client) { return reject(err || e); } }); - }; -} - -function withTempDb(name, options, client, operation, errorHandler) { - return withClient( - client, - client => done => { - const db = client.db(name, options); - operation.call(this, db)(() => db.dropDatabase(done)); - }, - errorHandler - ); -} - -/** - * Safely perform a test with provided MongoClient, ensuring client won't leak. - * - * @param {MongoClient} client - * @param {Function|Promise} operation - * @param {Function|Promise} [errorHandler] - */ -function withClient(client, operation, errorHandler) { - const cleanup = makeCleanupFn(client); + } return client .connect() @@ -102,48 +124,69 @@ function withClient(client, operation, errorHandler) { .then(() => cleanup(), cleanup); } -var assert = { - equal: function(a, b) { - expect(a).to.equal(b); - }, - - deepEqual: function(a, b) { - expect(a).to.eql(b); - }, - - strictEqual: function(a, b) { - expect(a).to.eql(b); - }, - - notEqual: function(a, b) { - expect(a).to.not.equal(b); - }, - - ok: function(a) { - expect(a).to.be.ok; - }, - - throws: function(func) { - expect(func).to.throw; +/** + * use as the `operation` of `withClient` + * + * @param {string} name database name + * @param {object} [options] database options + * @param {Function} testFn test function to execute + * @param {boolean} [drop] drop database after test + */ +function withDb(name, options, testFn, drop) { + if (typeof options === 'function') { + drop = testFn; + testFn = options; + options = {}; } -}; - -var delay = function(timeout) { - return new Promise(function(resolve) { - setTimeout(function() { - resolve(); - }, timeout); - }); -}; - -function ignoreNsNotFound(err) { - if (!err.message.match(/ns not found/)) throw err; + return client => + new Promise(resolve => { + const db = client.db(name, options); + testFn.call(this, db, drop ? () => db.dropDatabase(resolve) : resolve); + }); } -function dropCollection(dbObj, collectionName) { - return dbObj.dropCollection(collectionName).catch(ignoreNsNotFound); +/** + * Perform a test with a monitored MongoClient that will filter for certain commands. + * + * @param {string|Array} commands commands to filter for + * @param {object} [options] options to pass on to configuration.newClient + * @param {object} [options.queryOptions] connection string options + * @param {object} [options.clientOptions] MongoClient options + * @param {withMonitoredClientCallback} callback the test function + */ +function withMonitoredClient(commands, options, callback) { + if (arguments.length === 2) { + callback = options; + options = {}; + } + if (!Object.prototype.hasOwnProperty.call(callback, 'prototype')) { + throw new Error('withMonitoredClient callback can not be arrow function'); + } + return function(done) { + const configuration = this.configuration; + const client = configuration.newClient( + Object.assign({}, options.queryOptions), + Object.assign({ monitorCommands: true }, options.clientOptions) + ); + const events = []; + client.on('commandStarted', filterForCommands(commands, events)); + client.connect((err, client) => { + expect(err).to.not.exist; + function _done(err) { + client.close(err2 => done(err || err2)); + } + callback.bind(this)(client, events, _done); + }); + }; } +/** + * @callback withMonitoredClientCallback + * @param {MongoClient} client monitored client + * @param {Array} events record of monitored commands + * @param {Function} done trigger end of test and cleanup + */ + /** * A class for listening on specific events * @@ -210,59 +253,16 @@ class EventCollector { } } -/** - * Perform a test with a monitored MongoClient that will filter for certain commands. - * - * @param {string|Array} commands commands to filter for - * @param {object} [options] options to pass on to configuration.newClient - * @param {object} [options.queryOptions] connection string options - * @param {object} [options.clientOptions] MongoClient options - * @param {withMonitoredClientCallback} callback the test function - */ -function withMonitoredClient(commands, options, callback) { - if (arguments.length === 2) { - callback = options; - options = {}; - } - if (!Object.prototype.hasOwnProperty.call(callback, 'prototype')) { - throw new Error('withMonitoredClient callback can not be arrow function'); - } - return function(done) { - const configuration = this.configuration; - const client = configuration.newClient( - Object.assign({}, options.queryOptions), - Object.assign({ monitorCommands: true }, options.clientOptions) - ); - const events = []; - client.on('commandStarted', filterForCommands(commands, events)); - client.connect((err, client) => { - expect(err).to.not.exist; - function _done(err) { - client.close(err2 => done(err || err2)); - } - callback.bind(this)(client, events, _done); - }); - }; -} - -/** - * @callback withMonitoredClientCallback - * @param {MongoClient} client monitored client - * @param {Array} events record of monitored commands - * @param {Function} done trigger end of test and cleanup - */ - module.exports = { - connectToDb, - setupDatabase, assert, delay, - withClient, - withMonitoredClient, - withTempDb, + dropCollection, filterForCommands, filterOutCommands, ignoreNsNotFound, - dropCollection, + setupDatabase, + withClient, + withMonitoredClient, + withDb, EventCollector }; From b9055ce6c800524238349db67e4c027b935b234b Mon Sep 17 00:00:00 2001 From: emadum Date: Sat, 9 May 2020 22:07:00 -0400 Subject: [PATCH 02/17] cleanup --- test/functional/change_stream.test.js | 15 +++++---------- test/functional/index.test.js | 7 +++---- test/functional/logger.test.js | 18 ++++++------------ test/functional/shared.js | 2 +- 4 files changed, 15 insertions(+), 27 deletions(-) diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index bfc1ee64b03..63790dbeef0 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2,8 +2,7 @@ const assert = require('assert'); const { Transform } = require('stream'); const { MongoError, MongoNetworkError } = require('../../lib/error'); -const shared = require('./shared'); -const { setupDatabase, delay } = shared; +const { delay, setupDatabase, withClient, withDb } = require('./shared'); const co = require('co'); const mock = require('mongodb-mock-server'); const chai = require('chai'); @@ -2603,13 +2602,11 @@ describe('Change Streams', function() { it('should return null on single iteration of empty cursor', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, test: function() { - const withClient = shared.withClient.bind(this); - const withDb = shared.withDb.bind(this); - return withClient( + return withClient.bind(this)( withDb( 'testTryNext', { w: 'majority' }, - function(db, done) { + (db, done) => { const changeStream = db.collection('test').watch(); tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; @@ -2627,13 +2624,11 @@ describe('Change Streams', function() { it('should iterate a change stream until first empty batch', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, test: function() { - const withClient = shared.withClient.bind(this); - const withDb = shared.withDb.bind(this); - return withClient( + return withClient.bind(this)( withDb( 'testTryNext', { w: 'majority' }, - function(db, done) { + (db, done) => { const collection = db.collection('test'); const changeStream = collection.watch(); waitForStarted(changeStream, () => { diff --git a/test/functional/index.test.js b/test/functional/index.test.js index 7f4512a473a..9614083d5f8 100644 --- a/test/functional/index.test.js +++ b/test/functional/index.test.js @@ -3,6 +3,7 @@ var test = require('./shared').assert; var setupDatabase = require('./shared').setupDatabase; const expect = require('chai').expect; const shared = require('./shared'); +const { withClient, withDb, withMonitoredClient } = shared; describe('Indexes', function() { before(function() { @@ -1208,9 +1209,7 @@ describe('Indexes', function() { return { metadata: { requires: { mongodb: '<4.4' } }, test: function() { - const withClient = shared.withClient.bind(this); - const withDb = shared.withDb.bind(this); - return withClient( + return withClient.bind(this)( withDb('test', (db, done) => { const collection = db.collection('commitQuorum'); testCommand(db, collection, (err, result) => { @@ -1252,7 +1251,7 @@ describe('Indexes', function() { function commitQuorumTest(testCommand) { return { metadata: { requires: { mongodb: '>=4.4', topology: ['replicaset', 'sharded'] } }, - test: shared.withMonitoredClient('createIndexes', function(client, events, done) { + test: withMonitoredClient('createIndexes', function(client, events, done) { const db = client.db('test'); const collection = db.collection('commitQuorum'); collection.insertOne({ a: 1 }, function(err) { diff --git a/test/functional/logger.test.js b/test/functional/logger.test.js index 4f54b1e4e25..7b7e4eb312a 100644 --- a/test/functional/logger.test.js +++ b/test/functional/logger.test.js @@ -1,6 +1,6 @@ 'use strict'; const expect = require('chai').expect; -const shared = require('./shared'); +const { withClient, withDb } = require('./shared'); const Logger = require('../../lib/logger'); describe('Logger', function() { @@ -59,11 +59,9 @@ describe('Logger', function() { Logger.setCurrentLogger(function() {}); Logger.setLevel('debug'); - const withClient = shared.withClient.bind(this); - const withDb = shared.withDb.bind(this); - return withClient( + return withClient.bind(this)( this.configuration.newClient('mongodb://localhost:27017/test'), - withDb(this.configuration.db, function(db, done) { + withDb(this.configuration.db, (db, done) => { // perform any operation that gets logged db.collection('foo').findOne({}, function(err) { expect(err).to.not.exist; @@ -84,11 +82,9 @@ describe('Logger', function() { metadata: { requires: { topology: ['single'] } }, test: function() { - const withClient = shared.withClient.bind(this); - const withDb = shared.withDb.bind(this); - return withClient( + return withClient.bind(this)( this.configuration.newClient('mongodb://localhost:27017/test'), - withDb(this.configuration.db, function(db, done) { + withDb(this.configuration.db, (db, done) => { // Status var logged = false; @@ -130,9 +126,7 @@ describe('Logger', function() { Logger.filter('class', ['Cursor']); var logged = false; - const withClient = shared.withClient.bind(this); - const withDb = shared.withDb.bind(this); - return withClient( + return withClient.bind(this)( this.configuration.newClient('mongodb://localhost:27017/test'), withDb( this.configuration.db, diff --git a/test/functional/shared.js b/test/functional/shared.js index 81b32a54e07..794dd6cade2 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -141,7 +141,7 @@ function withDb(name, options, testFn, drop) { return client => new Promise(resolve => { const db = client.db(name, options); - testFn.call(this, db, drop ? () => db.dropDatabase(resolve) : resolve); + testFn(db, drop ? () => db.dropDatabase(resolve) : resolve); }); } From cf2590b5ba42f28ac42ec26354629729b779e42b Mon Sep 17 00:00:00 2001 From: emadum Date: Mon, 11 May 2020 12:56:24 -0400 Subject: [PATCH 03/17] handle callbacks in withClient --- test/functional/shared.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index 794dd6cade2..7267a710295 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -91,15 +91,20 @@ function setupDatabase(configuration, dbsToClean) { /** * Safely perform a test with provided MongoClient, ensuring client won't leak. * - * @param {MongoClient} [client] - * @param {Function|Promise} operation - * @param {Function|Promise} [errorHandler] + * @param {MongoClient} [client] if not provided, withClient must be bound to test function `this` + * @param {Function} operation (client):Promise or (client, done):void + * @param {Function} [errorHandler] */ function withClient(client, operation, errorHandler) { if (!(client instanceof MongoClient)) { - client = this.configuration.newClient(); - operation = client; errorHandler = operation; + operation = client; + client = this.configuration.newClient(); + } + + if (operation.length === 2) { + const callback = operation; + operation = client => new Promise(resolve => callback(client, resolve)); } function cleanup(err) { From 4ea6a7067d0d2bb0713bcdb28d37f023b106a990 Mon Sep 17 00:00:00 2001 From: emadum Date: Mon, 11 May 2020 14:13:54 -0400 Subject: [PATCH 04/17] refactor withClient to return test function if not called from it --- test/functional/change_stream.test.js | 82 +++++++++++++-------------- test/functional/index.test.js | 28 +++++---- test/functional/shared.js | 20 +++++-- 3 files changed, 67 insertions(+), 63 deletions(-) diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index 63790dbeef0..16b3237bff1 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2601,45 +2601,46 @@ describe('Change Streams', function() { describe('tryNext', function() { it('should return null on single iteration of empty cursor', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, - test: function() { - return withClient.bind(this)( - withDb( - 'testTryNext', - { w: 'majority' }, - (db, done) => { - const changeStream = db.collection('test').watch(); - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + test: withClient( + withDb( + 'testTryNext', + { w: 'majority' }, + (db, done) => { + const changeStream = db.collection('test').watch(); + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); - }, - true - ) - ); - } + changeStream.close(done); + }); + }, + true + ) + ) }); it('should iterate a change stream until first empty batch', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, - test: function() { - return withClient.bind(this)( - withDb( - 'testTryNext', - { w: 'majority' }, - (db, done) => { - const collection = db.collection('test'); - const changeStream = collection.watch(); - waitForStarted(changeStream, () => { - collection.insertOne({ a: 42 }, err => { - expect(err).to.not.exist; + test: withClient( + withDb( + 'testTryNext', + { w: 'majority' }, + (db, done) => { + const collection = db.collection('test'); + const changeStream = collection.watch(); + waitForStarted(changeStream, () => { + collection.insertOne({ a: 42 }, err => { + expect(err).to.not.exist; - collection.insertOne({ b: 24 }, err => { - expect(err).to.not.exist; - }); + collection.insertOne({ b: 24 }, err => { + expect(err).to.not.exist; }); }); + }); + + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.exist; tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; @@ -2647,21 +2648,16 @@ describe('Change Streams', function() { tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; - expect(doc).to.exist; - - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); + changeStream.close(done); }); }); - }, - true - ) - ); - } + }); + }, + true + ) + ) }); }); diff --git a/test/functional/index.test.js b/test/functional/index.test.js index 9614083d5f8..8efe04639ee 100644 --- a/test/functional/index.test.js +++ b/test/functional/index.test.js @@ -1208,21 +1208,19 @@ describe('Indexes', function() { function throwErrorTest(testCommand) { return { metadata: { requires: { mongodb: '<4.4' } }, - test: function() { - return withClient.bind(this)( - withDb('test', (db, done) => { - const collection = db.collection('commitQuorum'); - testCommand(db, collection, (err, result) => { - expect(err).to.exist; - expect(err.message).to.equal( - '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' - ); - expect(result).to.not.exist; - done(); - }); - }) - ); - } + test: withClient( + withDb('test', (db, done) => { + const collection = db.collection('commitQuorum'); + testCommand(db, collection, (err, result) => { + expect(err).to.exist; + expect(err.message).to.equal( + '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' + ); + expect(result).to.not.exist; + done(); + }); + }) + ) }; } it( diff --git a/test/functional/shared.js b/test/functional/shared.js index 7267a710295..3adabdd203c 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -99,7 +99,7 @@ function withClient(client, operation, errorHandler) { if (!(client instanceof MongoClient)) { errorHandler = operation; operation = client; - client = this.configuration.newClient(); + client = undefined; } if (operation.length === 2) { @@ -123,10 +123,20 @@ function withClient(client, operation, errorHandler) { }); } - return client - .connect() - .then(operation, errorHandler) - .then(() => cleanup(), cleanup); + function lambda() { + if (!client) { + client = this.configuration.newClient(); + } + return client + .connect() + .then(operation, errorHandler) + .then(() => cleanup(), cleanup); + } + + if (this && this.configuration) { + return lambda.call(this); + } + return lambda; } /** From e37232db3f5075872250ed9d3f499e05268f0cf3 Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 11:06:05 -0400 Subject: [PATCH 05/17] remove withClient binds in logger tests --- test/functional/logger.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/functional/logger.test.js b/test/functional/logger.test.js index 7b7e4eb312a..3bb215d7ca1 100644 --- a/test/functional/logger.test.js +++ b/test/functional/logger.test.js @@ -59,7 +59,7 @@ describe('Logger', function() { Logger.setCurrentLogger(function() {}); Logger.setLevel('debug'); - return withClient.bind(this)( + return withClient( this.configuration.newClient('mongodb://localhost:27017/test'), withDb(this.configuration.db, (db, done) => { // perform any operation that gets logged @@ -82,7 +82,7 @@ describe('Logger', function() { metadata: { requires: { topology: ['single'] } }, test: function() { - return withClient.bind(this)( + return withClient( this.configuration.newClient('mongodb://localhost:27017/test'), withDb(this.configuration.db, (db, done) => { // Status @@ -126,7 +126,7 @@ describe('Logger', function() { Logger.filter('class', ['Cursor']); var logged = false; - return withClient.bind(this)( + return withClient( this.configuration.newClient('mongodb://localhost:27017/test'), withDb( this.configuration.db, From bed7aad8a6afc7dd427a7ebc32bc6eb3cfed745f Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 12:03:49 -0400 Subject: [PATCH 06/17] add filter function to filterForCommands/filterOutCommands/withMonitoredClient --- test/functional/shared.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index 3adabdd203c..f26c3e45b5b 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -43,6 +43,11 @@ function dropCollection(dbObj, collectionName) { } function filterForCommands(commands, bag) { + if (typeof commands === 'function') { + return function(event) { + if (commands(event.commandName)) bag.push(event); + }; + } commands = Array.isArray(commands) ? commands : [commands]; return function(event) { if (commands.indexOf(event.commandName) !== -1) bag.push(event); @@ -50,6 +55,11 @@ function filterForCommands(commands, bag) { } function filterOutCommands(commands, bag) { + if (typeof commands === 'function') { + return function(event) { + if (!commands(event.commandName)) bag.push(event); + }; + } commands = Array.isArray(commands) ? commands : [commands]; return function(event) { if (commands.indexOf(event.commandName) === -1) bag.push(event); @@ -163,7 +173,7 @@ function withDb(name, options, testFn, drop) { /** * Perform a test with a monitored MongoClient that will filter for certain commands. * - * @param {string|Array} commands commands to filter for + * @param {string|Array|Function} commands commands to filter for * @param {object} [options] options to pass on to configuration.newClient * @param {object} [options.queryOptions] connection string options * @param {object} [options.clientOptions] MongoClient options From 48706c13af5a9976d28dedb07001848656bb4c28 Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 15:10:59 -0400 Subject: [PATCH 07/17] add withCollection helper --- test/functional/change_stream.test.js | 84 +++++++++++++++------------ test/functional/shared.js | 71 +++++++++++++++++++--- 2 files changed, 108 insertions(+), 47 deletions(-) diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index 16b3237bff1..7e9aee85929 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2,7 +2,7 @@ const assert = require('assert'); const { Transform } = require('stream'); const { MongoError, MongoNetworkError } = require('../../lib/error'); -const { delay, setupDatabase, withClient, withDb } = require('./shared'); +const { delay, setupDatabase, withClient, withDb, withCollection } = require('./shared'); const co = require('co'); const mock = require('mongodb-mock-server'); const chai = require('chai'); @@ -2599,44 +2599,58 @@ describe('Change Streams', function() { }); describe('tryNext', function() { + function withTemporaryCollectionOnDb(database, testFn) { + return withClient( + withDb( + database, + { helper: { drop: true } }, + withCollection( + { + collection: { w: 'majority' }, + helper: { create: true } + }, + testFn + ) + ) + ); + } it('should return null on single iteration of empty cursor', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, - test: withClient( - withDb( - 'testTryNext', - { w: 'majority' }, - (db, done) => { - const changeStream = db.collection('test').watch(); - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + test: withTemporaryCollectionOnDb( + 'testTryNext', + { helper: { drop: true } }, + (collection, done) => { + const changeStream = collection.watch(); + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); - }, - true - ) + changeStream.close(done); + }); + } ) }); it('should iterate a change stream until first empty batch', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, - test: withClient( - withDb( - 'testTryNext', - { w: 'majority' }, - (db, done) => { - const collection = db.collection('test'); - const changeStream = collection.watch(); - waitForStarted(changeStream, () => { - collection.insertOne({ a: 42 }, err => { - expect(err).to.not.exist; + test: withTemporaryCollectionOnDb( + 'testTryNext', + { helper: { drop: true } }, + (collection, done) => { + const changeStream = collection.watch(); + waitForStarted(changeStream, () => { + collection.insertOne({ a: 42 }, err => { + expect(err).to.not.exist; - collection.insertOne({ b: 24 }, err => { - expect(err).to.not.exist; - }); + collection.insertOne({ b: 24 }, err => { + expect(err).to.not.exist; }); }); + }); + + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.exist; tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; @@ -2644,19 +2658,13 @@ describe('Change Streams', function() { tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; - expect(doc).to.exist; - - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); + changeStream.close(done); }); }); - }, - true - ) + }); + } ) }); }); diff --git a/test/functional/shared.js b/test/functional/shared.js index f26c3e45b5b..834b6e37f37 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -149,24 +149,76 @@ function withClient(client, operation, errorHandler) { return lambda; } +/** + * use as the `testFn` of `withDb` + * + * @param {string} [name='test'] database name + * @param {object} [options] options + * @param {object} [options.collection={}] collection options + * @param {object} [options.helper={}] helper options + * @param {boolean} [options.helper.create] create collection before test + * @param {boolean} [options.helper.drop] drop collection after test + * @param {Function} testFn test function to execute + */ +function withCollection(name, options, testFn) { + if (arguments.length === 1) { + testFn = name; + name = 'test'; + options = { collection: {}, helper: {} }; + } else if (arguments.length === 2) { + testFn = options; + if (typeof name === 'string') { + options = { collection: {}, helper: {} }; + } else { + options = name; + name = 'test'; + } + } + function runTest(collection, done) { + testFn(collection, options.helper.drop ? () => collection.drop(done) : done); + } + if (options.helper.create) { + return (db, done) => + db.createCollection(name, options, (err, collection) => { + if (err) return done(err); + runTest(collection, done); + }); + } + return (db, done) => { + const collection = db.collection(name, options.collection); + runTest(collection, done); + }; +} + /** * use as the `operation` of `withClient` * - * @param {string} name database name - * @param {object} [options] database options + * @param {string} [name='test'] database name + * @param {object} [options] options + * @param {object} [options.db={}] database options + * @param {object} [options.helper={}] helper options + * @param {boolean} [options.helper.drop] drop database after test * @param {Function} testFn test function to execute - * @param {boolean} [drop] drop database after test + */ -function withDb(name, options, testFn, drop) { - if (typeof options === 'function') { - drop = testFn; +function withDb(name, options, testFn) { + if (arguments.length === 1) { + testFn = name; + name = 'test'; + options = { db: {}, helper: {} }; + } else if (arguments.length === 2) { testFn = options; - options = {}; + if (typeof name === 'string') { + options = { db: {}, helper: {} }; + } else { + options = name; + name = 'test'; + } } return client => new Promise(resolve => { - const db = client.db(name, options); - testFn(db, drop ? () => db.dropDatabase(resolve) : resolve); + const db = client.db(name, options.db); + testFn(db, options.helper.drop ? () => db.dropDatabase(resolve) : resolve); }); } @@ -289,5 +341,6 @@ module.exports = { withClient, withMonitoredClient, withDb, + withCollection, EventCollector }; From fe3dd10d03a7196cd5244e26614490dfcad40345 Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 15:31:38 -0400 Subject: [PATCH 08/17] clean up evg output --- .evergreen/install-dependencies.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.evergreen/install-dependencies.sh b/.evergreen/install-dependencies.sh index acfd7f2fe11..e856ee76f48 100644 --- a/.evergreen/install-dependencies.sh +++ b/.evergreen/install-dependencies.sh @@ -9,6 +9,7 @@ NPM_TMP_DIR="${NODE_ARTIFACTS_PATH}/tmp" # this needs to be explicitly exported for the nvm install below export NVM_DIR="${NODE_ARTIFACTS_PATH}/nvm" +export XDG_CONFIG_HOME=${NODE_ARTIFACTS_PATH} # create node artifacts path if needed mkdir -p ${NODE_ARTIFACTS_PATH} @@ -16,9 +17,9 @@ mkdir -p ${NPM_CACHE_DIR} mkdir -p "${NPM_TMP_DIR}" # install Node.js -curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash +curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.35.3/install.sh | bash [ -s "${NVM_DIR}/nvm.sh" ] && \. "${NVM_DIR}/nvm.sh" -nvm install --lts=${NODE_LTS_NAME} +nvm install --no-progress --lts=${NODE_LTS_NAME} # setup npm cache in a local directory cat < .npmrc From ae9a5c27733a7c139e102f3334aa8b1ce5b7981e Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 15:31:51 -0400 Subject: [PATCH 09/17] fix for tryNext tests --- test/functional/change_stream.test.js | 56 ++++++++++++--------------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index 7e9aee85929..ad6e63caa8a 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2616,37 +2616,34 @@ describe('Change Streams', function() { } it('should return null on single iteration of empty cursor', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, - test: withTemporaryCollectionOnDb( - 'testTryNext', - { helper: { drop: true } }, - (collection, done) => { - const changeStream = collection.watch(); - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + test: withTemporaryCollectionOnDb('testTryNext', (collection, done) => { + const changeStream = collection.watch(); + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); - } - ) + changeStream.close(done); + }); + }) }); it('should iterate a change stream until first empty batch', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, - test: withTemporaryCollectionOnDb( - 'testTryNext', - { helper: { drop: true } }, - (collection, done) => { - const changeStream = collection.watch(); - waitForStarted(changeStream, () => { - collection.insertOne({ a: 42 }, err => { - expect(err).to.not.exist; + test: withTemporaryCollectionOnDb('testTryNext', (collection, done) => { + const changeStream = collection.watch(); + waitForStarted(changeStream, () => { + collection.insertOne({ a: 42 }, err => { + expect(err).to.not.exist; - collection.insertOne({ b: 24 }, err => { - expect(err).to.not.exist; - }); + collection.insertOne({ b: 24 }, err => { + expect(err).to.not.exist; }); }); + }); + + tryNext(changeStream, (err, doc) => { + expect(err).to.not.exist; + expect(doc).to.exist; tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; @@ -2654,18 +2651,13 @@ describe('Change Streams', function() { tryNext(changeStream, (err, doc) => { expect(err).to.not.exist; - expect(doc).to.exist; - - tryNext(changeStream, (err, doc) => { - expect(err).to.not.exist; - expect(doc).to.not.exist; + expect(doc).to.not.exist; - changeStream.close(done); - }); + changeStream.close(done); }); }); - } - ) + }); + }) }); }); From 8dea5f714e5fac7275c2e6f556e1543d18bd7767 Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 15:32:55 -0400 Subject: [PATCH 10/17] display eslint version in check:lint script --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f35763b4770..b5718568b8c 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "check:atlas": "node test/tools/atlas_connectivity_tests.js", "check:bench": "node test/benchmarks/driverBench", "check:coverage": "nyc mocha --timeout 60000 --recursive test/functional test/unit", - "check:lint": "eslint index.js lib test", + "check:lint": "eslint -v && eslint index.js lib test", "check:test": "mocha --recursive test/functional test/unit", "check:types": "tsc -p tsconfig.check.json", "release": "standard-version -i HISTORY.md", From 0ebebe31d8923e5bba474f7126bf14472877fca5 Mon Sep 17 00:00:00 2001 From: emadum Date: Tue, 12 May 2020 18:41:26 -0400 Subject: [PATCH 11/17] remove withDb/withCollection helpers --- test/functional/change_stream.test.js | 22 ++--- test/functional/index.test.js | 27 +++--- test/functional/logger.test.js | 121 ++++++++++++-------------- test/functional/shared.js | 93 +++----------------- 4 files changed, 88 insertions(+), 175 deletions(-) diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index ad6e63caa8a..e2e7b8ce16d 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2,7 +2,7 @@ const assert = require('assert'); const { Transform } = require('stream'); const { MongoError, MongoNetworkError } = require('../../lib/error'); -const { delay, setupDatabase, withClient, withDb, withCollection } = require('./shared'); +const { delay, setupDatabase, withClient } = require('./shared'); const co = require('co'); const mock = require('mongodb-mock-server'); const chai = require('chai'); @@ -2600,19 +2600,13 @@ describe('Change Streams', function() { describe('tryNext', function() { function withTemporaryCollectionOnDb(database, testFn) { - return withClient( - withDb( - database, - { helper: { drop: true } }, - withCollection( - { - collection: { w: 'majority' }, - helper: { create: true } - }, - testFn - ) - ) - ); + return withClient((client, done) => { + const db = client.db(database); + db.createCollection('test', { w: 'majority' }, (err, collection) => { + if (err) return done(err); + testFn(collection, () => db.dropDatabase(done)); + }); + }); } it('should return null on single iteration of empty cursor', { metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } }, diff --git a/test/functional/index.test.js b/test/functional/index.test.js index 8efe04639ee..686a6b28239 100644 --- a/test/functional/index.test.js +++ b/test/functional/index.test.js @@ -3,7 +3,7 @@ var test = require('./shared').assert; var setupDatabase = require('./shared').setupDatabase; const expect = require('chai').expect; const shared = require('./shared'); -const { withClient, withDb, withMonitoredClient } = shared; +const { withClient, withMonitoredClient } = shared; describe('Indexes', function() { before(function() { @@ -1208,19 +1208,18 @@ describe('Indexes', function() { function throwErrorTest(testCommand) { return { metadata: { requires: { mongodb: '<4.4' } }, - test: withClient( - withDb('test', (db, done) => { - const collection = db.collection('commitQuorum'); - testCommand(db, collection, (err, result) => { - expect(err).to.exist; - expect(err.message).to.equal( - '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' - ); - expect(result).to.not.exist; - done(); - }); - }) - ) + test: withClient((client, done) => { + const db = client.db('test'); + const collection = db.collection('commitQuorum'); + testCommand(db, collection, (err, result) => { + expect(err).to.exist; + expect(err.message).to.equal( + '`commitQuorum` option for `createIndexes` not supported on servers < 4.4' + ); + expect(result).to.not.exist; + done(); + }); + }) }; } it( diff --git a/test/functional/logger.test.js b/test/functional/logger.test.js index 3bb215d7ca1..c698f43b717 100644 --- a/test/functional/logger.test.js +++ b/test/functional/logger.test.js @@ -1,6 +1,6 @@ 'use strict'; const expect = require('chai').expect; -const { withClient, withDb } = require('./shared'); +const { withClient } = require('./shared'); const Logger = require('../../lib/logger'); describe('Logger', function() { @@ -59,19 +59,17 @@ describe('Logger', function() { Logger.setCurrentLogger(function() {}); Logger.setLevel('debug'); - return withClient( - this.configuration.newClient('mongodb://localhost:27017/test'), - withDb(this.configuration.db, (db, done) => { - // perform any operation that gets logged - db.collection('foo').findOne({}, function(err) { - expect(err).to.not.exist; + return withClient('mongodb://localhost:27017/test', (client, done) => { + const db = client.db(this.configuration.db); + // perform any operation that gets logged + db.collection('foo').findOne({}, function(err) { + expect(err).to.not.exist; - // Clean up - Logger.reset(); - done(); - }); - }) - ); + // Clean up + Logger.reset(); + done(); + }); + }); } }); @@ -82,37 +80,35 @@ describe('Logger', function() { metadata: { requires: { topology: ['single'] } }, test: function() { - return withClient( - this.configuration.newClient('mongodb://localhost:27017/test'), - withDb(this.configuration.db, (db, done) => { - // Status - var logged = false; - - // Set the current logger - Logger.setCurrentLogger(function(msg, context) { - expect(msg).to.exist; - expect(context.type).to.equal('debug'); - expect(context.className).to.equal('Cursor'); - logged = true; - }); + return withClient('mongodb://localhost:27017/test', (client, done) => { + const db = client.db(this.configuration.db); + // Status + var logged = false; + + // Set the current logger + Logger.setCurrentLogger(function(msg, context) { + expect(msg).to.exist; + expect(context.type).to.equal('debug'); + expect(context.className).to.equal('Cursor'); + logged = true; + }); - // Set the filter - Logger.setLevel('debug'); - Logger.filter('class', ['Cursor']); - - // perform any operation that gets logged - db.collection('logging') - .find() - .toArray(function(err) { - expect(err).to.not.exist; - expect(logged).to.be.true; - - // Clean up - Logger.reset(); - done(); - }); - }) - ); + // Set the filter + Logger.setLevel('debug'); + Logger.filter('class', ['Cursor']); + + // perform any operation that gets logged + db.collection('logging') + .find() + .toArray(function(err) { + expect(err).to.not.exist; + expect(logged).to.be.true; + + // Clean up + Logger.reset(); + done(); + }); + }); } }); @@ -126,29 +122,24 @@ describe('Logger', function() { Logger.filter('class', ['Cursor']); var logged = false; - return withClient( - this.configuration.newClient('mongodb://localhost:27017/test'), - withDb( - this.configuration.db, - { - loggerLevel: 'debug', - logger: function() { - logged = true; - } - }, - (db, done) => { - // perform any operation that gets logged - db.collection('foo').findOne({}, function(err) { - expect(err).to.not.exist; - expect(logged).to.be.true; - - // Clean up - Logger.reset(); - done(); - }); + return withClient('mongodb://localhost:27017/test', (client, done) => { + const db = client.db(this.configuration.db, { + loggerLevel: 'debug', + logger: function() { + logged = true; } - ) - ); + }); + + // perform any operation that gets logged + db.collection('foo').findOne({}, function(err) { + expect(err).to.not.exist; + expect(logged).to.be.true; + + // Clean up + Logger.reset(); + done(); + }); + }); } }); }); diff --git a/test/functional/shared.js b/test/functional/shared.js index 834b6e37f37..e853cd2abd1 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -101,15 +101,19 @@ function setupDatabase(configuration, dbsToClean) { /** * Safely perform a test with provided MongoClient, ensuring client won't leak. * - * @param {MongoClient} [client] if not provided, withClient must be bound to test function `this` + * @param {string|MongoClient} [client] if not provided, withClient must be bound to test function `this` * @param {Function} operation (client):Promise or (client, done):void - * @param {Function} [errorHandler] */ -function withClient(client, operation, errorHandler) { - if (!(client instanceof MongoClient)) { - errorHandler = operation; +function withClient(client, operation) { + let connectionString; + if (arguments.length === 1) { operation = client; client = undefined; + } else { + if (typeof client === 'string') { + connectionString = client; + client = undefined; + } } if (operation.length === 2) { @@ -135,11 +139,11 @@ function withClient(client, operation, errorHandler) { function lambda() { if (!client) { - client = this.configuration.newClient(); + client = this.configuration.newClient(connectionString); } return client .connect() - .then(operation, errorHandler) + .then(operation) .then(() => cleanup(), cleanup); } @@ -149,79 +153,6 @@ function withClient(client, operation, errorHandler) { return lambda; } -/** - * use as the `testFn` of `withDb` - * - * @param {string} [name='test'] database name - * @param {object} [options] options - * @param {object} [options.collection={}] collection options - * @param {object} [options.helper={}] helper options - * @param {boolean} [options.helper.create] create collection before test - * @param {boolean} [options.helper.drop] drop collection after test - * @param {Function} testFn test function to execute - */ -function withCollection(name, options, testFn) { - if (arguments.length === 1) { - testFn = name; - name = 'test'; - options = { collection: {}, helper: {} }; - } else if (arguments.length === 2) { - testFn = options; - if (typeof name === 'string') { - options = { collection: {}, helper: {} }; - } else { - options = name; - name = 'test'; - } - } - function runTest(collection, done) { - testFn(collection, options.helper.drop ? () => collection.drop(done) : done); - } - if (options.helper.create) { - return (db, done) => - db.createCollection(name, options, (err, collection) => { - if (err) return done(err); - runTest(collection, done); - }); - } - return (db, done) => { - const collection = db.collection(name, options.collection); - runTest(collection, done); - }; -} - -/** - * use as the `operation` of `withClient` - * - * @param {string} [name='test'] database name - * @param {object} [options] options - * @param {object} [options.db={}] database options - * @param {object} [options.helper={}] helper options - * @param {boolean} [options.helper.drop] drop database after test - * @param {Function} testFn test function to execute - - */ -function withDb(name, options, testFn) { - if (arguments.length === 1) { - testFn = name; - name = 'test'; - options = { db: {}, helper: {} }; - } else if (arguments.length === 2) { - testFn = options; - if (typeof name === 'string') { - options = { db: {}, helper: {} }; - } else { - options = name; - name = 'test'; - } - } - return client => - new Promise(resolve => { - const db = client.db(name, options.db); - testFn(db, options.helper.drop ? () => db.dropDatabase(resolve) : resolve); - }); -} - /** * Perform a test with a monitored MongoClient that will filter for certain commands. * @@ -340,7 +271,5 @@ module.exports = { setupDatabase, withClient, withMonitoredClient, - withDb, - withCollection, EventCollector }; From d88e6092400e31c3d20d7cb140c772d7afce28ef Mon Sep 17 00:00:00 2001 From: emadum Date: Wed, 13 May 2020 07:26:54 -0400 Subject: [PATCH 12/17] fix lint error --- test/functional/shared.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index e853cd2abd1..17bcaedd2e5 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -1,6 +1,5 @@ 'use strict'; -const MongoClient = require('../../').MongoClient; const expect = require('chai').expect; // helpers for using chai.expect in the assert style From f0fcde88715ffd679e454028bcbffba6656fcfd2 Mon Sep 17 00:00:00 2001 From: emadum Date: Wed, 13 May 2020 12:16:13 -0400 Subject: [PATCH 13/17] use typescript syntax for documenting callbacks --- test/functional/shared.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index 17bcaedd2e5..b8ea4f0c1bc 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -97,11 +97,12 @@ function setupDatabase(configuration, dbsToClean) { ); } +/** @typedef {(client: MongoClient) => Promise | (client: MongoClient, done: Function) => void} withClientCallback */ /** * Safely perform a test with provided MongoClient, ensuring client won't leak. * - * @param {string|MongoClient} [client] if not provided, withClient must be bound to test function `this` - * @param {Function} operation (client):Promise or (client, done):void + * @param {string|MongoClient} [client] if not provided, `withClient` must be bound to test function `this` + * @param {withClientCallback} operation test operation to perform */ function withClient(client, operation) { let connectionString; @@ -152,6 +153,7 @@ function withClient(client, operation) { return lambda; } +/** @typedef {(client: MongoClient, events: Array, done: Function) => void} withMonitoredClientCallback */ /** * Perform a test with a monitored MongoClient that will filter for certain commands. * @@ -187,12 +189,7 @@ function withMonitoredClient(commands, options, callback) { }; } -/** - * @callback withMonitoredClientCallback - * @param {MongoClient} client monitored client - * @param {Array} events record of monitored commands - * @param {Function} done trigger end of test and cleanup - */ + /** * A class for listening on specific events From f894c1a6c36be5223cc9a14b720bd205e0e95cda Mon Sep 17 00:00:00 2001 From: emadum Date: Wed, 13 May 2020 12:28:56 -0400 Subject: [PATCH 14/17] refactor: use withClient in withMonitoredClient --- test/functional/shared.js | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index b8ea4f0c1bc..98484948c54 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -102,12 +102,12 @@ function setupDatabase(configuration, dbsToClean) { * Safely perform a test with provided MongoClient, ensuring client won't leak. * * @param {string|MongoClient} [client] if not provided, `withClient` must be bound to test function `this` - * @param {withClientCallback} operation test operation to perform + * @param {withClientCallback} callback the test function */ -function withClient(client, operation) { +function withClient(client, callback) { let connectionString; if (arguments.length === 1) { - operation = client; + callback = client; client = undefined; } else { if (typeof client === 'string') { @@ -116,9 +116,9 @@ function withClient(client, operation) { } } - if (operation.length === 2) { - const callback = operation; - operation = client => new Promise(resolve => callback(client, resolve)); + if (callback.length === 2) { + const cb = callback; + callback = client => new Promise(resolve => cb(client, resolve)); } function cleanup(err) { @@ -143,7 +143,7 @@ function withClient(client, operation) { } return client .connect() - .then(operation) + .then(callback) .then(() => cleanup(), cleanup); } @@ -171,26 +171,17 @@ function withMonitoredClient(commands, options, callback) { if (!Object.prototype.hasOwnProperty.call(callback, 'prototype')) { throw new Error('withMonitoredClient callback can not be arrow function'); } - return function(done) { - const configuration = this.configuration; - const client = configuration.newClient( + return function() { + const monitoredClient = this.configuration.newClient( Object.assign({}, options.queryOptions), Object.assign({ monitorCommands: true }, options.clientOptions) ); const events = []; - client.on('commandStarted', filterForCommands(commands, events)); - client.connect((err, client) => { - expect(err).to.not.exist; - function _done(err) { - client.close(err2 => done(err || err2)); - } - callback.bind(this)(client, events, _done); - }); + monitoredClient.on('commandStarted', filterForCommands(commands, events)); + return withClient(monitoredClient, (client, done) => callback(client, events, done)); }; } - - /** * A class for listening on specific events * From 91a7d1e3fe58ee4b5245d92b5fbc344537fa744a Mon Sep 17 00:00:00 2001 From: emadum Date: Wed, 13 May 2020 12:33:50 -0400 Subject: [PATCH 15/17] clean up destructuring in index test --- test/functional/index.test.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/functional/index.test.js b/test/functional/index.test.js index 686a6b28239..c51d9d252e6 100644 --- a/test/functional/index.test.js +++ b/test/functional/index.test.js @@ -1,9 +1,6 @@ 'use strict'; -var test = require('./shared').assert; -var setupDatabase = require('./shared').setupDatabase; -const expect = require('chai').expect; -const shared = require('./shared'); -const { withClient, withMonitoredClient } = shared; +const { expect } = require('chai'); +const { assert: test, setupDatabase, withClient, withMonitoredClient } = require('./shared'); describe('Indexes', function() { before(function() { From 23e49b72097a36c719cdaddfa117ca2a5382dead Mon Sep 17 00:00:00 2001 From: emadum Date: Wed, 13 May 2020 12:53:52 -0400 Subject: [PATCH 16/17] fix withMonitoredClient --- test/functional/shared.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index 98484948c54..8fec8c32322 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -178,7 +178,9 @@ function withMonitoredClient(commands, options, callback) { ); const events = []; monitoredClient.on('commandStarted', filterForCommands(commands, events)); - return withClient(monitoredClient, (client, done) => callback(client, events, done)); + return withClient(monitoredClient, (client, done) => + callback.bind(this)(client, events, done) + )(); }; } From 299927ff04588fae1609e8d59dba273e71cd7f3a Mon Sep 17 00:00:00 2001 From: emadum Date: Wed, 13 May 2020 13:17:03 -0400 Subject: [PATCH 17/17] fix helper tests --- test/functional/shared.js | 7 ++++++- test/functional/shared.test.js | 8 ++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/test/functional/shared.js b/test/functional/shared.js index 8fec8c32322..92e22d46d92 100644 --- a/test/functional/shared.js +++ b/test/functional/shared.js @@ -144,7 +144,12 @@ function withClient(client, callback) { return client .connect() .then(callback) - .then(() => cleanup(), cleanup); + .then(err => { + cleanup(); + if (err) { + throw err; + } + }, cleanup); } if (this && this.configuration) { diff --git a/test/functional/shared.test.js b/test/functional/shared.test.js index f8ae10268ed..fdeabeb3a63 100644 --- a/test/functional/shared.test.js +++ b/test/functional/shared.test.js @@ -36,7 +36,7 @@ describe('shared test utilities', function() { return innerDone(); }); }).bind(this); - encapsulatedTest(fakeDone); + encapsulatedTest().then(fakeDone); }); it('should propagate passed error to done', function(done) { @@ -59,7 +59,7 @@ describe('shared test utilities', function() { return innerDone(new Error('hello world')); }); }).bind(this); - encapsulatedTest(fakeDone); + encapsulatedTest().catch(fakeDone); }); it('should call done and close connection with promise', function(done) { @@ -82,7 +82,7 @@ describe('shared test utilities', function() { return innerDone(); }); }).bind(this); - encapsulatedTest(fakeDone); + encapsulatedTest().then(fakeDone); }); it('should propagate passed error to done from promise', function(done) { @@ -106,7 +106,7 @@ describe('shared test utilities', function() { return innerDone(new Error('hello world')); }); }).bind(this); - encapsulatedTest(fakeDone); + encapsulatedTest().catch(fakeDone); }); }); });