1 doassert = function(msg, obj) {
  2     // eval if msg is a function
  3     if (typeof(msg) == "function")
  4         msg = msg();
  5 
  6     if (typeof(msg) == "string" && msg.indexOf("assert") == 0)
  7         print(msg);
  8     else
  9         print("assert: " + msg);
 10 
 11     var ex;
 12     if (obj) {
 13         ex = _getErrorWithCode(obj, msg);
 14     } else {
 15         ex = Error(msg);
 16     }
 17     print(ex.stack);
 18     throw ex;
 19 };
 20 
 21 assert = function(b, msg) {
 22     if (arguments.length > 2) {
 23         doassert("Too many parameters to assert().");
 24     }
 25     if (arguments.length > 1 && typeof(msg) !== "string") {
 26         doassert("Non-string 'msg' parameters are invalid for assert().");
 27     }
 28     if (assert._debug && msg)
 29         print("in assert for: " + msg);
 30     if (b)
 31         return;
 32     doassert(msg == undefined ? "assert failed" : "assert failed : " + msg);
 33 };
 34 
 35 assert.automsg = function(b) {
 36     assert(eval(b), b);
 37 };
 38 
 39 assert._debug = false;
 40 
 41 assert.eq = function(a, b, msg) {
 42     if (assert._debug && msg)
 43         print("in assert for: " + msg);
 44 
 45     if (a == b)
 46         return;
 47 
 48     if ((a != null && b != null) && friendlyEqual(a, b))
 49         return;
 50 
 51     doassert("[" + tojson(a) + "] != [" + tojson(b) + "] are not equal : " + msg);
 52 };
 53 
 54 // Sort doc/obj fields and return new sorted obj
 55 sortDoc = function(doc) {
 56 
 57     // Helper to sort the elements of the array
 58     var sortElementsOfArray = function(arr) {
 59         var newArr = [];
 60         if (!arr || arr.constructor != Array)
 61             return arr;
 62         for (var i = 0; i < arr.length; i++) {
 63             newArr.push(sortDoc(arr[i]));
 64         }
 65         return newArr;
 66     };
 67 
 68     // not a container we can sort
 69     if (!(doc instanceof Object))
 70         return doc;
 71 
 72     // if it an array, sort the elements
 73     if (doc.constructor == Array)
 74         return sortElementsOfArray(doc);
 75 
 76     var newDoc = {};
 77     var fields = Object.keys(doc);
 78     if (fields.length > 0) {
 79         fields.sort();
 80         for (var i = 0; i < fields.length; i++) {
 81             var field = fields[i];
 82             if (doc.hasOwnProperty(field)) {
 83                 var tmp = doc[field];
 84 
 85                 if (tmp) {
 86                     // Sort recursively for Arrays and Objects (including bson ones)
 87                     if (tmp.constructor == Array)
 88                         tmp = sortElementsOfArray(tmp);
 89                     else if (tmp._bson || tmp.constructor == Object)
 90                         tmp = sortDoc(tmp);
 91                 }
 92                 newDoc[field] = tmp;
 93             }
 94         }
 95     } else {
 96         newDoc = doc;
 97     }
 98 
 99     return newDoc;
100 };
101 
102 assert.docEq = function(a, b, msg) {
103     if (assert._debug && msg)
104         print("in assert for: " + msg);
105 
106     if (a == b)
107         return;
108 
109     var aSorted = sortDoc(a);
110     var bSorted = sortDoc(b);
111 
112     if ((aSorted != null && bSorted != null) && friendlyEqual(aSorted, bSorted))
113         return;
114 
115     doassert("[" + tojson(aSorted) + "] != [" + tojson(bSorted) + "] are not equal : " + msg);
116 };
117 
118 assert.eq.automsg = function(a, b) {
119     assert.eq(eval(a), eval(b), "[" + a + "] != [" + b + "]");
120 };
121 
122 assert.neq = function(a, b, msg) {
123     if (assert._debug && msg)
124         print("in assert for: " + msg);
125     if (a != b)
126         return;
127 
128     doassert("[" + a + "] != [" + b + "] are equal : " + msg);
129 };
130 
131 assert.contains = function(o, arr, msg) {
132     var wasIn = false;
133     if (!Array.isArray(arr)) {
134         throw new Error("The second argument to assert.contains must be an array.");
135     }
136 
137     for (var i = 0; i < arr.length; i++) {
138         wasIn = arr[i] == o || ((arr[i] != null && o != null) && friendlyEqual(arr[i], o));
139         if (wasIn) {
140             break;
141         }
142     }
143 
144     if (!wasIn) {
145         doassert(tojson(o) + " was not in " + tojson(arr) + " : " + msg);
146     }
147 };
148 
149 assert.soon = function(f, msg, timeout /*ms*/, interval) {
150     if (assert._debug && msg)
151         print("in assert for: " + msg);
152 
153     if (msg) {
154         if (typeof(msg) != "function") {
155             msg = "assert.soon failed, msg:" + msg;
156         }
157     } else {
158         msg = "assert.soon failed: " + f;
159     }
160 
161     var start = new Date();
162     timeout = timeout || 30000;
163     interval = interval || 200;
164     var last;
165     while (1) {
166         if (typeof(f) == "string") {
167             if (eval(f))
168                 return;
169         } else {
170             if (f())
171                 return;
172         }
173 
174         diff = (new Date()).getTime() - start.getTime();
175         if (diff > timeout) {
176             doassert(msg);
177         }
178         sleep(interval);
179     }
180 };
181 
182 /**
183  * Wraps assert.soon to try...catch any function passed in.
184  */
185 assert.soonNoExcept = function(func, msg, timeout /*ms*/) {
186     /**
187      * Surrounds a function call by a try...catch to convert any exception to a print statement
188      * and return false.
189      */
190     function _convertExceptionToReturnStatus(func) {
191         try {
192             return func();
193         } catch (e) {
194             print("caught exception " + e);
195             return false;
196         }
197     }
198 
199     assert.soon((() => _convertExceptionToReturnStatus(func)), msg, timeout);
200 };
201 
202 assert.time = function(f, msg, timeout /*ms*/) {
203     if (assert._debug && msg)
204         print("in assert for: " + msg);
205 
206     var start = new Date();
207     timeout = timeout || 30000;
208     if (typeof(f) == "string") {
209         res = eval(f);
210     } else {
211         res = f();
212     }
213 
214     diff = (new Date()).getTime() - start.getTime();
215     if (diff > timeout)
216         doassert("assert.time failed timeout " + timeout + "ms took " + diff + "ms : " + f +
217                  ", msg:" + msg);
218     return res;
219 };
220 
221 assert.throws = function(func, params, msg) {
222     if (assert._debug && msg)
223         print("in assert for: " + msg);
224     if (params && typeof(params) == "string") {
225         throw("2nd argument to assert.throws has to be an array, not " + params);
226     }
227     try {
228         func.apply(null, params);
229     } catch (e) {
230         return e;
231     }
232     doassert("did not throw exception: " + msg);
233 };
234 
235 assert.doesNotThrow = function(func, params, msg) {
236     if (assert._debug && msg)
237         print("in assert for: " + msg);
238     if (params && typeof(params) == "string") {
239         throw("2nd argument to assert.throws has to be an array, not " + params);
240     }
241     var res;
242     try {
243         res = func.apply(null, params);
244     } catch (e) {
245         doassert("threw unexpected exception: " + e + " : " + msg);
246     }
247     return res;
248 };
249 
250 assert.throws.automsg = function(func, params) {
251     assert.throws(func, params, func.toString());
252 };
253 
254 assert.doesNotThrow.automsg = function(func, params) {
255     assert.doesNotThrow(func, params, func.toString());
256 };
257 
258 assert.commandWorked = function(res, msg) {
259     if (assert._debug && msg)
260         print("in assert for: " + msg);
261 
262     if (res.ok == 1)
263         return res;
264     doassert("command failed: " + tojson(res) + " : " + msg, res);
265 };
266 
267 assert.commandFailed = function(res, msg) {
268     if (assert._debug && msg)
269         print("in assert for: " + msg);
270 
271     if (res.ok == 0)
272         return res;
273     doassert("command worked when it should have failed: " + tojson(res) + " : " + msg);
274 };
275 
276 assert.commandFailedWithCode = function(res, code, msg) {
277     if (assert._debug && msg)
278         print("in assert for: " + msg);
279 
280     assert(!res.ok,
281            "Command result indicates success, but expected failure with code " + code + ": " +
282                tojson(res) + " : " + msg);
283     assert.eq(res.code,
284               code,
285               "Expected failure code did not match actual in command result: " + tojson(res) +
286                   " : " + msg);
287     return res;
288 };
289 
290 assert.isnull = function(what, msg) {
291     if (assert._debug && msg)
292         print("in assert for: " + msg);
293 
294     if (what == null)
295         return;
296     doassert("supposed to be null (" + (msg || "") + ") was: " + tojson(what));
297 };
298 
299 assert.lt = function(a, b, msg) {
300     if (assert._debug && msg)
301         print("in assert for: " + msg);
302 
303     if (a < b)
304         return;
305     doassert(a + " is not less than " + b + " : " + msg);
306 };
307 
308 assert.gt = function(a, b, msg) {
309     if (assert._debug && msg)
310         print("in assert for: " + msg);
311 
312     if (a > b)
313         return;
314     doassert(a + " is not greater than " + b + " : " + msg);
315 };
316 
317 assert.lte = function(a, b, msg) {
318     if (assert._debug && msg)
319         print("in assert for: " + msg);
320 
321     if (a <= b)
322         return;
323     doassert(a + " is not less than or eq " + b + " : " + msg);
324 };
325 
326 assert.gte = function(a, b, msg) {
327     if (assert._debug && msg)
328         print("in assert for: " + msg);
329 
330     if (a >= b)
331         return;
332     doassert(a + " is not greater than or eq " + b + " : " + msg);
333 };
334 
335 assert.between = function(a, b, c, msg, inclusive) {
336     if (assert._debug && msg)
337         print("in assert for: " + msg);
338 
339     if ((inclusive == undefined || inclusive == true) && a <= b && b <= c)
340         return;
341     else if (a < b && b < c)
342         return;
343     doassert(b + " is not between " + a + " and " + c + " : " + msg);
344 };
345 
346 assert.betweenIn = function(a, b, c, msg) {
347     assert.between(a, b, c, msg, true);
348 };
349 assert.betweenEx = function(a, b, c, msg) {
350     assert.between(a, b, c, msg, false);
351 };
352 
353 assert.close = function(a, b, msg, places) {
354     if (places === undefined) {
355         places = 4;
356     }
357 
358     // This treats 'places' as digits past the decimal point.
359     var absoluteError = Math.abs(a - b);
360     if (Math.round(absoluteError * Math.pow(10, places)) === 0) {
361         return;
362     }
363 
364     // This treats 'places' as significant figures.
365     var relativeError = Math.abs(absoluteError / b);
366     if (Math.round(relativeError * Math.pow(10, places)) === 0) {
367         return;
368     }
369 
370     doassert(a + " is not equal to " + b + " within " + places + " places, absolute error: " +
371              absoluteError + ", relative error: " + relativeError + " : " + msg);
372 };
373 
374 /**
375  * Asserts if the times in millis are not withing delta milliseconds, in either direction.
376  * Default Delta: 1 second
377  */
378 assert.closeWithinMS = function(a, b, msg, deltaMS) {
379     "use strict";
380     if (deltaMS === undefined) {
381         deltaMS = 1000;
382     }
383     var aMS = a instanceof Date ? a.getTime() : a;
384     var bMS = b instanceof Date ? b.getTime() : b;
385     var actualDelta = Math.abs(Math.abs(aMS) - Math.abs(bMS));
386     if (actualDelta <= deltaMS)
387         return;
388 
389     doassert(a + " is not equal to " + b + " within " + deltaMS + " millis, actual delta: " +
390              actualDelta + " millis : " + msg);
391 };
392 
393 assert.writeOK = function(res, msg) {
394 
395     var errMsg = null;
396 
397     if (res instanceof WriteResult) {
398         if (res.hasWriteError()) {
399             errMsg = "write failed with error: " + tojson(res);
400         } else if (res.hasWriteConcernError()) {
401             errMsg = "write concern failed with errors: " + tojson(res);
402         }
403     } else if (res instanceof BulkWriteResult) {
404         // Can only happen with bulk inserts
405         if (res.hasWriteErrors()) {
406             errMsg = "write failed with errors: " + tojson(res);
407         } else if (res.hasWriteConcernError()) {
408             errMsg = "write concern failed with errors: " + tojson(res);
409         }
410     } else if (res instanceof WriteCommandError) {
411         // Can only happen with bulk inserts
412         errMsg = "write command failed: " + tojson(res);
413     } else {
414         if (!res || !res.ok) {
415             errMsg = "unknown type of write result, cannot check ok: " + tojson(res);
416         }
417     }
418 
419     if (errMsg) {
420         if (msg)
421             errMsg = errMsg + ": " + msg;
422         doassert(errMsg, res);
423     }
424 
425     return res;
426 };
427 
428 assert.writeError = function(res, msg) {
429 
430     var errMsg = null;
431 
432     if (res instanceof WriteResult) {
433         if (!res.hasWriteError() && !res.hasWriteConcernError()) {
434             errMsg = "no write error: " + tojson(res);
435         }
436     } else if (res instanceof BulkWriteResult) {
437         // Can only happen with bulk inserts
438         if (!res.hasWriteErrors() && !res.hasWriteConcernError()) {
439             errMsg = "no write errors: " + tojson(res);
440         }
441     } else if (res instanceof WriteCommandError) {
442         // Can only happen with bulk inserts
443         // No-op since we're expecting an error
444     } else {
445         if (!res || res.ok) {
446             errMsg = "unknown type of write result, cannot check error: " + tojson(res);
447         }
448     }
449 
450     if (errMsg) {
451         if (msg)
452             errMsg = errMsg + ": " + msg;
453         doassert(errMsg);
454     }
455 
456     return res;
457 };
458 
459 assert.gleOK = function(res, msg) {
460 
461     var errMsg = null;
462 
463     if (!res) {
464         errMsg = "missing first argument, no response to check";
465     } else if (!res.ok) {
466         errMsg = "getLastError failed: " + tojson(res);
467     } else if ('code' in res || 'errmsg' in res || ('err' in res && res['err'] != null)) {
468         errMsg = "write or write concern failed: " + tojson(res);
469     }
470 
471     if (errMsg) {
472         if (msg)
473             errMsg = errMsg + ": " + msg;
474         doassert(errMsg, res);
475     }
476 
477     return res;
478 };
479 
480 assert.gleSuccess = function(dbOrGLEDoc, msg) {
481     var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc;
482     if (gle.err) {
483         if (typeof(msg) == "function")
484             msg = msg(gle);
485         doassert("getLastError not null:" + tojson(gle) + " :" + msg, gle);
486     }
487     return gle;
488 };
489 
490 assert.gleError = function(dbOrGLEDoc, msg) {
491     var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc;
492     if (!gle.err) {
493         if (typeof(msg) == "function")
494             msg = msg(gle);
495         doassert("getLastError is null: " + tojson(gle) + " :" + msg);
496     }
497 };
498 
499 assert.gleErrorCode = function(dbOrGLEDoc, code, msg) {
500     var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc;
501     if (!gle.err || gle.code != code) {
502         if (typeof(msg) == "function")
503             msg = msg(gle);
504         doassert("getLastError is null or has code other than \"" + code + "\": " + tojson(gle) +
505                  " :" + msg);
506     }
507 };
508 
509 assert.gleErrorRegex = function(dbOrGLEDoc, regex, msg) {
510     var gle = dbOrGLEDoc instanceof DB ? dbOrGLEDoc.getLastErrorObj() : dbOrGLEDoc;
511     if (!gle.err || !regex.test(gle.err)) {
512         if (typeof(msg) == "function")
513             msg = msg(gle);
514         doassert("getLastError is null or doesn't match regex (" + regex + "): " + tojson(gle) +
515                  " :" + msg);
516     }
517 };
518