diff options
Diffstat (limited to 'jstests/core')
99 files changed, 1109 insertions, 295 deletions
diff --git a/jstests/core/apply_ops1.js b/jstests/core/apply_ops1.js index 8ce15b1df22..7088145c718 100644 --- a/jstests/core/apply_ops1.js +++ b/jstests/core/apply_ops1.js @@ -286,9 +286,54 @@ 'applyOps should fail on unknown operation type "x" with valid "ns" value'); assert.eq(0, t.find().count(), "Non-zero amount of documents in collection to start"); + + /** + * Test function for running CRUD operations on non-existent namespaces using various + * combinations of invalid namespaces (collection/database), allowAtomic and alwaysUpsert. + * + * Leave 'expectedErrorCode' undefined if this command is expected to run successfully. + */ + function testCrudOperationOnNonExistentNamespace(optype, o, o2, expectedErrorCode) { + expectedErrorCode = expectedErrorCode || ErrorCodes.OK; + const t2 = db.getSiblingDB('apply_ops1_no_such_db').getCollection('t'); + [t, t2].forEach(coll => { + const op = {op: optype, ns: coll.getFullName(), o: o, o2: o2}; + [false, true].forEach(allowAtomic => { + [false, true].forEach(alwaysUpsert => { + const cmd = { + applyOps: [op], + allowAtomic: allowAtomic, + alwaysUpsert: alwaysUpsert + }; + jsTestLog('Testing applyOps on non-existent namespace: ' + tojson(cmd)); + if (expectedErrorCode === ErrorCodes.OK) { + assert.commandWorked(db.adminCommand(cmd)); + } else { + assert.commandFailedWithCode(db.adminCommand(cmd), expectedErrorCode); + } + }); + }); + }); + } + + // Insert and update operations on non-existent collections/databases should return + // NamespaceNotFound. + testCrudOperationOnNonExistentNamespace('i', {_id: 0}, {}, ErrorCodes.NamespaceNotFound); + testCrudOperationOnNonExistentNamespace('u', {x: 0}, {_id: 0}, ErrorCodes.NamespaceNotFound); + + // Delete operations on non-existent collections/databases should return OK for idempotency + // reasons. + testCrudOperationOnNonExistentNamespace('d', {_id: 0}, {}); + assert.commandFailed( - db.adminCommand({applyOps: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}]}), - "Applying an insert operation on a non-existent collection should fail"); + db.adminCommand({ + applyOps: [{ + "op": "c", + "ns": "admin.$cmd", + "o": {applyOps: [{"op": "i", "ns": t.getFullName(), "o": {_id: 5, x: 17}}]} + }] + }), + "Applying a nested insert operation on a non-existent collection should fail"); assert.commandWorked(db.createCollection(t.getName())); var a = assert.commandWorked( diff --git a/jstests/core/apply_ops_atomicity.js b/jstests/core/apply_ops_atomicity.js index 911ce32a311..e704815a889 100644 --- a/jstests/core/apply_ops_atomicity.js +++ b/jstests/core/apply_ops_atomicity.js @@ -29,10 +29,12 @@ var newDBName = "apply_ops_atomicity"; var newDB = db.getSiblingDB(newDBName); assert.commandWorked(newDB.dropDatabase()); - // Do an update on a non-existent database, since only 'u' ops can implicitly create - // collections. - assert.commandWorked(newDB.runCommand( - {applyOps: [{op: "u", ns: newDBName + ".foo", o: {_id: 5, x: 17}, o2: {_id: 5, x: 16}}]})); + // Updates on a non-existent database no longer implicitly create collections and will fail with + // a NamespaceNotFound error. + assert.commandFailedWithCode(newDB.runCommand({ + applyOps: [{op: "u", ns: newDBName + ".foo", o: {_id: 5, x: 17}, o2: {_id: 5, x: 16}}] + }), + ErrorCodes.NamespaceNotFound); var sawTooManyLocksError = false; diff --git a/jstests/core/autocomplete.js b/jstests/core/autocomplete.js new file mode 100644 index 00000000000..6eb6e21a7a3 --- /dev/null +++ b/jstests/core/autocomplete.js @@ -0,0 +1,42 @@ +/** + * Validate auto complete works for various javascript types implemented by C++. + */ +(function() { + 'use strict'; + + function testAutoComplete(prefix) { + // This method updates a global object with an array of strings on success. + shellAutocomplete(prefix); + return __autocomplete__; + } + + // Create a collection + db.auto_complete_coll.insert({}); + + // Validate DB auto completion + const db_stuff = testAutoComplete('db.'); + + // Verify we enumerate built-in methods + assert.contains('db.prototype', db_stuff); + assert.contains('db.hasOwnProperty', db_stuff); + assert.contains('db.toString(', db_stuff); + + // Verify we have some methods we added + assert.contains('db.adminCommand(', db_stuff); + assert.contains('db.runCommand(', db_stuff); + + // Verify we enumerate collections + assert.contains('db.auto_complete_coll', db_stuff); + + // Validate Collection autocompletion + const coll_stuff = testAutoComplete('db.auto_complete_coll.'); + + // Verify we enumerate built-in methods + assert.contains('db.auto_complete_coll.prototype', coll_stuff); + assert.contains('db.auto_complete_coll.hasOwnProperty', coll_stuff); + assert.contains('db.auto_complete_coll.toString(', coll_stuff); + + // Verify we have some methods we added + assert.contains('db.auto_complete_coll.aggregate(', coll_stuff); + assert.contains('db.auto_complete_coll.runCommand(', coll_stuff); +})();
\ No newline at end of file diff --git a/jstests/core/batch_write_command_delete.js b/jstests/core/batch_write_command_delete.js index 2aefcea6a7f..99b5f8e3a61 100644 --- a/jstests/core/batch_write_command_delete.js +++ b/jstests/core/batch_write_command_delete.js @@ -1,3 +1,5 @@ +// @tags: [assumes_write_concern_unchanged] + // // Ensures that mongod respects the batch write protocols for delete // diff --git a/jstests/core/batch_write_command_insert.js b/jstests/core/batch_write_command_insert.js index 274f35513e7..3fa6cf98756 100644 --- a/jstests/core/batch_write_command_insert.js +++ b/jstests/core/batch_write_command_insert.js @@ -1,3 +1,5 @@ +// @tags: [assumes_write_concern_unchanged] + // // Ensures that mongod respects the batch write protocol for inserts // diff --git a/jstests/core/batch_write_command_update.js b/jstests/core/batch_write_command_update.js index 2d9d2d699b2..987525e2515 100644 --- a/jstests/core/batch_write_command_update.js +++ b/jstests/core/batch_write_command_update.js @@ -1,3 +1,5 @@ +// @tags: [assumes_write_concern_unchanged] + // // Ensures that mongod respects the batch write protocols for updates // diff --git a/jstests/core/bypass_doc_validation.js b/jstests/core/bypass_doc_validation.js index d9bca81ab6d..c2cdfff0bc9 100644 --- a/jstests/core/bypass_doc_validation.js +++ b/jstests/core/bypass_doc_validation.js @@ -1,5 +1,7 @@ // Test the bypassDocumentValidation flag with some database commands. The test uses relevant shell // helpers when they're available for the respective server commands. +// +// @tags: [requires_collmod_command] (function() { 'use strict'; diff --git a/jstests/core/capped6.js b/jstests/core/capped6.js index e94e7ea44e8..ca216fbe96a 100644 --- a/jstests/core/capped6.js +++ b/jstests/core/capped6.js @@ -1,4 +1,11 @@ // Test NamespaceDetails::cappedTruncateAfter via "captrunc" command +// +// @tags: [ +// # This test attempts to perform read operations on a capped collection after truncating +// # documents using the captrunc command. The writes from the captrunc command aren't guaranteed +// # to become visible until a later w="majority" write occurs. +// assumes_write_concern_unchanged, +// ] (function() { var coll = db.capped6; diff --git a/jstests/core/collation_plan_cache.js b/jstests/core/collation_plan_cache.js index 0eec77388e4..790bbbadaa6 100644 --- a/jstests/core/collation_plan_cache.js +++ b/jstests/core/collation_plan_cache.js @@ -1,4 +1,11 @@ // Integration testing for the plan cache and index filter commands with collation. +// +// @tags: [ +// # This test attempts to perform queries and introspect the server's plan cache entries. The +// # former operation may be routed to a secondary in the replica set, whereas the latter must be +// # routed to the primary. +// assumes_read_preference_unchanged, +// ] (function() { 'use strict'; @@ -237,4 +244,4 @@ assert.eq(0, coll.runCommand('planCacheListFilters').filters.length, 'unexpected number of plan cache filters'); -})();
\ No newline at end of file +})(); diff --git a/jstests/core/collection_info_cache_race.js b/jstests/core/collection_info_cache_race.js index d57fc3340db..8fcde050e99 100644 --- a/jstests/core/collection_info_cache_race.js +++ b/jstests/core/collection_info_cache_race.js @@ -5,9 +5,9 @@ var coll = db.collection_info_cache_race; coll.drop(); assert.commandWorked(db.createCollection(coll.getName(), {autoIndexId: false})); // Fails when SERVER-16502 was not fixed, due to invariant -assert.writeOK(coll.save({_id: false}, {writeConcern: {w: 1}})); +assert.writeOK(coll.save({_id: false})); coll.drop(); assert.commandWorked(db.createCollection(coll.getName(), {autoIndexId: false})); assert.eq(null, coll.findOne()); -assert.writeOK(coll.save({_id: false}, {writeConcern: {w: 1}})); +assert.writeOK(coll.save({_id: false})); diff --git a/jstests/core/collmod.js b/jstests/core/collmod.js index 16f9694560c..e366041bd99 100644 --- a/jstests/core/collmod.js +++ b/jstests/core/collmod.js @@ -1,5 +1,7 @@ // Basic js tests for the collMod command. // Test setting the usePowerOf2Sizes flag, and modifying TTL indexes. +// +// @tags: [requires_collmod_command] function debug(x) { // printjson( x ); diff --git a/jstests/core/collmod_bad_spec.js b/jstests/core/collmod_bad_spec.js index ccce81fd4b1..c3d5e7a148e 100644 --- a/jstests/core/collmod_bad_spec.js +++ b/jstests/core/collmod_bad_spec.js @@ -2,6 +2,8 @@ // // Tests that a collMod with a bad specification does not cause any changes, and does not crash the // server. +// +// @tags: [requires_collmod_command] (function() { "use strict"; diff --git a/jstests/core/commands_that_do_not_write_do_not_accept_wc.js b/jstests/core/commands_that_do_not_write_do_not_accept_wc.js index ef5f8762a42..a9c72e108a1 100644 --- a/jstests/core/commands_that_do_not_write_do_not_accept_wc.js +++ b/jstests/core/commands_that_do_not_write_do_not_accept_wc.js @@ -2,6 +2,8 @@ * This file tests commands that do not support write concern. It passes both valid and invalid * writeConcern fields to commands and expects the commands to fail with a writeConcernNotSupported * error. + * + * @tags: [assumes_write_concern_unchanged] */ (function() { diff --git a/jstests/core/constructors.js b/jstests/core/constructors.js index 9e2cd26bbe8..93842780b42 100644 --- a/jstests/core/constructors.js +++ b/jstests/core/constructors.js @@ -1,4 +1,6 @@ // Tests to see what validity checks are done for 10gen specific object construction +// +// @tags: [requires_eval_command] // Takes a list of constructors and returns a new list with an extra entry for each constructor with // "new" prepended diff --git a/jstests/core/count10.js b/jstests/core/count10.js index 2a1853c399a..453775c97f5 100644 --- a/jstests/core/count10.js +++ b/jstests/core/count10.js @@ -1,4 +1,11 @@ // Test that interrupting a count returns an error code. +// +// @tags: [ +// # This test attempts to perform a count command and find it using the currentOp command. The +// # former operation may be routed to a secondary in the replica set, whereas the latter must be +// # routed to the primary. +// assumes_read_preference_unchanged, +// ] t = db.count10; t.drop(); diff --git a/jstests/core/count_plan_summary.js b/jstests/core/count_plan_summary.js index 48891d21e8e..365f289c457 100644 --- a/jstests/core/count_plan_summary.js +++ b/jstests/core/count_plan_summary.js @@ -1,5 +1,11 @@ -// Test that the plan summary string appears in db.currentOp() for -// count operations. SERVER-14064. +// Test that the plan summary string appears in db.currentOp() for count operations. SERVER-14064. +// +// @tags: [ +// # This test attempts to perform a find command and find it using the currentOp command. The +// # former operation may be routed to a secondary in the replica set, whereas the latter must be +// # routed to the primary. +// assumes_read_preference_unchanged, +// ] var t = db.jstests_count_plan_summary; t.drop(); diff --git a/jstests/core/crud_api.js b/jstests/core/crud_api.js index c9dbfb40c85..d572d4b90a5 100644 --- a/jstests/core/crud_api.js +++ b/jstests/core/crud_api.js @@ -1,3 +1,5 @@ +// @tags: [assumes_write_concern_unchanged] + (function() { "use strict"; diff --git a/jstests/core/diagdata.js b/jstests/core/diagdata.js index 490e4a3eb2b..6938f8c5102 100644 --- a/jstests/core/diagdata.js +++ b/jstests/core/diagdata.js @@ -1,4 +1,5 @@ // Test that verifies getDiagnosticData returns FTDC data +load('jstests/libs/ftdc.js'); (function() { "use strict"; @@ -6,28 +7,5 @@ // Verify we require admin database assert.commandFailed(db.diagdata.runCommand("getDiagnosticData")); - // We need to retry a few times if run this test immediately after mongod is started as FTDC may - // not have run yet. - var foundGoodDocument = false; - - for (var i = 0; i < 60; ++i) { - var result = db.adminCommand("getDiagnosticData"); - assert.commandWorked(result); - - var data = result.data; - - if (!data.hasOwnProperty("start")) { - // Wait a little longer for FTDC to start - sleep(500); - } else { - // Check for a few common properties to ensure we got data - assert(data.hasOwnProperty("serverStatus"), - "does not have 'serverStatus' in '" + tojson(data) + "'"); - assert(data.hasOwnProperty("end"), "does not have 'end' in '" + tojson(data) + "'"); - foundGoodDocument = true; - } - } - assert(foundGoodDocument, - "getDiagnosticData failed to return a non-empty command, is FTDC running?"); - + verifyGetDiagnosticData(db.getSiblingDB('admin')); })(); diff --git a/jstests/core/doc_validation.js b/jstests/core/doc_validation.js index a30763869e7..95dbae1b219 100644 --- a/jstests/core/doc_validation.js +++ b/jstests/core/doc_validation.js @@ -1,4 +1,6 @@ // Test basic inserts and updates with document validation. +// +// @tags: [requires_collmod_command] (function() { "use strict"; diff --git a/jstests/core/doc_validation_invalid_validators.js b/jstests/core/doc_validation_invalid_validators.js index b78b31c0977..70262f5de43 100644 --- a/jstests/core/doc_validation_invalid_validators.js +++ b/jstests/core/doc_validation_invalid_validators.js @@ -1,5 +1,7 @@ // Verify invalid validator statements won't work and that we // can't create validated collections on restricted databases. +// +// @tags: [requires_collmod_command] (function() { "use strict"; diff --git a/jstests/core/doc_validation_options.js b/jstests/core/doc_validation_options.js index 8a96685e48f..d1434af6d45 100644 --- a/jstests/core/doc_validation_options.js +++ b/jstests/core/doc_validation_options.js @@ -1,3 +1,4 @@ +// @tags: [requires_collmod_command] (function() { "use strict"; diff --git a/jstests/core/dropdb_race.js b/jstests/core/dropdb_race.js index bd5e7e5ddba..d8b49d08174 100644 --- a/jstests/core/dropdb_race.js +++ b/jstests/core/dropdb_race.js @@ -1,4 +1,6 @@ // test dropping a db with simultaneous commits +// +// @tags: [assumes_write_concern_unchanged] m = db.getMongo(); baseName = "jstests_dur_droprace"; diff --git a/jstests/core/elemMatchProjection.js b/jstests/core/elemMatchProjection.js index 4e80e8a296e..5060d8a6346 100644 --- a/jstests/core/elemMatchProjection.js +++ b/jstests/core/elemMatchProjection.js @@ -4,35 +4,59 @@ t.drop(); date1 = new Date(); +// Generate monotonically increasing _id values. ObjectIds generated by the shell are not guaranteed +// to be monotically increasing, and we will depend on the _id sort order later in the test. +var currentId = 0; +function nextId() { + return ++currentId; +} + // Insert various styles of arrays for (i = 0; i < 100; i++) { - t.insert({group: 1, x: [1, 2, 3, 4, 5]}); - t.insert({group: 2, x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}]}); + t.insert({_id: nextId(), group: 1, x: [1, 2, 3, 4, 5]}); + t.insert({_id: nextId(), group: 2, x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}]}); t.insert({ + _id: nextId(), group: 3, x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}], y: [{aa: 1, bb: 2}, {aa: 2, cc: 3}, {aa: 1, dd: 5}] }); - t.insert({group: 3, x: [{a: 1, b: 3}, {a: -6, c: 3}]}); - t.insert({group: 4, x: [{a: 1, b: 4}, {a: -6, c: 3}]}); - t.insert({group: 5, x: [new Date(), 5, 10, 'string', new ObjectId(), 123.456]}); + t.insert({_id: nextId(), group: 3, x: [{a: 1, b: 3}, {a: -6, c: 3}]}); + t.insert({_id: nextId(), group: 4, x: [{a: 1, b: 4}, {a: -6, c: 3}]}); + t.insert({_id: nextId(), group: 5, x: [new Date(), 5, 10, 'string', new ObjectId(), 123.456]}); t.insert({ + _id: nextId(), group: 6, x: [{a: 'string', b: date1}, {a: new ObjectId(), b: 1.2345}, {a: 'string2', b: date1}] }); - t.insert({group: 7, x: [{y: [1, 2, 3, 4]}]}); - t.insert({group: 8, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); - t.insert({group: 9, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}, {z: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); - t.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); - t.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); + t.insert({_id: nextId(), group: 7, x: [{y: [1, 2, 3, 4]}]}); + t.insert({_id: nextId(), group: 8, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); + t.insert({ + _id: nextId(), + group: 9, + x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}, {z: [{a: 1, b: 2}, {a: 3, b: 4}]}] + }); + t.insert({ + _id: nextId(), + group: 10, + x: [{a: 1, b: 2}, {a: 3, b: 4}], + y: [{c: 1, d: 2}, {c: 3, d: 4}] + }); + t.insert({ + _id: nextId(), + group: 10, + x: [{a: 1, b: 2}, {a: 3, b: 4}], + y: [{c: 1, d: 2}, {c: 3, d: 4}] + }); t.insert({ + _id: nextId(), group: 11, x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}], covered: [{aa: 1, bb: 2}, {aa: 2, cc: 3}, {aa: 1, dd: 5}] }); - t.insert({group: 12, x: {y: [{a: 1, b: 1}, {a: 1, b: 2}]}}); - t.insert({group: 13, x: [{a: 1, b: 1}, {a: 1, b: 2}]}); - t.insert({group: 13, x: [{a: 1, b: 2}, {a: 1, b: 1}]}); + t.insert({_id: nextId(), group: 12, x: {y: [{a: 1, b: 1}, {a: 1, b: 2}]}}); + t.insert({_id: nextId(), group: 13, x: [{a: 1, b: 1}, {a: 1, b: 2}]}); + t.insert({_id: nextId(), group: 13, x: [{a: 1, b: 2}, {a: 1, b: 1}]}); } t.ensureIndex({ group: 1, diff --git a/jstests/core/error2.js b/jstests/core/error2.js index 6f0b95bc17e..fb6a8e6e3b2 100644 --- a/jstests/core/error2.js +++ b/jstests/core/error2.js @@ -1,4 +1,5 @@ // Test that client gets stack trace on failed invoke +// @tags: [requires_eval_command] f = db.jstests_error2; diff --git a/jstests/core/eval0.js b/jstests/core/eval0.js index 5802f2597cb..c21c6be66c6 100644 --- a/jstests/core/eval0.js +++ b/jstests/core/eval0.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + assert.writeOK(db.evalprep.insert({}), "db must exist for eval to succeed"); db.evalprep.drop(); assert.eq(17, diff --git a/jstests/core/eval1.js b/jstests/core/eval1.js index 8b139cae02a..b5bffac892e 100644 --- a/jstests/core/eval1.js +++ b/jstests/core/eval1.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.eval1; t.drop(); diff --git a/jstests/core/eval3.js b/jstests/core/eval3.js index c4f8be21056..b95837a6817 100644 --- a/jstests/core/eval3.js +++ b/jstests/core/eval3.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.eval3; t.drop(); diff --git a/jstests/core/eval4.js b/jstests/core/eval4.js index 0d120b393de..9b0c2a49d82 100644 --- a/jstests/core/eval4.js +++ b/jstests/core/eval4.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.eval4; t.drop(); diff --git a/jstests/core/eval5.js b/jstests/core/eval5.js index 46bd679dd77..815365ac8b9 100644 --- a/jstests/core/eval5.js +++ b/jstests/core/eval5.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.eval5; t.drop(); diff --git a/jstests/core/eval6.js b/jstests/core/eval6.js index 31258f6917b..96b43b3516c 100644 --- a/jstests/core/eval6.js +++ b/jstests/core/eval6.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.eval6; t.drop(); diff --git a/jstests/core/eval7.js b/jstests/core/eval7.js index 80197fcdde6..3bace093db1 100644 --- a/jstests/core/eval7.js +++ b/jstests/core/eval7.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + assert.writeOK(db.evalprep.insert({}), "db must exist for eval to succeed"); db.evalprep.drop(); diff --git a/jstests/core/eval9.js b/jstests/core/eval9.js index 1480ff6519c..82c230a8a95 100644 --- a/jstests/core/eval9.js +++ b/jstests/core/eval9.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + assert.writeOK(db.evalprep.insert({}), "db must exist for eval to succeed"); db.evalprep.drop(); diff --git a/jstests/core/eval_mr.js b/jstests/core/eval_mr.js index 4a3dc8dad6c..33ca96eea01 100644 --- a/jstests/core/eval_mr.js +++ b/jstests/core/eval_mr.js @@ -1,4 +1,6 @@ // Test that the eval command can't be used to invoke the mapReduce command. SERVER-17889. +// +// @tags: [requires_eval_command] (function() { "use strict"; db.eval_mr.drop(); diff --git a/jstests/core/eval_nolock.js b/jstests/core/eval_nolock.js index 9511784becb..0fde2666f5d 100644 --- a/jstests/core/eval_nolock.js +++ b/jstests/core/eval_nolock.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.eval_nolock; t.drop(); diff --git a/jstests/core/evala.js b/jstests/core/evala.js index 7ccf33ac754..09241eeedee 100644 --- a/jstests/core/evala.js +++ b/jstests/core/evala.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.evala; t.drop(); diff --git a/jstests/core/evalb.js b/jstests/core/evalb.js index 3391c4cc4f2..2de16f4ea83 100644 --- a/jstests/core/evalb.js +++ b/jstests/core/evalb.js @@ -1,5 +1,7 @@ // Check the return value of a db.eval function running a database query, and ensure the function's // contents are logged in the profile log. +// +// @tags: [requires_eval_command] // Use a reserved database name to avoid a conflict in the parallel test suite. var stddb = db; diff --git a/jstests/core/evald.js b/jstests/core/evald.js index 8049d2ba8ae..43e74fc4600 100644 --- a/jstests/core/evald.js +++ b/jstests/core/evald.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + t = db.jstests_evald; t.drop(); diff --git a/jstests/core/evale.js b/jstests/core/evale.js index 1ddc8519fc6..20384e0d741 100644 --- a/jstests/core/evale.js +++ b/jstests/core/evale.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + t = db.jstests_evale; t.drop(); diff --git a/jstests/core/evalg.js b/jstests/core/evalg.js index 18503659217..56cb1dedd5f 100644 --- a/jstests/core/evalg.js +++ b/jstests/core/evalg.js @@ -1,4 +1,6 @@ // SERVER-17499: Test behavior of getMore on aggregation cursor under eval command. +// +// @tags: [requires_eval_command] db.evalg.drop(); for (var i = 0; i < 102; ++i) { db.evalg.insert({}); diff --git a/jstests/core/evalh.js b/jstests/core/evalh.js index 11e672f6bf4..b9c0d486d59 100644 --- a/jstests/core/evalh.js +++ b/jstests/core/evalh.js @@ -1,5 +1,7 @@ /** * Test that db.eval does not support auth. + * + * @tags: [requires_eval_command] */ (function() { 'use strict'; diff --git a/jstests/core/evalj.js b/jstests/core/evalj.js index f2326fff365..d6ef46430de 100644 --- a/jstests/core/evalj.js +++ b/jstests/core/evalj.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + (function() { "use strict"; diff --git a/jstests/core/existsa.js b/jstests/core/existsa.js index e9430b489a3..d98fd3f2d68 100644 --- a/jstests/core/existsa.js +++ b/jstests/core/existsa.js @@ -1,101 +1,111 @@ -// Sparse indexes are disallowed for $exists:false queries. SERVER-3918 - -t = db.jstests_existsa; -t.drop(); - -t.save({}); -t.save({a: 1}); -t.save({a: {x: 1}, b: 1}); - -/** Configure testing of an index { <indexKeyField>:1 }. */ -function setIndex(_indexKeyField) { - indexKeyField = _indexKeyField; - indexKeySpec = {}; - indexKeySpec[indexKeyField] = 1; - t.ensureIndex(indexKeySpec, {sparse: true}); -} -setIndex('a'); - -/** @return count when hinting the index to use. */ -function hintedCount(query) { - return t.find(query).hint(indexKeySpec).itcount(); -} - -/** The query field does not exist and the sparse index is not used without a hint. */ -function assertMissing(query, expectedMissing, expectedIndexedMissing) { - expectedMissing = expectedMissing || 1; - expectedIndexedMissing = expectedIndexedMissing || 0; - assert.eq(expectedMissing, t.count(query)); - // We also shouldn't get a different count depending on whether - // an index is used or not. - assert.eq(expectedIndexedMissing, hintedCount(query)); -} - -/** The query field exists and the sparse index is used without a hint. */ -function assertExists(query, expectedExists) { - expectedExists = expectedExists || 2; - assert.eq(expectedExists, t.count(query)); - // An $exists:true predicate generates no index filters. Add another predicate on the index key - // to trigger use of the index. - andClause = {}; - andClause[indexKeyField] = {$ne: null}; - Object.extend(query, {$and: [andClause]}); - assert.eq(expectedExists, t.count(query)); - assert.eq(expectedExists, hintedCount(query)); -} - -/** The query field exists and the sparse index is not used without a hint. */ -function assertExistsUnindexed(query, expectedExists) { - expectedExists = expectedExists || 2; - assert.eq(expectedExists, t.count(query)); - // Even with another predicate on the index key, the sparse index is disallowed. - andClause = {}; - andClause[indexKeyField] = {$ne: null}; - Object.extend(query, {$and: [andClause]}); - assert.eq(expectedExists, t.count(query)); - assert.eq(expectedExists, hintedCount(query)); -} - -// $exists:false queries match the proper number of documents and disallow the sparse index. -assertMissing({a: {$exists: false}}); -assertMissing({a: {$not: {$exists: true}}}); -assertMissing({$and: [{a: {$exists: false}}]}); -assertMissing({$or: [{a: {$exists: false}}]}); -assertMissing({$nor: [{a: {$exists: true}}]}); -assertMissing({'a.x': {$exists: false}}, 2, 1); - -// Currently a sparse index is disallowed even if the $exists:false query is on a different field. -assertMissing({b: {$exists: false}}, 2, 1); -assertMissing({b: {$exists: false}, a: {$ne: 6}}, 2, 1); -assertMissing({b: {$not: {$exists: true}}}, 2, 1); - -// Top level $exists:true queries match the proper number of documents -// and use the sparse index on { a : 1 }. -assertExists({a: {$exists: true}}); - -// Nested $exists queries match the proper number of documents and disallow the sparse index. -assertExistsUnindexed({$nor: [{a: {$exists: false}}]}); -assertExistsUnindexed({$nor: [{'a.x': {$exists: false}}]}, 1); -assertExistsUnindexed({a: {$not: {$exists: false}}}); - -// Nested $exists queries disallow the sparse index in some cases where it is not strictly -// necessary to do so. (Descriptive tests.) -assertExistsUnindexed({$nor: [{b: {$exists: false}}]}, 1); // Unindexed field. -assertExists({$or: [{a: {$exists: true}}]}); // $exists:true not $exists:false. - -// Behavior is similar with $elemMatch. -t.drop(); -t.save({a: [{}]}); -t.save({a: [{b: 1}]}); -t.save({a: [{b: 1}]}); -setIndex('a.b'); - -assertMissing({a: {$elemMatch: {b: {$exists: false}}}}); -// A $elemMatch predicate is treated as nested, and the index should be used for $exists:true. -assertExists({a: {$elemMatch: {b: {$exists: true}}}}); - -// A non sparse index will not be disallowed. -t.drop(); -t.save({}); -t.ensureIndex({a: 1}); -assert.eq(1, t.find({a: {$exists: false}}).itcount()); +/** + * Tests that sparse indexes are disallowed for $exists:false queries. + */ +(function() { + "use strict"; + + const coll = db.jstests_existsa; + coll.drop(); + + assert.writeOK(coll.insert({})); + assert.writeOK(coll.insert({a: 1})); + assert.writeOK(coll.insert({a: {x: 1}, b: 1})); + + let indexKeySpec = {}; + let indexKeyField = ''; + + /** Configure testing of an index { <indexKeyField>:1 }. */ + function setIndex(_indexKeyField) { + indexKeyField = _indexKeyField; + indexKeySpec = {}; + indexKeySpec[indexKeyField] = 1; + coll.ensureIndex(indexKeySpec, {sparse: true}); + } + setIndex('a'); + + /** @return count when hinting the index to use. */ + function hintedCount(query) { + return coll.find(query).hint(indexKeySpec).itcount(); + } + + /** The query field does not exist and the sparse index is not used without a hint. */ + function assertMissing(query, expectedMissing = 1, expectedIndexedMissing = 0) { + assert.eq(expectedMissing, coll.count(query)); + // We also shouldn't get a different count depending on whether + // an index is used or not. + assert.eq(expectedIndexedMissing, hintedCount(query)); + } + + /** The query field exists and the sparse index is used without a hint. */ + function assertExists(query, expectedExists = 2) { + assert.eq(expectedExists, coll.count(query)); + // An $exists:true predicate generates no index filters. Add another predicate on the index + // key to trigger use of the index. + let andClause = {}; + andClause[indexKeyField] = {$ne: null}; + Object.extend(query, {$and: [andClause]}); + assert.eq(expectedExists, coll.count(query)); + assert.eq(expectedExists, hintedCount(query)); + } + + /** The query field exists and the sparse index is not used without a hint. */ + function assertExistsUnindexed(query, expectedExists = 2) { + assert.eq(expectedExists, coll.count(query)); + // Even with another predicate on the index key, the sparse index is disallowed. + let andClause = {}; + andClause[indexKeyField] = {$ne: null}; + Object.extend(query, {$and: [andClause]}); + assert.eq(expectedExists, coll.count(query)); + assert.eq(expectedExists, hintedCount(query)); + } + + // $exists:false queries match the proper number of documents and disallow the sparse index. + assertMissing({a: {$exists: false}}); + assertMissing({a: {$not: {$exists: true}}}); + assertMissing({$and: [{a: {$exists: false}}]}); + assertMissing({$or: [{a: {$exists: false}}]}); + assertMissing({$nor: [{a: {$exists: true}}]}); + assertMissing({'a.x': {$exists: false}}, 2, 1); + + // Currently a sparse index is disallowed even if the $exists:false query is on a different + // field. + assertMissing({b: {$exists: false}}, 2, 1); + assertMissing({b: {$exists: false}, a: {$ne: 6}}, 2, 1); + assertMissing({b: {$not: {$exists: true}}}, 2, 1); + + // Top level $exists:true queries match the proper number of documents + // and use the sparse index on { a : 1 }. + assertExists({a: {$exists: true}}); + + // Nested $exists queries match the proper number of documents and disallow the sparse index. + assertExistsUnindexed({$nor: [{a: {$exists: false}}]}); + assertExistsUnindexed({$nor: [{'a.x': {$exists: false}}]}, 1); + assertExistsUnindexed({a: {$not: {$exists: false}}}); + + // Nested $exists queries disallow the sparse index in some cases where it is not strictly + // necessary to do so. (Descriptive tests.) + assertExistsUnindexed({$nor: [{b: {$exists: false}}]}, 1); // Unindexed field. + assertExists({$or: [{a: {$exists: true}}]}); // $exists:true not $exists:false. + + // Behavior is similar with $elemMatch. + coll.drop(); + assert.writeOK(coll.insert({a: [{}]})); + assert.writeOK(coll.insert({a: [{b: 1}]})); + assert.writeOK(coll.insert({a: [{b: [1]}]})); + setIndex('a.b'); + + assertMissing({a: {$elemMatch: {b: {$exists: false}}}}); + + // A $elemMatch predicate is treated as nested, and the index should be used for $exists:true. + assertExists({a: {$elemMatch: {b: {$exists: true}}}}); + + // A $not within $elemMatch should not attempt to use a sparse index for $exists:false. + assertExistsUnindexed({'a.b': {$elemMatch: {$not: {$exists: false}}}}, 1); + assertExistsUnindexed({'a.b': {$elemMatch: {$gt: 0, $not: {$exists: false}}}}, 1); + + // A non sparse index will not be disallowed. + coll.drop(); + assert.writeOK(coll.insert({})); + coll.ensureIndex({a: 1}); + assert.eq(1, coll.find({a: {$exists: false}}).itcount()); +})(); diff --git a/jstests/core/fsync.js b/jstests/core/fsync.js index 13ff20e4177..2ad625d88ba 100644 --- a/jstests/core/fsync.js +++ b/jstests/core/fsync.js @@ -6,6 +6,8 @@ * - Confirm that writes can progress after fsyncUnlock * - Confirm that the command can be run repeatedly without breaking things * - Confirm that the pseudo commands and eval can perform fsyncLock/Unlock + * + * @tags: [requires_eval_command] */ (function() { "use strict"; diff --git a/jstests/core/fts_dotted_prefix_fields.js b/jstests/core/fts_dotted_prefix_fields.js new file mode 100644 index 00000000000..f811c4a7203 --- /dev/null +++ b/jstests/core/fts_dotted_prefix_fields.js @@ -0,0 +1,15 @@ +// Test that text search works correct when the text index has dotted paths as the non-text +// prefixes. +(function() { + "use strict"; + + let coll = db.fts_dotted_prefix_fields; + coll.drop(); + assert.commandWorked(coll.createIndex({"a.x": 1, "a.y": 1, "b.x": 1, "b.y": 1, words: "text"})); + assert.writeOK(coll.insert({a: {x: 1, y: 2}, b: {x: 3, y: 4}, words: "lorem ipsum dolor sit"})); + assert.writeOK(coll.insert({a: {x: 1, y: 2}, b: {x: 5, y: 4}, words: "lorem ipsum dolor sit"})); + + assert.eq(1, + coll.find({$text: {$search: "lorem ipsum"}, "a.x": 1, "a.y": 2, "b.x": 3, "b.y": 4}) + .itcount()); +}()); diff --git a/jstests/core/fts_trailing_fields.js b/jstests/core/fts_trailing_fields.js new file mode 100644 index 00000000000..2c7f79b423d --- /dev/null +++ b/jstests/core/fts_trailing_fields.js @@ -0,0 +1,22 @@ +// Tests for predicates which can use the trailing field of a text index. +(function() { + "use strict"; + + const coll = db.fts_trailing_fields; + + coll.drop(); + assert.commandWorked(coll.createIndex({a: 1, b: "text", c: 1})); + assert.writeOK(coll.insert({a: 2, b: "lorem ipsum"})); + + assert.eq(0, coll.find({a: 2, $text: {$search: "lorem"}, c: {$exists: true}}).itcount()); + assert.eq(1, coll.find({a: 2, $text: {$search: "lorem"}, c: null}).itcount()); + assert.eq(1, coll.find({a: 2, $text: {$search: "lorem"}, c: {$exists: false}}).itcount()); + + // An equality predicate on the leading field isn't useful, but it shouldn't cause any problems. + // Same with an $elemMatch predicate on one of the trailing fields. + coll.drop(); + assert.commandWorked(coll.createIndex({a: 1, b: "text", "c.d": 1})); + assert.writeOK(coll.insert({a: 2, b: "lorem ipsum", c: {d: 3}})); + assert.eq(0, coll.find({a: [1, 2], $text: {$search: "lorem"}}).itcount()); + assert.eq(0, coll.find({a: 2, $text: {$search: "lorem"}, c: {$elemMatch: {d: 3}}}).itcount()); +}()); diff --git a/jstests/core/function_string_representations.js b/jstests/core/function_string_representations.js new file mode 100644 index 00000000000..af66e9160a9 --- /dev/null +++ b/jstests/core/function_string_representations.js @@ -0,0 +1,38 @@ +/** Demonstrate that mapReduce can accept functions represented by strings. + * Some drivers do not have a type which represents a Javascript function. These languages represent + * the arguments to mapReduce as strings. + */ + +(function() { + "use strict"; + + var col = db.function_string_representations; + col.drop(); + assert.writeOK(col.insert({ + _id: "abc123", + ord_date: new Date("Oct 04, 2012"), + status: 'A', + price: 25, + items: [{sku: "mmm", qty: 5, price: 2.5}, {sku: "nnn", qty: 5, price: 2.5}] + })); + + var mapFunction = "function() {emit(this._id, this.price);}"; + var reduceFunction = "function(keyCustId, valuesPrices) {return Array.sum(valuesPrices);}"; + assert.commandWorked(col.mapReduce(mapFunction, reduceFunction, {out: "map_reduce_example"})); + + // Provided strings may end with semicolons and/or whitespace + mapFunction += " ; "; + reduceFunction += " ; "; + assert.commandWorked(col.mapReduce(mapFunction, reduceFunction, {out: "map_reduce_example"})); + + // $where exhibits the same behavior + var whereFunction = "function() {return this.price === 25;}"; + assert.eq(1, col.find({$where: whereFunction}).itcount()); + + whereFunction += ";"; + assert.eq(1, col.find({$where: whereFunction}).itcount()); + + // db.eval does not need to be tested, as it accepts code fragments, not functions. + // system.js does not need to be tested, as its contents types' are preserved, and + // strings are not promoted into functions. +})(); diff --git a/jstests/core/geo_2d_trailing_fields.js b/jstests/core/geo_2d_trailing_fields.js new file mode 100644 index 00000000000..8f9c881ae4c --- /dev/null +++ b/jstests/core/geo_2d_trailing_fields.js @@ -0,0 +1,45 @@ +// Tests for predicates which can use the trailing field of a 2d index. +(function() { + "use strict"; + + const coll = db.geo_2d_trailing_fields; + + const isMaster = assert.commandWorked(db.adminCommand({isMaster: 1})); + const isMongos = (isMaster.msg === "isdbgrid"); + + coll.drop(); + assert.commandWorked(coll.createIndex({a: "2d", b: 1})); + assert.writeOK(coll.insert({a: [0, 0]})); + + // Verify that $near queries handle existence predicates over the trailing fields correctly. + if (!isMongos) { + assert.eq(0, coll.find({a: {$near: [0, 0]}, b: {$exists: true}}).itcount()); + assert.eq(1, coll.find({a: {$near: [0, 0]}, b: null}).itcount()); + assert.eq(1, coll.find({a: {$near: [0, 0]}, b: {$exists: false}}).itcount()); + } + + // Verify that non-near 2d queries handle existence predicates over the trailing fields + // correctly. + assert.eq(0, + coll.find({a: {$geoWithin: {$center: [[0, 0], 1]}}, b: {$exists: true}}).itcount()); + assert.eq(1, coll.find({a: {$geoWithin: {$center: [[0, 0], 1]}}, b: null}).itcount()); + assert.eq(1, + coll.find({a: {$geoWithin: {$center: [[0, 0], 1]}}, b: {$exists: false}}).itcount()); + + coll.drop(); + assert.commandWorked(coll.createIndex({a: "2d", "b.c": 1})); + assert.writeOK(coll.insert({a: [0, 0], b: [{c: 2}, {c: 3}]})); + + // Verify that $near queries correctly handle predicates which cannot be covered due to array + // semantics. + if (!isMongos) { + assert.eq(0, coll.find({a: {$near: [0, 0]}, "b.c": [2, 3]}).itcount()); + assert.eq(0, coll.find({a: {$near: [0, 0]}, "b.c": {$type: "array"}}).itcount()); + } + + // Verify that non-near 2d queries correctly handle predicates which cannot be covered due to + // array semantics. + assert.eq(0, coll.find({a: {$geoWithin: {$center: [[0, 0], 1]}}, "b.c": [2, 3]}).itcount()); + assert.eq( + 0, coll.find({a: {$geoWithin: {$center: [[0, 0], 1]}}, "b.c": {$type: "array"}}).itcount()); +}()); diff --git a/jstests/core/geo_s2cursorlimitskip.js b/jstests/core/geo_s2cursorlimitskip.js index 427fbf8fe29..dc645dc68af 100644 --- a/jstests/core/geo_s2cursorlimitskip.js +++ b/jstests/core/geo_s2cursorlimitskip.js @@ -1,4 +1,11 @@ // Test various cursor behaviors +// +// @tags: [ +// # This test attempts to enable profiling on a server and then get profiling data by reading +// # from the "system.profile" collection. The former operation must be routed to the primary in +// # a replica set, whereas the latter may be routed to a secondary. +// assumes_read_preference_unchanged, +// ] var testDB = db.getSiblingDB("geo_s2cursorlimitskip"); var t = testDB.geo_s2getmmm; diff --git a/jstests/core/geo_update_btree.js b/jstests/core/geo_update_btree.js index a85d4274415..b4fa24df57e 100644 --- a/jstests/core/geo_update_btree.js +++ b/jstests/core/geo_update_btree.js @@ -1,4 +1,6 @@ // Tests whether the geospatial search is stable under btree updates +// +// @tags: [assumes_write_concern_unchanged] var coll = db.getCollection("jstests_geo_update_btree"); coll.drop(); diff --git a/jstests/core/getlog2.js b/jstests/core/getlog2.js index 597a85e20ee..e5287ea8c1b 100644 --- a/jstests/core/getlog2.js +++ b/jstests/core/getlog2.js @@ -1,4 +1,11 @@ // tests getlog as well as slow querying logging +// +// @tags: [ +// # This test attempts to perform a find command and see that it ran using the getLog command. +// # The former operation may be routed to a secondary in the replica set, whereas the latter must +// # be routed to the primary. +// assumes_read_preference_unchanged, +// ] glcol = db.getLogTest2; glcol.drop(); diff --git a/jstests/core/index_elemmatch2.js b/jstests/core/index_elemmatch2.js new file mode 100644 index 00000000000..ecd24035284 --- /dev/null +++ b/jstests/core/index_elemmatch2.js @@ -0,0 +1,63 @@ +/** + * Test that queries containing $elemMatch correctly use an index if each child expression is + * compatible with the index. + */ +(function() { + "use strict"; + + load("jstests/libs/analyze_plan.js"); + + const coll = db.elemMatch_index; + coll.drop(); + + assert.writeOK(coll.insert({a: 1})); + assert.writeOK(coll.insert({a: [{}]})); + assert.writeOK(coll.insert({a: [1, null]})); + assert.writeOK(coll.insert({a: [{type: "Point", coordinates: [0, 0]}]})); + + assert.commandWorked(coll.createIndex({a: 1}, {sparse: true})); + + function assertIndexResults(coll, query, useIndex, nReturned) { + const explainPlan = coll.find(query).explain("executionStats"); + assert.eq(isIxscan(explainPlan.queryPlanner.winningPlan), useIndex); + assert.eq(explainPlan.executionStats.nReturned, nReturned); + } + + assertIndexResults(coll, {a: {$elemMatch: {$exists: false}}}, false, 0); + + // An $elemMatch predicate is treated as nested, and the index should be used for $exists:true. + assertIndexResults(coll, {a: {$elemMatch: {$exists: true}}}, true, 3); + + // $not within $elemMatch should not attempt to use a sparse index for $exists:false. + assertIndexResults(coll, {a: {$elemMatch: {$not: {$exists: false}}}}, false, 3); + assertIndexResults(coll, {a: {$elemMatch: {$gt: 0, $not: {$exists: false}}}}, false, 1); + + // $geo within $elemMatch should not attempt to use a non-geo index. + assertIndexResults( + coll, + { + a: { + $elemMatch: { + $geoWithin: { + $geometry: + {type: "Polygon", coordinates: [[[0, 0], [0, 1], [1, 0], [0, 0]]]} + } + } + } + }, + false, + 1); + + // $in with a null value within $elemMatch should use a sparse index. + assertIndexResults(coll, {a: {$elemMatch: {$in: [null]}}}, true, 1); + + // $eq with a null value within $elemMatch should use a sparse index. + assertIndexResults(coll, {a: {$elemMatch: {$eq: null}}}, true, 1); + + // A negated regex within $elemMatch should not use an index, sparse or not. + assertIndexResults(coll, {a: {$elemMatch: {$not: {$in: [/^a/]}}}}, false, 3); + + coll.dropIndexes(); + assert.commandWorked(coll.createIndex({a: 1})); + assertIndexResults(coll, {a: {$elemMatch: {$not: {$in: [/^a/]}}}}, false, 3); +})(); diff --git a/jstests/core/index_filter_commands.js b/jstests/core/index_filter_commands.js index 8684be3b2b9..58f78d0514e 100644 --- a/jstests/core/index_filter_commands.js +++ b/jstests/core/index_filter_commands.js @@ -6,20 +6,24 @@ * Displays index filters for all query shapes in a collection. * * - planCacheClearFilters - * Clears index filter for a single query shape or, - * if the query shape is omitted, all filters for the collection. + * Clears index filter for a single query shape or, if the query shape is omitted, all filters for + * the collection. * * - planCacheSetFilter * Sets index filter for a query shape. Overrides existing filter. * - * Not a lot of data access in this test suite. Hint commands - * manage a non-persistent mapping in the server of - * query shape to list of index specs. + * Not a lot of data access in this test suite. Hint commands manage a non-persistent mapping in the + * server of query shape to list of index specs. * - * Only time we might need to execute a query is to check the plan - * cache state. We would do this with the planCacheListPlans command - * on the same query shape with the index filters. + * Only time we might need to execute a query is to check the plan cache state. We would do this + * with the planCacheListPlans command on the same query shape with the index filters. * + * @tags: [ + * # This test attempts to perform queries with plan cache filters set up. The former operation + * # may be routed to a secondary in the replica set, whereas the latter must be routed to the + * # primary. + * assumes_read_preference_unchanged, + * ] */ load("jstests/libs/analyze_plan.js"); diff --git a/jstests/core/index_stats.js b/jstests/core/index_stats.js index 60b37fd571e..ee4d13d4d0a 100644 --- a/jstests/core/index_stats.js +++ b/jstests/core/index_stats.js @@ -1,3 +1,10 @@ +// @tags: [ +// # This test attempts to perform write operations and get index usage statistics using the +// # $indexStats stage. The former operation must be routed to the primary in a replica set, +// # whereas the latter may be routed to a secondary. +// assumes_read_preference_unchanged, +// ] + (function() { "use strict"; diff --git a/jstests/core/js3.js b/jstests/core/js3.js index 4d46c25bbf7..c808e7ec75a 100644 --- a/jstests/core/js3.js +++ b/jstests/core/js3.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.jstests_js3; diff --git a/jstests/core/js7.js b/jstests/core/js7.js index 810f4692d4f..99cddd114f8 100644 --- a/jstests/core/js7.js +++ b/jstests/core/js7.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + t = db.jstests_js7; t.drop(); diff --git a/jstests/core/js9.js b/jstests/core/js9.js index 515fa883aea..e0703ac39ea 100644 --- a/jstests/core/js9.js +++ b/jstests/core/js9.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + c = db.jstests_js9; c.drop(); diff --git a/jstests/core/js_jit.js b/jstests/core/js_jit.js new file mode 100644 index 00000000000..4ccdd2917ae --- /dev/null +++ b/jstests/core/js_jit.js @@ -0,0 +1,40 @@ +/** + * Validate various native types continue to work when run in JITed code. + * + * In SERVER-30362, the JIT would not compile natives types which had custom getProperty + * implementations correctly. We force the JIT to kick in by using large loops. + */ +(function() { + 'use strict'; + + function testDBCollection() { + const c = new DBCollection(null, null, "foo", "test.foo"); + for (let i = 0; i < 100000; i++) { + if (c.toString() != "test.foo") { + throw i; + } + } + } + + function testDB() { + const c = new DB(null, "test"); + for (let i = 0; i < 100000; i++) { + if (c.toString() != "test") { + throw i; + } + } + } + + function testDBQuery() { + const c = DBQuery('a', 'b', 'c', 'd'); + for (let i = 0; i < 100000; i++) { + if (c.toString() != "DBQuery: d -> null") { + throw i; + } + } + } + + testDBCollection(); + testDB(); + testDBQuery(); +})();
\ No newline at end of file diff --git a/jstests/core/list_collections1.js b/jstests/core/list_collections1.js index c8c3f92fbc9..ff1aac304ff 100644 --- a/jstests/core/list_collections1.js +++ b/jstests/core/list_collections1.js @@ -72,8 +72,8 @@ // var getListCollectionsCursor = function(options, subsequentBatchSize) { - return new DBCommandCursor( - mydb.getMongo(), mydb.runCommand("listCollections", options), subsequentBatchSize); + var res = mydb.runCommand("listCollections", options); + return new DBCommandCursor(res._mongo, res, subsequentBatchSize); }; var cursorCountMatching = function(cursor, pred) { @@ -282,9 +282,9 @@ assert.commandWorked(mydb.createCollection("quux")); res = mydb.runCommand("listCollections", {cursor: {batchSize: 0}}); - cursor = new DBCommandCursor(mydb.getMongo(), res, 2); + cursor = new DBCommandCursor(res._mongo, res, 2); cursor.close(); - cursor = new DBCommandCursor(mydb.getMongo(), res, 2); + cursor = new DBCommandCursor(res._mongo, res, 2); assert.throws(function() { cursor.hasNext(); }); diff --git a/jstests/core/list_collections_filter.js b/jstests/core/list_collections_filter.js index e0d18f055d0..0516d57a417 100644 --- a/jstests/core/list_collections_filter.js +++ b/jstests/core/list_collections_filter.js @@ -19,8 +19,8 @@ filter = {}; } - var cursor = new DBCommandCursor(mydb.getMongo(), - mydb.runCommand("listCollections", {filter: filter})); + var res = mydb.runCommand("listCollections", {filter: filter}); + var cursor = new DBCommandCursor(res._mongo, res); function stripToName(result) { return result.name; } diff --git a/jstests/core/list_indexes.js b/jstests/core/list_indexes.js index de0f4473980..6ec9a7a13e1 100644 --- a/jstests/core/list_indexes.js +++ b/jstests/core/list_indexes.js @@ -27,8 +27,8 @@ // var getListIndexesCursor = function(coll, options, subsequentBatchSize) { - return new DBCommandCursor( - coll.getDB().getMongo(), coll.runCommand("listIndexes", options), subsequentBatchSize); + var res = coll.runCommand("listIndexes", options); + return new DBCommandCursor(res._mongo, res, subsequentBatchSize); }; var cursorGetIndexSpecs = function(cursor) { @@ -163,9 +163,9 @@ assert.commandWorked(coll.ensureIndex({c: 1}, {unique: true})); res = coll.runCommand("listIndexes", {cursor: {batchSize: 0}}); - cursor = new DBCommandCursor(coll.getDB().getMongo(), res, 2); + cursor = new DBCommandCursor(res._mongo, res, 2); cursor.close(); - cursor = new DBCommandCursor(coll.getDB().getMongo(), res, 2); + cursor = new DBCommandCursor(res._mongo, res, 2); assert.throws(function() { cursor.hasNext(); }); diff --git a/jstests/core/list_indexes_invalidation.js b/jstests/core/list_indexes_invalidation.js index b8cbe5eb134..9fe94de5efc 100644 --- a/jstests/core/list_indexes_invalidation.js +++ b/jstests/core/list_indexes_invalidation.js @@ -19,7 +19,7 @@ printjson(res); // Ensure the cursor has data, rename or drop the collection, and exhaust the cursor. - let cursor = new DBCommandCursor(db.getMongo(), res); + let cursor = new DBCommandCursor(res._mongo, res); let errMsg = 'expected more data from command ' + tojson(cmd) + ', with result ' + tojson(res); assert(cursor.hasNext(), errMsg); diff --git a/jstests/core/list_namespaces_invalidation.js b/jstests/core/list_namespaces_invalidation.js index 6f8033b5fe4..85eb26510bb 100644 --- a/jstests/core/list_namespaces_invalidation.js +++ b/jstests/core/list_namespaces_invalidation.js @@ -1,4 +1,6 @@ // SERVER-27996/SERVER-28022 Missing invalidation for system.namespaces writes +// +// @tags: [requires_collmod_command] (function() { 'use strict'; let dbInvalidName = 'system_namespaces_invalidations'; diff --git a/jstests/core/max_time_ms.js b/jstests/core/max_time_ms.js index 0442ffcba68..efe3dabcc6d 100644 --- a/jstests/core/max_time_ms.js +++ b/jstests/core/max_time_ms.js @@ -1,4 +1,12 @@ // Tests query/command option $maxTimeMS. +// +// @tags: [ +// # This test attempts to perform read operations after having enabled the maxTimeAlwaysTimeOut +// # failpoint. The former operations may be routed to a secondary in the replica set, whereas the +// # latter must be routed to the primary. +// assumes_read_preference_unchanged, +// requires_collmod_command, +// ] var t = db.max_time_ms; var exceededTimeLimit = 50; // ErrorCodes::ExceededTimeLimit diff --git a/jstests/core/mr4.js b/jstests/core/mr4.js index 58ea303f7e8..683583acecd 100644 --- a/jstests/core/mr4.js +++ b/jstests/core/mr4.js @@ -9,7 +9,7 @@ t.save({x: 4, tags: ["b", "c"]}); m = function() { this.tags.forEach(function(z) { - emit(z, {count: xx}); + emit(z, {count: xx.val}); }); }; @@ -21,7 +21,7 @@ r = function(key, values) { return {count: total}; }; -res = t.mapReduce(m, r, {out: "mr4_out", scope: {xx: 1}}); +res = t.mapReduce(m, r, {out: "mr4_out", scope: {xx: {val: 1}}}); z = res.convertToSingleObject(); assert.eq(3, Object.keySet(z).length, "A1"); @@ -31,7 +31,7 @@ assert.eq(3, z.c.count, "A4"); res.drop(); -res = t.mapReduce(m, r, {scope: {xx: 2}, out: "mr4_out"}); +res = t.mapReduce(m, r, {scope: {xx: {val: 2}}, out: "mr4_out"}); z = res.convertToSingleObject(); assert.eq(3, Object.keySet(z).length, "A1"); diff --git a/jstests/core/mr_killop.js b/jstests/core/mr_killop.js index 78e98f0bcaa..e85986e2fc0 100644 --- a/jstests/core/mr_killop.js +++ b/jstests/core/mr_killop.js @@ -4,7 +4,7 @@ t = db.jstests_mr_killop; t.drop(); t2 = db.jstests_mr_killop_out; t2.drop(); - +db.adminCommand({"configureFailPoint": 'mr_killop_test_fp', "mode": 'alwaysOn'}); function debug(x) { // printjson( x ); } @@ -171,3 +171,4 @@ var loop = function() { }; runMRTests(loop, false); runFinalizeTests(loop, false); +db.adminCommand({"configureFailPoint": 'mr_killop_test_fp', "mode": 'off'}); diff --git a/jstests/core/mr_optim.js b/jstests/core/mr_optim.js index 7437753ca67..1c525ae3de3 100644 --- a/jstests/core/mr_optim.js +++ b/jstests/core/mr_optim.js @@ -3,8 +3,17 @@ t = db.mr_optim; t.drop(); +// We drop the output collection to ensure the test can be run multiple times successfully. We +// explicitly avoid using the DBCollection#drop() shell helper to avoid implicitly sharding the +// collection during the sharded_collections_jscore_passthrough.yml test suite when reading the +// results from the output collection in the reformat() function. +var res = db.runCommand({drop: "mr_optim_out"}); +if (res.ok !== 1) { + assert.commandFailedWithCode(res, ErrorCodes.NamespaceNotFound); +} + for (var i = 0; i < 1000; ++i) { - t.save({a: Math.random(1000), b: Math.random(10000)}); + assert.writeOK(t.save({a: Math.random(1000), b: Math.random(10000)})); } function m() { @@ -21,7 +30,7 @@ function reformat(r) { if (r.results) cursor = r.results; else - cursor = r.find(); + cursor = r.find().sort({_id: 1}); cursor.forEach(function(z) { x[z._id] = z.value; }); @@ -43,4 +52,4 @@ res.drop(); assert.eq(x, x2, "object from inline and collection are not equal"); -t.drop();
\ No newline at end of file +t.drop(); diff --git a/jstests/core/no_db_created.js b/jstests/core/no_db_created.js index 3491914d470..b67b193494c 100644 --- a/jstests/core/no_db_created.js +++ b/jstests/core/no_db_created.js @@ -1,4 +1,6 @@ // checks that operations do not create a database +// +// @tags: [requires_collmod_command] (function() { "use strict"; @@ -32,4 +34,4 @@ noDB(mydb); assert.writeOK(coll.insert({})); mydb.dropDatabase(); -}());
\ No newline at end of file +}()); diff --git a/jstests/core/notablescan.js b/jstests/core/notablescan.js index 80306c08cf2..bb4c170a603 100644 --- a/jstests/core/notablescan.js +++ b/jstests/core/notablescan.js @@ -1,4 +1,11 @@ // check notablescan mode +// +// @tags: [ +// # This test attempts to perform read operations after having enabled the notablescan server +// # parameter. The former operations may be routed to a secondary in the replica set, whereas the +// # latter must be routed to the primary. +// assumes_read_preference_unchanged, +// ] t = db.test_notablescan; t.drop(); diff --git a/jstests/core/operation_latency_histogram.js b/jstests/core/operation_latency_histogram.js index 1e3f1a59b95..947a8be6520 100644 --- a/jstests/core/operation_latency_histogram.js +++ b/jstests/core/operation_latency_histogram.js @@ -1,4 +1,10 @@ // Checks that histogram counters for collections are updated as we expect. +// +// This test attempts to perform write operations and get latency statistics using the $collStats +// stage. The former operation must be routed to the primary in a replica set, whereas the latter +// may be routed to a secondary. +// +// @tags: [assumes_read_preference_unchanged] (function() { "use strict"; diff --git a/jstests/core/plan_cache_clear.js b/jstests/core/plan_cache_clear.js index 8f9cf0ea302..778239616b5 100644 --- a/jstests/core/plan_cache_clear.js +++ b/jstests/core/plan_cache_clear.js @@ -1,5 +1,12 @@ // Test clearing of the plan cache, either manually through the planCacheClear command, // or due to system events such as an index build. +// +// @tags: [ +// # This test attempts to perform queries and introspect/manipulate the server's plan cache +// # entries. The former operation may be routed to a secondary in the replica set, whereas the +// # latter must be routed to the primary. +// assumes_read_preference_unchanged, +// ] var t = db.jstests_plan_cache_clear; t.drop(); diff --git a/jstests/core/plan_cache_list_plans.js b/jstests/core/plan_cache_list_plans.js index 7ca599483ff..3359980ab07 100644 --- a/jstests/core/plan_cache_list_plans.js +++ b/jstests/core/plan_cache_list_plans.js @@ -1,4 +1,11 @@ // Test the planCacheListPlans command. +// +// @tags: [ +// # This test attempts to perform queries and introspect the server's plan cache entries. The +// # former operation may be routed to a secondary in the replica set, whereas the latter must be +// # routed to the primary. +// assumes_read_preference_unchanged, +// ] var t = db.jstests_plan_cache_list_plans; t.drop(); diff --git a/jstests/core/plan_cache_list_shapes.js b/jstests/core/plan_cache_list_shapes.js index 1c9ecdf9e1b..61c3111cd8a 100644 --- a/jstests/core/plan_cache_list_shapes.js +++ b/jstests/core/plan_cache_list_shapes.js @@ -1,5 +1,12 @@ // Test the planCacheListQueryShapes command, which returns a list of query shapes // for the queries currently cached in the collection. +// +// @tags: [ +// # This test attempts to perform queries with plan cache filters set up. The former operation +// # may be routed to a secondary in the replica set, whereas the latter must be routed to the +// # primary. +// assumes_read_preference_unchanged, +// ] var t = db.jstests_plan_cache_list_shapes; t.drop(); diff --git a/jstests/core/plan_cache_shell_helpers.js b/jstests/core/plan_cache_shell_helpers.js index dc990b19dcc..6c4ff185014 100644 --- a/jstests/core/plan_cache_shell_helpers.js +++ b/jstests/core/plan_cache_shell_helpers.js @@ -1,4 +1,11 @@ // Test the shell helpers which wrap the plan cache commands. +// +// @tags: [ +// # This test attempts to perform queries and introspect the server's plan cache entries. The +// # former operation may be routed to a secondary in the replica set, whereas the latter must be +// # routed to the primary. +// assumes_read_preference_unchanged, +// ] var t = db.jstests_plan_cache_shell_helpers; t.drop(); diff --git a/jstests/core/profile2.js b/jstests/core/profile2.js index bb1605abd1e..9da6410d2ac 100644 --- a/jstests/core/profile2.js +++ b/jstests/core/profile2.js @@ -24,7 +24,7 @@ assert(result.hasOwnProperty('millis')); assert(result.hasOwnProperty('query')); assert.eq('string', typeof(result.query)); // String value is truncated. -assert(result.query.match(/filter: { a: "a+\.\.\." } }$/)); +assert(result.query.match(/filter: { a: "a+\.\.\." }/)); assert.commandWorked(coll.getDB().runCommand({profile: 0})); coll.getDB().system.profile.drop(); diff --git a/jstests/core/profile_getmore.js b/jstests/core/profile_getmore.js index a9272567b1a..3f6f492597e 100644 --- a/jstests/core/profile_getmore.js +++ b/jstests/core/profile_getmore.js @@ -24,12 +24,13 @@ var cursor = coll.find({a: {$gt: 0}}).sort({a: 1}).batchSize(2); cursor.next(); // Perform initial query and consume first of 2 docs returned. - var cursorId = getLatestProfilerEntry(testDB).cursorid; // Save cursorid from find. + var cursorId = + getLatestProfilerEntry(testDB, {op: "query"}).cursorid; // Save cursorid from find. cursor.next(); // Consume second of 2 docs from initial query. cursor.next(); // getMore performed, leaving open cursor. - var profileObj = getLatestProfilerEntry(testDB); + var profileObj = getLatestProfilerEntry(testDB, {op: "getmore"}); assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj)); assert.eq(profileObj.op, "getmore", tojson(profileObj)); @@ -67,7 +68,7 @@ cursor.next(); // Consume second of 2 docs from initial query. cursor.next(); // getMore performed, leaving open cursor. - profileObj = getLatestProfilerEntry(testDB); + profileObj = getLatestProfilerEntry(testDB, {op: "getmore"}); assert.eq(profileObj.hasSortStage, true, tojson(profileObj)); @@ -83,7 +84,7 @@ cursor.next(); // Perform initial query and consume first of 3 docs returned. cursor.itcount(); // Exhaust the cursor. - profileObj = getLatestProfilerEntry(testDB); + profileObj = getLatestProfilerEntry(testDB, {op: "getmore"}); assert(profileObj.hasOwnProperty("cursorid"), tojson(profileObj)); // cursorid should always be present on getMore. @@ -101,12 +102,12 @@ assert.commandWorked(coll.createIndex({a: 1})); var cursor = coll.aggregate([{$match: {a: {$gte: 0}}}], {cursor: {batchSize: 0}}); - var cursorId = getLatestProfilerEntry(testDB).cursorid; + var cursorId = getLatestProfilerEntry(testDB, {"command.aggregate": coll.getName()}).cursorid; assert.neq(0, cursorId); cursor.next(); // Consume the result set. - profileObj = getLatestProfilerEntry(testDB); + profileObj = getLatestProfilerEntry(testDB, {op: "getmore"}); assert.eq(profileObj.ns, coll.getFullName(), tojson(profileObj)); assert.eq(profileObj.op, "getmore", tojson(profileObj)); diff --git a/jstests/core/profile_insert.js b/jstests/core/profile_insert.js index 3994c896bda..836ec2ecff2 100644 --- a/jstests/core/profile_insert.js +++ b/jstests/core/profile_insert.js @@ -1,4 +1,6 @@ // Confirms that profiled insert execution contains all expected metrics with proper values. +// +// @tags: [assumes_write_concern_unchanged] (function() { "use strict"; diff --git a/jstests/core/recursion.js b/jstests/core/recursion.js index 926250be20d..1db491c8ae9 100644 --- a/jstests/core/recursion.js +++ b/jstests/core/recursion.js @@ -1,5 +1,7 @@ -// Basic tests for a form of stack recursion that's been shown to cause C++ -// side stack overflows in the past. See SERVER-19614. +// Basic tests for a form of stack recursion that's been shown to cause C++ side stack overflows in +// the past. See SERVER-19614. +// +// @tags: [requires_eval_command] (function() { "use strict"; diff --git a/jstests/core/regex_not_id.js b/jstests/core/regex_not_id.js index 1f15250f240..35b2c858867 100644 --- a/jstests/core/regex_not_id.js +++ b/jstests/core/regex_not_id.js @@ -3,10 +3,10 @@ var testColl = db.regex_not_id; testColl.drop(); -assert.writeOK(testColl.insert({_id: "ABCDEF1"}, {writeConcern: {w: 1}})); +assert.writeOK(testColl.insert({_id: "ABCDEF1"})); // Should be an error. -assert.writeError(testColl.insert({_id: /^A/}, {writeConcern: {w: 1}})); +assert.writeError(testColl.insert({_id: /^A/})); // _id doesn't have to be first; still disallowed -assert.writeError(testColl.insert({xxx: "ABCDEF", _id: /ABCDEF/}, {writeConcern: {w: 1}}));
\ No newline at end of file +assert.writeError(testColl.insert({xxx: "ABCDEF", _id: /ABCDEF/})); diff --git a/jstests/core/remove8.js b/jstests/core/remove8.js index 563e4708cf9..3c9fd6a11a1 100644 --- a/jstests/core/remove8.js +++ b/jstests/core/remove8.js @@ -1,3 +1,4 @@ +// @tags: [requires_eval_command] t = db.remove8; t.drop(); diff --git a/jstests/core/rename4.js b/jstests/core/rename4.js index 185193deaa9..756918db5f6 100644 --- a/jstests/core/rename4.js +++ b/jstests/core/rename4.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + t = db.jstests_rename4; t.drop(); diff --git a/jstests/core/rename_stayTemp.js b/jstests/core/rename_stayTemp.js index d8451af2d2d..8dee8f89fea 100644 --- a/jstests/core/rename_stayTemp.js +++ b/jstests/core/rename_stayTemp.js @@ -11,7 +11,7 @@ function ns(coll) { function istemp(name) { var result = db.runCommand("listCollections", {filter: {name: name}}); assert(result.ok); - var collections = new DBCommandCursor(db.getMongo(), result).toArray(); + var collections = new DBCommandCursor(result._mongo, result).toArray(); assert.eq(1, collections.length); return collections[0].options.temp ? true : false; } diff --git a/jstests/core/shell_connection_strings.js b/jstests/core/shell_connection_strings.js new file mode 100644 index 00000000000..22861e6ce25 --- /dev/null +++ b/jstests/core/shell_connection_strings.js @@ -0,0 +1,33 @@ +// Test mongo shell connect strings. +(function() { + 'use strict'; + + const mongod = new MongoURI(db.getMongo().host).servers[0]; + const host = mongod.host; + const port = mongod.port; + + function testConnect(ok, ...args) { + const exitCode = runMongoProgram('mongo', '--eval', ';', ...args); + if (ok) { + assert.eq(exitCode, 0, "failed to connect with `" + args.join(' ') + "`"); + } else { + assert.neq( + exitCode, 0, "unexpectedly succeeded connecting with `" + args.join(' ') + "`"); + } + } + + testConnect(true, `${host}:${port}`); + testConnect(true, `${host}:${port}/test`); + testConnect(true, `${host}:${port}/admin`); + testConnect(true, host, '--port', port); + testConnect(true, '--host', host, '--port', port, 'test'); + testConnect(true, '--host', host, '--port', port, 'admin'); + testConnect(true, `mongodb://${host}:${port}/test`); + testConnect(true, `mongodb://${host}:${port}/test?connectTimeoutMS=10000`); + + // if a full URI is provided, you cannot also specify host or port + testConnect(false, `${host}/test`, '--port', port); + testConnect(false, `mongodb://${host}:${port}/test`, '--port', port); + testConnect(false, `mongodb://${host}:${port}/test`, '--host', host); + testConnect(false, `mongodb://${host}:${port}/test`, '--host', host, '--port', port); +})(); diff --git a/jstests/core/shell_writeconcern.js b/jstests/core/shell_writeconcern.js index f3f190061cf..e3e7a23a9aa 100644 --- a/jstests/core/shell_writeconcern.js +++ b/jstests/core/shell_writeconcern.js @@ -1,7 +1,10 @@ "use strict"; + // check that shell writeconcern work correctly // 1.) tests that it can be set on each level and is inherited // 2.) tests that each operation (update/insert/remove/save) take and ensure a write concern +// +// @tags: [assumes_write_concern_unchanged] var collA = db.shell_wc_a; var collB = db.shell_wc_b; diff --git a/jstests/core/stages_delete.js b/jstests/core/stages_delete.js index f8e7380c75a..98a270cedf3 100644 --- a/jstests/core/stages_delete.js +++ b/jstests/core/stages_delete.js @@ -1,3 +1,9 @@ +// @tags: [ +// # This test attempts to remove documents using the stageDebug command, which doesn't support +// # specifying a writeConcern. +// assumes_write_concern_unchanged, +// ] + // Test basic delete stage functionality. var coll = db.stages_delete; var collScanStage = {cscan: {args: {direction: 1}, filter: {deleteMe: true}}}; diff --git a/jstests/core/startup_log.js b/jstests/core/startup_log.js index 3b0cbe3464d..c73013d1744 100644 --- a/jstests/core/startup_log.js +++ b/jstests/core/startup_log.js @@ -1,101 +1,108 @@ -load('jstests/aggregation/extras/utils.js');
-
-(function() {
- 'use strict';
-
- // Check that smallArray is entirely contained by largeArray
- // returns false if a member of smallArray is not in largeArray
- function arrayIsSubset(smallArray, largeArray) {
- for (var i = 0; i < smallArray.length; i++) {
- if (!Array.contains(largeArray, smallArray[i])) {
- print("Could not find " + smallArray[i] + " in largeArray");
- return false;
- }
- }
-
- return true;
- }
-
- // Test startup_log
- var stats = db.getSisterDB("local").startup_log.stats();
- assert(stats.capped);
-
- var latestStartUpLog =
- db.getSisterDB("local").startup_log.find().sort({$natural: -1}).limit(1).next();
- var serverStatus = db._adminCommand("serverStatus");
- var cmdLine = db._adminCommand("getCmdLineOpts").parsed;
-
- // Test that the startup log has the expected keys
- var verbose = false;
- var expectedKeys =
- ["_id", "hostname", "startTime", "startTimeLocal", "cmdLine", "pid", "buildinfo"];
- var keys = Object.keySet(latestStartUpLog);
- assert(arrayEq(expectedKeys, keys, verbose), 'startup_log keys failed');
-
- // Tests _id implicitly - should be comprised of host-timestamp
- // Setup expected startTime and startTimeLocal from the supplied timestamp
- var _id = latestStartUpLog._id.split('-'); // _id should consist of host-timestamp
- var _idUptime = _id.pop();
- var _idHost = _id.join('-');
- var uptimeSinceEpochRounded = Math.floor(_idUptime / 1000) * 1000;
- var startTime = new Date(uptimeSinceEpochRounded); // Expected startTime
-
- assert.eq(_idHost, latestStartUpLog.hostname, "Hostname doesn't match one from _id");
- assert.eq(serverStatus.host.split(':')[0],
- latestStartUpLog.hostname,
- "Hostname doesn't match one in server status");
- assert.closeWithinMS(startTime,
- latestStartUpLog.startTime,
- "StartTime doesn't match one from _id",
- 2000); // Expect less than 2 sec delta
- assert.eq(cmdLine, latestStartUpLog.cmdLine, "cmdLine doesn't match that from getCmdLineOpts");
- assert.eq(serverStatus.pid, latestStartUpLog.pid, "pid doesn't match that from serverStatus");
-
- // Test buildinfo
- var buildinfo = db.runCommand("buildinfo");
- delete buildinfo.ok; // Delete extra meta info not in startup_log
- var isMaster = db._adminCommand("ismaster");
-
- // Test buildinfo has the expected keys
- var expectedKeys = [
- "version",
- "gitVersion",
- "allocator",
- "versionArray",
- "javascriptEngine",
- "openssl",
- "buildEnvironment",
- "debug",
- "maxBsonObjectSize",
- "bits",
- "modules"
- ];
-
- var keys = Object.keySet(latestStartUpLog.buildinfo);
- // Disabled to check
- assert(arrayIsSubset(expectedKeys, keys),
- "buildinfo keys failed! \n expected:\t" + expectedKeys + "\n actual:\t" + keys);
- assert.eq(buildinfo,
- latestStartUpLog.buildinfo,
- "buildinfo doesn't match that from buildinfo command");
-
- // Test version and version Array
- var version = latestStartUpLog.buildinfo.version.split('-')[0];
- var versionArray = latestStartUpLog.buildinfo.versionArray;
- var versionArrayCleaned = versionArray.slice(0, 3);
- if (versionArray[3] == -100) {
- versionArrayCleaned[2] -= 1;
- }
-
- assert.eq(serverStatus.version,
- latestStartUpLog.buildinfo.version,
- "Mongo version doesn't match that from ServerStatus");
- assert.eq(
- version, versionArrayCleaned.join('.'), "version doesn't match that from the versionArray");
- var jsEngine = latestStartUpLog.buildinfo.javascriptEngine;
- assert((jsEngine == "none") || jsEngine.startsWith("mozjs"));
- assert.eq(isMaster.maxBsonObjectSize,
- latestStartUpLog.buildinfo.maxBsonObjectSize,
- "maxBsonObjectSize doesn't match one from ismaster");
-
-})();
+/** + * This test attempts to read from the "local.startup_log" collection and assert that it has an + * entry matching the server's response from the "getCmdLineOpts" command. The former operation may + * be routed to a secondary in the replica set, whereas the latter must be routed to the primary. + * + * @tags: [assumes_read_preference_unchanged] + */ +load('jstests/aggregation/extras/utils.js'); + +(function() { + 'use strict'; + + // Check that smallArray is entirely contained by largeArray + // returns false if a member of smallArray is not in largeArray + function arrayIsSubset(smallArray, largeArray) { + for (var i = 0; i < smallArray.length; i++) { + if (!Array.contains(largeArray, smallArray[i])) { + print("Could not find " + smallArray[i] + " in largeArray"); + return false; + } + } + + return true; + } + + // Test startup_log + var stats = db.getSisterDB("local").startup_log.stats(); + assert(stats.capped); + + var latestStartUpLog = + db.getSisterDB("local").startup_log.find().sort({$natural: -1}).limit(1).next(); + var serverStatus = db._adminCommand("serverStatus"); + var cmdLine = db._adminCommand("getCmdLineOpts").parsed; + + // Test that the startup log has the expected keys + var verbose = false; + var expectedKeys = + ["_id", "hostname", "startTime", "startTimeLocal", "cmdLine", "pid", "buildinfo"]; + var keys = Object.keySet(latestStartUpLog); + assert(arrayEq(expectedKeys, keys, verbose), 'startup_log keys failed'); + + // Tests _id implicitly - should be comprised of host-timestamp + // Setup expected startTime and startTimeLocal from the supplied timestamp + var _id = latestStartUpLog._id.split('-'); // _id should consist of host-timestamp + var _idUptime = _id.pop(); + var _idHost = _id.join('-'); + var uptimeSinceEpochRounded = Math.floor(_idUptime / 1000) * 1000; + var startTime = new Date(uptimeSinceEpochRounded); // Expected startTime + + assert.eq(_idHost, latestStartUpLog.hostname, "Hostname doesn't match one from _id"); + assert.eq(serverStatus.host.split(':')[0], + latestStartUpLog.hostname, + "Hostname doesn't match one in server status"); + assert.closeWithinMS(startTime, + latestStartUpLog.startTime, + "StartTime doesn't match one from _id", + 2000); // Expect less than 2 sec delta + assert.eq(cmdLine, latestStartUpLog.cmdLine, "cmdLine doesn't match that from getCmdLineOpts"); + assert.eq(serverStatus.pid, latestStartUpLog.pid, "pid doesn't match that from serverStatus"); + + // Test buildinfo + var buildinfo = db.runCommand("buildinfo"); + delete buildinfo.ok; // Delete extra meta info not in startup_log + var isMaster = db._adminCommand("ismaster"); + + // Test buildinfo has the expected keys + var expectedKeys = [ + "version", + "gitVersion", + "allocator", + "versionArray", + "javascriptEngine", + "openssl", + "buildEnvironment", + "debug", + "maxBsonObjectSize", + "bits", + "modules" + ]; + + var keys = Object.keySet(latestStartUpLog.buildinfo); + // Disabled to check + assert(arrayIsSubset(expectedKeys, keys), + "buildinfo keys failed! \n expected:\t" + expectedKeys + "\n actual:\t" + keys); + assert.eq(buildinfo, + latestStartUpLog.buildinfo, + "buildinfo doesn't match that from buildinfo command"); + + // Test version and version Array + var version = latestStartUpLog.buildinfo.version.split('-')[0]; + var versionArray = latestStartUpLog.buildinfo.versionArray; + var versionArrayCleaned = versionArray.slice(0, 3); + if (versionArray[3] == -100) { + versionArrayCleaned[2] -= 1; + } + + assert.eq(serverStatus.version, + latestStartUpLog.buildinfo.version, + "Mongo version doesn't match that from ServerStatus"); + assert.eq( + version, versionArrayCleaned.join('.'), "version doesn't match that from the versionArray"); + var jsEngine = latestStartUpLog.buildinfo.javascriptEngine; + assert((jsEngine == "none") || jsEngine.startsWith("mozjs")); + assert.eq(isMaster.maxBsonObjectSize, + latestStartUpLog.buildinfo.maxBsonObjectSize, + "maxBsonObjectSize doesn't match one from ismaster"); + +})(); diff --git a/jstests/core/storefunc.js b/jstests/core/storefunc.js index 8598e9cc62b..15abc56421e 100644 --- a/jstests/core/storefunc.js +++ b/jstests/core/storefunc.js @@ -1,3 +1,5 @@ +// @tags: [requires_eval_command] + // Use a private sister database to avoid conflicts with other tests that use system.js var testdb = db.getSisterDB("storefunc"); var res; diff --git a/jstests/core/top.js b/jstests/core/top.js index 819b41b0981..3d98f5a7b2d 100644 --- a/jstests/core/top.js +++ b/jstests/core/top.js @@ -1,5 +1,11 @@ /** * 1. check top numbers are correct + * + * This test attempts to perform read operations and get statistics using the top command. The + * former operation may be routed to a secondary in the replica set, whereas the latter must be + * routed to the primary. + * + * @tags: [assumes_read_preference_unchanged] */ (function() { load("jstests/libs/stats.js"); diff --git a/jstests/core/update_affects_indexes.js b/jstests/core/update_affects_indexes.js new file mode 100644 index 00000000000..91db3ebe565 --- /dev/null +++ b/jstests/core/update_affects_indexes.js @@ -0,0 +1,100 @@ +// This is a regression test for SERVER-32048. It checks that index keys are correctly updated when +// an update modifier implicitly creates a new array element. +(function() { + "use strict"; + + let coll = db.update_affects_indexes; + coll.drop(); + let indexKeyPattern = {"a.b": 1}; + assert.commandWorked(coll.createIndex(indexKeyPattern)); + + // Tests that the document 'docId' has all the index keys in 'expectedKeys' and none of the + // index keys in 'unexpectedKeys'. + function assertExpectedIndexKeys(docId, expectedKeys, unexpectedKeys) { + for (let key of expectedKeys) { + let res = coll.find(docId).hint(indexKeyPattern).min(key).returnKey().toArray(); + assert.eq(1, res.length, tojson(res)); + assert.eq(key, res[0]); + } + + for (let key of unexpectedKeys) { + let res = coll.find(docId).hint(indexKeyPattern).min(key).returnKey().toArray(); + if (res.length > 0) { + assert.eq(1, res.length, tojson(res)); + assert.neq(key, res[0]); + } + } + } + + // $set implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 0, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 0}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 0}, {$set: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 0}, [{"a.b": 0}, {"a.b": null}], []); + + // $set implicitly creates array element beyond end of array. + assert.writeOK(coll.insert({_id: 1, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 1}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 1}, {$set: {"a.3.c": 0}})); + assertExpectedIndexKeys({_id: 1}, [{"a.b": 0}, {"a.b": null}], []); + + // $set implicitly creates array element in empty array (no index key changes needed). + assert.writeOK(coll.insert({_id: 2, a: []})); + assertExpectedIndexKeys({_id: 2}, [{"a.b": null}], []); + assert.writeOK(coll.update({_id: 2}, {$set: {"a.0.c": 0}})); + assertExpectedIndexKeys({_id: 2}, [{"a.b": null}], []); + + // $inc implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 3, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 3}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 3}, {$inc: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 3}, [{"a.b": 0}, {"a.b": null}], []); + + // $mul implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 4, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 4}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 4}, {$mul: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 4}, [{"a.b": 0}, {"a.b": null}], []); + + // $addToSet implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 5, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 5}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 5}, {$addToSet: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 5}, [{"a.b": 0}, {"a.b": null}], []); + + // $bit implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 6, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 6}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 6}, {$bit: {"a.1.c": {and: NumberInt(1)}}})); + assertExpectedIndexKeys({_id: 6}, [{"a.b": 0}, {"a.b": null}], []); + + // $min implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 7, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 7}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 7}, {$min: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 7}, [{"a.b": 0}, {"a.b": null}], []); + + // $max implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 8, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 8}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 8}, {$max: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 8}, [{"a.b": 0}, {"a.b": null}], []); + + // $currentDate implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 9, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 9}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 9}, {$currentDate: {"a.1.c": true}})); + assertExpectedIndexKeys({_id: 9}, [{"a.b": 0}, {"a.b": null}], []); + + // $push implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 10, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 10}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 10}, {$push: {"a.1.c": 0}})); + assertExpectedIndexKeys({_id: 10}, [{"a.b": 0}, {"a.b": null}], []); + + // $pushAll implicitly creates array element at end of array. + assert.writeOK(coll.insert({_id: 11, a: [{b: 0}]})); + assertExpectedIndexKeys({_id: 11}, [{"a.b": 0}], [{"a.b": null}]); + assert.writeOK(coll.update({_id: 11}, {$pushAll: {"a.1.c": [0]}})); + assertExpectedIndexKeys({_id: 11}, [{"a.b": 0}, {"a.b": null}], []); +}()); diff --git a/jstests/core/update_multi5.js b/jstests/core/update_multi5.js index e610462a620..9e9550bc554 100644 --- a/jstests/core/update_multi5.js +++ b/jstests/core/update_multi5.js @@ -7,8 +7,8 @@ assert.writeOK(t.insert({path: 'r1', subscribers: [1, 2]})); assert.writeOK(t.insert({path: 'r2', subscribers: [3, 4]})); - var res = assert.writeOK(t.update( - {}, {$addToSet: {subscribers: 5}}, {upsert: false, multi: true, writeConcern: {w: 1}})); + var res = + assert.writeOK(t.update({}, {$addToSet: {subscribers: 5}}, {upsert: false, multi: true})); assert.eq(res.nMatched, 2, tojson(res)); diff --git a/jstests/core/views/invalid_system_views.js b/jstests/core/views/invalid_system_views.js index 3ba282d2ca1..49ac600ba8b 100644 --- a/jstests/core/views/invalid_system_views.js +++ b/jstests/core/views/invalid_system_views.js @@ -1,6 +1,8 @@ /** * Tests that invalid view definitions in system.views do not impact valid commands on existing * collections. + * + * @tags: [requires_collmod_command] */ (function() { "use strict"; diff --git a/jstests/core/views/views_all_commands.js b/jstests/core/views/views_all_commands.js index 0115672dcda..e1798803a1b 100644 --- a/jstests/core/views/views_all_commands.js +++ b/jstests/core/views/views_all_commands.js @@ -48,6 +48,8 @@ * * skipStandalone * If true, do not run this command on a standalone mongod. + * + * @tags: [requires_collmod_command] */ (function() { diff --git a/jstests/core/views/views_basic.js b/jstests/core/views/views_basic.js index d6032b105df..b4a2f177982 100644 --- a/jstests/core/views/views_basic.js +++ b/jstests/core/views/views_basic.js @@ -13,7 +13,7 @@ let res = viewsDB.runCommand(cmd); assert.commandWorked(res); - let cursor = new DBCommandCursor(db.getMongo(), res, 5); + let cursor = new DBCommandCursor(res._mongo, res, 5); let actual = cursor.toArray(); assert(arrayEq(actual, expected), "actual: " + tojson(cursor.toArray()) + ", expected:" + tojson(expected)); diff --git a/jstests/core/views/views_change.js b/jstests/core/views/views_change.js index 002284095c5..a62e7e7f04f 100644 --- a/jstests/core/views/views_change.js +++ b/jstests/core/views/views_change.js @@ -1,6 +1,7 @@ /** * Tests the behavior of views when the backing view or collection is changed. - * @tags: [requires_find_command] + * + * @tags: [requires_collmod_command, requires_find_command] */ (function() { "use strict"; diff --git a/jstests/core/views/views_collation.js b/jstests/core/views/views_collation.js index 9e4ed7feb30..38557358ce9 100644 --- a/jstests/core/views/views_collation.js +++ b/jstests/core/views/views_collation.js @@ -1,9 +1,13 @@ /** * Tests the behavior of operations when interacting with a view's default collation. + * + * @tags: [requires_collmod_command] */ (function() { "use strict"; + load("jstests/libs/analyze_plan.js"); + let viewsDB = db.getSiblingDB("views_collation"); assert.commandWorked(viewsDB.dropDatabase()); assert.commandWorked(viewsDB.runCommand({create: "simpleCollection"})); @@ -58,6 +62,15 @@ assert.commandWorked(viewsDB.runCommand({count: "filView"})); assert.commandWorked(viewsDB.runCommand({distinct: "filView", key: "x"})); + // Explain of operations that do not specify a collation succeed. + assert.commandWorked(viewsDB.runCommand({aggregate: "filView", pipeline: [], explain: true})); + assert.commandWorked( + viewsDB.runCommand({explain: {find: "filView"}, verbosity: "allPlansExecution"})); + assert.commandWorked( + viewsDB.runCommand({explain: {count: "filView"}, verbosity: "allPlansExecution"})); + assert.commandWorked(viewsDB.runCommand( + {explain: {distinct: "filView", key: "x"}, verbosity: "allPlansExecution"})); + // Operations with a matching collation succeed. assert.commandWorked( viewsDB.runCommand({aggregate: "filView", pipeline: [], collation: {locale: "fil"}})); @@ -66,6 +79,18 @@ assert.commandWorked( viewsDB.runCommand({distinct: "filView", key: "x", collation: {locale: "fil"}})); + // Explain of operations with a matching collation succeed. + assert.commandWorked(viewsDB.runCommand( + {aggregate: "filView", pipeline: [], explain: true, collation: {locale: "fil"}})); + assert.commandWorked(viewsDB.runCommand( + {explain: {find: "filView", collation: {locale: "fil"}}, verbosity: "allPlansExecution"})); + assert.commandWorked(viewsDB.runCommand( + {explain: {count: "filView", collation: {locale: "fil"}}, verbosity: "allPlansExecution"})); + assert.commandWorked(viewsDB.runCommand({ + explain: {distinct: "filView", key: "x", collation: {locale: "fil"}}, + verbosity: "allPlansExecution" + })); + // Attempting to override the non-simple default collation of a view fails. assert.commandFailedWithCode( viewsDB.runCommand({aggregate: "filView", pipeline: [], collation: {locale: "en"}}), @@ -90,6 +115,46 @@ viewsDB.runCommand({distinct: "filView", key: "x", collation: {locale: "simple"}}), ErrorCodes.OptionNotSupportedOnView); + // Attempting to override the default collation of a view with explain fails. + assert.commandFailedWithCode( + viewsDB.runCommand( + {aggregate: "filView", pipeline: [], explain: true, collation: {locale: "en"}}), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode( + viewsDB.runCommand( + {aggregate: "filView", pipeline: [], explain: true, collation: {locale: "simple"}}), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode(viewsDB.runCommand({ + explain: {find: "filView", collation: {locale: "fr"}}, + verbosity: "allPlansExecution" + }), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode(viewsDB.runCommand({ + explain: {find: "filView", collation: {locale: "simple"}}, + verbosity: "allPlansExecution" + }), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode(viewsDB.runCommand({ + explain: {count: "filView", collation: {locale: "zh"}}, + verbosity: "allPlansExecution" + }), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode(viewsDB.runCommand({ + explain: {count: "filView", collation: {locale: "simple"}}, + verbosity: "allPlansExecution" + }), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode(viewsDB.runCommand({ + explain: {distinct: "filView", key: "x", collation: {locale: "es"}}, + verbosity: "allPlansExecution" + }), + ErrorCodes.OptionNotSupportedOnView); + assert.commandFailedWithCode(viewsDB.runCommand({ + explain: {distinct: "filView", key: "x", collation: {locale: "simple"}}, + verbosity: "allPlansExecution" + }), + ErrorCodes.OptionNotSupportedOnView); + const lookupSimpleView = { $lookup: {from: "simpleView", localField: "x", foreignField: "x", as: "result"} }; @@ -253,4 +318,56 @@ viewsDB.runCommand( {collMod: "esView", viewOn: "simpleCollection", pipeline: [graphLookupFilView]}), ErrorCodes.OptionNotSupportedOnView); + + // Make sure that when an operation does not specify the collation, it correctly uses the + // default collation associated with the view. For this, we set up a new backing collection with + // a case-insensitive view. + assert.commandWorked(viewsDB.runCommand({create: "case_sensitive_coll"})); + assert.commandWorked(viewsDB.runCommand({ + create: "case_insensitive_view", + viewOn: "case_sensitive_coll", + collation: {locale: "en", strength: 1} + })); + + assert.writeOK(viewsDB.case_sensitive_coll.insert({f: "case"})); + assert.writeOK(viewsDB.case_sensitive_coll.insert({f: "Case"})); + assert.writeOK(viewsDB.case_sensitive_coll.insert({f: "CASE"})); + + let explain, cursorStage; + + // Test that aggregate against a view with a default collation correctly uses the collation. + assert.eq(1, viewsDB.case_sensitive_coll.aggregate([{$match: {f: "case"}}]).itcount()); + assert.eq(3, viewsDB.case_insensitive_view.aggregate([{$match: {f: "case"}}]).itcount()); + explain = viewsDB.case_insensitive_view.explain().aggregate([{$match: {f: "case"}}]); + cursorStage = getAggPlanStage(explain, "$cursor"); + assert.neq(null, cursorStage, tojson(explain)); + assert.eq(1, cursorStage.$cursor.queryPlanner.collation.strength, tojson(cursorStage)); + + // Test that count against a view with a default collation correctly uses the collation. + assert.eq(1, viewsDB.case_sensitive_coll.count({f: "case"})); + assert.eq(3, viewsDB.case_insensitive_view.count({f: "case"})); + explain = viewsDB.case_insensitive_view.explain().count({f: "case"}); + cursorStage = getAggPlanStage(explain, "$cursor"); + assert.neq(null, cursorStage, tojson(explain)); + assert.eq(1, cursorStage.$cursor.queryPlanner.collation.strength, tojson(cursorStage)); + + // Test that distinct against a view with a default collation correctly uses the collation. + assert.eq(3, viewsDB.case_sensitive_coll.distinct("f").length); + assert.eq(1, viewsDB.case_insensitive_view.distinct("f").length); + explain = viewsDB.case_insensitive_view.explain().distinct("f"); + cursorStage = getAggPlanStage(explain, "$cursor"); + assert.neq(null, cursorStage, tojson(explain)); + assert.eq(1, cursorStage.$cursor.queryPlanner.collation.strength, tojson(cursorStage)); + + // Test that find against a view with a default collation correctly uses the collation. + let findRes = viewsDB.runCommand({find: "case_sensitive_coll", filter: {f: "case"}}); + assert.commandWorked(findRes); + assert.eq(1, findRes.cursor.firstBatch.length); + findRes = viewsDB.runCommand({find: "case_insensitive_view", filter: {f: "case"}}); + assert.commandWorked(findRes); + assert.eq(3, findRes.cursor.firstBatch.length); + explain = viewsDB.runCommand({explain: {find: "case_insensitive_view", filter: {f: "case"}}}); + cursorStage = getAggPlanStage(explain, "$cursor"); + assert.neq(null, cursorStage, tojson(explain)); + assert.eq(1, cursorStage.$cursor.queryPlanner.collation.strength, tojson(cursorStage)); }()); diff --git a/jstests/core/views/views_find.js b/jstests/core/views/views_find.js index c196e980ce5..53c9d4e8b98 100644 --- a/jstests/core/views/views_find.js +++ b/jstests/core/views/views_find.js @@ -15,7 +15,7 @@ let assertFindResultEq = function(cmd, expected, ordered) { let res = viewsDB.runCommand(cmd); assert.commandWorked(res); - let arr = new DBCommandCursor(db.getMongo(), res, 5).toArray(); + let arr = new DBCommandCursor(res._mongo, res, 5).toArray(); let errmsg = tojson({expected: expected, got: arr}); if (typeof(ordered) === "undefined" || !ordered) diff --git a/jstests/core/views/views_rename.js b/jstests/core/views/views_rename.js new file mode 100644 index 00000000000..ad656671ea1 --- /dev/null +++ b/jstests/core/views/views_rename.js @@ -0,0 +1,17 @@ +(function() { + // SERVER-30406 Test that renaming system.views correctly invalidates the view catalog + 'use strict'; + db.view.drop(); + db.coll.drop(); + assert.commandWorked(db.createView("view", "coll", [])); + assert.writeOK(db.coll.insert({_id: 1})); + assert.eq(db.view.find().count(), 1, "couldn't find document in view"); + assert.commandWorked(db.system.views.renameCollection("views", /*dropTarget*/ true)); + assert.eq(db.view.find().count(), + 0, + "find on view should have returned no results after renaming away system.views"); + assert.commandWorked(db.views.renameCollection("system.views")); + assert.eq(db.view.find().count(), + 1, + "find on view should have worked again after renaming system.views back in place"); +})(); diff --git a/jstests/core/views/views_stats.js b/jstests/core/views/views_stats.js index 75feb857c9a..22261e9fa81 100644 --- a/jstests/core/views/views_stats.js +++ b/jstests/core/views/views_stats.js @@ -1,4 +1,10 @@ // Test that top and latency histogram statistics are recorded for views. +// +// This test attempts to perform write operations and get latency statistics using the $collStats +// stage. The former operation must be routed to the primary in a replica set, whereas the latter +// may be routed to a secondary. +// +// @tags: [assumes_read_preference_unchanged] (function() { "use strict"; diff --git a/jstests/core/views/views_validation.js b/jstests/core/views/views_validation.js index 84c7f1d3510..b32750a082f 100644 --- a/jstests/core/views/views_validation.js +++ b/jstests/core/views/views_validation.js @@ -1,3 +1,4 @@ +// @tags: [requires_collmod_command] (function() { "use strict"; let viewsDb = db.getSiblingDB("views_validation"); diff --git a/jstests/core/write_result.js b/jstests/core/write_result.js index 86486089c68..453be4ca1c9 100644 --- a/jstests/core/write_result.js +++ b/jstests/core/write_result.js @@ -1,6 +1,8 @@ // // Tests the behavior of single writes using write commands // +// @tags: [assumes_write_concern_unchanged] +// var coll = db.write_result; coll.drop(); |
