Files
mongo/jstests/core/mr_fail_invalid_js.js

117 lines
3.8 KiB
JavaScript

// Tests that mapReduce fails gracefully when given a map or reduce function which fails in some
// way.
// @tags: [
// # mapReduce does not support afterClusterTime.
// does_not_support_causal_consistency,
// does_not_support_stepdowns,
// uses_map_reduce_with_temp_collections,
// ]
(function() {
"use strict";
const coll = db.mr_fail_invalid_js;
const outputColl = db.mr_fail_invalid_js_out;
// Test that a map or reduce function which references a path which doesn't exist fails gracefully.
(function testReferencingInvalidPaths() {
coll.drop();
outputColl.drop();
assert.commandWorked(coll.insert([
{x: 1, tags: ["a", "b"]},
{x: 2, tags: ["b", "c"]},
{x: 3, tags: ["c", "a"]},
{x: 4, tags: ["b", "c"]}
]));
let reduceFn = function(key, values) {
return Array.sum(values);
};
const goodMapFn = function() {
for (let tag of this.tags) {
emit(tag, 1);
}
};
assert.commandWorked(coll.mapReduce(goodMapFn, reduceFn, {out: {merge: outputColl.getName()}}));
outputColl.drop();
// mapReduce fails when attempting to merge a missing key.
const singleInvalidPathMapFn = function() {
emit(this.missing_field, this.x);
};
assert.throws(() => coll.mapReduce(
singleInvalidPathMapFn, reduceFn, {out: {merge: outputColl.getName()}}),
[]);
// Now test that a traversal through a missing path will cause an error.
const badMapFn = function() {
emit(this._id, this.missing_field.nested_missing);
};
assert.throws(
() => coll.mapReduce(newMapFn, reduceFn, {out: {merge: outputColl.getName()}}),
[],
"expected mapReduce to throw because map function references path that does not exist");
// Test the same thing but in the reduce function.
reduceFn = function(k, v) {
return v.missing_field.increasingly_missing.all_hope_is_lost;
};
assert.throws(
() => coll.mapReduce(goodMapFn, reduceFn, outputColl.getName()),
[],
"expected mapReduce to throw because reduce function references path that does not exist");
}());
// Test that a map function which supplies the wrong number of arguments to 'emit' will fail
// gracefully.
(function testBadCallToEmit() {
coll.drop();
outputColl.drop();
assert.commandWorked(coll.insert([{a: [1, 2, 3]}, {a: [2, 3, 4]}]));
const goodMapFn = function() {
for (let i = 0; i < this.a.length; i++) {
emit(this.a[i], 1);
}
};
const goodReduceFn = function(k, v) {
let total = 0;
for (let i = 0; i < v.length; i++)
total += v[i];
return total;
};
// First test that a valid command succeeds.
let res = coll.mapReduce(goodMapFn, goodReduceFn, {out: {merge: outputColl.getName()}});
assert.eq([{_id: 1, value: 1}, {_id: 2, value: 2}, {_id: 3, value: 2}, {_id: 4, value: 1}],
outputColl.find().sort({_id: 1}).toArray());
assert(outputColl.drop());
const badMapFn = function() {
for (let i = 0; i < this.a.length; i++) {
emit(this.a[i]);
}
};
const error = assert.commandFailed(db.runCommand({
mapReduce: coll.getName(),
map: badMapFn,
reduce: goodReduceFn,
out: outputColl.getName()
}));
assert(error.errmsg.indexOf("emit") >= 0, () => tojson(error));
// Test that things are still in an ok state and the next mapReduce can succeed.
outputColl.drop();
assert.commandWorked(
coll.mapReduce(goodMapFn, goodReduceFn, {out: {merge: outputColl.getName()}}));
assert.eq([{_id: 1, value: 1}, {_id: 2, value: 2}, {_id: 3, value: 2}, {_id: 4, value: 1}],
outputColl.find().sort({_id: 1}).toArray());
assert(outputColl.drop());
}());
}());