From 9a179608cfda6fe24126b569e278553a775a4544 Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 25 Jan 2023 10:27:06 -0500 Subject: [PATCH 01/20] test(NODE-4999): Convert issue repro to failing test --- .../read-write-concern/write_concern.test.ts | 111 ++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 test/integration/read-write-concern/write_concern.test.ts diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts new file mode 100644 index 00000000000..aaa68b90229 --- /dev/null +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -0,0 +1,111 @@ +const { expect } = require('chai'); +const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); + +const mock = require('../../tools/mongodb-mock/index'); +const { MongoClient, Collection, Db } = require('../../../src'); + +describe('Write Concern', function() { + it('should respect writeConcern from uri', function(done) { + const client = this.configuration.newClient( + `${this.configuration.url()}&w=0&monitorCommands=true` + ); + const events: any[] = []; + client.on('commandStarted', event => { + if (event.commandName === 'insert') { + events.push(event); + } + }); + + expect(client.writeConcern).to.eql({ w: 0 }); + client + .db('test') + .collection('test') + .insertOne({ a: 1 }, (err, result) => { + expect(err).to.not.exist; + expect(result).to.exist; + expect(events).to.be.an('array').with.lengthOf(1); + expect(events[0]).to.containSubset({ + commandName: 'insert', + command: { + writeConcern: { w: 0 } + } + }); + client.close(done); + }); + }); + + describe('mock server write concern test', () => { + let server; + before(() => { + return mock.createServer().then(s => { + server = s; + }); + }); + + after(() => mock.cleanup()); + + // TODO: NODE-3816 + it.skip('should pipe writeConcern from client down to API call', function() { + server.setMessageHandler(request => { + if (request.document && request.document[LEGACY_HELLO_COMMAND]) { + return request.reply(mock.HELLO); + } + expect(request.document.writeConcern).to.exist; + expect(request.document.writeConcern.w).to.equal('majority'); + return request.reply({ ok: 1 }); + }); + + const uri = `mongodb://${server.uri()}`; + const client = new MongoClient(uri, { writeConcern: 'majority' }); + return client + .connect() + .then(() => { + const db = client.db('wc_test'); + const collection = db.collection('wc'); + + return collection.insertMany([{ a: 2 }]); + }) + .then(() => { + return client.close(); + }); + }); + }); + + describe('must not affect read operations', function() { + let client: typeof MongoClient; + let db: typeof Db; + let col: typeof Collection; + + beforeEach(async function() { + client = this.configuration.newClient({ 'writeConcern': { w: 0 } }); + await client.connect(); + db = client.db('test'); + await db.dropCollection('writeConcernTest').catch(() => null); + col = db.collection('writeConcernTest'); + + const docs: any[] = []; + for (let i = 0; i < 1028; i++) { + docs.push({ a: i }); + } + + await col.insertMany(docs); + }); + + afterEach(async function() { + await client.close(); + }); + + describe('when writeConcern=0', function() { + it('does not throw an error when getMore is called on cursor', async function() { + const findResult = col.find({}); + let err; + + await findResult.toArray() + .catch(e => { + err = e; + }); + expect(err).to.not.exist; + }); + }); + }); +}); From dc466425c5933f38215af8e648d7338c467d32bd Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 25 Jan 2023 16:05:06 -0500 Subject: [PATCH 02/20] test(NODE-4999): Got repro working --- .../read-write-concern/write_concern.test.js | 44 ++++++- .../read-write-concern/write_concern.test.ts | 111 ------------------ 2 files changed, 40 insertions(+), 115 deletions(-) delete mode 100644 test/integration/read-write-concern/write_concern.test.ts diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.js index 53d27500e45..050d92dfb60 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.js @@ -1,10 +1,8 @@ -'use strict'; - const { expect } = require('chai'); -const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); -const mock = require('../../tools/mongodb-mock/index'); const { MongoClient } = require('../../../src'); +const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); +const mock = require('../../tools/mongodb-mock/index'); describe('Write Concern', function () { it('should respect writeConcern from uri', function (done) { @@ -72,4 +70,42 @@ describe('Write Concern', function () { }); }); }); + + describe('must not affect read operations', function () { + let client; + let db; + let col; + + beforeEach(async function () { + client = this.configuration.newClient({ writeConcern: { w: 0 } }); + await client.connect(); + db = client.db('test'); + await db.dropCollection('writeConcernTest').catch(() => null); + col = db.collection('writeConcernTest'); + + const docs = []; + for (let i = 0; i < 1028; i++) { + docs.push({ a: i }); + } + + await col.insertMany(docs); + }); + + afterEach(async function () { + await client.close(); + }); + + describe('when writeConcern=0', function () { + it('does not throw an error when getMore is called on cursor', async function () { + const findResult = col.find({}); + let err; + + await findResult.toArray().catch(e => { + throw e; + }); + + expect(err).to.not.exist; + }); + }); + }); }); diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts deleted file mode 100644 index aaa68b90229..00000000000 --- a/test/integration/read-write-concern/write_concern.test.ts +++ /dev/null @@ -1,111 +0,0 @@ -const { expect } = require('chai'); -const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); - -const mock = require('../../tools/mongodb-mock/index'); -const { MongoClient, Collection, Db } = require('../../../src'); - -describe('Write Concern', function() { - it('should respect writeConcern from uri', function(done) { - const client = this.configuration.newClient( - `${this.configuration.url()}&w=0&monitorCommands=true` - ); - const events: any[] = []; - client.on('commandStarted', event => { - if (event.commandName === 'insert') { - events.push(event); - } - }); - - expect(client.writeConcern).to.eql({ w: 0 }); - client - .db('test') - .collection('test') - .insertOne({ a: 1 }, (err, result) => { - expect(err).to.not.exist; - expect(result).to.exist; - expect(events).to.be.an('array').with.lengthOf(1); - expect(events[0]).to.containSubset({ - commandName: 'insert', - command: { - writeConcern: { w: 0 } - } - }); - client.close(done); - }); - }); - - describe('mock server write concern test', () => { - let server; - before(() => { - return mock.createServer().then(s => { - server = s; - }); - }); - - after(() => mock.cleanup()); - - // TODO: NODE-3816 - it.skip('should pipe writeConcern from client down to API call', function() { - server.setMessageHandler(request => { - if (request.document && request.document[LEGACY_HELLO_COMMAND]) { - return request.reply(mock.HELLO); - } - expect(request.document.writeConcern).to.exist; - expect(request.document.writeConcern.w).to.equal('majority'); - return request.reply({ ok: 1 }); - }); - - const uri = `mongodb://${server.uri()}`; - const client = new MongoClient(uri, { writeConcern: 'majority' }); - return client - .connect() - .then(() => { - const db = client.db('wc_test'); - const collection = db.collection('wc'); - - return collection.insertMany([{ a: 2 }]); - }) - .then(() => { - return client.close(); - }); - }); - }); - - describe('must not affect read operations', function() { - let client: typeof MongoClient; - let db: typeof Db; - let col: typeof Collection; - - beforeEach(async function() { - client = this.configuration.newClient({ 'writeConcern': { w: 0 } }); - await client.connect(); - db = client.db('test'); - await db.dropCollection('writeConcernTest').catch(() => null); - col = db.collection('writeConcernTest'); - - const docs: any[] = []; - for (let i = 0; i < 1028; i++) { - docs.push({ a: i }); - } - - await col.insertMany(docs); - }); - - afterEach(async function() { - await client.close(); - }); - - describe('when writeConcern=0', function() { - it('does not throw an error when getMore is called on cursor', async function() { - const findResult = col.find({}); - let err; - - await findResult.toArray() - .catch(e => { - err = e; - }); - expect(err).to.not.exist; - }); - }); - }); -}); From 704e3fc75d53eb3e7aa8ff3be82233f9c310e740 Mon Sep 17 00:00:00 2001 From: Warren James Date: Thu, 26 Jan 2023 15:59:17 -0500 Subject: [PATCH 03/20] test(NODE-4999): Add tests to check for desired behaviour --- .../read-write-concern/write_concern.test.js | 96 +++++++++++++------ 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.js index 050d92dfb60..b7a55286de0 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.js @@ -72,39 +72,81 @@ describe('Write Concern', function () { }); describe('must not affect read operations', function () { - let client; - let db; - let col; - - beforeEach(async function () { - client = this.configuration.newClient({ writeConcern: { w: 0 } }); - await client.connect(); - db = client.db('test'); - await db.dropCollection('writeConcernTest').catch(() => null); - col = db.collection('writeConcernTest'); - - const docs = []; - for (let i = 0; i < 1028; i++) { - docs.push({ a: i }); - } + describe('when writeConcern = 0', function () { + describe('does not throw an error when getMore is called on cursor', function () { + let client; + let db; + let col; + + beforeEach(async function () { + client = this.configuration.newClient({ writeConcern: { w: 0 } }); + await client.connect(); + db = client.db('writeConcernTest'); + col = db.collection('writeConcernTest'); + + const docs = []; + for (let i = 0; i < 10; i++) { + docs.push({ a: i, b: i + 1 }); + } - await col.insertMany(docs); - }); + await col.insertMany(docs); + }); - afterEach(async function () { - await client.close(); - }); + afterEach(async function () { + await db.dropDatabase(); + await client.close(); + }); - describe('when writeConcern=0', function () { - it('does not throw an error when getMore is called on cursor', async function () { - const findResult = col.find({}); - let err; + it('find', async function () { + const findResult = col.find({}, { batchSize: 2 }); + const err = await findResult.toArray().catch(e => e); - await findResult.toArray().catch(e => { - throw e; + expect(err).to.not.be.instanceOf(Error); }); - expect(err).to.not.exist; + it('listCollections', async function () { + let collections = []; + for (let i = 0; i < 10; i++) { + collections.push(`writeConcernTestCol${i + 1}`); + } + + for (const colName of collections) { + await db.createCollection(colName).catch(() => null); + } + + const cols = db.listCollections({}, { batchSize: 2 }); + + const err = await cols.toArray().catch(e => e); + + expect(err).to.not.be.instanceOf(Error); + }); + + it('aggregate', async function () { + const aggResult = col.aggregate([{ $match: { a: { $gte: 0 } } }], { batchSize: 2 }); + const err = await aggResult.toArray().catch(e => e); + + expect(err).to.not.be.instanceOf(Error); + }); + + it('listIndexes', async function () { + await col.createIndex({ a: 1 }); + await col.createIndex({ b: -1 }); + await col.createIndex({ a: 1, b: -1 }); + + const listIndexesResult = col.listIndexes({ batchSize: 2 }); + const err = await listIndexesResult.toArray().catch(e => e); + + expect(err).to.not.be.instanceOf(Error); + }); + + it('changeStream', async function () { + let changeStream = col.watch(undefined, { batchSize: 2 }); + col.updateMany({}, [{ $addFields: { A: 1 } }]); + + const err = await changeStream.next().catch(e => e); + + expect(err).to.not.be.instanceOf(Error); + }); }); }); }); From fa76f2d6f518267de89f041c8c4433287ebc7acd Mon Sep 17 00:00:00 2001 From: Warren James Date: Thu, 26 Jan 2023 16:23:13 -0500 Subject: [PATCH 04/20] fix(NODE-4999): WIP fix --- src/operations/find.ts | 3 ++- src/operations/list_collections.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/operations/find.ts b/src/operations/find.ts index 33aedd6b5f2..0f60e86d683 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -14,7 +14,8 @@ import { Aspect, defineAspects, Hint } from './operation'; * @typeParam TSchema - Unused schema definition, deprecated usage, only specify `FindOptions` with no generic */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface FindOptions extends CommandOperationOptions { +export interface FindOptions + extends Omit { /** Sets the limit of documents returned in the query. */ limit?: number; /** Set to sort the documents coming back from the query. Array of indexes, `[['a', 1]]` etc. */ diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index ae1c15ec20f..2d01869726f 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -7,7 +7,7 @@ import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ -export interface ListCollectionsOptions extends CommandOperationOptions { +export interface ListCollectionsOptions extends Omit { /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */ nameOnly?: boolean; /** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */ From 4d7ff6912511100acb079b4e579e471783cfddc9 Mon Sep 17 00:00:00 2001 From: Warren James Date: Fri, 27 Jan 2023 14:26:56 -0500 Subject: [PATCH 05/20] fix(NODE-4999): Start implementing fix --- src/collection.ts | 1 + src/cursor/change_stream_cursor.ts | 1 + src/operations/aggregate.ts | 2 ++ src/operations/find.ts | 7 ++-- src/operations/indexes.ts | 2 +- src/operations/list_collections.ts | 4 +-- src/operations/list_databases.ts | 2 +- .../read-write-concern/write_concern.test.js | 34 +++++++++++-------- 8 files changed, 31 insertions(+), 22 deletions(-) diff --git a/src/collection.ts b/src/collection.ts index d61ffcb2855..1217ef0cccd 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -1524,6 +1524,7 @@ export class Collection { options = pipeline; pipeline = []; } + options.writeConcern = undefined; return new ChangeStream(this, pipeline, resolveOptions(this, options)); } diff --git a/src/cursor/change_stream_cursor.ts b/src/cursor/change_stream_cursor.ts index b5a4ae46307..4793234ebdb 100644 --- a/src/cursor/change_stream_cursor.ts +++ b/src/cursor/change_stream_cursor.ts @@ -143,6 +143,7 @@ export class ChangeStreamCursor< const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, { ...this.cursorOptions, ...this.options, + writeConcern: undefined, session }); diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index a48a89ca5b2..5613b1ae163 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -101,6 +101,8 @@ export class AggregateOperation extends CommandOperation { if (this.hasWriteStage && this.writeConcern) { Object.assign(command, { writeConcern: this.writeConcern }); + } else { + Object.assign(this.options, { writeConcern: undefined }); } if (options.bypassDocumentValidation === true) { diff --git a/src/operations/find.ts b/src/operations/find.ts index 0f60e86d683..7d39bfd64b5 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -119,6 +119,7 @@ export class FindOperation extends CommandOperation { { ...this.options, ...this.bsonOptions, + writeConcern: undefined, documentsReturnedIn: 'firstBatch', session }, @@ -142,9 +143,9 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp if (projection && Array.isArray(projection)) { projection = projection.length ? projection.reduce((result, field) => { - result[field] = 1; - return result; - }, {}) + result[field] = 1; + return result; + }, {}) : { _id: 1 }; } diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 3a3b72dc30c..4e709d33c95 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -394,7 +394,7 @@ export class ListIndexesOperation extends CommandOperation { constructor(collection: Collection, options?: ListIndexesOptions) { super(collection, options); - this.options = options ?? {}; + this.options = {...options, writeConcern: undefined} ?? {}; this.collectionNamespace = collection.s.namespace; } diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 2d01869726f..2249f63646e 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -7,7 +7,7 @@ import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; /** @public */ -export interface ListCollectionsOptions extends Omit { +export interface ListCollectionsOptions extends CommandOperationOptions { /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */ nameOnly?: boolean; /** Since 4.0: If true and nameOnly is true, allows a user without the required privilege (i.e. listCollections action on the database) to run the command when access control is enforced. */ @@ -28,7 +28,7 @@ export class ListCollectionsOperation extends CommandOperation { constructor(db: Db, filter: Document, options?: ListCollectionsOptions) { super(db, options); - this.options = options ?? {}; + this.options = { ...options, writeConcern: undefined } ?? {}; this.db = db; this.filter = filter; this.nameOnly = !!this.options.nameOnly; diff --git a/src/operations/list_databases.ts b/src/operations/list_databases.ts index 537c9529037..d2255c34335 100644 --- a/src/operations/list_databases.ts +++ b/src/operations/list_databases.ts @@ -30,7 +30,7 @@ export class ListDatabasesOperation extends CommandOperation mock.cleanup()); // TODO: NODE-3816 - it.skip('should pipe writeConcern from client down to API call', function () { + it.skip('should pipe writeConcern from client down to API call', function() { server.setMessageHandler(request => { if (request.document && request.document[LEGACY_HELLO_COMMAND]) { return request.reply(mock.HELLO); @@ -71,40 +71,40 @@ describe('Write Concern', function () { }); }); - describe('must not affect read operations', function () { - describe('when writeConcern = 0', function () { - describe('does not throw an error when getMore is called on cursor', function () { + describe('must not affect read operations', function() { + describe('when writeConcern = 0', function() { + describe('does not throw an error when getMore is called on cursor', function() { let client; let db; let col; - beforeEach(async function () { + beforeEach(async function() { client = this.configuration.newClient({ writeConcern: { w: 0 } }); await client.connect(); db = client.db('writeConcernTest'); col = db.collection('writeConcernTest'); const docs = []; - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 100; i++) { docs.push({ a: i, b: i + 1 }); } await col.insertMany(docs); }); - afterEach(async function () { + afterEach(async function() { await db.dropDatabase(); await client.close(); }); - it('find', async function () { + it('find', async function() { const findResult = col.find({}, { batchSize: 2 }); const err = await findResult.toArray().catch(e => e); expect(err).to.not.be.instanceOf(Error); }); - it('listCollections', async function () { + it('listCollections', async function() { let collections = []; for (let i = 0; i < 10; i++) { collections.push(`writeConcernTestCol${i + 1}`); @@ -121,14 +121,14 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - it('aggregate', async function () { + it('aggregate', async function() { const aggResult = col.aggregate([{ $match: { a: { $gte: 0 } } }], { batchSize: 2 }); const err = await aggResult.toArray().catch(e => e); expect(err).to.not.be.instanceOf(Error); }); - it('listIndexes', async function () { + it('listIndexes', async function() { await col.createIndex({ a: 1 }); await col.createIndex({ b: -1 }); await col.createIndex({ a: 1, b: -1 }); @@ -139,12 +139,16 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - it('changeStream', async function () { + it('changeStream', async function() { let changeStream = col.watch(undefined, { batchSize: 2 }); - col.updateMany({}, [{ $addFields: { A: 1 } }]); + + setTimeout(() => { + col.updateMany({}, [{ $addFields: { A: 1 } }]); + }); const err = await changeStream.next().catch(e => e); + expect(err).to.not.be.instanceOf(Error); }); }); From 52f4cc552b6ba259dd6834cc4ee4590e136e3427 Mon Sep 17 00:00:00 2001 From: Warren James Date: Fri, 27 Jan 2023 14:31:50 -0500 Subject: [PATCH 06/20] fix(NODE-4999): revert unneeded type change and run eslint --- src/operations/find.ts | 9 +++--- src/operations/indexes.ts | 2 +- .../read-write-concern/write_concern.test.js | 29 ++++++++++--------- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/operations/find.ts b/src/operations/find.ts index 7d39bfd64b5..55ca2141d04 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -14,8 +14,7 @@ import { Aspect, defineAspects, Hint } from './operation'; * @typeParam TSchema - Unused schema definition, deprecated usage, only specify `FindOptions` with no generic */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export interface FindOptions - extends Omit { +export interface FindOptions extends CommandOperationOptions { /** Sets the limit of documents returned in the query. */ limit?: number; /** Set to sort the documents coming back from the query. Array of indexes, `[['a', 1]]` etc. */ @@ -143,9 +142,9 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp if (projection && Array.isArray(projection)) { projection = projection.length ? projection.reduce((result, field) => { - result[field] = 1; - return result; - }, {}) + result[field] = 1; + return result; + }, {}) : { _id: 1 }; } diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 4e709d33c95..8ae96eaefb7 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -394,7 +394,7 @@ export class ListIndexesOperation extends CommandOperation { constructor(collection: Collection, options?: ListIndexesOptions) { super(collection, options); - this.options = {...options, writeConcern: undefined} ?? {}; + this.options = { ...options, writeConcern: undefined } ?? {}; this.collectionNamespace = collection.s.namespace; } diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.js index 006352840ef..2f2888d8060 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.js @@ -4,8 +4,10 @@ const { MongoClient } = require('../../../src'); const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); const mock = require('../../tools/mongodb-mock/index'); -describe('Write Concern', function() { - it('should respect writeConcern from uri', function(done) { +const { setTimeout } = require('node:timers'); + +describe('Write Concern', function () { + it('should respect writeConcern from uri', function (done) { const client = this.configuration.newClient( `${this.configuration.url()}&w=0&monitorCommands=true` ); @@ -45,7 +47,7 @@ describe('Write Concern', function() { after(() => mock.cleanup()); // TODO: NODE-3816 - it.skip('should pipe writeConcern from client down to API call', function() { + it.skip('should pipe writeConcern from client down to API call', function () { server.setMessageHandler(request => { if (request.document && request.document[LEGACY_HELLO_COMMAND]) { return request.reply(mock.HELLO); @@ -71,14 +73,14 @@ describe('Write Concern', function() { }); }); - describe('must not affect read operations', function() { - describe('when writeConcern = 0', function() { - describe('does not throw an error when getMore is called on cursor', function() { + describe('must not affect read operations', function () { + describe('when writeConcern = 0', function () { + describe('does not throw an error when getMore is called on cursor', function () { let client; let db; let col; - beforeEach(async function() { + beforeEach(async function () { client = this.configuration.newClient({ writeConcern: { w: 0 } }); await client.connect(); db = client.db('writeConcernTest'); @@ -92,19 +94,19 @@ describe('Write Concern', function() { await col.insertMany(docs); }); - afterEach(async function() { + afterEach(async function () { await db.dropDatabase(); await client.close(); }); - it('find', async function() { + it('find', async function () { const findResult = col.find({}, { batchSize: 2 }); const err = await findResult.toArray().catch(e => e); expect(err).to.not.be.instanceOf(Error); }); - it('listCollections', async function() { + it('listCollections', async function () { let collections = []; for (let i = 0; i < 10; i++) { collections.push(`writeConcernTestCol${i + 1}`); @@ -121,14 +123,14 @@ describe('Write Concern', function() { expect(err).to.not.be.instanceOf(Error); }); - it('aggregate', async function() { + it('aggregate', async function () { const aggResult = col.aggregate([{ $match: { a: { $gte: 0 } } }], { batchSize: 2 }); const err = await aggResult.toArray().catch(e => e); expect(err).to.not.be.instanceOf(Error); }); - it('listIndexes', async function() { + it('listIndexes', async function () { await col.createIndex({ a: 1 }); await col.createIndex({ b: -1 }); await col.createIndex({ a: 1, b: -1 }); @@ -139,7 +141,7 @@ describe('Write Concern', function() { expect(err).to.not.be.instanceOf(Error); }); - it('changeStream', async function() { + it('changeStream', async function () { let changeStream = col.watch(undefined, { batchSize: 2 }); setTimeout(() => { @@ -148,7 +150,6 @@ describe('Write Concern', function() { const err = await changeStream.next().catch(e => e); - expect(err).to.not.be.instanceOf(Error); }); }); From 877cd734ae784e42cdd3f200e40ee2bd679da737 Mon Sep 17 00:00:00 2001 From: Warren James Date: Fri, 27 Jan 2023 15:02:04 -0500 Subject: [PATCH 07/20] test(NODE-4999): Fix import and extend timeout --- test/integration/read-write-concern/write_concern.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.js index 2f2888d8060..5cc7ee93bbf 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.js @@ -4,7 +4,7 @@ const { MongoClient } = require('../../../src'); const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); const mock = require('../../tools/mongodb-mock/index'); -const { setTimeout } = require('node:timers'); +const { setTimeout } = require('timers'); describe('Write Concern', function () { it('should respect writeConcern from uri', function (done) { @@ -146,7 +146,7 @@ describe('Write Concern', function () { setTimeout(() => { col.updateMany({}, [{ $addFields: { A: 1 } }]); - }); + }, 500); const err = await changeStream.next().catch(e => e); From 7f73b5ba3596a1c1b328771a58925f2b56f9d525 Mon Sep 17 00:00:00 2001 From: Warren James Date: Fri, 27 Jan 2023 15:22:11 -0500 Subject: [PATCH 08/20] fix(NODE-4999): simplify changes --- src/change_stream.ts | 2 +- src/collection.ts | 1 - src/cursor/change_stream_cursor.ts | 1 - src/operations/aggregate.ts | 4 ++-- src/operations/find.ts | 3 +-- src/operations/indexes.ts | 2 +- src/operations/list_collections.ts | 2 +- src/operations/list_databases.ts | 2 +- 8 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index 7056f6d9dc6..44df28eb382 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -594,7 +594,7 @@ export class ChangeStream< super(); this.pipeline = pipeline; - this.options = options; + this.options = { ...options, writeConcern: undefined }; if (parent instanceof Collection) { this.type = CHANGE_DOMAIN_TYPES.COLLECTION; diff --git a/src/collection.ts b/src/collection.ts index 1217ef0cccd..d61ffcb2855 100644 --- a/src/collection.ts +++ b/src/collection.ts @@ -1524,7 +1524,6 @@ export class Collection { options = pipeline; pipeline = []; } - options.writeConcern = undefined; return new ChangeStream(this, pipeline, resolveOptions(this, options)); } diff --git a/src/cursor/change_stream_cursor.ts b/src/cursor/change_stream_cursor.ts index 4793234ebdb..b5a4ae46307 100644 --- a/src/cursor/change_stream_cursor.ts +++ b/src/cursor/change_stream_cursor.ts @@ -143,7 +143,6 @@ export class ChangeStreamCursor< const aggregateOperation = new AggregateOperation(this.namespace, this.pipeline, { ...this.cursorOptions, ...this.options, - writeConcern: undefined, session }); diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 5613b1ae163..79406b5e567 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -65,6 +65,8 @@ export class AggregateOperation extends CommandOperation { if (this.hasWriteStage) { this.trySecondaryWrite = true; + } else { + this.options.writeConcern = undefined; } if (this.explain && this.writeConcern) { @@ -101,8 +103,6 @@ export class AggregateOperation extends CommandOperation { if (this.hasWriteStage && this.writeConcern) { Object.assign(command, { writeConcern: this.writeConcern }); - } else { - Object.assign(this.options, { writeConcern: undefined }); } if (options.bypassDocumentValidation === true) { diff --git a/src/operations/find.ts b/src/operations/find.ts index 55ca2141d04..f083942c160 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -77,7 +77,7 @@ export class FindOperation extends CommandOperation { ) { super(collection, options); - this.options = options; + this.options = { ...options, writeConcern: undefined }; this.ns = ns; if (typeof filter !== 'object' || Array.isArray(filter)) { @@ -118,7 +118,6 @@ export class FindOperation extends CommandOperation { { ...this.options, ...this.bsonOptions, - writeConcern: undefined, documentsReturnedIn: 'firstBatch', session }, diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 8ae96eaefb7..8005d931063 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -394,7 +394,7 @@ export class ListIndexesOperation extends CommandOperation { constructor(collection: Collection, options?: ListIndexesOptions) { super(collection, options); - this.options = { ...options, writeConcern: undefined } ?? {}; + this.options = { ...options, writeConcern: undefined }; this.collectionNamespace = collection.s.namespace; } diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 2249f63646e..c432f1f1862 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -28,7 +28,7 @@ export class ListCollectionsOperation extends CommandOperation { constructor(db: Db, filter: Document, options?: ListCollectionsOptions) { super(db, options); - this.options = { ...options, writeConcern: undefined } ?? {}; + this.options = { ...options, writeConcern: undefined }; this.db = db; this.filter = filter; this.nameOnly = !!this.options.nameOnly; diff --git a/src/operations/list_databases.ts b/src/operations/list_databases.ts index d2255c34335..537c9529037 100644 --- a/src/operations/list_databases.ts +++ b/src/operations/list_databases.ts @@ -30,7 +30,7 @@ export class ListDatabasesOperation extends CommandOperation Date: Fri, 27 Jan 2023 15:39:43 -0500 Subject: [PATCH 09/20] test(NODE-4999): Ensure test only runs on replica sets and sharded clusters --- .../read-write-concern/write_concern.test.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.js index 5cc7ee93bbf..4e885476fc6 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.js @@ -141,17 +141,19 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - it('changeStream', async function () { - let changeStream = col.watch(undefined, { batchSize: 2 }); + if (client.options.replicaset) { + it('changeStream', async function () { + let changeStream = col.watch(undefined, { batchSize: 2 }); - setTimeout(() => { - col.updateMany({}, [{ $addFields: { A: 1 } }]); - }, 500); + setTimeout(() => { + col.updateMany({}, [{ $addFields: { A: 1 } }]); + }, 500); - const err = await changeStream.next().catch(e => e); + const err = await changeStream.next().catch(e => e); - expect(err).to.not.be.instanceOf(Error); - }); + expect(err).to.not.be.instanceOf(Error); + }); + } }); }); }); From 82557e3cfab24859a6fed4d711eb875556ba8f96 Mon Sep 17 00:00:00 2001 From: Warren James Date: Fri, 27 Jan 2023 16:48:42 -0500 Subject: [PATCH 10/20] test(NODE-4999): conditionally execute test --- .../read-write-concern/write_concern.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.js index 4e885476fc6..e3a8f62bc3f 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.js @@ -141,8 +141,8 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - if (client.options.replicaset) { - it('changeStream', async function () { + it('changeStream', async function () { + if (this.configuration.options.replicaSet) { let changeStream = col.watch(undefined, { batchSize: 2 }); setTimeout(() => { @@ -152,8 +152,10 @@ describe('Write Concern', function () { const err = await changeStream.next().catch(e => e); expect(err).to.not.be.instanceOf(Error); - }); - } + } else { + this.skip(); + } + }); }); }); }); From 0f06672c5292a7a2e14e3acd5c28d39b32a69be7 Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 30 Jan 2023 09:43:34 -0500 Subject: [PATCH 11/20] fix(NODE-4999): explicitly delete writeConcern key --- src/change_stream.ts | 3 ++- src/operations/aggregate.ts | 2 +- src/operations/find.ts | 3 ++- src/operations/indexes.ts | 3 ++- src/operations/list_collections.ts | 3 ++- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index 44df28eb382..42ee012785b 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -594,7 +594,8 @@ export class ChangeStream< super(); this.pipeline = pipeline; - this.options = { ...options, writeConcern: undefined }; + this.options = { ...options }; + delete this.options.writeConcern; if (parent instanceof Collection) { this.type = CHANGE_DOMAIN_TYPES.COLLECTION; diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 79406b5e567..9fb879c48e9 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -66,7 +66,7 @@ export class AggregateOperation extends CommandOperation { if (this.hasWriteStage) { this.trySecondaryWrite = true; } else { - this.options.writeConcern = undefined; + delete this.options.writeConcern; } if (this.explain && this.writeConcern) { diff --git a/src/operations/find.ts b/src/operations/find.ts index f083942c160..87a6af88908 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -77,7 +77,8 @@ export class FindOperation extends CommandOperation { ) { super(collection, options); - this.options = { ...options, writeConcern: undefined }; + this.options = { ...options }; + delete this.options.writeConcern; this.ns = ns; if (typeof filter !== 'object' || Array.isArray(filter)) { diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index 8005d931063..31cef117a5d 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -394,7 +394,8 @@ export class ListIndexesOperation extends CommandOperation { constructor(collection: Collection, options?: ListIndexesOptions) { super(collection, options); - this.options = { ...options, writeConcern: undefined }; + this.options = { ...options }; + delete this.options.writeConcern; this.collectionNamespace = collection.s.namespace; } diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index c432f1f1862..a2a44f1d3db 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -28,7 +28,8 @@ export class ListCollectionsOperation extends CommandOperation { constructor(db: Db, filter: Document, options?: ListCollectionsOptions) { super(db, options); - this.options = { ...options, writeConcern: undefined }; + this.options = { ...options }; + delete this.options.writeConcern; this.db = db; this.filter = filter; this.nameOnly = !!this.options.nameOnly; From 2f0a3a6ac108a29faf3f76f201406158bb79a393 Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 30 Jan 2023 09:57:40 -0500 Subject: [PATCH 12/20] test(NODE-4999): Convert test to typescript --- ..._concern.test.js => write_concern.test.ts} | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) rename test/integration/read-write-concern/{write_concern.test.js => write_concern.test.ts} (86%) diff --git a/test/integration/read-write-concern/write_concern.test.js b/test/integration/read-write-concern/write_concern.test.ts similarity index 86% rename from test/integration/read-write-concern/write_concern.test.js rename to test/integration/read-write-concern/write_concern.test.ts index e3a8f62bc3f..4292eaff2d7 100644 --- a/test/integration/read-write-concern/write_concern.test.js +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -1,17 +1,18 @@ -const { expect } = require('chai'); +import { expect } from 'chai'; +import { setTimeout } from 'timers'; -const { MongoClient } = require('../../../src'); -const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); -const mock = require('../../tools/mongodb-mock/index'); - -const { setTimeout } = require('timers'); +import { Collection } from '../../../src/collection'; +import { LEGACY_HELLO_COMMAND } from '../../../src/constants'; +import { Db } from '../../../src/db'; +import { MongoClient } from '../../../src/mongo_client'; +import mock from '../../tools/mongodb-mock/index'; describe('Write Concern', function () { it('should respect writeConcern from uri', function (done) { const client = this.configuration.newClient( `${this.configuration.url()}&w=0&monitorCommands=true` ); - const events = []; + const events: any[] = []; client.on('commandStarted', event => { if (event.commandName === 'insert') { events.push(event); @@ -58,7 +59,7 @@ describe('Write Concern', function () { }); const uri = `mongodb://${server.uri()}`; - const client = new MongoClient(uri, { writeConcern: 'majority' }); + const client = new MongoClient(uri, { writeConcern: { w: 'majority' } }); return client .connect() .then(() => { @@ -76,9 +77,9 @@ describe('Write Concern', function () { describe('must not affect read operations', function () { describe('when writeConcern = 0', function () { describe('does not throw an error when getMore is called on cursor', function () { - let client; - let db; - let col; + let client: MongoClient; + let db: Db; + let col: Collection; beforeEach(async function () { client = this.configuration.newClient({ writeConcern: { w: 0 } }); @@ -86,7 +87,7 @@ describe('Write Concern', function () { db = client.db('writeConcernTest'); col = db.collection('writeConcernTest'); - const docs = []; + const docs: any[] = []; for (let i = 0; i < 100; i++) { docs.push({ a: i, b: i + 1 }); } @@ -107,7 +108,7 @@ describe('Write Concern', function () { }); it('listCollections', async function () { - let collections = []; + const collections: any[] = []; for (let i = 0; i < 10; i++) { collections.push(`writeConcernTestCol${i + 1}`); } @@ -143,7 +144,7 @@ describe('Write Concern', function () { it('changeStream', async function () { if (this.configuration.options.replicaSet) { - let changeStream = col.watch(undefined, { batchSize: 2 }); + const changeStream = col.watch(undefined, { batchSize: 2 }); setTimeout(() => { col.updateMany({}, [{ $addFields: { A: 1 } }]); From 164731c89948bc246c4f904208aa738a02d95da6 Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 30 Jan 2023 11:12:17 -0500 Subject: [PATCH 13/20] fix(NODE-4999): ensure that copy is made of options object --- src/operations/aggregate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 9fb879c48e9..ea0b7fa4387 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -44,7 +44,7 @@ export class AggregateOperation extends CommandOperation { constructor(ns: MongoDBNamespace, pipeline: Document[], options?: AggregateOptions) { super(undefined, { ...options, dbName: ns.db }); - this.options = options ?? {}; + this.options = { ...options }; // Covers when ns.collection is null, undefined or the empty string, use DB_AGGREGATE_COLLECTION this.target = ns.collection || DB_AGGREGATE_COLLECTION; From 9ab65497e5a3465296d4969a3cf14030fe9fa2ae Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 30 Jan 2023 11:13:38 -0500 Subject: [PATCH 14/20] test(NODE-4999): extend timeout for changeStream test and change find test to check for option deep equality --- test/integration/read-write-concern/write_concern.test.ts | 4 ++-- test/unit/operations/find.test.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts index 4292eaff2d7..b48def13bee 100644 --- a/test/integration/read-write-concern/write_concern.test.ts +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -5,7 +5,7 @@ import { Collection } from '../../../src/collection'; import { LEGACY_HELLO_COMMAND } from '../../../src/constants'; import { Db } from '../../../src/db'; import { MongoClient } from '../../../src/mongo_client'; -import mock from '../../tools/mongodb-mock/index'; +import * as mock from '../../tools/mongodb-mock/index'; describe('Write Concern', function () { it('should respect writeConcern from uri', function (done) { @@ -148,7 +148,7 @@ describe('Write Concern', function () { setTimeout(() => { col.updateMany({}, [{ $addFields: { A: 1 } }]); - }, 500); + }, 1000); const err = await changeStream.next().catch(e => e); diff --git a/test/unit/operations/find.test.ts b/test/unit/operations/find.test.ts index 43de87b617e..870541624b7 100644 --- a/test/unit/operations/find.test.ts +++ b/test/unit/operations/find.test.ts @@ -29,7 +29,7 @@ describe('FindOperation', function () { }); it('sets options', function () { - expect(operation.options).to.equal(options); + expect(operation.options).to.deep.equal(options); }); it('sets filter', function () { From 2a26d173131700f60d91cf105e6fa7ffb80c04a6 Mon Sep 17 00:00:00 2001 From: Warren James Date: Mon, 30 Jan 2023 11:22:49 -0500 Subject: [PATCH 15/20] test(NODE-4999): Change test names and ensure that checks in find.test.ts test for deep equality --- .../read-write-concern/write_concern.test.ts | 16 ++++++++-------- test/unit/operations/find.test.ts | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts index b48def13bee..f40c8641886 100644 --- a/test/integration/read-write-concern/write_concern.test.ts +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -74,9 +74,9 @@ describe('Write Concern', function () { }); }); - describe('must not affect read operations', function () { - describe('when writeConcern = 0', function () { - describe('does not throw an error when getMore is called on cursor', function () { + context('when performing read operations', function () { + context('when writeConcern = 0', function () { + describe('cursor creating operations with a getMore', function () { let client: MongoClient; let db: Db; let col: Collection; @@ -100,14 +100,14 @@ describe('Write Concern', function () { await client.close(); }); - it('find', async function () { + it('succeeds on find', async function () { const findResult = col.find({}, { batchSize: 2 }); const err = await findResult.toArray().catch(e => e); expect(err).to.not.be.instanceOf(Error); }); - it('listCollections', async function () { + it('succeeds on listCollections', async function () { const collections: any[] = []; for (let i = 0; i < 10; i++) { collections.push(`writeConcernTestCol${i + 1}`); @@ -124,14 +124,14 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - it('aggregate', async function () { + it('succeeds on aggregate', async function () { const aggResult = col.aggregate([{ $match: { a: { $gte: 0 } } }], { batchSize: 2 }); const err = await aggResult.toArray().catch(e => e); expect(err).to.not.be.instanceOf(Error); }); - it('listIndexes', async function () { + it('succeeds on listIndexes', async function () { await col.createIndex({ a: 1 }); await col.createIndex({ b: -1 }); await col.createIndex({ a: 1, b: -1 }); @@ -142,7 +142,7 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - it('changeStream', async function () { + it('succeeds on changeStream', async function () { if (this.configuration.options.replicaSet) { const changeStream = col.watch(undefined, { batchSize: 2 }); diff --git a/test/unit/operations/find.test.ts b/test/unit/operations/find.test.ts index 870541624b7..9325341c57c 100644 --- a/test/unit/operations/find.test.ts +++ b/test/unit/operations/find.test.ts @@ -25,7 +25,7 @@ describe('FindOperation', function () { const operation = new FindOperation(undefined, namespace, filter, options); it('sets the namespace', function () { - expect(operation.ns).to.equal(namespace); + expect(operation.ns).to.deep.equal(namespace); }); it('sets options', function () { @@ -33,7 +33,7 @@ describe('FindOperation', function () { }); it('sets filter', function () { - expect(operation.filter).to.equal(filter); + expect(operation.filter).to.deep.equal(filter); }); }); From 17ebd06f14e4325f5204597b92ce72a2c178c247 Mon Sep 17 00:00:00 2001 From: Warren James Date: Tue, 31 Jan 2023 14:59:39 -0500 Subject: [PATCH 16/20] WIP --- test/integration/read-write-concern/write_concern.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts index f40c8641886..b01bd571b27 100644 --- a/test/integration/read-write-concern/write_concern.test.ts +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -146,9 +146,9 @@ describe('Write Concern', function () { if (this.configuration.options.replicaSet) { const changeStream = col.watch(undefined, { batchSize: 2 }); - setTimeout(() => { + setImmediate(() => { col.updateMany({}, [{ $addFields: { A: 1 } }]); - }, 1000); + }); const err = await changeStream.next().catch(e => e); From d77a47b2d12c2756dc0276ba1d934b5cee0e6082 Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 1 Feb 2023 10:35:57 -0500 Subject: [PATCH 17/20] test(NODE-4999): Fix for failing ci test --- .../read-write-concern/write_concern.test.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts index b01bd571b27..224ed2b439d 100644 --- a/test/integration/read-write-concern/write_concern.test.ts +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -142,19 +142,18 @@ describe('Write Concern', function () { expect(err).to.not.be.instanceOf(Error); }); - it('succeeds on changeStream', async function () { - if (this.configuration.options.replicaSet) { + it('succeeds on changeStream', { + metadata: { requires: { topology: 'replicaset' } }, + async test() { const changeStream = col.watch(undefined, { batchSize: 2 }); - setImmediate(() => { + setTimeout(() => { col.updateMany({}, [{ $addFields: { A: 1 } }]); - }); + }, 1000); const err = await changeStream.next().catch(e => e); expect(err).to.not.be.instanceOf(Error); - } else { - this.skip(); } }); }); From 0c44d2a165f55c4a25efda6faed390e571a57ecb Mon Sep 17 00:00:00 2001 From: Warren James Date: Wed, 1 Feb 2023 11:47:25 -0500 Subject: [PATCH 18/20] chore(NODE-4999): change mongo-client-encryption version --- .evergreen/run-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index f24d2f166e3..b39576c5a86 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -52,7 +52,7 @@ else source "$DRIVERS_TOOLS"/.evergreen/csfle/set-temp-creds.sh fi -npm install mongodb-client-encryption@"2.4.0-alpha.2" +npm install mongodb-client-encryption@"2.4.0" npm install @mongodb-js/zstd npm install snappy From c5050a893130216f7d7164ae88d9594c747c965c Mon Sep 17 00:00:00 2001 From: Warren James Date: Thu, 2 Feb 2023 14:13:32 -0500 Subject: [PATCH 19/20] test(NODE-4999): Fix changeStream test --- .../read-write-concern/write_concern.test.ts | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts index 224ed2b439d..71c5ef63b3b 100644 --- a/test/integration/read-write-concern/write_concern.test.ts +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { setTimeout } from 'timers'; +import { on, once } from 'events'; import { Collection } from '../../../src/collection'; import { LEGACY_HELLO_COMMAND } from '../../../src/constants'; @@ -146,13 +146,28 @@ describe('Write Concern', function () { metadata: { requires: { topology: 'replicaset' } }, async test() { const changeStream = col.watch(undefined, { batchSize: 2 }); - - setTimeout(() => { - col.updateMany({}, [{ $addFields: { A: 1 } }]); - }, 1000); - - const err = await changeStream.next().catch(e => e); - + const changes = on(changeStream, 'change'); + await once(changeStream.cursor, 'init'); + + await col.insertMany( + [ + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 }, + { a: 10 } + ], + { writeConcern: { w: 'majority' } } + ); + + const err = await changes.next().catch(e => e); expect(err).to.not.be.instanceOf(Error); } }); From b1c0c2ac861f6f4c63e91c767b38bad0d9df68cb Mon Sep 17 00:00:00 2001 From: Warren James Date: Thu, 2 Feb 2023 16:46:14 -0500 Subject: [PATCH 20/20] Update test/integration/read-write-concern/write_concern.test.ts Co-authored-by: Neal Beeken --- test/integration/read-write-concern/write_concern.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/read-write-concern/write_concern.test.ts b/test/integration/read-write-concern/write_concern.test.ts index 71c5ef63b3b..94cb2987689 100644 --- a/test/integration/read-write-concern/write_concern.test.ts +++ b/test/integration/read-write-concern/write_concern.test.ts @@ -47,7 +47,7 @@ describe('Write Concern', function () { after(() => mock.cleanup()); - // TODO: NODE-3816 + // TODO(NODE-3816): the mock server response is looking for writeConcern on all messages, but endSessions doesn't have it it.skip('should pipe writeConcern from client down to API call', function () { server.setMessageHandler(request => { if (request.document && request.document[LEGACY_HELLO_COMMAND]) {