From 0ef251655271b1a9200d5f23344092e9513c5379 Mon Sep 17 00:00:00 2001 From: Kwabena Ampofo Date: Thu, 5 May 2022 16:11:46 -0400 Subject: [PATCH] fix(NODE-3565): Improve error message for insertMany with partially empty array (#3221) --- src/bulk/common.ts | 3 ++ src/operations/insert.ts | 9 +++- test/integration/crud/bulk.test.js | 85 +++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/src/bulk/common.ts b/src/bulk/common.ts index 36afe6d04d..9a050f918a 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -1133,6 +1133,9 @@ export abstract class BulkOperationBase { /** Specifies a raw operation to perform in the bulk write. */ raw(op: AnyBulkWriteOperation): this { + if (op == null || typeof op !== 'object') { + throw new MongoInvalidArgumentError('Operation must be an object with an operation key'); + } if ('insertOne' in op) { const forceServerObjectId = shouldForceServerObjectId(this); if (op.insertOne && op.insertOne.document == null) { diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 4a31017bff..6475a5cfe6 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -136,7 +136,14 @@ export class InsertManyOperation extends AbstractOperation { ); bulkWriteOperation.execute(server, session, (err, res) => { - if (err || res == null) return callback(err); + if (err || res == null) { + if (err && err.message === 'Operation must be an object with an operation key') { + err = new MongoInvalidArgumentError( + 'Collection.insertMany() cannot be called with an array that has null/undefined values' + ); + } + return callback(err); + } callback(undefined, { acknowledged: writeConcern?.w !== 0 ?? true, insertedCount: res.insertedCount, diff --git a/test/integration/crud/bulk.test.js b/test/integration/crud/bulk.test.js index bfd9a897ee..ceb908c0f7 100644 --- a/test/integration/crud/bulk.test.js +++ b/test/integration/crud/bulk.test.js @@ -7,7 +7,12 @@ const { ignoreNsNotFound, assert: test } = require('../shared'); -const { Long, MongoBatchReExecutionError, MongoDriverError } = require('../../../src'); +const { + Long, + MongoBatchReExecutionError, + MongoDriverError, + MongoInvalidArgumentError +} = require('../../../src'); const crypto = require('crypto'); const chai = require('chai'); @@ -22,6 +27,84 @@ describe('Bulk', function () { before(function () { return setupDatabase(this.configuration); }); + describe('BulkOperationBase', () => { + describe('#raw()', function () { + let client; + beforeEach(async function () { + client = this.configuration.newClient(); + await client.connect(); + }); + afterEach(async function () { + await client.close(); + }); + context('when called with an undefined operation', function () { + it('should throw a MongoInvalidArgument error ', async function () { + const bulkOp = client.db('test').collection('test').initializeUnorderedBulkOp(); + expect(() => bulkOp.raw(undefined)).to.throw(MongoInvalidArgumentError); + expect(() => bulkOp.raw(true)).to.throw(MongoInvalidArgumentError); + expect(() => bulkOp.raw(3)).to.throw(MongoInvalidArgumentError); + }); + + it('should throw an error with the specifc message: "Operation must be an object with an operation key"', async function () { + const bulkOp = client.db('test').collection('test').initializeUnorderedBulkOp(); + expect(() => bulkOp.raw(undefined)) + .to.throw(MongoInvalidArgumentError) + .to.match(/Operation must be an object with an operation key/); + }); + }); + + context('when called with a valid operation', function () { + it('should not throw a MongoInvalidArgument error', async function () { + try { + client.db('test').collection('test').initializeUnorderedBulkOp().raw({ insertOne: {} }); + } catch (error) { + expect(error).not.to.exist; + } + }); + }); + }); + }); + + describe('Collection', function () { + describe('#insertMany()', function () { + let client; + beforeEach(async function () { + client = this.configuration.newClient(); + await client.connect(); + }); + afterEach(async function () { + await client.close(); + }); + context('when passed an invalid docs argument', function () { + it('should throw a MongoInvalidArgument error', async function () { + try { + const docs = []; + docs[1] = { color: 'red' }; + await client.db('test').collection('test').insertMany(docs); + expect.fail('Expected insertMany to throw error, failed to throw error'); + } catch (error) { + expect(error).to.be.instanceOf(MongoInvalidArgumentError); + expect(error.message).to.equal( + 'Collection.insertMany() cannot be called with an array that has null/undefined values' + ); + } + }); + }); + context('when passed a valid document list', function () { + it('insertMany should not throw a MongoInvalidArgument error when called with a valid operation', async function () { + try { + let result = await client + .db('test') + .collection('test') + .insertMany([{ color: 'blue' }]); + expect(result).to.exist; + } catch (error) { + expect(error).not.to.exist; + } + }); + }); + }); + }); context('promise tests', () => { it('Should correctly execute unordered bulk operation in promise form', function (done) {