From 55d9d7ef6ff1f62e9fcdc9bcbdcb44fc6c806372 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Sun, 12 May 2024 08:04:39 -0400 Subject: [PATCH] fix(model): make `bulkWrite()` and `insertMany()` throw if `throwOnValidationError` set and all ops invalid Fix #14572 --- lib/model.js | 16 ++++++++++++++++ test/model.test.js | 44 +++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 3 deletions(-) diff --git a/lib/model.js b/lib/model.js index 5e0a105c479..13a0a877a8d 100644 --- a/lib/model.js +++ b/lib/model.js @@ -3233,6 +3233,14 @@ Model.$__insertMany = function(arr, options, callback) { // Quickly escape while there aren't any valid docAttributes if (docAttributes.length === 0) { + if (throwOnValidationError) { + return callback(new MongooseBulkWriteError( + validationErrors, + results, + null, + 'insertMany' + )); + } if (rawResult) { const res = { acknowledged: true, @@ -3588,6 +3596,14 @@ Model.bulkWrite = async function bulkWrite(ops, options) { validOps = validOps.sort().map(index => ops[index]); if (validOps.length === 0) { + if (options.throwOnValidationError && validationErrors.length) { + throw new MongooseBulkWriteError( + validationErrors, + results, + res, + 'bulkWrite' + ); + } return getDefaultBulkwriteResult(); } diff --git a/test/model.test.js b/test/model.test.js index 9d35f207000..74ba4463833 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -4062,7 +4062,32 @@ describe('Model', function() { assert.ok(doc.createdAt.valueOf() >= now.valueOf()); assert.ok(doc.updatedAt); assert.ok(doc.updatedAt.valueOf() >= now.valueOf()); + }); + + it('throwOnValidationError (gh-14572)', async function() { + const schema = new Schema({ + num: Number + }); + + const M = db.model('Test', schema); + + const ops = [ + { + insertOne: { + document: { + num: 'not a number' + } + } + } + ]; + const err = await M.bulkWrite( + ops, + { ordered: false, throwOnValidationError: true } + ).then(() => null, err => err); + assert.ok(err); + assert.equal(err.name, 'MongooseBulkWriteError'); + assert.equal(err.validationErrors[0].errors['num'].name, 'CastError'); }); it('with child timestamps and array filters (gh-7032)', async function() { @@ -6602,14 +6627,14 @@ describe('Model', function() { }); it('insertMany should throw an error if there were operations that failed validation, ' + - 'but all operations that passed validation succeeded (gh-13256)', async function() { + 'but all operations that passed validation succeeded (gh-14572) (gh-13256)', async function() { const userSchema = new Schema({ age: { type: Number } }); const User = db.model('User', userSchema); - const err = await User.insertMany([ + let err = await User.insertMany([ new User({ age: 12 }), new User({ age: 12 }), new User({ age: 'NaN' }) @@ -6623,7 +6648,20 @@ describe('Model', function() { assert.ok(err.results[2] instanceof Error); assert.equal(err.results[2].errors['age'].name, 'CastError'); - const docs = await User.find(); + let docs = await User.find(); + assert.deepStrictEqual(docs.map(doc => doc.age), [12, 12]); + + err = await User.insertMany([ + new User({ age: 'NaN' }) + ], { ordered: false, throwOnValidationError: true }) + .then(() => null) + .catch(err => err); + + assert.ok(err); + assert.equal(err.name, 'MongooseBulkWriteError'); + assert.equal(err.validationErrors[0].errors['age'].name, 'CastError'); + + docs = await User.find(); assert.deepStrictEqual(docs.map(doc => doc.age), [12, 12]); });