diff --git a/lib/cmap/connection.js b/lib/cmap/connection.js index d4f9a54f31..81925b9621 100644 --- a/lib/cmap/connection.js +++ b/lib/cmap/connection.js @@ -122,7 +122,7 @@ class Connection extends EventEmitter { if (issue.isTimeout) { op.cb( new MongoNetworkTimeoutError(`connection ${this.id} to ${this.address} timed out`, { - beforeHandshake: !!this[kIsMaster] + beforeHandshake: this.ismaster == null }) ); } else if (issue.isClose) { diff --git a/lib/core/error.js b/lib/core/error.js index b4816e6017..702e3cecde 100644 --- a/lib/core/error.js +++ b/lib/core/error.js @@ -102,8 +102,8 @@ class MongoNetworkError extends MongoError { super(message); this.name = 'MongoNetworkError'; - if (options && options.beforeHandshake === true) { - this[kBeforeHandshake] = true; + if (options && typeof options.beforeHandshake === 'boolean') { + this[kBeforeHandshake] = options.beforeHandshake; } } } diff --git a/test/unit/cmap/connection.test.js b/test/unit/cmap/connection.test.js index 1dac1e35ee..39be21dc35 100644 --- a/test/unit/cmap/connection.test.js +++ b/test/unit/cmap/connection.test.js @@ -66,4 +66,62 @@ describe('Connection', function() { } ); }); + + it('should throw a network error with kBeforeHandshake set to false on timeout after hand shake', function(done) { + server.setMessageHandler(request => { + const doc = request.document; + if (doc.ismaster) { + request.reply(mock.DEFAULT_ISMASTER_36); + } + // respond to no other requests to trigger timeout event + }); + + const address = server.address(); + const options = { + bson: new BSON(), + connectionType: Connection, + host: address.host, + port: address.port + }; + + connect(options, (err, conn) => { + expect(err).to.be.a('undefined'); + expect(conn).to.be.instanceOf(Connection); + expect(conn) + .to.have.property('ismaster') + .that.is.a('object'); + + conn.command('$admin.cmd', { ping: 1 }, { socketTimeout: 50 }, err => { + const beforeHandshakeSymbol = Object.getOwnPropertySymbols(err)[0]; + expect(beforeHandshakeSymbol).to.be.a('symbol'); + expect(err).to.have.property(beforeHandshakeSymbol, false); + + done(); + }); + }); + }); + + it('should throw a network error with kBeforeHandshake set to true on timeout before hand shake', function(done) { + // respond to no requests to trigger timeout event + server.setMessageHandler(() => {}); + + const address = server.address(); + const options = { + bson: new BSON(), + connectionType: Connection, + host: address.host, + port: address.port, + socketTimeout: 50 + }; + + connect(options, (err, conn) => { + expect(conn).to.be.a('undefined'); + + const beforeHandshakeSymbol = Object.getOwnPropertySymbols(err)[0]; + expect(beforeHandshakeSymbol).to.be.a('symbol'); + expect(err).to.have.property(beforeHandshakeSymbol, true); + + done(); + }); + }); }); diff --git a/test/unit/error.test.js b/test/unit/error.test.js new file mode 100644 index 0000000000..fd173d308c --- /dev/null +++ b/test/unit/error.test.js @@ -0,0 +1,22 @@ +'use strict'; + +const expect = require('chai').expect; +const MongoNetworkError = require('../../lib/core/error').MongoNetworkError; + +describe('MongoErrors', function() { + describe('MongoNetworkError', function() { + it('should only define beforeHandshake symbol if boolean option passed in', function() { + const errorWithOptionTrue = new MongoNetworkError('', { beforeHandshake: true }); + expect(Object.getOwnPropertySymbols(errorWithOptionTrue).length).to.equal(1); + + const errorWithOptionFalse = new MongoNetworkError('', { beforeHandshake: false }); + expect(Object.getOwnPropertySymbols(errorWithOptionFalse).length).to.equal(1); + + const errorWithBadOption = new MongoNetworkError('', { beforeHandshake: 'not boolean' }); + expect(Object.getOwnPropertySymbols(errorWithBadOption).length).to.equal(0); + + const errorWithoutOption = new MongoNetworkError(''); + expect(Object.getOwnPropertySymbols(errorWithoutOption).length).to.equal(0); + }); + }); +});