/** * Tests update and findAndModify command behavior with update modifiers. * * @tags: [ * # The test is designed to work with an unsharded collection. * assumes_unsharded_collection, * # Performs modifications that if repeated would fail the test. * requires_non_retryable_writes, * ] */ (function() { "use strict"; const testDB = db.getSiblingDB(jsTestName()); assert.commandWorked(testDB.dropDatabase()); const coll = testDB.update_modifiers; // 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)); // Remove all existing documents and then insert the new test case's documents. assert.commandWorked(coll.remove({})); assert.commandWorked(coll.insert(testCase.inputDocuments)); // Issue the update command specified in the test case. const result = testCase.command(coll, testCase.query, testCase.update, testCase.arrayFilters); if (testCase.expectedErrorCode == undefined) { // Verify that the command succeeded and collection's contents match the expected results. assert.commandWorked(result); assert.docEq(testCase.expectedResults, coll.find({}).sort({_id: 1}).toArray()); } else { assert.commandFailedWithCode(result, testCase.expectedErrorCode); } } // 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); } // 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); } // 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]}], }, ]; for (const command of [updateCommand, findAndModifyCommand]) { for (const testCase of testCases) { executeUpdateTestCase(Object.assign({command: command}, testCase)); } } })();