diff options
Diffstat (limited to 'jstests/core/verify_update_mods.js')
-rw-r--r-- | jstests/core/verify_update_mods.js | 374 |
1 files changed, 301 insertions, 73 deletions
diff --git a/jstests/core/verify_update_mods.js b/jstests/core/verify_update_mods.js index 134668e62bd..184b98ef14d 100644 --- a/jstests/core/verify_update_mods.js +++ b/jstests/core/verify_update_mods.js @@ -1,82 +1,310 @@ -// Cannot implicitly shard accessed collections because of following errmsg: A single -// update/delete on a sharded collection must contain an exact match on _id or contain the shard -// key. -// @tags: [assumes_unsharded_collection, requires_non_retryable_writes] +/** + * Tests update and findAndModify command behavior with update modifiers. + * + * @tags: [ + * requires_fcv_42, + * # The test is designed to work with an unsharded collection. + * assumes_unsharded_collection, + * # The coll.update command does not work with $set operator in compatibility write mode. + * requires_find_command, + * # Performs modifications that if repeated would fail the test. + * requires_non_retryable_writes, + * ] + */ +(function() { +"use strict"; -// Verify update mods exist -var res; -t = db.update_mods; -t.drop(); +const testDB = db.getSiblingDB(jsTestName()); +assert.commandWorked(testDB.dropDatabase()); +const coll = testDB.update_modifiers; -t.save({_id: 1}); -res = t.update({}, {$set: {a: 1}}); -assert.writeOK(res); -t.remove({}); +// Executes a test case which inserts documents into the test collection, issues an update command, +// and verifies that the results are as expected. +function executeUpdateTestCase(testCase) { + jsTestLog(tojson(testCase)); -t.save({_id: 1}); -res = t.update({}, {$unset: {a: 1}}); -assert.writeOK(res); -t.remove({}); + // Remove all existing documents and then insert the new test case's documents. + assert.commandWorked(coll.remove({})); + assert.commandWorked(coll.insert(testCase.inputDocuments)); -t.save({_id: 1}); -res = t.update({}, {$inc: {a: 1}}); -assert.writeOK(res); -t.remove({}); + // Issue the update command specified in the test case. + const result = testCase.command(coll, testCase.query, testCase.update, testCase.arrayFilters); -t.save({_id: 1}); -res = t.update({}, {$mul: {a: 1}}); -assert.writeOK(res); -t.remove({}); + if (testCase.expectedErrorCode == undefined) { + // Verify that the command succeeded and collection's contents match the expected results. + assert.commandWorked(result); + assert.docEq(coll.find({}).sort({_id: 1}).toArray(), testCase.expectedResults); + } else { + assert.commandFailedWithCode(result, testCase.expectedErrorCode); + } +} -t.save({_id: 1}); -res = t.update({}, {$push: {a: 1}}); -assert.writeOK(res); -t.remove({}); +// Issues the update command and returns the response. +function updateCommand(coll, query, update, arrayFilters) { + const commandOptions = {upsert: true}; + if (arrayFilters !== undefined) { + commandOptions.arrayFilters = arrayFilters; + } + return coll.update(query, update, commandOptions); +} -res = t.update({}, {$addToSet: {a: 1}}); -assert.writeOK(res); -t.remove({}); +// Issues the findAndModify command and returns the response. +function findAndModifyCommand(coll, query, update, arrayFilters) { + const command = {query: query, update: update, upsert: true}; + if (arrayFilters !== undefined) { + command.arrayFilters = arrayFilters; + } + return coll.runCommand("findAndModify", command); +} -t.save({_id: 1}); -res = t.update({}, {$pull: {a: 1}}); -assert.writeOK(res); -t.remove({}); +// Tests all relevant update modifiers with set and empty update documents. +const testCases = [ + { + query: {_id: 1}, + update: {$set: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 2}, + update: {$set: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}, {_id: 2}], + }, + { + query: {_id: 1}, + update: {$set: {a: 2}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 2}], + }, + { + query: {_id: 1}, + update: {$set: {}}, + arrayFilters: [{"element": {$gt: 6}}], + inputDocuments: [{_id: 1, a: [1]}], + expectedErrorCode: ErrorCodes.FailedToParse, + }, + { + query: {_id: 1}, + update: {$unset: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$unset: {a: 1}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1}], + }, + { + query: {_id: 1}, + update: {$inc: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$inc: {a: 1}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 2}], + }, + { + query: {_id: 1}, + update: {$mul: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$mul: {a: 2}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 2}], + }, + { + query: {_id: 1}, + update: {$push: {}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: [1]}], + }, + { + query: {_id: 1}, + update: {$push: {a: 2}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: [1, 2]}], + }, + { + query: {_id: 1}, + update: {$addToSet: {}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: [1]}], + }, + { + query: {_id: 1}, + update: {$addToSet: {a: 2}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: [1, 2]}], + }, + { + query: {_id: 1}, + update: {$pull: {}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: [1]}], + }, + { + query: {_id: 1}, + update: {$pull: {a: 1}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: []}], + }, + { + query: {_id: 1}, + update: {$rename: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$rename: {a: "b"}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, b: 1}], + }, + { + query: {_id: 1}, + update: {$bit: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$bit: {a: {and: NumberLong(1)}}}, + inputDocuments: [{_id: 1, a: NumberLong(2)}], + expectedResults: [{_id: 1, a: NumberLong(0)}], + }, + { + query: {_id: 1}, + update: {$bit: {a: {and: NumberLong(3)}}}, + inputDocuments: [], + expectedResults: [{_id: 1, a: NumberLong(0)}], + }, + { + query: {_id: 1}, + update: {$bit: {b: {or: NumberLong(3)}}}, + inputDocuments: [], + expectedResults: [{_id: 1, b: NumberLong(3)}], + }, + { + query: {_id: 1}, + update: {$bit: {"c.d": {or: NumberInt(3)}}}, + inputDocuments: [], + expectedResults: [{_id: 1, c: {d: NumberInt(3)}}], + }, + { + query: {_id: 1}, + update: {$max: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$max: {a: 2}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 2}], + }, + { + query: {_id: 1}, + update: {$min: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$min: {a: 0}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 0}], + }, + { + query: {_id: 1}, + update: {$currentDate: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$setOnInsert: {}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 2}, + update: {$setOnInsert: {a: 1}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}, {_id: 2, a: 1}], + }, + { + query: {_id: 1}, + update: {$setOnInsert: {a: 2}}, + inputDocuments: [{_id: 1, a: 1}], + expectedResults: [{_id: 1, a: 1}], + }, + { + query: {_id: 1}, + update: {$pop: {}}, + inputDocuments: [{_id: 1, a: [1]}], + expectedResults: [{_id: 1, a: [1]}], + }, + { + query: {_id: 1}, + update: {$pop: {a: true}}, + inputDocuments: [{_id: 1, a: [1, 2]}], + expectedErrorCode: ErrorCodes.FailedToParse, + }, + { + query: {_id: 1}, + update: {$pop: {a: 1}}, + inputDocuments: [{_id: 1, a: [1, 2]}], + expectedResults: [{_id: 1, a: [1]}], + }, + { + query: {_id: 1}, + update: {$pop: {a: -1}}, + inputDocuments: [{_id: 1, a: [1, 2]}], + expectedResults: [{_id: 1, a: [2]}], + }, + { + query: {_id: 1}, + update: {$pop: {a: 1}}, + inputDocuments: [{_id: 1, a: []}], + expectedResults: [{_id: 1, a: []}], + }, + { + query: {_id: 1}, + update: {$pop: {a: 1}}, + inputDocuments: [], + expectedResults: [{_id: 1}], + }, + { + query: {_id: 1}, + update: {$currentDate: {}}, + inputDocuments: [{_id: 1}], + expectedResults: [{_id: 1}], + }, + { + query: {_id: 1}, + update: {$pullAll: {}}, + inputDocuments: [{_id: 1, a: [1, 2]}], + expectedResults: [{_id: 1, a: [1, 2]}], + }, + { + query: {_id: 1}, + update: {$pullAll: {a: [1]}}, + inputDocuments: [{_id: 1, a: [1, 2, 1]}], + expectedResults: [{_id: 1, a: [2]}], + }, +]; -t.save({_id: 1}); -res = t.update({}, {$pop: {a: true}}); -assert.writeError(res); -t.remove({}); - -t.save({_id: 1}); -res = t.update({}, {$rename: {a: "b"}}); -assert.writeOK(res); -t.remove({}); - -t.save({_id: 1}); -res = t.update({}, {$bit: {a: {and: NumberLong(1)}}}); -assert.writeOK(res); -t.remove({}); - -// SERVER-3223 test $bit can do an upsert -t.update({_id: 1}, {$bit: {a: {and: NumberLong(3)}}}, true); -assert.eq(t.findOne({_id: 1}).a, NumberLong(0), "$bit upsert with and"); -t.update({_id: 2}, {$bit: {b: {or: NumberLong(3)}}}, true); -assert.eq(t.findOne({_id: 2}).b, NumberLong(3), "$bit upsert with or (long)"); -t.update({_id: 3}, {$bit: {"c.d": {or: NumberInt(3)}}}, true); -assert.eq(t.findOne({_id: 3}).c.d, NumberInt(3), "$bit upsert with or (int)"); -t.remove({}); - -t.save({_id: 1}); -res = t.update({}, {$currentDate: {a: true}}); -assert.writeOK(res); -t.remove({}); - -t.save({_id: 1}); -res = t.update({}, {$max: {a: 1}}); -assert.writeOK(res); -t.remove({}); - -t.save({_id: 1}); -res = t.update({}, {$min: {a: 1}}); -assert.writeOK(res); -t.remove({}); +for (const command of [updateCommand, findAndModifyCommand]) { + for (const testCase of testCases) { + executeUpdateTestCase(Object.assign({command: command}, testCase)); + } +} +})(); |