0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 09:32:32 +01:00

SERVER-31335 cmd assertions check write errors

This commit is contained in:
Kevin Albertson 2017-12-29 12:53:14 -05:00
parent 9d1f323a13
commit 5e28f4a352
20 changed files with 533 additions and 167 deletions

View File

@ -5,8 +5,8 @@
var authzErrorCode = 13;
var hasAuthzError = function(result) {
assert(result.hasWriteError());
assert.eq(authzErrorCode, result.getWriteError().code);
assert(result instanceof WriteCommandError);
assert.eq(authzErrorCode, result.code);
};
var st = new ShardingTest({
@ -107,7 +107,7 @@ db3.auth('spencer', 'pwd');
// s1/db2 should update its cache in 10 seconds.
assert.soon(function() {
var res = db2.foo.update({}, {$inc: {a: 1}});
if (res.hasWriteError()) {
if (res instanceof WriteCommandError) {
return false;
}
return db2.foo.findOne().a == 3;
@ -133,7 +133,7 @@ db3.auth('spencer', 'pwd');
// s1/db2 should update its cache in 10 seconds.
assert.soon(function() {
var res = db2.foo.update({}, {$inc: {a: 1}});
return res.hasWriteError() && res.getWriteError().code == authzErrorCode;
return res instanceof WriteCommandError && res.code == authzErrorCode;
}, "Mongos did not update its user cache after 10 seconds", 10 * 1000);
// We manually invalidate the cache on s1/db3.
@ -155,7 +155,7 @@ db3.auth('spencer', 'pwd');
// s1/db2 should update its cache in 10 seconds.
assert.soon(function() {
return !db2.foo.update({}, {$inc: {a: 1}}).hasWriteError();
return !(db2.foo.update({}, {$inc: {a: 1}}) instanceof WriteCommandError);
}, "Mongos did not update its user cache after 10 seconds", 10 * 1000);
// We manually invalidate the cache on s1/db3.

View File

@ -6,8 +6,8 @@ function runAllRoleManagementCommandsTests(conn, writeConcern) {
'use strict';
var hasAuthzError = function(result) {
assert(result.hasWriteError());
assert.eq(ErrorCodes.Unauthorized, result.getWriteError().code);
assert(result instanceof WriteCommandError);
assert.eq(ErrorCodes.Unauthorized, result.code);
};
var userAdminConn = new Mongo(conn.host);

View File

@ -6,8 +6,8 @@
function runTest(conn) {
var authzErrorCode = 13;
var hasAuthzError = function(result) {
assert(result.hasWriteError());
assert.eq(authzErrorCode, result.getWriteError().code);
assert(result instanceof WriteCommandError);
assert.eq(authzErrorCode, result.code);
};
conn.getDB('admin').createUser({user: 'admin', pwd: 'pwd', roles: ['root']});

View File

@ -6,8 +6,8 @@ function runAllUserManagementCommandsTests(conn, writeConcern) {
'use strict';
var hasAuthzError = function(result) {
assert(result.hasWriteError());
assert.eq(ErrorCodes.Unauthorized, result.getWriteError().code);
assert(result instanceof WriteCommandError);
assert.eq(ErrorCodes.Unauthorized, result.code);
};
conn.getDB('admin').createUser({user: 'admin', pwd: 'pwd', roles: ['root']}, writeConcern);

View File

@ -166,7 +166,7 @@ request = {
ordered: true
};
result = coll.runCommand(request);
assert.commandWorked(result);
assert.commandWorkedIgnoringWriteErrors(result);
assert.eq(1, result.n);
assert(result.writeErrors != null);
assert.eq(1, result.writeErrors.length);
@ -187,7 +187,7 @@ request = {
ordered: false
};
result = coll.runCommand(request);
assert.commandWorked(result);
assert.commandWorkedIgnoringWriteErrors(result);
assert.eq(1, result.n);
assert.eq(2, result.writeErrors.length);

View File

@ -8,7 +8,6 @@ var resultLower = dbLowerCase.c.insert({});
assert.eq(1, resultLower.nInserted);
var resultUpper = dbUpperCase.c.insert({});
assert.eq(0, resultUpper.nInserted);
assert.writeError(resultUpper);
assert.commandFailed(resultUpper);
assert.eq(-1, db.getMongo().getDBNames().indexOf("dbcase2test_dbnameA"));

View File

@ -296,7 +296,7 @@
delete: coll.getName(),
deletes: [{q: {_id: 0}, limit: 1}, {q: {$expr: "$$unbound"}, limit: 1}]
});
assert.commandWorked(writeRes);
assert.commandWorkedIgnoringWriteErrors(writeRes);
assert.eq(writeRes.writeErrors[0].code, 17276, tojson(writeRes));
assert.eq(writeRes.n, 1, tojson(writeRes));
@ -344,7 +344,7 @@
update: coll.getName(),
updates: [{q: {_id: 0}, u: {$set: {b: 6}}}, {q: {$expr: "$$unbound"}, u: {$set: {b: 6}}}]
});
assert.commandWorked(writeRes);
assert.commandWorkedIgnoringWriteErrors(writeRes);
assert.eq(writeRes.writeErrors[0].code, 17276, tojson(writeRes));
assert.eq(writeRes.n, 1, tojson(writeRes));
})();

View File

@ -37,7 +37,7 @@ function assertSchemaMatch(coll, schema, doc, valid) {
assert.writeOK(res, errmsg + " during insert document validation");
} else {
assert.writeErrorWithCode(res,
ErrorCodes.DocumentFailedValidation,
ErrorCodes.DocumentValidationFailure,
errmsg + " during insert document validation");
}
@ -57,7 +57,7 @@ function assertSchemaMatch(coll, schema, doc, valid) {
assert.writeOK(res, errmsg + " during update document validation in strict mode");
} else {
assert.writeErrorWithCode(res,
ErrorCodes.DocumentFailedValidation,
ErrorCodes.DocumentValidationFailure,
errmsg + " during update document validation in strict mode");
}
}

View File

@ -0,0 +1,254 @@
(function() {
"use strict";
const conn = MongoRunner.runMongod();
const db = conn.getDB("commandAssertions");
const kFakeErrCode = 1234567890;
const tests = [];
function setup() {
db.coll.drop();
assert.writeOK(db.coll.insert({_id: 1}));
}
// Raw command responses.
tests.push(function rawCommandOk() {
const res = db.runCommand({"ping": 1});
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function rawCommandErr() {
const res = db.runCommand({"IHopeNobodyEverMakesThisACommand": 1});
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.CommandNotFound));
// commandFailedWithCode should succeed if any of the passed error codes are matched.
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.CommandNotFound, kFakeErrCode]));
});
tests.push(function rawCommandWriteOk() {
const res = db.runCommand({insert: "coll", documents: [{_id: 2}]});
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function rawCommandWriteErr() {
const res = db.runCommand({insert: "coll", documents: [{_id: 1}]});
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
tests.push(function collInsertWriteOk() {
const res = db.coll.insert({_id: 2});
assert(res instanceof WriteResult);
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function collInsertWriteErr() {
const res = db.coll.insert({_id: 1});
assert(res instanceof WriteResult);
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
tests.push(function collMultiInsertWriteOk() {
const res = db.coll.insert([{_id: 3}, {_id: 2}]);
assert(res instanceof BulkWriteResult);
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function collMultiInsertWriteErr() {
const res = db.coll.insert([{_id: 1}, {_id: 2}]);
assert(res instanceof BulkWriteResult);
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
// Test when the insert command fails with ok:0 (i.e. not failing due to write err)
tests.push(function collInsertCmdErr() {
const res = db.coll.insert({x: 1}, {writeConcern: {"bad": 1}});
assert(res instanceof WriteCommandError);
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.FailedToParse));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.FailedToParse, kFakeErrCode]));
});
tests.push(function collMultiInsertCmdErr() {
const res = db.coll.insert([{x: 1}, {x: 2}], {writeConcern: {"bad": 1}});
assert(res instanceof WriteCommandError);
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.FailedToParse));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.FailedToParse, kFakeErrCode]));
});
tests.push(function mapReduceOk() {
const res = db.coll.mapReduce(
function() {
emit(this._id, 0);
},
function(k, v) {
return v[0];
},
{out: "coll_out"});
assert(res instanceof MapReduceResult);
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function mapReduceErr() {
// db.coll.mapReduce throws if the command response has ok:0
// Instead manually construct a MapReduceResult with ok:0
const res = new MapReduceResult(db, {
"ok": 0,
"errmsg": "Example Error",
"code": ErrorCodes.JSInterpreterFailure,
"codeName": "JSInterpreterFailure"
});
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() =>
assert.commandFailedWithCode(res, ErrorCodes.JSInterpreterFailure));
assert.doesNotThrow(() => assert.commandFailedWithCode(
res, [ErrorCodes.JSInterpreterFailure, kFakeErrCode]));
});
tests.push(function errObject() {
// Some functions throw an Error with a code property attached.
let threw = false;
let res = null;
try {
db.eval("this is a syntax error");
} catch (e) {
threw = true;
res = e;
}
assert(threw);
assert(res instanceof Error);
assert(res.hasOwnProperty("code"));
assert.throws(() => assert.commandWorked(res));
assert.throws(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.InternalError));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.InternalError, kFakeErrCode]));
});
tests.push(function crudInsertOneOk() {
const res = db.coll.insertOne({_id: 2});
assert(res.hasOwnProperty("acknowledged"));
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function crudInsertOneErr() {
let threw = false;
let res = null;
try {
db.coll.insertOne({_id: 1});
} catch (e) {
threw = true;
res = e;
}
assert(threw);
assert(res instanceof WriteError);
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
tests.push(function crudInsertManyOk() {
const res = db.coll.insertMany([{_id: 2}, {_id: 3}]);
assert(res.hasOwnProperty("acknowledged"));
assert.doesNotThrow(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.throws(() => assert.commandFailed(res));
assert.throws(() => assert.commandFailedWithCode(res, 0));
});
tests.push(function crudInsertManyErr() {
let threw = false;
let res = null;
try {
db.coll.insertMany([{_id: 1}, {_id: 2}]);
} catch (e) {
threw = true;
res = e;
}
assert(threw);
assert(res instanceof BulkWriteError);
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
tests.push(function rawMultiWriteErr() {
// Do an unordered bulk insert with duplicate keys to produce multiple write errors.
const res =
db.runCommand({"insert": "coll", documents: [{_id: 1}, {_id: 1}], ordered: false});
assert(res.writeErrors.length == 2, "did not get multiple write errors");
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
tests.push(function bulkMultiWriteErr() {
// Do an unordered bulk insert with duplicate keys to produce multiple write errors.
const res = db.coll.insert([{_id: 1}, {_id: 1}], {ordered: false});
assert.throws(() => assert.commandWorked(res));
assert.doesNotThrow(() => assert.commandWorkedIgnoringWriteErrors(res));
assert.doesNotThrow(() => assert.commandFailed(res));
assert.doesNotThrow(() => assert.commandFailedWithCode(res, ErrorCodes.DuplicateKey));
assert.doesNotThrow(
() => assert.commandFailedWithCode(res, [ErrorCodes.DuplicateKey, kFakeErrCode]));
});
tests.forEach((test) => {
jsTest.log(`Starting test '${test.name}'`);
setup();
test();
});
})();

View File

@ -14,4 +14,4 @@ assert(v.valid);
db.runCommand({applyOps: [{op: 'u', ns: 'a\0b'}]});
var res = db["a\0a"].insert({});
assert(res.hasWriteError(), "A write to collection a\0a succceeded");
assert(res instanceof WriteCommandError, "A write to collection a\0a succceeded");

View File

@ -157,7 +157,7 @@ request = {
writeConcern: {wtimeout: 1},
ordered: false
};
result = assert.commandWorked(coll.runCommand(request));
result = assert.commandWorkedIgnoringWriteErrors(coll.runCommand(request));
assert.eq(1, result.n);
assert.eq(result.writeErrors.length, 1);
assert.eq(result.writeErrors[0].index, 1);

View File

@ -45,8 +45,8 @@
// Make sure that node 2 cannot write anything. Because it is lagged and replication
// has been stopped, it shouldn't be able to become master.
assert.writeErrorWithCode(nodes[2].getDB(name).bar.insert({z: 100}, writeConcern),
ErrorCodes.NotMaster);
assert.commandFailedWithCode(nodes[2].getDB(name).bar.insert({z: 100}, writeConcern),
ErrorCodes.NotMaster);
// Confirm that the most up-to-date node becomes primary
// after the default catchup delay.

View File

@ -190,7 +190,7 @@
assert.writeOK(coll.insert({_id: 1}));
},
confirmFunc: function(res) {
assert.commandWorked(res);
assert.commandWorkedIgnoringWriteErrors(res);
assert.eq(res.n, 0);
assert.eq(res.writeErrors[0].code, ErrorCodes.DuplicateKey);
assert.eq(coll.count({_id: 1}), 1);

View File

@ -77,12 +77,12 @@
// Ensure dropping the `config.transactions` collection breaks the retryable writes feature, but
// doesn't crash the server
assert(config.transactions.drop());
var res = assert.commandWorked(db.runCommand(cmdObj2));
var res = assert.commandWorkedIgnoringWriteErrors(db.runCommand(cmdObj2));
assert.eq(0, res.nModified);
assert.eq(1, db.user.find({_id: 1}).toArray()[0].x);
assert(config.dropDatabase());
res = assert.commandWorked(db.runCommand(cmdObj2));
res = assert.commandWorkedIgnoringWriteErrors(db.runCommand(cmdObj2));
assert.eq(0, res.nModified);
assert.eq(1, db.user.find({_id: 1}).toArray()[0].x);

View File

@ -61,7 +61,7 @@
jsTestLog("Ensure that writes start failing with NotMaster errors");
assert.soonNoExcept(function() {
assert.writeErrorWithCode(primary.getDB(name).foo.insert({x: 2}), ErrorCodes.NotMaster);
assert.commandFailedWithCode(primary.getDB(name).foo.insert({x: 2}), ErrorCodes.NotMaster);
return true;
});

View File

@ -54,7 +54,7 @@
jsTestLog("Ensure that writes start failing with NotMaster errors");
assert.soonNoExcept(function() {
assert.writeErrorWithCode(primary.getDB(name).foo.insert({x: 2}), ErrorCodes.NotMaster);
assert.commandFailedWithCode(primary.getDB(name).foo.insert({x: 2}), ErrorCodes.NotMaster);
return true;
});

View File

@ -313,7 +313,7 @@
lsid: lsid,
txnNumber: NumberLong(1),
};
var res = assert.commandWorked(testDb.runCommand(cmd));
var res = assert.commandWorkedIgnoringWriteErrors(testDb.runCommand(cmd));
assert.eq(1,
res.writeErrors.length,
'expected only one write error, received: ' + tojson(res.writeErrors));
@ -334,7 +334,7 @@
lsid: lsid,
txnNumber: NumberLong(1),
};
res = assert.commandWorked(testDb.runCommand(cmd));
res = assert.commandWorkedIgnoringWriteErrors(testDb.runCommand(cmd));
assert.eq(1,
res.writeErrors.length,
'expected only one write error, received: ' + tojson(res.writeErrors));

View File

@ -394,40 +394,244 @@ assert.doesNotThrow.automsg = function(func, params) {
assert.doesNotThrow(func, params, func.toString());
};
assert.commandWorked = function(res, msg) {
if (assert._debug && msg)
print("in assert for: " + msg);
(function() {
function _rawReplyOkAndNoWriteErrors(raw) {
if (raw.ok === 0) {
return false;
}
if (res.ok == 1)
return res;
doassert("command failed: " + tojson(res) + " : " + msg, res);
};
// A write command response may have ok:1 but write errors.
if (raw.hasOwnProperty("writeErrors") && raw.writeErrors.length > 0) {
return false;
}
assert.commandFailed = function(res, msg) {
if (assert._debug && msg)
print("in assert for: " + msg);
if (res.ok == 0)
return res;
doassert("command worked when it should have failed: " + tojson(res) + " : " + msg);
};
assert.commandFailedWithCode = function(res, code, msg) {
if (assert._debug && msg)
print("in assert for: " + msg);
if (!Array.isArray(code)) {
code = [code];
return true;
}
assert(!res.ok,
"Command result indicates success, but expected failure with code " + tojson(code) +
": " + tojson(res) + " : " + msg);
assert(code.indexOf(res.code) >= 0,
"Expected failure code " + tojson(code) + " did not match actual in command result: " +
tojson(res) + " : " + msg);
return res;
};
// Returns whether res is a type which may have write errors (not command errors).
// These types imply that the write command succeeded.
function _isWriteResultType(res) {
return res instanceof WriteResult || res instanceof WriteError ||
res instanceof BulkWriteResult || res instanceof BulkWriteError;
}
function _assertCommandWorked(res, msg, {ignoreWriteErrors}) {
if (assert._debug && msg) {
print("in assert for: " + msg);
}
if (typeof res !== "object") {
doassert("unknown response given to commandWorked");
}
const failMsg = "command failed: " + tojson(res) + " : " + msg;
if (_isWriteResultType(res)) {
// These can only contain write errors, not command errors.
if (!ignoreWriteErrors) {
assert.writeOK(res, msg);
}
} else if (res instanceof WriteCommandError || res instanceof Error) {
// A WriteCommandError implies ok:0.
// Error objects may have a `code` property added (e.g.
// DBCollection.prototype.mapReduce) without a `ok` property.
doassert(failMsg, res);
} else if (res.hasOwnProperty("ok")) {
// Handle raw command responses or cases like MapReduceResult which extend command
// response.
if (ignoreWriteErrors) {
if (res.ok === 0) {
doassert(failMsg, res);
}
} else {
if (!_rawReplyOkAndNoWriteErrors(res)) {
doassert(failMsg, res);
}
}
} else if (res.hasOwnProperty("acknowledged")) {
// CRUD api functions return plain js objects with an acknowledged property.
// no-op.
} else {
doassert("unknown type of result, cannot check ok: " + tojson(res) + " : " + msg, res);
}
return res;
}
const kAnyErrorCode = Object.create(null);
function _assertCommandFailed(res, expectedCode, msg) {
if (assert._debug && msg) {
print("in assert for: " + msg);
}
if (typeof res !== "object") {
doassert("unknown response given to commandFailed");
}
if (expectedCode !== kAnyErrorCode && !Array.isArray(expectedCode)) {
expectedCode = [expectedCode];
}
const failMsg = "command worked when it should have failed: " + tojson(res) + " : " + msg;
const failCodeMsg = (expectedCode !== kAnyErrorCode)
? "command did not fail with any of the following codes " + tojson(expectedCode) + " " +
tojson(res) + " : " + msg
: "";
if (_isWriteResultType(res)) {
// These can only contain write errors, not command errors.
assert.writeErrorWithCode(res, expectedCode, msg);
} else if (res instanceof WriteCommandError || res instanceof Error) {
// A WriteCommandError implies ok:0.
// Error objects may have a `code` property added (e.g.
// DBCollection.prototype.mapReduce) without a `ok` property.
if (expectedCode !== kAnyErrorCode) {
if (!res.hasOwnProperty("code") || !expectedCode.includes(res.code)) {
doassert(failCodeMsg, res);
}
}
} else if (res.hasOwnProperty("ok")) {
// Handle raw command responses or cases like MapReduceResult which extend command
// response.
if (_rawReplyOkAndNoWriteErrors(res)) {
doassert(failMsg, res);
}
if (expectedCode !== kAnyErrorCode) {
let foundCode = false;
if (res.hasOwnProperty("code") && expectedCode.includes(res.code)) {
foundCode = true;
} else if (res.hasOwnProperty("writeErrors")) {
foundCode = res.writeErrors.some((err) => expectedCode.includes(err.code));
}
if (!foundCode) {
doassert(failCodeMsg, res);
}
}
} else if (res.hasOwnProperty("acknowledged")) {
// CRUD api functions return plain js objects with an acknowledged property.
doassert(failMsg);
} else {
doassert("unknown type of result, cannot check error: " + tojson(res) + " : " + msg,
res);
}
return res;
}
assert.commandWorked = function(res, msg) {
return _assertCommandWorked(res, msg, {ignoreWriteErrors: false});
};
assert.commandWorkedIgnoringWriteErrors = function(res, msg) {
return _assertCommandWorked(res, msg, {ignoreWriteErrors: true});
};
assert.commandFailed = function(res, msg) {
return _assertCommandFailed(res, kAnyErrorCode, msg);
};
// expectedCode can be an array of possible codes.
assert.commandFailedWithCode = function(res, expectedCode, msg) {
return _assertCommandFailed(res, expectedCode, msg);
};
assert.writeOK = function(res, msg) {
var errMsg = null;
if (res instanceof WriteResult) {
if (res.hasWriteError()) {
errMsg = "write failed with error: " + tojson(res);
} else if (res.hasWriteConcernError()) {
errMsg = "write concern failed with errors: " + tojson(res);
}
} else if (res instanceof BulkWriteResult) {
// Can only happen with bulk inserts
if (res.hasWriteErrors()) {
errMsg = "write failed with errors: " + tojson(res);
} else if (res.hasWriteConcernError()) {
errMsg = "write concern failed with errors: " + tojson(res);
}
} else if (res instanceof WriteCommandError || res instanceof WriteError ||
res instanceof BulkWriteError) {
errMsg = "write command failed: " + tojson(res);
} else {
if (!res || !res.ok) {
errMsg = "unknown type of write result, cannot check ok: " + tojson(res);
}
}
if (errMsg) {
if (msg)
errMsg = errMsg + ": " + msg;
doassert(errMsg, res);
}
return res;
};
assert.writeError = function(res, msg) {
return assert.writeErrorWithCode(res, kAnyErrorCode, msg);
};
// If expectedCode is an array then this asserts that the found code is one of the codes in
// the expectedCode array.
assert.writeErrorWithCode = function(res, expectedCode, msg) {
if (expectedCode === undefined) {
doassert("assert.writeErrorWithCode called with undefined error code");
}
var errMsg = null;
var writeErrorCodes = new Set();
if (res instanceof WriteResult) {
if (res.hasWriteError()) {
writeErrorCodes.add(res.getWriteError().code);
} else if (res.hasWriteConcernError()) {
writeErrorCodes.add(res.getWriteConcernError().code);
} else {
errMsg = "no write error: " + tojson(res);
}
} else if (res instanceof BulkWriteResult || res instanceof BulkWriteError) {
// Can only happen with bulk inserts
if (res.hasWriteErrors()) {
// Save every write error code.
res.getWriteErrors().forEach((we) => writeErrorCodes.add(we.code));
} else if (res.hasWriteConcernError()) {
writeErrorCodes.add(res.getWriteConcernError().code);
} else {
errMsg = "no write errors: " + tojson(res);
}
} else if (res instanceof WriteCommandError) {
// Can only happen with bulk inserts
// No-op since we're expecting an error
} else if (res instanceof WriteError) {
writeErrorCodes.add(res.code);
} else {
if (!res || res.ok) {
errMsg = "unknown type of write result, cannot check error: " + tojson(res);
}
}
if (!errMsg && expectedCode !== kAnyErrorCode) {
if (!Array.isArray(expectedCode)) {
expectedCode = [expectedCode];
}
const found = expectedCode.some((ec) => writeErrorCodes.has(ec));
if (!found) {
errMsg = "found code(s) " + tojson(Array.from(writeErrorCodes)) +
" does not match any of the expected codes " + tojson(expectedCode);
}
}
if (errMsg) {
if (msg)
errMsg = errMsg + ": " + msg;
doassert(errMsg);
}
return res;
};
})();
assert.isnull = function(what, msg) {
if (assert._debug && msg)
@ -532,102 +736,6 @@ assert.closeWithinMS = function(a, b, msg, deltaMS) {
actualDelta + " millis : " + msg);
};
assert.writeOK = function(res, msg) {
var errMsg = null;
if (res instanceof WriteResult) {
if (res.hasWriteError()) {
errMsg = "write failed with error: " + tojson(res);
} else if (res.hasWriteConcernError()) {
errMsg = "write concern failed with errors: " + tojson(res);
}
} else if (res instanceof BulkWriteResult) {
// Can only happen with bulk inserts
if (res.hasWriteErrors()) {
errMsg = "write failed with errors: " + tojson(res);
} else if (res.hasWriteConcernError()) {
errMsg = "write concern failed with errors: " + tojson(res);
}
} else if (res instanceof WriteCommandError) {
// Can only happen with bulk inserts
errMsg = "write command failed: " + tojson(res);
} else {
if (!res || !res.ok) {
errMsg = "unknown type of write result, cannot check ok: " + tojson(res);
}
}
if (errMsg) {
if (msg)
errMsg = errMsg + ": " + msg;
doassert(errMsg, res);
}
return res;
};
assert.writeError = function(res, msg) {
return assert.writeErrorWithCode(res, null, msg);
};
// If expectedCode is an array then this asserts that the found code is one of the codes in
// the expectedCode array.
assert.writeErrorWithCode = function(res, expectedCode, msg) {
var errMsg = null;
var foundCode = null;
if (res instanceof WriteResult) {
if (res.hasWriteError()) {
foundCode = res.getWriteError().code;
} else if (res.hasWriteConcernError()) {
foundCode = res.getWriteConcernError().code;
} else {
errMsg = "no write error: " + tojson(res);
}
} else if (res instanceof BulkWriteResult) {
// Can only happen with bulk inserts
if (res.hasWriteErrors()) {
if (res.getWriteErrorCount() > 1 && expectedCode != null) {
errMsg = "can't check for specific code when there was more than one write error";
} else {
foundCode = res.getWriteErrorAt(0).code;
}
} else if (res.hasWriteConcernError()) {
foundCode = res.getWriteConcernError().code;
} else {
errMsg = "no write errors: " + tojson(res);
}
} else if (res instanceof WriteCommandError) {
// Can only happen with bulk inserts
// No-op since we're expecting an error
} else {
if (!res || res.ok) {
errMsg = "unknown type of write result, cannot check error: " + tojson(res);
}
}
if (!errMsg && expectedCode) {
if (Array.isArray(expectedCode)) {
if (!expectedCode.includes(foundCode)) {
errMsg = "found code " + foundCode + " does not match any of the expected codes " +
tojson(expectedCode);
}
} else if (foundCode != expectedCode) {
errMsg = "found code " + foundCode + " does not match expected code " + expectedCode;
}
}
if (errMsg) {
if (msg)
errMsg = errMsg + ": " + msg;
doassert(errMsg);
}
return res;
};
assert.gleOK = function(res, msg) {
var errMsg = null;

View File

@ -419,14 +419,6 @@ var _bulk_api_module = (function() {
this.shellPrint = function() {
return this.toString();
};
this.toSingleResult = function() {
// This is *only* safe to do with a WriteCommandError from the bulk api when the bulk is
// known to be of size == 1
var bulkResult = getEmptyBulkResult();
bulkResult.writeErrors.push({code: this.code, index: 0, errmsg: this.errmsg});
return new BulkWriteResult(bulkResult, NONE).toSingleResult();
};
};
WriteCommandError.prototype = Object.create(Error.prototype);
@ -1210,6 +1202,7 @@ var _bulk_api_module = (function() {
module.BulkWriteResult = BulkWriteResult;
module.BulkWriteError = BulkWriteError;
module.WriteCommandError = WriteCommandError;
module.WriteError = WriteError;
module.initializeUnorderedBulkOp = function() {
return new Bulk(this, false);
};
@ -1227,6 +1220,7 @@ WriteResult = _bulk_api_module.WriteResult;
BulkWriteResult = _bulk_api_module.BulkWriteResult;
BulkWriteError = _bulk_api_module.BulkWriteError;
WriteCommandError = _bulk_api_module.WriteCommandError;
WriteError = _bulk_api_module.WriteError;
/***********************************************************
* Adds the initializers of bulk operations to the db collection

View File

@ -261,6 +261,9 @@ DBCollection.prototype.findOne = function(query, fields, options, readConcern, c
return ret;
};
// Returns a WriteResult for a single insert or a BulkWriteResult for a multi-insert if write
// command succeeded, but may contain write errors.
// Returns a WriteCommandError if the write command responded with ok:0.
DBCollection.prototype.insert = function(obj, options) {
if (!obj)
throw Error("no object passed to insert!");
@ -317,7 +320,7 @@ DBCollection.prototype.insert = function(obj, options) {
if (ex instanceof BulkWriteError) {
result = isMultiInsert ? ex.toResult() : ex.toSingleResult();
} else if (ex instanceof WriteCommandError) {
result = isMultiInsert ? ex : ex.toSingleResult();
result = ex;
} else {
// Other exceptions rethrown as-is.
throw ex;
@ -374,6 +377,8 @@ DBCollection.prototype._parseRemove = function(t, justOne) {
return {"query": query, "justOne": justOne, "wc": wc, "collation": collation};
};
// Returns a WriteResult if write command succeeded, but may contain write errors.
// Returns a WriteCommandError if the write command responded with ok:0.
DBCollection.prototype.remove = function(t, justOne) {
var parsed = this._parseRemove(t, justOne);
var query = parsed.query;
@ -402,8 +407,10 @@ DBCollection.prototype.remove = function(t, justOne) {
try {
result = bulk.execute(wc).toSingleResult();
} catch (ex) {
if (ex instanceof BulkWriteError || ex instanceof WriteCommandError) {
if (ex instanceof BulkWriteError) {
result = ex.toSingleResult();
} else if (ex instanceof WriteCommandError) {
result = ex;
} else {
// Other exceptions thrown
throw Error(ex);
@ -475,6 +482,8 @@ DBCollection.prototype._parseUpdate = function(query, obj, upsert, multi) {
};
};
// Returns a WriteResult if write command succeeded, but may contain write errors.
// Returns a WriteCommandError if the write command responded with ok:0.
DBCollection.prototype.update = function(query, obj, upsert, multi) {
var parsed = this._parseUpdate(query, obj, upsert, multi);
var query = parsed.query;
@ -514,8 +523,10 @@ DBCollection.prototype.update = function(query, obj, upsert, multi) {
try {
result = bulk.execute(wc).toSingleResult();
} catch (ex) {
if (ex instanceof BulkWriteError || ex instanceof WriteCommandError) {
if (ex instanceof BulkWriteError) {
result = ex.toSingleResult();
} else if (ex instanceof WriteCommandError) {
result = ex;
} else {
// Other exceptions thrown
throw Error(ex);