mongo/jstests/core/validate_js_timestamp.js

163 lines
5.5 KiB
JavaScript

/**
* Test validation of Timestamp objects that get returned from $function execution, whether they are
* returned directly or as a value in a document.
*
* The Timestamp constructor ensures that users cannot _create_ an invalid timestamp, but there is
* nothing stopping a user function from modifying the timestamp afterwards with invalid values.
*
* @tags: [
* requires_fcv_63,
* requires_scripting
* ]
*/
(function() {
"use strict";
/**
* Each test case executes a pipeline (created by the `testPipeline' function below) that mutates a
* Timestamp object according to the 'assignments' field and ensures that execution fails with the
* expected error code and error message.
*/
const testCases = [
{
assignments: {t: 20000000000},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp time"
},
{
assignments: {i: 20000000000},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp increment"
},
{
assignments: {t: -1},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp time"
},
{
assignments: {i: -1},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp increment"
},
{
assignments: {t: "str"},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp time"
},
{
assignments: {i: "str"},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp increment"
},
{
assignments: {t: {foo: "bar"}},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp time"
},
{
assignments: {i: {foo: "bar"}},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp increment"
},
{
assignments: {t: [2]},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp time"
},
{
assignments: {i: [2]},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp increment"
},
{
assignments: {t: "str1", i: "str2"},
expectedErrorCode: ErrorCodes.BadValue,
errorShouldContain: "Timestamp"
},
{
assignments: {t: "REMOVE"},
expectedErrorCode: 6900900,
errorShouldContain: "missing timestamp field"
},
{
assignments: {i: "REMOVE"},
expectedErrorCode: 6900901,
errorShouldContain: "missing increment field"
},
{
assignments: {i: "REMOVE", t: "REMOVE"},
expectedErrorCode: [6900900, 6900901],
errorShouldContain: "missing"
},
];
/**
* The test pipeline evaluates a $function expression on exactly one document, passing 'assignments'
* and 'embedInObject' as its arguments.
*
* The test function creates a valid Timestamp but then mutates it, bypassing any validation checks,
* and returns it, either as a scalar or as a value in a document, depending on the 'embedInObject'
* argument.
*
* Mutation follows the specification in the 'assignments' argument: each field-value pair (f, v) in
* 'assignments' is executed as a 'timestamp.f = v' assignemnt, except when v is the special
* "REMOVE" value, which deletes the field from the timestamp.
*/
function testPipeline(assignments, embedInObject) {
return [
{$documents: [{unvalidatedUserData: assignments, embedInObject: embedInObject}]},
{
$project: {
computedField: {
$function: {
body: function(assignments, embedInObject) {
let timestamp = Timestamp(1, 1);
for (let [field, value] of Object.entries(JSON.parse(assignments))) {
if (value !== "REMOVE") {
timestamp[field] = value;
} else {
delete timestamp[field];
}
}
return embedInObject ? {result: timestamp} : timestamp;
},
args: ["$unvalidatedUserData", "$embedInObject"],
lang: "js"
}
}
}
}
];
}
/**
* Execute each test case twice: once with 'embedInObject' set to false and once with it set to
* true.
*/
for (let {assignments, expectedErrorCode, errorShouldContain} of testCases) {
let error = assert.commandFailedWithCode(
db.runCommand(
{aggregate: 1, pipeline: testPipeline(tojson(assignments), false), cursor: {}}),
expectedErrorCode);
assert(error.errmsg.indexOf(errorShouldContain) >= 0, error);
error = assert.commandFailedWithCode(
db.runCommand(
{aggregate: 1, pipeline: testPipeline(tojson(assignments), true), cursor: {}}),
expectedErrorCode);
assert(error.errmsg.indexOf(errorShouldContain) >= 0, error);
}
/**
* Additionally, test a case where the function execution makes a legal modification to a
* Timestamp object, producing a valid timestamp.
*/
let result = db.aggregate(testPipeline(tojson({t: 123.0, i: 456.0}), false)).toArray();
assert.sameMembers(result, [{computedField: Timestamp(123, 456)}]);
result = db.aggregate(testPipeline(tojson({t: 123.0, i: 456.0}), true)).toArray();
assert.sameMembers(result, [{computedField: {result: Timestamp(123, 456)}}]);
}());