/** * Utility for testing the parsing of densify-like commands. I.e. $_internalDensify and $densify. */ export let parseUtil = (function(db, coll, stageName, options = {}) { function run(stage, extraCommandArgs = options) { return coll.runCommand(Object.merge( {aggregate: coll.getName(), pipeline: [stage], cursor: {}}, extraCommandArgs)); } function runTest(stageName) { // Required fields. const kIDLRequiredFieldErrorCode = 40414; assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: 1.0}}}), kIDLRequiredFieldErrorCode, "BSON field '$densify.range.bounds' is missing but a required field"); assert.commandFailedWithCode(run({[stageName]: {range: {step: 1.0, bounds: "full"}}}), kIDLRequiredFieldErrorCode, "BSON field '$densify.field' is missing but a required field"); assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {bounds: "full"}}}), kIDLRequiredFieldErrorCode, "BSON field '$densify.range.step' is missing but a required field"); // Wrong types assert.commandFailedWithCode( run({[stageName]: {field: 1.0, range: {step: 1.0, bounds: "full"}}}), ErrorCodes.TypeMismatch, "BSON field '$densify.field' is the wrong type 'double', expected type 'string'"); assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: "invalid", bounds: "full"}}}), ErrorCodes.TypeMismatch, "BSON field '$densify.range.step' is the wrong type 'string', expected types '[int, decimal, double, long']"); assert.commandFailedWithCode( run({ [stageName]: {field: "a", partitionByFields: "incorrect", range: {step: 1.0, bounds: "full"}} }), ErrorCodes.TypeMismatch, "BSON field '$densify.partitionByFields' is the wrong type 'string', expected type 'array'"); // 'partitionByFields' contains the field that is being desified. assert.commandFailedWithCode( run({ [stageName]: {field: "a", partitionByFields: ["a"], range: {step: 1.0, bounds: "full"}} }), 8993000, "BSON field '$densify.partitionByFields' contains the field that is being densified"); assert.commandFailedWithCode( run({ [stageName]: {field: "a.b", partitionByFields: ["a"], range: {step: 1.0, bounds: "full"}} }), 9554500, "The field that is being densified contains the BSON field '$densify.partitionByFields'"); assert.commandFailedWithCode( run({ [stageName]: {field: "a", partitionByFields: ["a.b"], range: {step: 1.0, bounds: "full"}} }), 8993000, "BSON field '$densify.partitionByFields' contains the field that is being densified"); assert.commandFailedWithCode( run({ [stageName]: { field: "a", range: { step: 1.0, bounds: [new ISODate("2020-01-01"), new ISODate("2020-01-02")], unit: 1000 } } }), ErrorCodes.TypeMismatch, "BSON field '$densify.range.unit' is the wrong type 'double', expected type 'string'"); // Logical errors // Too few bounds assert.commandFailedWithCode( run({ [stageName]: { field: "a", range: {step: 1.0, bounds: [new ISODate("2020-01-01")], unit: "second"} } }), 5733403, "a bounding array in a range statement must have exactly two elements"); // Too many elements assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: 1.0, bounds: [0, 1, 2]}}}), 5733403, "a bounding array in a range statement must have exactly two elements"); // Negative step assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: -1.0, bounds: [0, 1]}}}), 5733401, "the step parameter in a range statement must be a strictly positive numeric value"); // Field path expression instead of field path assert.commandFailedWithCode( run({[stageName]: {field: "$a", range: {step: 1.0, bounds: [0, 1]}}}), 16410, "FieldPath field names may not start with '$'. Consider using $getField or $setField."); // Field path expression instead of field path assert.commandFailedWithCode( run({ [stageName]: {field: "a", partitionByFields: ["$b"], range: {step: 1.0, bounds: [0, 1]}} }), 16410, "FieldPath field names may not start with '$'. Consider using $getField or $setField."); // Partition bounds but not partitionByFields assert.commandFailedWithCode( run({ [stageName]: {field: "a", partitionByFields: [], range: {step: 1.0, bounds: "partition"}} }), 5733408, "one may not specify the bounds as 'partition' without specifying a non-empty array of partitionByFields. You may have meant to specify 'full' bounds."); assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: 1.0, bounds: "partition"}}}), 5733408, "one may not specify the bounds as 'partition' without specifying a non-empty array of partitionByFields. You may have meant to specify 'full' bounds."); // Left bound greater than right bound assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: 1.0, bounds: [2, 1]}}}), 5733402, "the bounds in a range statement must be the string 'full', 'partition', or an ascending array of two numbers or two dates"); assert.commandFailedWithCode( run({ [stageName]: { field: "a", range: { step: 1.0, bounds: [new ISODate("2020-01-01"), new ISODate("2019-01-01")], unit: "second" } } }), 5733402, "the bounds in a range statement must be the string 'full', 'partition', or an ascending array of two numbers or two dates"); // Unit with numeric bounds assert.commandFailedWithCode( run({[stageName]: {field: "a", range: {step: 1.0, bounds: [1, 2], unit: "second"}}}), 5733409, "numeric bounds may not have unit parameter"); // Mixed numeric and date bounds assert.commandFailedWithCode( run({ [stageName]: {field: "a", range: {step: 1.0, bounds: [1, new ISODate("2020-01-01")]}} }), 5733406, "a bounding array must contain either both dates or both numeric types"); assert.commandFailedWithCode( run({ [stageName]: { field: "a", range: {step: 1.0, bounds: [new ISODate("2020-01-01"), 1], unit: "second"} } }), 5733402, "a bounding array must be an ascending array of either two dates or two numbers"); // Non-whole number step with date bounds assert.commandFailedWithCode( run({ [stageName]: { field: "a", range: { step: 1.1, bounds: [new ISODate("2020-01-01"), new ISODate("2020-01-03")], unit: "second" } } }), 6586400, "The step parameter in a range satement must be a whole number when densifying a date range"); // Positive test cases assert.commandWorked(run({[stageName]: {field: "a", range: {step: 1.0, bounds: [1, 2]}}})); assert.commandWorked(run({ [stageName]: { field: "a", range: { step: 1.0, bounds: [new ISODate("2020-01-01"), new ISODate("2021-01-01")], unit: "day" } } })); assert.commandWorked(run({ [stageName]: { field: "a", partitionByFields: ["b", "c"], range: { step: 1.0, bounds: [new ISODate("2020-01-01"), new ISODate("2021-01-01")], unit: "week" } } })); assert.commandWorked(run({ [stageName]: { field: "a", partitionByFields: ["b", "c"], range: {step: 1.0, bounds: "partition", unit: "second"} } })); assert.commandWorked(run({ [stageName]: { field: "a", partitionByFields: ["b", "c"], range: {step: 1.0, bounds: "full", unit: "second"} } })); assert.commandWorked(run({ [stageName]: {field: "a", partitionByFields: ["b", "c"], range: {step: 1.0, bounds: "full"}} })); assert.commandWorked(run({ [stageName]: { field: "a", partitionByFields: [ "b", ], range: {step: 1.0, bounds: "partition"} } })); assert.commandWorked(run({[stageName]: {field: "a", range: {step: 1.0, bounds: "full"}}})); } runTest(stageName); });