From 2a78d5a185ce1a6e8f35ce89dae479fcd0877bc6 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 2 Nov 2021 20:39:40 +0100 Subject: [PATCH] feat(NODE-3469,NODE-3615,NODE-3507): update min and max wire versions (#3014) --- .evergreen/config.yml | 168 -------- .evergreen/generate_evergreen_tasks.js | 6 +- src/cmap/wire_protocol/constants.ts | 8 +- src/operations/list_collections.ts | 9 +- test/functional/buffering_proxy.test.js | 4 +- test/functional/change_stream.test.js | 4 +- test/functional/collations.test.js | 265 +------------ test/functional/collection.test.js | 2 +- test/functional/command_write_concern.test.js | 2 +- test/functional/max_staleness.test.js | 2 +- test/functional/replicaset_mock.test.js | 4 +- test/functional/unit-sdam/monitoring.test.js | 17 +- test/functional/unit-sdam/srv_polling.test.js | 4 +- test/functional/unit-sdam/topology.test.js | 14 +- test/functional/unit-sessions/client.test.js | 4 +- .../unit-sessions/collection.test.js | 6 +- .../functional/unit_bypass_validation.test.js | 10 +- .../unit_create_index_error.test.js | 2 +- .../unit_db_list_collections.test.js | 76 ---- test/functional/view.test.js | 2 +- test/functional/write_concern.test.js | 2 +- .../README.rst | 365 ++++++++++++++++-- .../load-balanced/discover_load_balancer.yml | 3 + .../monitoring/discovered_standalone.json | 2 +- .../monitoring/discovered_standalone.yml | 2 +- .../replica_set_with_no_primary.json | 2 +- .../replica_set_with_no_primary.yml | 77 ++-- .../monitoring/replica_set_with_primary.json | 2 +- .../monitoring/replica_set_with_primary.yml | 77 ++-- .../monitoring/replica_set_with_removal.json | 2 +- .../monitoring/replica_set_with_removal.yml | 2 +- .../monitoring/required_replica_set.json | 2 +- .../monitoring/required_replica_set.yml | 2 +- .../monitoring/standalone.json | 2 +- .../monitoring/standalone.yml | 2 +- ...ne_suppress_equal_description_changes.json | 4 +- ...one_suppress_equal_description_changes.yml | 4 +- test/tools/common.js | 16 +- test/tools/mock.js | 4 +- test/unit/_MISC_scram_iterations.test.js | 6 +- test/unit/cmap/connect.test.js | 4 +- test/unit/cmap/connection.test.js | 6 +- test/unit/cmap/connection_pool.test.js | 14 +- .../unit/cmap/wire_protocol/constants.test.js | 33 ++ test/unit/cursor/find_cursor.test.js | 2 +- test/unit/operations/list_collections.test.js | 79 ++++ test/unit/sessions.test.js | 4 +- test/unit/unit_bulk_write.test.js | 2 +- test/unit/unit_client.test.js | 2 +- test/unit/unit_snappy.test.js | 2 +- test/unit/unit_wire_version.test.js | 8 +- 51 files changed, 634 insertions(+), 709 deletions(-) delete mode 100644 test/functional/unit_db_list_collections.test.js create mode 100644 test/unit/cmap/wire_protocol/constants.test.js create mode 100644 test/unit/operations/list_collections.test.js diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 179b417404..074824c2ba 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -852,138 +852,6 @@ tasks: VERSION: '3.6' TOPOLOGY: sharded_cluster - func: run tests - - name: test-3.4-server - tags: - - '3.4' - - server - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.4' - TOPOLOGY: server - - func: run tests - - name: test-3.4-replica_set - tags: - - '3.4' - - replica_set - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.4' - TOPOLOGY: replica_set - - func: run tests - - name: test-3.4-sharded_cluster - tags: - - '3.4' - - sharded_cluster - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.4' - TOPOLOGY: sharded_cluster - - func: run tests - - name: test-3.2-server - tags: - - '3.2' - - server - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.2' - TOPOLOGY: server - - func: run tests - - name: test-3.2-replica_set - tags: - - '3.2' - - replica_set - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.2' - TOPOLOGY: replica_set - - func: run tests - - name: test-3.2-sharded_cluster - tags: - - '3.2' - - sharded_cluster - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.2' - TOPOLOGY: sharded_cluster - - func: run tests - - name: test-3.0-server - tags: - - '3.0' - - server - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.0' - TOPOLOGY: server - - func: run tests - - name: test-3.0-replica_set - tags: - - '3.0' - - replica_set - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.0' - TOPOLOGY: replica_set - - func: run tests - - name: test-3.0-sharded_cluster - tags: - - '3.0' - - sharded_cluster - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '3.0' - TOPOLOGY: sharded_cluster - - func: run tests - - name: test-2.6-server - tags: - - '2.6' - - server - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '2.6' - TOPOLOGY: server - - func: run tests - - name: test-2.6-replica_set - tags: - - '2.6' - - replica_set - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '2.6' - TOPOLOGY: replica_set - - func: run tests - - name: test-2.6-sharded_cluster - tags: - - '2.6' - - sharded_cluster - commands: - - func: install dependencies - - func: bootstrap mongo-orchestration - vars: - VERSION: '2.6' - TOPOLOGY: sharded_cluster - - func: run tests - name: test-latest-server-v1-api tags: - latest @@ -1810,18 +1678,6 @@ buildvariants: - test-3.6-server - test-3.6-replica_set - test-3.6-sharded_cluster - - test-3.4-server - - test-3.4-replica_set - - test-3.4-sharded_cluster - - test-3.2-server - - test-3.2-replica_set - - test-3.2-sharded_cluster - - test-3.0-server - - test-3.0-replica_set - - test-3.0-sharded_cluster - - test-2.6-server - - test-2.6-replica_set - - test-2.6-sharded_cluster - test-latest-server-v1-api - test-atlas-connectivity - test-atlas-data-lake @@ -1891,18 +1747,6 @@ buildvariants: - test-3.6-server - test-3.6-replica_set - test-3.6-sharded_cluster - - test-3.4-server - - test-3.4-replica_set - - test-3.4-sharded_cluster - - test-3.2-server - - test-3.2-replica_set - - test-3.2-sharded_cluster - - test-3.0-server - - test-3.0-replica_set - - test-3.0-sharded_cluster - - test-2.6-server - - test-2.6-replica_set - - test-2.6-sharded_cluster - test-latest-server-v1-api - test-atlas-connectivity - test-atlas-data-lake @@ -1973,18 +1817,6 @@ buildvariants: - test-3.6-server - test-3.6-replica_set - test-3.6-sharded_cluster - - test-3.4-server - - test-3.4-replica_set - - test-3.4-sharded_cluster - - test-3.2-server - - test-3.2-replica_set - - test-3.2-sharded_cluster - - test-3.0-server - - test-3.0-replica_set - - test-3.0-sharded_cluster - - test-2.6-server - - test-2.6-replica_set - - test-2.6-sharded_cluster - test-latest-server-v1-api - test-atlas-data-lake - test-ocsp-valid-cert-server-staples diff --git a/.evergreen/generate_evergreen_tasks.js b/.evergreen/generate_evergreen_tasks.js index 1b87110507..1693b36826 100644 --- a/.evergreen/generate_evergreen_tasks.js +++ b/.evergreen/generate_evergreen_tasks.js @@ -3,7 +3,7 @@ const fs = require('fs'); const yaml = require('js-yaml'); const LATEST_EFFECTIVE_VERSION = '5.0'; -const MONGODB_VERSIONS = ['latest', '5.0', '4.4', '4.2', '4.0', '3.6', '3.4', '3.2', '3.0', '2.6']; +const MONGODB_VERSIONS = ['latest', '5.0', '4.4', '4.2', '4.0', '3.6']; const NODE_VERSIONS = ['erbium', 'fermium']; NODE_VERSIONS.sort(); const LOWEST_LTS = NODE_VERSIONS[0]; @@ -34,7 +34,7 @@ const OPERATING_SYSTEMS = [ clientEncryption: false // TODO(NODE-3401): Unskip when Windows no longer fails to launch mongocryptd occasionally } ].map(osConfig => ({ - mongoVersion: '>=2.6', + mongoVersion: '>=3.6', nodeVersion: LOWEST_LTS, auth: false, // TODO test auth? clientEncryption: true, @@ -484,7 +484,7 @@ OPERATING_SYSTEMS.forEach( name: osName, display_name: osDisplayName, run_on, - mongoVersion = '>=2.6', + mongoVersion = '>=3.6', nodeVersions = NODE_VERSIONS, clientEncryption, msvsVersion diff --git a/src/cmap/wire_protocol/constants.ts b/src/cmap/wire_protocol/constants.ts index f37be8266b..4555bc11f6 100644 --- a/src/cmap/wire_protocol/constants.ts +++ b/src/cmap/wire_protocol/constants.ts @@ -1,7 +1,7 @@ -export const MIN_SUPPORTED_SERVER_VERSION = '2.6'; -export const MAX_SUPPORTED_SERVER_VERSION = '5.0'; -export const MIN_SUPPORTED_WIRE_VERSION = 2; -export const MAX_SUPPORTED_WIRE_VERSION = 13; +export const MIN_SUPPORTED_SERVER_VERSION = '3.6'; +export const MAX_SUPPORTED_SERVER_VERSION = '5.1'; +export const MIN_SUPPORTED_WIRE_VERSION = 6; +export const MAX_SUPPORTED_WIRE_VERSION = 14; export const OP_REPLY = 1; export const OP_UPDATE = 2001; export const OP_INSERT = 2002; diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 4ba93ea82c..82c8004d37 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -90,14 +90,17 @@ export class ListCollectionsOperation extends CommandOperation { return; } - const command = { + return super.executeCommand(server, session, this.generateCommand(), callback); + } + + /* This is here for the purpose of unit testing the final command that gets sent. */ + generateCommand(): Document { + return { listCollections: 1, filter: this.filter, cursor: this.batchSize ? { batchSize: this.batchSize } : {}, nameOnly: this.nameOnly }; - - return super.executeCommand(server, session, command, callback); } } diff --git a/test/functional/buffering_proxy.test.js b/test/functional/buffering_proxy.test.js index 5a54c23bb9..49c749cee8 100644 --- a/test/functional/buffering_proxy.test.js +++ b/test/functional/buffering_proxy.test.js @@ -35,7 +35,7 @@ describe.skip('Buffering Proxy', function () { var electionIds = [new ObjectId(0), new ObjectId(1)]; // Default message fields - var defaultFields = Object.assign({}, mock.DEFAULT_ISMASTER, { + var defaultFields = Object.assign({}, mock.HELLO, { setName: 'rs', setVersion: 1, electionId: electionIds[0], @@ -252,7 +252,7 @@ describe.skip('Buffering Proxy', function () { var electionIds = [new ObjectId(0), new ObjectId(1)]; // Default message fields - var defaultFields = Object.assign({}, mock.DEFAULT_ISMASTER, { + var defaultFields = Object.assign({}, mock.HELLO, { setName: 'rs', setVersion: 1, electionId: electionIds[0], diff --git a/test/functional/change_stream.test.js b/test/functional/change_stream.test.js index 64ede4e61f..a24122850f 100644 --- a/test/functional/change_stream.test.js +++ b/test/functional/change_stream.test.js @@ -2082,7 +2082,7 @@ describe('Change Streams', function () { ismaster() { const uri = this.server.uri(); - return Object.assign({}, mock.DEFAULT_ISMASTER_36, { + return Object.assign({}, mock.HELLO, { ismaster: true, secondary: false, me: uri, @@ -2828,7 +2828,7 @@ context('NODE-2626 - handle null changes without error', function () { mockServer.setMessageHandler(req => { const doc = req.document; if (doc.ismaster || doc.hello) { - return req.reply(mock.DEFAULT_ISMASTER_36); + return req.reply(mock.HELLO); } if (doc.aggregate) { return req.reply({ diff --git a/test/functional/collations.test.js b/test/functional/collations.test.js index fd237d3e79..7fd921315d 100644 --- a/test/functional/collations.test.js +++ b/test/functional/collations.test.js @@ -21,7 +21,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -57,7 +57,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -95,7 +95,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -131,7 +131,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -172,7 +172,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -208,7 +208,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -246,7 +246,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -282,7 +282,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -320,7 +320,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -356,7 +356,7 @@ describe('Collation', function () { test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -393,83 +393,12 @@ describe('Collation', function () { } }); - it('Fail due to no support for collation', { - metadata: { requires: { generators: true, topology: 'single' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 4 })]; - - testContext.server.setMessageHandler(request => { - const doc = request.document; - if (doc.ismaster || doc.hello) { - request.reply(primary[0]); - } else if (doc.find) { - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .collection('test') - .findOne({ a: 1 }, { collation: { caseLevel: true } }) - .then(() => Promise.reject('this test should fail')) - .catch(err => { - expect(err).to.exist; - expect(err.message).to.match(/does not support collation/); - return client.close(); - }); - }); - } - }); - - it('Fail command due to no support for collation', { - metadata: { requires: { generators: true, topology: 'single' } }, - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 4 })]; - - testContext.server.setMessageHandler(request => { - var doc = request.document; - if (doc.ismaster || doc.hello) { - request.reply(primary[0]); - } else if (doc.find) { - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .command({ count: 'test', query: {}, collation: { caseLevel: true } }) - .then(() => Promise.reject('should not succeed')) - .catch(err => { - expect(err).to.exist; - expect(err.message).to.equal( - `Server ${testContext.server.uri()} does not support collation` - ); - - return client.close(); - }); - }); - } - }); - it('Successfully pass through collation to bulkWrite command', { metadata: { requires: { generators: true, topology: 'single' } }, test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -518,113 +447,12 @@ describe('Collation', function () { } }); - it('Successfully fail bulkWrite due to unsupported collation in update', { - metadata: { requires: { generators: true, topology: 'single' } }, - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 4 })]; - - testContext.server.setMessageHandler(request => { - const doc = request.document; - if (doc.ismaster || doc.hello) { - request.reply(primary[0]); - } else if (doc.update) { - request.reply({ ok: 1 }); - } else if (doc.delete) { - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .collection('test') - .bulkWrite( - [ - { - updateOne: { - filter: { a: 2 }, - update: { $set: { a: 2 } }, - upsert: true, - collation: { caseLevel: true } - } - }, - { deleteOne: { filter: { c: 1 } } } - ], - { ordered: true } - ) - .then(() => { - throw new Error('should not succeed'); - }) - .catch(err => { - expect(err).to.exist; - expect(err.message).to.match(/does not support collation/); - }) - .then(() => client.close()); - }); - } - }); - - it('Successfully fail bulkWrite due to unsupported collation in delete', { - metadata: { requires: { generators: true, topology: 'single' } }, - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 4 })]; - - testContext.server.setMessageHandler(request => { - const doc = request.document; - if (doc.ismaster || doc.hello) { - request.reply(primary[0]); - } else if (doc.update) { - request.reply({ ok: 1 }); - } else if (doc.delete) { - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .collection('test') - .bulkWrite( - [ - { - updateOne: { - filter: { a: 2 }, - update: { $set: { a: 2 } }, - upsert: true - } - }, - { deleteOne: { filter: { c: 1 }, collation: { caseLevel: true } } } - ], - { ordered: true } - ) - .then(() => { - throw new Error('should not succeed'); - }) - .catch(err => { - expect(err).to.exist; - expect(err.message).to.match(/does not support collation/); - }) - .then(() => client.close()); - }); - } - }); - it('Successfully create index with collation', { metadata: { requires: { generators: true, topology: 'single' } }, test: function () { const configuration = this.configuration; const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER)]; + const primary = [Object.assign({}, mock.HELLO)]; let commandResult; testContext.server.setMessageHandler(request => { @@ -657,75 +485,6 @@ describe('Collation', function () { } }); - it('Fail to create index with collation due to no capabilities', { - metadata: { requires: { generators: true, topology: 'single' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 4 })]; - - testContext.server.setMessageHandler(request => { - const doc = request.document; - if (doc.ismaster || doc.hello) { - request.reply(primary[0]); - } else if (doc.createIndexes) { - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .collection('test') - .createIndex({ a: 1 }, { collation: { caseLevel: true } }) - .then(() => Promise.reject('should not succeed')) - .catch(err => { - expect(err).to.exist; - expect(err.message).to.match(/does not support collation$/); - }) - .then(() => client.close()); - }); - } - }); - - it('Fail to create indexs with collation due to no capabilities', { - metadata: { requires: { generators: true, topology: 'single' } }, - - test: function () { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testContext.server.uri()}/test`); - const primary = [Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 4 })]; - - testContext.server.setMessageHandler(request => { - const doc = request.document; - if (doc.ismaster || doc.hello) { - request.reply(primary[0]); - } else if (doc.createIndexes) { - request.reply({ ok: 1 }); - } else if (doc.endSessions) { - request.reply({ ok: 1 }); - } - }); - - return client.connect().then(() => { - const db = client.db(configuration.db); - - return db - .collection('test') - .createIndexes([{ key: { a: 1 }, collation: { caseLevel: true } }]) - .then(() => Promise.reject('should not succeed')) - .catch(err => { - expect(err.message).to.match(/does not support collation$/); - return client.close(); - }); - }); - } - }); - it('cursor count method should return the correct number when used with collation set', { metadata: { requires: { mongodb: '>=3.4.0' } }, test: function (done) { diff --git a/test/functional/collection.test.js b/test/functional/collection.test.js index 47183dfffd..938f935b29 100644 --- a/test/functional/collection.test.js +++ b/test/functional/collection.test.js @@ -637,7 +637,7 @@ describe('Collection', function () { } if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } diff --git a/test/functional/command_write_concern.test.js b/test/functional/command_write_concern.test.js index adaab4ee4a..5f412bbe40 100644 --- a/test/functional/command_write_concern.test.js +++ b/test/functional/command_write_concern.test.js @@ -11,7 +11,7 @@ class WriteConcernTest { this.configuration = configuration; this.responseDecoration = {}; const electionIds = [new ObjectId(), new ObjectId()]; - const defaultFields = Object.assign({}, mock.DEFAULT_ISMASTER, { + const defaultFields = Object.assign({}, mock.HELLO, { setName: 'rs', setVersion: 1, electionId: electionIds[0], diff --git a/test/functional/max_staleness.test.js b/test/functional/max_staleness.test.js index 634819ece7..cdbacb800a 100644 --- a/test/functional/max_staleness.test.js +++ b/test/functional/max_staleness.test.js @@ -11,7 +11,7 @@ describe('Max Staleness', function () { return mock.createServer().then(server => { test.server = server; - const defaultFields = Object.assign({}, mock.DEFAULT_ISMASTER, { msg: 'isdbgrid' }); + const defaultFields = Object.assign({}, mock.HELLO, { msg: 'isdbgrid' }); // Primary server states const serverIsMaster = [Object.assign({}, defaultFields)]; diff --git a/test/functional/replicaset_mock.test.js b/test/functional/replicaset_mock.test.js index 8008650f6f..08c0ecce30 100644 --- a/test/functional/replicaset_mock.test.js +++ b/test/functional/replicaset_mock.test.js @@ -9,12 +9,12 @@ describe('ReplSet (mocks)', function () { afterEach(() => mock.cleanup()); beforeEach(() => { // Default message fields - const defaultFields = Object.assign({}, mock.DEFAULT_ISMASTER, { + const defaultFields = Object.assign({}, mock.HELLO, { msg: 'isdbgrid' }); // Default message fields - const defaultRSFields = Object.assign({}, mock.DEFAULT_ISMASTER, { + const defaultRSFields = Object.assign({}, mock.HELLO, { setName: 'rs', setVersion: 1, electionId: new ObjectId(), diff --git a/test/functional/unit-sdam/monitoring.test.js b/test/functional/unit-sdam/monitoring.test.js index 0081c6195d..8bc96aef83 100644 --- a/test/functional/unit-sdam/monitoring.test.js +++ b/test/functional/unit-sdam/monitoring.test.js @@ -26,7 +26,7 @@ describe('monitoring', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } @@ -65,7 +65,7 @@ describe('monitoring', function () { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } @@ -92,7 +92,7 @@ describe('monitoring', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } }); @@ -109,7 +109,7 @@ describe('monitoring', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } }); @@ -127,7 +127,7 @@ describe('monitoring', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - setTimeout(() => request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)), 250); + setTimeout(() => request.reply(Object.assign({}, mock.HELLO)), 250); } }); @@ -177,7 +177,7 @@ describe('monitoring', function () { return; } - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } }); @@ -217,10 +217,7 @@ describe('monitoring', function () { expect(docs[1]).to.have.property('hello', true); done(); } else if (doc.ismaster || doc.hello) { - setTimeout( - () => request.reply(Object.assign({ helloOk: true }, mock.DEFAULT_ISMASTER)), - 250 - ); + setTimeout(() => request.reply(Object.assign({ helloOk: true }, mock.HELLO)), 250); } }); diff --git a/test/functional/unit-sdam/srv_polling.test.js b/test/functional/unit-sdam/srv_polling.test.js index 87ac367fd1..cf1d3520cd 100644 --- a/test/functional/unit-sdam/srv_polling.test.js +++ b/test/functional/unit-sdam/srv_polling.test.js @@ -299,7 +299,7 @@ describe('Mongos SRV Polling', function () { return records.map(r => `${r.name}:${r.port}`); } - const MONGOS_DEFAULT_ISMASTER = Object.assign({}, mock.DEFAULT_ISMASTER_36, { + const MONGOS_HELLO = Object.assign({}, mock.HELLO, { msg: 'isdbgrid' }); @@ -329,7 +329,7 @@ describe('Mongos SRV Polling', function () { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, MONGOS_DEFAULT_ISMASTER)); + request.reply(Object.assign({}, MONGOS_HELLO)); } }); }); diff --git a/test/functional/unit-sdam/topology.test.js b/test/functional/unit-sdam/topology.test.js index 2bef4c7744..b178ba0d18 100644 --- a/test/functional/unit-sdam/topology.test.js +++ b/test/functional/unit-sdam/topology.test.js @@ -41,7 +41,7 @@ describe('Topology (unit)', function () { const doc = request.document; if (doc.ismaster || doc.hello) { ismasters.push(doc); - request.reply(mock.DEFAULT_ISMASTER); + request.reply(mock.HELLO); } else { request.reply({ ok: 1 }); } @@ -110,7 +110,7 @@ describe('Topology (unit)', function () { setTimeout(() => { this.emit( 'descriptionReceived', - new ServerDescription('someserver:27019', { ok: 1, maxWireVersion: 5 }) + new ServerDescription('someserver:27019', { ok: 1, maxWireVersion: 6 }) ); }, 20); }); @@ -130,7 +130,7 @@ describe('Topology (unit)', function () { setTimeout(() => { this.emit( 'descriptionReceived', - new ServerDescription(this.name, { ok: 1, msg: 'isdbgrid', maxWireVersion: 5 }) + new ServerDescription(this.name, { ok: 1, msg: 'isdbgrid', maxWireVersion: 6 }) ); }, 20); }); @@ -153,7 +153,7 @@ describe('Topology (unit)', function () { let initialIsMasterSent = false; if ((doc.ismaster || doc.hello) && !initialIsMasterSent) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); initialIsMasterSent = true; } else { // black hole all other operations @@ -188,7 +188,7 @@ describe('Topology (unit)', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 9 })); + request.reply(Object.assign({}, mock.HELLO, { maxWireVersion: 9 })); } else if (doc.insert) { request.reply({ ok: 0, message: 'node is recovering', code: 11600 }); } else { @@ -225,7 +225,7 @@ describe('Topology (unit)', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 9 })); + request.reply(Object.assign({}, mock.HELLO, { maxWireVersion: 9 })); } else if (doc.insert) { request.reply({ ok: 0, message: 'not master' }); } else { @@ -262,7 +262,7 @@ describe('Topology (unit)', function () { mockServer.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER, { maxWireVersion: 9 })); + request.reply(Object.assign({}, mock.HELLO, { maxWireVersion: 9 })); } else if (doc.insert) { request.connection.destroy(); } else { diff --git a/test/functional/unit-sessions/client.test.js b/test/functional/unit-sessions/client.test.js index 4844f25d6e..045843b8f5 100644 --- a/test/functional/unit-sessions/client.test.js +++ b/test/functional/unit-sessions/client.test.js @@ -20,7 +20,7 @@ describe('Sessions - client/unit', function () { test.server.setMessageHandler(request => { var doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } @@ -116,7 +116,7 @@ describe('Sessions - client/unit', function () { var doc = request.document; if (doc.ismaster || doc.hello) { request.reply( - Object.assign({}, mock.DEFAULT_ISMASTER, { + Object.assign({}, mock.HELLO, { logicalSessionTimeoutMinutes: 10 }) ); diff --git a/test/functional/unit-sessions/collection.test.js b/test/functional/unit-sessions/collection.test.js index 3070f67b6e..7ab7b3715b 100644 --- a/test/functional/unit-sessions/collection.test.js +++ b/test/functional/unit-sessions/collection.test.js @@ -22,9 +22,7 @@ describe('Sessions - unit/sessions', function () { test.server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply( - Object.assign({ logicalSessionTimeoutMinutes: 15 }, mock.DEFAULT_ISMASTER_36) - ); + request.reply(Object.assign({ logicalSessionTimeoutMinutes: 15 }, mock.HELLO)); } else if (doc.insert) { request.reply({ ok: 1, operationTime: insertOperationTime }); } else if (doc.find) { @@ -62,7 +60,7 @@ describe('Sessions - unit/sessions', function () { test.server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } else if (doc.count || doc.aggregate || doc.endSessions) { request.reply({ ok: 1 }); } diff --git a/test/functional/unit_bypass_validation.test.js b/test/functional/unit_bypass_validation.test.js index 738c72ea0e..e6e2b5e8f9 100644 --- a/test/functional/unit_bypass_validation.test.js +++ b/test/functional/unit_bypass_validation.test.js @@ -29,7 +29,7 @@ describe('bypass document validation', function () { ok: 1, cursor: { firstBatch: [{}], - id: 23, + id: 0, ns: 'test.test' } }); @@ -39,7 +39,7 @@ describe('bypass document validation', function () { } if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } @@ -92,7 +92,7 @@ describe('bypass document validation', function () { } if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } @@ -149,7 +149,7 @@ describe('bypass document validation', function () { } if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } @@ -198,7 +198,7 @@ describe('bypass document validation', function () { } if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } diff --git a/test/functional/unit_create_index_error.test.js b/test/functional/unit_create_index_error.test.js index 25db992c26..5939eb9198 100644 --- a/test/functional/unit_create_index_error.test.js +++ b/test/functional/unit_create_index_error.test.js @@ -20,7 +20,7 @@ describe('CreateIndexError', function () { const doc = request.document; if (doc.ismaster || doc.hello) { - return request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + return request.reply(Object.assign({}, mock.HELLO)); } if (doc.createIndexes) { diff --git a/test/functional/unit_db_list_collections.test.js b/test/functional/unit_db_list_collections.test.js deleted file mode 100644 index 9baa658ad4..0000000000 --- a/test/functional/unit_db_list_collections.test.js +++ /dev/null @@ -1,76 +0,0 @@ -'use strict'; - -const mock = require('../tools/mock'); -const expect = require('chai').expect; - -describe('db.listCollections', function () { - const testHarness = {}; - afterEach(() => mock.cleanup()); - beforeEach(() => { - return mock.createServer().then(server => { - server.setMessageHandler(request => { - const doc = request.document; - - if (doc.ismaster || doc.hello) { - return request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); - } - - if (doc.listCollections) { - return request.reply({ - ok: 1, - cursor: { - id: 0, - ns: 'test.$cmd.listCollections', - firstBatch: [{ name: 'test', type: 'collection' }] - } - }); - } - }); - testHarness.server = server; - }); - }); - - [ - { - description: 'should always send nameOnly option, defaulting to false', - command: db => db.listCollections().toArray(() => {}), - listCollectionsValue: false - }, - { - description: 'should propagate the nameOnly option', - command: db => db.listCollections({}, { nameOnly: true }).toArray(() => {}), - listCollectionsValue: true - }, - { - description: 'should send nameOnly: true for db.collections', - command: db => db.collections(() => {}), - listCollectionsValue: true - } - ].forEach(config => { - function testFn(done) { - const configuration = this.configuration; - const client = configuration.newClient(`mongodb://${testHarness.server.uri()}/test`, { - monitorCommands: true - }); - - client.connect((err, client) => { - const db = client.db('foo'); - - client.on('commandStarted', e => { - if (e.commandName === 'listCollections') { - try { - expect(e).to.have.nested.property('command.nameOnly', config.listCollectionsValue); - client.close(done); - } catch (err) { - client.close(() => done(err)); - } - } - }); - - config.command(db); - }); - } - - it(config.description, { test: testFn, metadata: { requires: { mongodb: '>=2.7.6' } } }); - }); -}); diff --git a/test/functional/view.test.js b/test/functional/view.test.js index d29899e3a4..c907c2ac87 100644 --- a/test/functional/view.test.js +++ b/test/functional/view.test.js @@ -13,7 +13,7 @@ describe('Views', function () { const configuration = this.configuration; // Default message fields - var defaultFields = Object.assign({}, mock.DEFAULT_ISMASTER); + var defaultFields = Object.assign({}, mock.HELLO); // Primary server states var primary = [Object.assign({}, defaultFields)]; diff --git a/test/functional/write_concern.test.js b/test/functional/write_concern.test.js index 91a12d5c5e..260ebc3794 100644 --- a/test/functional/write_concern.test.js +++ b/test/functional/write_concern.test.js @@ -99,7 +99,7 @@ describe('Write Concern', function () { it('should pipe writeConcern from client down to API call', function () { server.setMessageHandler(request => { if (request.document && request.document.ismaster) { - return request.reply(mock.DEFAULT_ISMASTER); + return request.reply(mock.HELLO); } expect(request.document.writeConcern).to.exist; expect(request.document.writeConcern.w).to.equal('majority'); diff --git a/test/spec/server-discovery-and-monitoring/README.rst b/test/spec/server-discovery-and-monitoring/README.rst index e520b31722..7dcaaa8fa5 100644 --- a/test/spec/server-discovery-and-monitoring/README.rst +++ b/test/spec/server-discovery-and-monitoring/README.rst @@ -2,10 +2,17 @@ Server Discovery And Monitoring Tests ===================================== +.. contents:: + +---- + The YAML and JSON files in this directory tree are platform-independent tests that drivers can use to prove their conformance to the Server Discovery And Monitoring Spec. +Additional prose tests, that cannot be represented as spec tests, are +described and MUST be implemented. + Version ------- @@ -23,22 +30,52 @@ Each YAML file has the following keys: A phase of the test optionally sends inputs to the client, then tests the client's resulting TopologyDescription. -Each phase object has two keys: +Each phase object has the following keys: +- description: (optional) A textual description of this phase. - responses: (optional) An array of "response" objects. If not provided, the test runner should construct the client and perform assertions specified in the outcome object without processing any responses. +- applicationErrors: (optional) An array of "applicationError" objects. - outcome: An "outcome" object representing the TopologyDescription. A response is a pair of values: - The source, for example "a:27017". - This is the address the client sent the "ismaster" command to. -- An ismaster response, for example `{ok: 1, ismaster: true}`. + This is the address the client sent the "hello" or legacy hello command to. +- A hello or legacy hello response, for example ``{ok: 1, helloOk: true, isWritablePrimary: true}``. If the response includes an electionId it is shown in extended JSON like - `{"$oid": "000000000000000000000002"}`. + ``{"$oid": "000000000000000000000002"}``. The empty response `{}` indicates a network error - when attempting to call "ismaster". + when attempting to call "hello" or legacy hello. + +An "applicationError" object has the following keys: + +- address: The source address, for example "a:27017". +- generation: (optional) The error's generation number, for example ``1``. + When absent this value defaults to the pool's current generation number. +- maxWireVersion: The ``maxWireVersion`` of the connection the error occurs + on, for example ``9``. Added to support testing the behavior of "not writable primary" + errors on <4.2 and >=4.2 servers. +- when: A string describing when this mock error should occur. Supported + values are: + + - "beforeHandshakeCompletes": Simulate this mock error as if it occurred + during a new connection's handshake for an application operation. + - "afterHandshakeCompletes": Simulate this mock error as if it occurred + on an established connection for an application operation (i.e. after + the connection pool check out succeeds). + +- type: The type of error to mock. Supported values are: + + - "command": A command error. Always accompanied with a "response". + - "network": A non-timeout network error. + - "timeout": A network timeout error. + +- response: (optional) A command error response, for example + ``{ok: 0, errmsg: "not primary"}``. Present if and only if ``type`` is + "command". Note the server only returns "not primary" if the "hello" command + has been run on this connection. Otherwise the legacy error message is returned. In non-monitoring tests, an "outcome" represents the correct TopologyDescription that results from processing the responses in the phases @@ -56,21 +93,28 @@ so far. It has the following keys: A "server" object represents a correct ServerDescription within the client's current TopologyDescription. It has the following keys: -- type: A ServerType name, like "RSSecondary". +- type: A ServerType name, like "RSSecondary". See `ServerType <../server-discovery-and-monitoring.rst#servertype>`_ for details pertaining to async and multi-threaded drivers. - setName: A string with the expected replica set name, or null. - setVersion: absent or an integer. - electionId: absent, null, or an ObjectId. - logicalSessionTimeoutMinutes: absent, null, or an integer. - minWireVersion: absent or an integer. - maxWireVersion: absent or an integer. +- topologyVersion: absent, null, or a topologyVersion document. +- pool: (optional) A "pool" object. + +A "pool" object represents a correct connection pool for a given server. +It has the following keys: + +- generation: This server's expected pool generation, like ``0``. In monitoring tests, an "outcome" contains a list of SDAM events that should -have been published by the client as a result of processing ismaster responses -in the current phase. Any SDAM events published by the client during its +have been published by the client as a result of processing hello or legacy hello +responses in the current phase. Any SDAM events published by the client during its construction (that is, prior to processing any of the responses) should be -combined with the events published during processing of ismaster responses -of the first phase of the test. A test MAY explicitly verify events published -during client construction by providing an empty responses array for the +combined with the events published during processing of hello or legacy hello +responses of the first phase of the test. A test MAY explicitly verify events +published during client construction by providing an empty responses array for the first phase. @@ -80,12 +124,12 @@ Use as unittests Mocking ~~~~~~~ -Drivers should be able to test their server discovery and monitoring logic -without any network I/O, by parsing ismaster responses from the test file -and passing them into the driver code. Parts of the client and monitoring -code may need to be mocked or subclassed to achieve this. `A reference -implementation for PyMongo 3.x is available here -`_. +Drivers should be able to test their server discovery and monitoring logic without +any network I/O, by parsing hello (or legacy hello) and application error from the +test file and passing them into the driver code. Parts of the client and +monitoring code may need to be mocked or subclassed to achieve this. +`A reference implementation for PyMongo 3.10.1 is available here +`_. Initialization ~~~~~~~~~~~~~~ @@ -113,9 +157,16 @@ events published during client construction. Test Phases ~~~~~~~~~~~ -For each phase in the file, parse the "responses" array. -Pass in the responses in order to the driver code. -If a response is the empty object `{}`, simulate a network error. +For each phase in the file: + +#. Parse the "responses" array. Pass in the responses in order to the driver + code. If a response is the empty object ``{}``, simulate a network error. + +#. Parse the "applicationErrors" array. For each element, simulate the given + error as if it occurred while running an application operation. Note that + it is sufficient to construct a mock error and call the procedure which + updates the topology, e.g. + ``topology.handleApplicationError(address, generation, maxWireVersion, error)``. For non-monitoring tests, once all responses are processed, assert that the phase's "outcome" object @@ -125,11 +176,277 @@ For monitoring tests, once all responses are processed, assert that the events collected so far by the SDAM event listener are equivalent to the events specified in the phase. -Some fields such as "logicalSessionTimeoutMinutes" or "compatible" were added -later and haven't been added to all test files. If these fields are present, -test that they are equivalent to the fields of the driver's current -TopologyDescription. +Some fields such as "logicalSessionTimeoutMinutes", "compatible", and +"topologyVersion" were added later and haven't been added to all test files. +If these fields are present, test that they are equivalent to the fields of +the driver's current TopologyDescription or ServerDescription. For monitoring tests, clear the list of events collected so far. Continue until all phases have been executed. + +Integration Tests +----------------- + +Integration tests are provided in the "integration" directory. + +Test Format +~~~~~~~~~~~ + +The same as the `Transactions Spec Test format +`_ with the following +additions: + +- The ``runOn`` requirement gains a new field: + + - ``authEnabled`` (optional): If True, skip this test if auth is not enabled. + If False, skip this test if auth is enabled. If this field is omitted, + this test can be run on clusters with or without auth. + +Special Test Operations +~~~~~~~~~~~~~~~~~~~~~~~ + +Certain operations that appear in the "operations" array do not correspond to +API methods but instead represent special test operations. Such operations are +defined on the "testRunner" object and are documented in the +`Transactions Spec Test +`_. + +Additional, SDAM test specific operations are documented here: + +configureFailPoint +'''''''''''''''''' + +The "configureFailPoint" operation instructs the test runner to configure +the given server failpoint on the "admin" database. The runner MUST disable +this failpoint at the end of the test. For example:: + + - name: configureFailPoint + object: testRunner + arguments: + failPoint: + configureFailPoint: failCommand + mode: { times: 1 } + data: + failCommands: ["insert"] + closeConnection: true + +Tests that use the "configureFailPoint" operation do not include +``configureFailPoint`` commands in their command expectations. Drivers MUST +ensure that ``configureFailPoint`` commands do not appear in the list of logged +commands, either by manually filtering it from the list of observed commands or +by using a different MongoClient to execute ``configureFailPoint``. + +Note, similar to the ``tests.failPoint`` field described in the `Transactions +Spec Test format `_ tests +with ``useMultipleMongoses: true`` will not contain a ``configureFailPoint`` +operation. + +wait +'''' + +The "wait" operation instructs the test runner to sleep for "ms" +milliseconds. For example:: + + - name: wait + object: testRunner + arguments: + ms: 1000 + +waitForEvent +'''''''''''' + +The "waitForEvent" operation instructs the test runner to wait until the test's +MongoClient has published a specific event a given number of times. For +example, the following instructs the test runner to wait for at least one +PoolClearedEvent to be published:: + + - name: waitForEvent + object: testRunner + arguments: + event: PoolClearedEvent + count: 1 + +Note that "count" includes events that were published while running previous +operations. + +If the "waitForEvent" operation is not satisfied after 10 seconds, the +operation is considered an error. + +ServerMarkedUnknownEvent +```````````````````````` + +The ServerMarkedUnknownEvent may appear as an event in `waitForEvent`_ and +`assertEventCount`_. This event is defined as ServerDescriptionChangedEvent +where newDescription.type is ``Unknown``. + +assertEventCount +'''''''''''''''' + +The "assertEventCount" operation instructs the test runner to assert the test's +MongoClient has published a specific event a given number of times. For +example, the following instructs the test runner to assert that a single +PoolClearedEvent was published:: + + - name: assertEventCount + object: testRunner + arguments: + event: PoolClearedEvent + count: 1 + +recordPrimary +''''''''''''' + +The "recordPrimary" operation instructs the test runner to record the current +primary of the test's MongoClient. For example:: + + - name: recordPrimary + object: testRunner + +runAdminCommand +''''''''''''''' + +The "runAdminCommand" operation instructs the test runner to run the given +command on the admin database. Drivers MUST run this command on a different +MongoClient from the one used for test operations. For example:: + + - name: runAdminCommand + object: testRunner + command_name: replSetFreeze + arguments: + command: + replSetFreeze: 0 + readPreference: + mode: Secondary + +waitForPrimaryChange +'''''''''''''''''''' + +The "waitForPrimaryChange" operation instructs the test runner to wait up to +"timeoutMS" milliseconds for the MongoClient to discover a new primary server. +The new primary should be different from the one recorded by "recordPrimary". +For example:: + + - name: waitForPrimaryChange + object: testRunner + arguments: + timeoutMS: 15000 + +To implement, Drivers can subscribe to ServerDescriptionChangedEvents and wait +for an event where newDescription.type is ``RSPrimary`` and the address is +different from the one previously recorded by "recordPrimary". + +startThread +''''''''''' + +The "startThread" operation instructs the test runner to start a new thread +with the provided "name". The `runOnThread`_ and `waitForThread`_ operations +reference a thread by its "name". For example:: + + - name: startThread + object: testRunner + arguments: + name: thread1 + +runOnThread +''''''''''' + +The "runOnThread" operation instructs the test runner to schedule an operation +to be run on the given thread. runOnThread MUST NOT wait for the scheduled +operation to complete. For example:: + + - name: runOnThread + object: testRunner + arguments: + name: thread1 + operation: + name: insertOne + object: collection + arguments: + document: + _id: 2 + error: true + +waitForThread +''''''''''''' + +The "waitForThread" operation instructs the test runner to stop the given +thread, wait for it to complete, and assert that the thread exited without +any errors. For example:: + + - name: waitForThread + object: testRunner + arguments: + name: thread1 + +Prose Tests +----------- + +The following prose tests cannot be represented as spec tests and MUST be +implemented. + +Streaming protocol Tests +~~~~~~~~~~~~~~~~~~~~~~~~ + +Drivers that implement the streaming protocol (multi-threaded or +asynchronous drivers) must implement the following tests. Each test should be +run against a standalone, replica set, and sharded cluster unless otherwise +noted. + +Some of these cases should already be tested with the old protocol; in +that case just verify the test cases succeed with the new protocol. + +1. Configure the client with heartbeatFrequencyMS set to 500, + overriding the default of 10000. Assert the client processes + hello and legacy hello replies more frequently (approximately every 500ms). + +RTT Tests +~~~~~~~~~ + +Run the following test(s) on MongoDB 4.4+. + +1. Test that RTT is continuously updated. + + #. Create a client with ``heartbeatFrequencyMS=500``, + ``appName=streamingRttTest``, and subscribe to server events. + + #. Run a find command to wait for the server to be discovered. + + #. Sleep for 2 seconds. This must be long enough for multiple heartbeats + to succeed. + + #. Assert that each ``ServerDescriptionChangedEvent`` includes a non-zero + RTT. + + #. Configure the following failpoint to block hello or legacy hello commands + for 250ms which should add extra latency to each RTT check:: + + db.adminCommand({ + configureFailPoint: "failCommand", + mode: {times: 1000}, + data: { + failCommands: ["hello"], // or the legacy hello command + blockConnection: true, + blockTimeMS: 500, + appName: "streamingRttTest", + }, + }); + + #. Wait for the server's RTT to exceed 250ms. Eventually the average RTT + should also exceed 500ms but we use 250ms to speed up the test. Note + that the `Server Description Equality`_ rule means that + ServerDescriptionChangedEvents will not be published. This test may + need to use a driver specific helper to obtain the latest RTT instead. + If the RTT does not exceed 250ms after 10 seconds, consider the test + failed. + + #. Disable the failpoint:: + + db.adminCommand({ + configureFailPoint: "failCommand", + mode: "off", + }); + +.. Section for links. + +.. _Server Description Equality: /source/server-discovery-and-monitoring/server-discovery-and-monitoring.rst#server-description-equality diff --git a/test/spec/server-discovery-and-monitoring/load-balanced/discover_load_balancer.yml b/test/spec/server-discovery-and-monitoring/load-balanced/discover_load_balancer.yml index 549f4c7d8e..20083d98be 100644 --- a/test/spec/server-discovery-and-monitoring/load-balanced/discover_load_balancer.yml +++ b/test/spec/server-discovery-and-monitoring/load-balanced/discover_load_balancer.yml @@ -1,6 +1,9 @@ description: "Load balancer can be discovered and only has the address property set" + uri: "mongodb://a/?loadBalanced=true" + phases: + # There should be no monitoring in LoadBalanced mode, so no responses are necessary to get the topology into the # correct state. - outcome: diff --git a/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.json b/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.json index caf31b3391..dd8f7fc51e 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.json @@ -11,7 +11,7 @@ "helloOk": true, "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ] ], diff --git a/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.yml b/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.yml index 0ad4462b9c..5d808f2600 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/discovered_standalone.yml @@ -5,7 +5,7 @@ phases: responses: - - "a:27017" - - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 4 } + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 6 } outcome: events: diff --git a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.json b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.json index 768aa7a3e1..950e32efe1 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.json @@ -19,7 +19,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ] ], diff --git a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.yml b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.yml index 8a49869f60..ce9cdf46e6 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_no_primary.yml @@ -1,11 +1,11 @@ description: "Monitoring a topology that is a replica set with no primary connected" uri: "mongodb://a,b" -phases: - - - responses: - - +phases: + - + responses: + - - "a:27017" - - + - ok: 1 helloOk: true isWritablePrimary: false @@ -13,101 +13,100 @@ phases: setName: "rs" setVersion: 1 primary: "b:27017" - hosts: + hosts: - "a:27017" - "b:27017" minWireVersion: 0 - maxWireVersion: 4 - outcome: - events: - - - topology_opening_event: + maxWireVersion: 6 + outcome: + events: + - + topology_opening_event: topologyId: "42" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" servers: [] - newDescription: + newDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "a:27017" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "b:27017" - - - server_description_changed_event: + - + server_description_changed_event: topologyId: "42" address: "a:27017" - previousDescription: + previousDescription: address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "b:27017" setName: "rs" type: "RSSecondary" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: topologyType: "ReplicaSetNoPrimary" setName: "rs" - servers: - - + servers: + - address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "b:27017" setName: "rs" type: "RSSecondary" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "PossiblePrimary" - diff --git a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.json b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.json index da66403541..2ad94d6e6a 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.json @@ -18,7 +18,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ] ], diff --git a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.yml b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.yml index 016240f49d..2c78919063 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_primary.yml @@ -1,112 +1,111 @@ description: "Monitoring a topology that is a replica set with a primary connected" uri: "mongodb://a,b" -phases: - - - responses: - - +phases: + - + responses: + - - "a:27017" - - + - ok: 1 helloOk: true isWritablePrimary: true setName: "rs" setVersion: 1 primary: "a:27017" - hosts: + hosts: - "a:27017" - "b:27017" minWireVersion: 0 - maxWireVersion: 4 - outcome: - events: - - - topology_opening_event: + maxWireVersion: 6 + outcome: + events: + - + topology_opening_event: topologyId: "42" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" servers: [] - newDescription: + newDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "a:27017" - - - server_opening_event: + - + server_opening_event: topologyId: "42" address: "b:27017" - - - server_description_changed_event: + - + server_description_changed_event: topologyId: "42" address: "a:27017" - previousDescription: + previousDescription: address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "a:27017" setName: "rs" type: "RSPrimary" - - - topology_description_changed_event: + - + topology_description_changed_event: topologyId: "42" - previousDescription: + previousDescription: topologyType: "Unknown" - servers: - - + servers: + - address: "a:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - newDescription: + newDescription: topologyType: "ReplicaSetWithPrimary" setName: "rs" - servers: - - + servers: + - address: "a:27017" arbiters: [] - hosts: + hosts: - "a:27017" - "b:27017" passives: [] primary: "a:27017" setName: "rs" type: "RSPrimary" - - + - address: "b:27017" arbiters: [] hosts: [] passives: [] type: "Unknown" - diff --git a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.json b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.json index 16941021a3..ae28faa30c 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.json @@ -69,7 +69,7 @@ "a:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ], [ diff --git a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.yml b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.yml index e2277abd32..2471f47680 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/replica_set_with_removal.yml @@ -50,7 +50,7 @@ phases: primary: "a:27017", hosts: [ "a:27017" ], minWireVersion: 0, - maxWireVersion: 4 + maxWireVersion: 6 } - - "b:27017" diff --git a/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.json b/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.json index 1d5237058f..401c5d99c5 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.json @@ -18,7 +18,7 @@ "b:27017" ], "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ] ], diff --git a/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.yml b/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.yml index 4463967668..7a060128f3 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/required_replica_set.yml @@ -14,7 +14,7 @@ phases: primary: "a:27017", hosts: [ "a:27017", "b:27017" ], minWireVersion: 0, - maxWireVersion: 4 + maxWireVersion: 6 } outcome: events: diff --git a/test/spec/server-discovery-and-monitoring/monitoring/standalone.json b/test/spec/server-discovery-and-monitoring/monitoring/standalone.json index aeb8aaed35..821a1525d4 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/standalone.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/standalone.json @@ -11,7 +11,7 @@ "helloOk": true, "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ] ], diff --git a/test/spec/server-discovery-and-monitoring/monitoring/standalone.yml b/test/spec/server-discovery-and-monitoring/monitoring/standalone.yml index 780a032a13..d9f6bcfaf3 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/standalone.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/standalone.yml @@ -5,7 +5,7 @@ phases: responses: - - "a:27017" - - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 4 } + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 6 } outcome: events: diff --git a/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.json b/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.json index d8cd00c4ea..5958e2d26c 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.json +++ b/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.json @@ -11,7 +11,7 @@ "helloOk": true, "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ], [ @@ -21,7 +21,7 @@ "helloOk": true, "isWritablePrimary": true, "minWireVersion": 0, - "maxWireVersion": 4 + "maxWireVersion": 6 } ] ], diff --git a/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.yml b/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.yml index 00c2e770b9..f2f83ffbac 100644 --- a/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.yml +++ b/test/spec/server-discovery-and-monitoring/monitoring/standalone_suppress_equal_description_changes.yml @@ -5,10 +5,10 @@ phases: responses: - - "a:27017" - - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 4 } + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 6 } - - "a:27017" - - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 4 } + - { ok: 1, helloOk: true, isWritablePrimary: true, minWireVersion: 0, maxWireVersion: 6 } outcome: events: diff --git a/test/tools/common.js b/test/tools/common.js index 64c4d14ec0..1c15c63ed0 100644 --- a/test/tools/common.js +++ b/test/tools/common.js @@ -16,7 +16,7 @@ class ReplSetFixture { setup(options) { options = options || {}; - const ismaster = options.ismaster ? options.ismaster : mock.DEFAULT_ISMASTER_36; + const ismaster = options.ismaster ? options.ismaster : mock.HELLO; return Promise.all([ mock.createServer(), @@ -110,19 +110,6 @@ class ReplSetFixture { } } -class MongosFixture { - setup(options) { - options = options || {}; - const ismaster = options.ismaster ? options.ismaster : mock.DEFAULT_ISMASTER; - return Promise.all([mock.createServer(), mock.createServer()]).then(servers => { - this.servers = servers; - this.defaultFields = Object.assign({}, ismaster, { - msg: 'isdbgrid' - }); - }); - } -} - /** * Creates a cluster time for use in unit testing cluster time gossiping and * causal consistency. @@ -160,7 +147,6 @@ function sessionCleanupHandler(session, sessionPool, done) { module.exports = { ReplSetFixture: ReplSetFixture, - MongosFixture: MongosFixture, genClusterTime: genClusterTime, sessionCleanupHandler }; diff --git a/test/tools/mock.js b/test/tools/mock.js index 0db171308a..72dede7826 100644 --- a/test/tools/mock.js +++ b/test/tools/mock.js @@ -3,7 +3,6 @@ const { createServer: superCreateServer, cleanup, - DEFAULT_ISMASTER, DEFAULT_ISMASTER_36 // eslint-disable-next-line no-restricted-modules } = require('mongodb-mock-server'); @@ -70,6 +69,5 @@ function createServer(port, host, options) { module.exports = { createServer, cleanup, - DEFAULT_ISMASTER, - DEFAULT_ISMASTER_36 + HELLO: DEFAULT_ISMASTER_36 }; diff --git a/test/unit/_MISC_scram_iterations.test.js b/test/unit/_MISC_scram_iterations.test.js index 87745707da..a0d83b2971 100644 --- a/test/unit/_MISC_scram_iterations.test.js +++ b/test/unit/_MISC_scram_iterations.test.js @@ -35,7 +35,7 @@ describe('SCRAM Iterations Tests', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - return request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + return request.reply(Object.assign({}, mock.HELLO)); } else if (doc.saslStart) { return request.reply({ ok: 1, @@ -81,7 +81,7 @@ describe('SCRAM Iterations Tests', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - return request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + return request.reply(Object.assign({}, mock.HELLO)); } else if (doc.saslStart) { return request.reply({ ok: 1, @@ -128,7 +128,7 @@ describe('SCRAM Iterations Tests', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - return request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + return request.reply(Object.assign({}, mock.HELLO)); } else if (doc.saslStart) { return request.reply({ ok: 1, diff --git a/test/unit/cmap/connect.test.js b/test/unit/cmap/connect.test.js index 272d8348d0..e310a152b6 100644 --- a/test/unit/cmap/connect.test.js +++ b/test/unit/cmap/connect.test.js @@ -38,7 +38,7 @@ describe('Connect Tests', function () { if (doc.ismaster || doc.hello) { whatHappened.ismaster = true; request.reply( - Object.assign({}, mock.DEFAULT_ISMASTER, { + Object.assign({}, mock.HELLO, { $clusterTime }) ); @@ -68,7 +68,7 @@ describe('Connect Tests', function () { if (doc.ismaster || doc.hello) { whatHappened.ismaster = true; request.reply( - Object.assign({}, mock.DEFAULT_ISMASTER, { + Object.assign({}, mock.HELLO, { $clusterTime, arbiterOnly: true }) diff --git a/test/unit/cmap/connection.test.js b/test/unit/cmap/connection.test.js index 414af516b5..a18bef7761 100644 --- a/test/unit/cmap/connection.test.js +++ b/test/unit/cmap/connection.test.js @@ -17,7 +17,7 @@ describe('Connection - unit/cmap', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } // blackhole all other requests @@ -40,7 +40,7 @@ describe('Connection - unit/cmap', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } // blackhole all other requests @@ -65,7 +65,7 @@ describe('Connection - unit/cmap', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } // respond to no other requests to trigger timeout event }); diff --git a/test/unit/cmap/connection_pool.test.js b/test/unit/cmap/connection_pool.test.js index 1910aba932..37d32a6ae4 100644 --- a/test/unit/cmap/connection_pool.test.js +++ b/test/unit/cmap/connection_pool.test.js @@ -45,7 +45,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } else { // destroy on any other command request.connection.destroy(); @@ -88,7 +88,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } else { // blackhole other requests } @@ -118,7 +118,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } }); @@ -154,7 +154,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } }); @@ -209,7 +209,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } }); @@ -235,7 +235,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } }); @@ -407,7 +407,7 @@ describe('Connection Pool', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(mock.DEFAULT_ISMASTER_36); + request.reply(mock.HELLO); } }); }); diff --git a/test/unit/cmap/wire_protocol/constants.test.js b/test/unit/cmap/wire_protocol/constants.test.js new file mode 100644 index 0000000000..38503c6fc9 --- /dev/null +++ b/test/unit/cmap/wire_protocol/constants.test.js @@ -0,0 +1,33 @@ +const { expect } = require('chai'); +const { + MIN_SUPPORTED_SERVER_VERSION, + MAX_SUPPORTED_SERVER_VERSION, + MIN_SUPPORTED_WIRE_VERSION, + MAX_SUPPORTED_WIRE_VERSION +} = require('../../../../src/cmap/wire_protocol/constants'); + +describe('Wire Protocol Constants', function () { + describe('MIN_SUPPORTED_SERVER_VERSION', function () { + it('returns 3.6', function () { + expect(MIN_SUPPORTED_SERVER_VERSION).to.equal('3.6'); + }); + }); + + describe('MAX_SUPPORTED_SERVER_VERSION', function () { + it('returns 5.1', function () { + expect(MAX_SUPPORTED_SERVER_VERSION).to.equal('5.1'); + }); + }); + + describe('MIN_SUPPORTED_WIRE_VERSION', function () { + it('returns 6', function () { + expect(MIN_SUPPORTED_WIRE_VERSION).to.equal(6); + }); + }); + + describe('MAX_SUPPORTED_WIRE_VERSION', function () { + it('returns 14', function () { + expect(MAX_SUPPORTED_WIRE_VERSION).to.equal(14); + }); + }); +}); diff --git a/test/unit/cursor/find_cursor.test.js b/test/unit/cursor/find_cursor.test.js index 406375464c..9bb1c2cdfb 100644 --- a/test/unit/cursor/find_cursor.test.js +++ b/test/unit/cursor/find_cursor.test.js @@ -31,7 +31,7 @@ describe('Find Cursor', function () { const doc = request.document; if (doc.ismaster || doc.hello) { request.reply( - Object.assign({}, mock.DEFAULT_ISMASTER, { + Object.assign({}, mock.HELLO, { maxWireVersion: 6 }) ); diff --git a/test/unit/operations/list_collections.test.js b/test/unit/operations/list_collections.test.js new file mode 100644 index 0000000000..541edc37f7 --- /dev/null +++ b/test/unit/operations/list_collections.test.js @@ -0,0 +1,79 @@ +'use strict'; + +const { expect } = require('chai'); +const { ListCollectionsOperation } = require('../../../src/operations/list_collections'); + +describe('ListCollectionsOperation', function () { + const db = 'test'; + + describe('#constructor', function () { + context('when nameOnly is provided', function () { + context('when nameOnly is true', function () { + const operation = new ListCollectionsOperation(db, {}, { nameOnly: true, dbName: db }); + + it('sets nameOnly to true', function () { + expect(operation.nameOnly).to.be.true; + }); + }); + + context('when nameOnly is false', function () { + const operation = new ListCollectionsOperation(db, {}, { nameOnly: false, dbName: db }); + + it('sets nameOnly to false', function () { + expect(operation.nameOnly).to.be.false; + }); + }); + }); + + context('when nameOnly is not provided', function () { + const operation = new ListCollectionsOperation(db, {}, { dbName: db }); + + it('sets nameOnly to false', function () { + expect(operation.nameOnly).to.be.false; + }); + }); + }); + + describe('#generateCommand', function () { + context('when nameOnly is provided', function () { + context('when nameOnly is true', function () { + const operation = new ListCollectionsOperation(db, {}, { nameOnly: true, dbName: db }); + + it('sets nameOnly to true', function () { + expect(operation.generateCommand()).to.deep.equal({ + listCollections: 1, + cursor: {}, + filter: {}, + nameOnly: true + }); + }); + }); + + context('when nameOnly is false', function () { + const operation = new ListCollectionsOperation(db, {}, { nameOnly: false, dbName: db }); + + it('sets nameOnly to false', function () { + expect(operation.generateCommand()).to.deep.equal({ + listCollections: 1, + cursor: {}, + filter: {}, + nameOnly: false + }); + }); + }); + }); + + context('when nameOnly is not provided', function () { + const operation = new ListCollectionsOperation(db, {}, { dbName: db }); + + it('sets nameOnly to false', function () { + expect(operation.generateCommand()).to.deep.equal({ + listCollections: 1, + cursor: {}, + filter: {}, + nameOnly: false + }); + }); + }); + }); +}); diff --git a/test/unit/sessions.test.js b/test/unit/sessions.test.js index 1b556c64c3..1ad37e5455 100644 --- a/test/unit/sessions.test.js +++ b/test/unit/sessions.test.js @@ -226,9 +226,7 @@ describe('Sessions - unit/core', function () { test.server.setMessageHandler(request => { var doc = request.document; if (doc.ismaster || doc.hello) { - request.reply( - Object.assign({}, mock.DEFAULT_ISMASTER, { logicalSessionTimeoutMinutes: 10 }) - ); + request.reply(Object.assign({}, mock.HELLO, { logicalSessionTimeoutMinutes: 10 })); } }); }) diff --git a/test/unit/unit_bulk_write.test.js b/test/unit/unit_bulk_write.test.js index 23eae36273..21f800af07 100644 --- a/test/unit/unit_bulk_write.test.js +++ b/test/unit/unit_bulk_write.test.js @@ -34,7 +34,7 @@ describe('Bulk Writes', function () { test.server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } else if (doc.insert) { diff --git a/test/unit/unit_client.test.js b/test/unit/unit_client.test.js index 14c034d8fa..5432f75f76 100644 --- a/test/unit/unit_client.test.js +++ b/test/unit/unit_client.test.js @@ -21,7 +21,7 @@ describe('Client (unit)', function () { const doc = request.document; if (doc.ismaster || doc.hello) { handshake = doc; - request.reply(Object.assign({}, mock.DEFAULT_ISMASTER)); + request.reply(Object.assign({}, mock.HELLO)); } else if (doc.endSessions) { request.reply({ ok: 1 }); } diff --git a/test/unit/unit_snappy.test.js b/test/unit/unit_snappy.test.js index 10a01ca96e..8f2dc41af8 100644 --- a/test/unit/unit_snappy.test.js +++ b/test/unit/unit_snappy.test.js @@ -38,7 +38,7 @@ describe('Compression', function () { server.setMessageHandler(request => { const doc = request.document; if (doc.ismaster || doc.hello) { - return request.reply({ ...mock.DEFAULT_ISMASTER, compression: ['snappy'] }); + return request.reply({ ...mock.HELLO, compression: ['snappy'] }); } if (doc.insert === 'snappy') { return request.reply({ ok: 1 }); diff --git a/test/unit/unit_wire_version.test.js b/test/unit/unit_wire_version.test.js index 290cec64ac..2174c31b8b 100644 --- a/test/unit/unit_wire_version.test.js +++ b/test/unit/unit_wire_version.test.js @@ -6,8 +6,8 @@ const { MongoServerSelectionError, MongoClient } = require('../../src'); const minCompatErrMsg = `minimum wire version ${ Number.MAX_SAFE_INTEGER - 1 -}, but this version of the Node.js Driver requires at most 13`; -const maxCompatErrMsg = `reports maximum wire version 1, but this version of the Node.js Driver requires at least 2`; +}, but this version of the Node.js Driver requires at most 14`; +const maxCompatErrMsg = `reports maximum wire version 1, but this version of the Node.js Driver requires at least 6`; describe('Wire Protocol Version', () => { /** @type {mock.MockServer} */ @@ -18,7 +18,7 @@ describe('Wire Protocol Version', () => { const doc = req.document; if (doc.ismaster || doc.hello) { const hello = { - ...mock.DEFAULT_ISMASTER_36, + ...mock.HELLO, minWireVersion: min, maxWireVersion: max }; @@ -35,7 +35,7 @@ describe('Wire Protocol Version', () => { await mock.cleanup(); }); - describe('minimum is greater than 13', () => { + describe('minimum is greater than 14', () => { it('should raise a compatibility error', async function () { setWireProtocolMessageHandler(Number.MAX_SAFE_INTEGER - 1, Number.MAX_SAFE_INTEGER);