summaryrefslogtreecommitdiff
path: root/jstests
diff options
context:
space:
mode:
authorJustin Seyster <justin.seyster@mongodb.com>2018-03-05 18:15:32 -0500
committerJustin Seyster <justin.seyster@mongodb.com>2018-03-06 11:24:06 -0500
commit34e83ee29028ba17beedb4f2a00dc622751a3e51 (patch)
tree090f6b2fccb19b82abf86693704778173645eba8 /jstests
parent02b3f51e5a7e505b9dd6ebccc2c7632259767661 (diff)
downloadmongo-34e83ee29028ba17beedb4f2a00dc622751a3e51.tar.gz
SERVER-33447 jstest for $convert
Diffstat (limited to 'jstests')
-rw-r--r--jstests/aggregation/expressions/convert.js311
1 files changed, 311 insertions, 0 deletions
diff --git a/jstests/aggregation/expressions/convert.js b/jstests/aggregation/expressions/convert.js
new file mode 100644
index 00000000000..362cf7b1923
--- /dev/null
+++ b/jstests/aggregation/expressions/convert.js
@@ -0,0 +1,311 @@
+/**
+ * Tests behavior of $convert aggregation operator.
+ */
+(function() {
+ "use strict";
+
+ const coll = db.expression_convert;
+ function populateCollection(documentList) {
+ coll.drop();
+ var bulk = coll.initializeOrderedBulkOp();
+ documentList.forEach(doc => bulk.insert(doc));
+ assert.writeOK(bulk.execute());
+ }
+
+ //
+ // One test document for each possible conversion. Edge cases for these conversions are tested
+ // in expression_convert_test.cpp.
+ //
+ var conversionTestDocs = [
+ {_id: 0, input: 1.9, target: "double", expected: 1.9},
+ {_id: 1, input: 1.9, target: "string", expected: "1.9"},
+ {_id: 2, input: 1.9, target: "bool", expected: true},
+ {_id: 3, input: 1.9, target: "date", expected: ISODate("1970-01-01T00:00:00.001Z")},
+ {_id: 4, input: 1.9, target: "int", expected: NumberInt(1)},
+ {_id: 5, input: 1.9, target: "long", expected: NumberLong(1)},
+ {_id: 6, input: 1.9, target: "decimal", expected: NumberDecimal(1.9)},
+
+ {_id: 7, input: "1.9", target: "double", expected: 1.9},
+ {_id: 8, input: "str", target: "string", expected: "str"},
+ {
+ _id: 9,
+ input: "0123456789abcdef01234567",
+ target: "objectId",
+ expected: ObjectId("0123456789abcdef01234567")
+ },
+ {_id: 10, input: "", target: "bool", expected: true},
+ {
+ _id: 11,
+ input: "1970-01-01T00:00:00.001Z",
+ target: "date",
+ expected: ISODate("1970-01-01T00:00:00.001Z")
+ },
+ {_id: 12, input: "1", target: "int", expected: NumberInt(1)},
+ {_id: 13, input: "1", target: "long", expected: NumberLong(1)},
+ {_id: 14, input: "1.9", target: "decimal", expected: NumberDecimal("1.9")},
+
+ {
+ _id: 15,
+ input: ObjectId("0123456789abcdef01234567"),
+ target: "string",
+ expected: "0123456789abcdef01234567"
+ },
+ {_id: 16, input: ObjectId("0123456789abcdef01234567"), target: "bool", expected: true},
+ {
+ _id: 17,
+ input: ObjectId("0123456789abcdef01234567"),
+ target: "objectId",
+ expected: ObjectId("0123456789abcdef01234567")
+ },
+ {
+ _id: 18,
+ input: ObjectId("0123456789abcdef01234567"),
+ target: "date",
+ expected: ISODate("1970-08-09T22:25:43Z")
+ },
+
+ {_id: 19, input: false, target: "double", expected: 0.0},
+ {_id: 20, input: false, target: "string", expected: "false"},
+ {_id: 21, input: false, target: "bool", expected: false},
+ {_id: 22, input: false, target: "int", expected: NumberInt(0)},
+ {_id: 23, input: false, target: "long", expected: NumberLong(0)},
+ {_id: 24, input: false, target: "decimal", expected: NumberDecimal(0)},
+
+ {_id: 25, input: ISODate("1970-01-01T00:00:00.123Z"), target: "double", expected: 123.0},
+ {
+ _id: 26,
+ input: ISODate("1970-01-01T00:00:00.123Z"),
+ target: "string",
+ expected: "1970-01-01T00:00:00.123Z"
+ },
+ {_id: 27, input: ISODate("1970-01-01T00:00:00.123Z"), target: "bool", expected: true},
+ {
+ _id: 28,
+ input: ISODate("1970-01-01T00:00:00.123Z"),
+ target: "date",
+ expected: ISODate("1970-01-01T00:00:00.123Z")
+ },
+ {
+ _id: 29,
+ input: ISODate("1970-01-01T00:00:00.123Z"),
+ target: "long",
+ expected: NumberLong(123)
+ },
+ {
+ _id: 30,
+ input: ISODate("1970-01-01T00:00:00.123Z"),
+ target: "decimal",
+ expected: NumberDecimal("123")
+ },
+
+ {_id: 31, input: NumberInt(1), target: "double", expected: 1.0},
+ {_id: 32, input: NumberInt(1), target: "string", expected: "1"},
+ {_id: 33, input: NumberInt(1), target: "bool", expected: true},
+ {_id: 34, input: NumberInt(1), target: "int", expected: NumberInt(1)},
+ {_id: 35, input: NumberInt(1), target: "long", expected: NumberLong(1)},
+ {_id: 36, input: NumberInt(1), target: "decimal", expected: NumberDecimal("1")},
+
+ {_id: 37, input: NumberLong(1), target: "double", expected: 1.0},
+ {_id: 38, input: NumberLong(1), target: "string", expected: "1"},
+ {_id: 39, input: NumberLong(1), target: "bool", expected: true},
+ {
+ _id: 40,
+ input: NumberLong(1),
+ target: "date",
+ expected: ISODate("1970-01-01T00:00:00.001Z")
+ },
+ {_id: 41, input: NumberLong(1), target: "int", expected: NumberInt(1)},
+ {_id: 42, input: NumberLong(1), target: "long", expected: NumberLong(1)},
+ {_id: 43, input: NumberLong(1), target: "decimal", expected: NumberDecimal("1")},
+
+ {_id: 44, input: NumberDecimal("1.9"), target: "double", expected: 1.9},
+ {_id: 45, input: NumberDecimal("1.9"), target: "string", expected: "1.9"},
+ {_id: 46, input: NumberDecimal("1.9"), target: "bool", expected: true},
+ {
+ _id: 47,
+ input: NumberDecimal("1.9"),
+ target: "date",
+ expected: ISODate("1970-01-01T00:00:00.001Z")
+ },
+ {_id: 48, input: NumberDecimal("1.9"), target: "int", expected: NumberInt(1)},
+ {_id: 49, input: NumberDecimal("1.9"), target: "long", expected: NumberLong(1)},
+ {_id: 50, input: NumberDecimal("1.9"), target: "decimal", expected: NumberDecimal("1.9")},
+
+ {_id: 51, input: MinKey, target: "bool", expected: true},
+ {_id: 52, input: {foo: 1, bar: 2}, target: "bool", expected: true},
+ {_id: 53, input: [1, 2], target: "bool", expected: true},
+ {
+ _id: 54,
+ input: BinData(0, "BBBBBBBBBBBBBBBBBBBBBBBBBBBB"),
+ target: "bool",
+ expected: true
+ },
+ {_id: 55, input: /B*/, target: "bool", expected: true},
+ {_id: 56, input: new DBRef("db.test", "oid"), target: "bool", expected: true},
+ {_id: 57, input: function() {}, target: "bool", expected: true},
+ // Symbol and CodeWScope are not supported from JavaScript, so we can't test them here.
+ {_id: 58, input: new Timestamp(1 / 1000, 1), target: "bool", expected: true},
+ {_id: 59, input: MinKey, target: "bool", expected: true}
+ ];
+ populateCollection(conversionTestDocs);
+
+ // Test $convert on each document.
+ var pipeline = [
+ {
+ $project: {
+ output: {$convert: {to: "$target", input: "$input"}},
+ target: "$target",
+ expected: "$expected"
+ }
+ },
+ {$addFields: {outputType: {$type: "$output"}}},
+ {$sort: {_id: 1}}
+ ];
+ var aggResult = coll.aggregate(pipeline).toArray();
+ assert.eq(aggResult.length, conversionTestDocs.length);
+
+ aggResult.forEach(doc => {
+ assert.eq(doc.output, doc.expected, "Unexpected conversion: _id = " + doc._id);
+ assert.eq(doc.outputType, doc.target, "Conversion to incorrect type: _id = " + doc._id);
+ });
+
+ // Test each conversion using the shorthand $toBool, $toString, etc. syntax.
+ pipeline = [
+ {
+ $project: {
+ output: {
+ $switch: {
+ branches: [
+ {case: {$eq: ["$target", "double"]}, then: {$toDouble: "$input"}},
+ {case: {$eq: ["$target", "string"]}, then: {$toString: "$input"}},
+ {case: {$eq: ["$target", "objectId"]}, then: {$toObjectId: "$input"}},
+ {case: {$eq: ["$target", "bool"]}, then: {$toBool: "$input"}},
+ {case: {$eq: ["$target", "date"]}, then: {$toDate: "$input"}},
+ {case: {$eq: ["$target", "int"]}, then: {$toInt: "$input"}},
+ {case: {$eq: ["$target", "long"]}, then: {$toLong: "$input"}},
+ {case: {$eq: ["$target", "decimal"]}, then: {$toDecimal: "$input"}}
+ ]
+ }
+ },
+ target: "$target",
+ expected: "$expected"
+ }
+ },
+ {$addFields: {outputType: {$type: "$output"}}},
+ {$sort: {_id: 1}}
+ ];
+ aggResult = coll.aggregate(pipeline).toArray();
+ assert.eq(aggResult.length, conversionTestDocs.length);
+
+ aggResult.forEach(doc => {
+ assert.eq(doc.output, doc.expected, "Unexpected conversion: _id = " + doc._id);
+ assert.eq(doc.outputType, doc.target, "Conversion to incorrect type: _id = " + doc._id);
+ });
+
+ // Test a $convert expression with "onError" to make sure that error handling still allows an
+ // error in the "input" expression to propagate.
+ assert.throws(function() {
+ coll.aggregate([{
+ $project:
+ {output: {$convert: {to: "string", input: {$divide: [1, 0]}, onError: "ERROR"}}}
+ }]);
+ }, [], "Pipeline should have failed");
+
+ //
+ // One test document for each conversion that is _not_ supported.
+ //
+ var illegalConversionTestDocs = [
+ {_id: 0, input: 1.9, target: "objectId"},
+
+ {_id: 1, input: ObjectId("0123456789abcdef01234567"), target: "double"},
+ {_id: 2, input: ObjectId("0123456789abcdef01234567"), target: "int"},
+ {_id: 3, input: ObjectId("0123456789abcdef01234567"), target: "long"},
+ {_id: 4, input: ObjectId("0123456789abcdef01234567"), target: "decimal"},
+
+ {_id: 5, input: false, target: "objectId"},
+ {_id: 6, input: false, target: "date"},
+
+ {_id: 7, input: ISODate("1970-01-01T00:00:00.123Z"), target: "objectId"},
+ {_id: 8, input: ISODate("1970-01-01T00:00:00.123Z"), target: "int"},
+
+ {_id: 9, input: NumberInt(1), target: "objectId"},
+ {_id: 10, input: NumberInt(1), target: "date"},
+
+ {_id: 11, input: NumberLong(1), target: "objectId"},
+
+ {_id: 12, input: NumberDecimal("1.9"), target: "objectId"},
+ ];
+ populateCollection(illegalConversionTestDocs);
+
+ // Test each document to ensure that the conversion throws an error.
+ illegalConversionTestDocs.forEach(doc => {
+ pipeline = [
+ {$match: {_id: doc._id}},
+ {$project: {output: {$convert: {to: "$target", input: "$input"}}}}
+ ];
+
+ assert.throws(function() {
+ coll.aggregate(pipeline);
+ }, [], "Conversion should have failed: _id = " + doc._id);
+ });
+
+ // Test that each illegal conversion uses the 'onError' value.
+ pipeline = [
+ {$project: {output: {$convert: {to: "$target", input: "$input", onError: "ERROR"}}}},
+ {$sort: {_id: 1}}
+ ];
+ var aggResult = coll.aggregate(pipeline).toArray();
+ assert.eq(aggResult.length, illegalConversionTestDocs.length);
+
+ aggResult.forEach(doc => {
+ assert.eq(doc.output, "ERROR", "Unexpected result: _id = " + doc._id);
+ });
+
+ // Test that, when onError is missing, the missing value propagates to the result.
+ pipeline = [
+ {
+ $project: {
+ _id: false,
+ output: {$convert: {to: "$target", input: "$input", onError: "$$REMOVE"}}
+ }
+ },
+ {$sort: {_id: 1}}
+ ];
+ var aggResult = coll.aggregate(pipeline).toArray();
+ assert.eq(aggResult.length, illegalConversionTestDocs.length);
+
+ aggResult.forEach(doc => {
+ assert.eq(doc, {});
+ });
+
+ //
+ // One test document for each "nullish" value.
+ //
+ var nullTestDocs =
+ [{_id: 0, input: null}, {_id: 1, input: undefined}, {_id: 2, /* input is missing */}];
+ populateCollection(nullTestDocs);
+
+ // Test that all nullish inputs result in the 'onNull' output.
+ pipeline = [
+ {$project: {output: {$convert: {to: "int", input: "$input", onNull: "NULL"}}}},
+ {$sort: {_id: 1}}
+ ];
+ var aggResult = coll.aggregate(pipeline).toArray();
+ assert.eq(aggResult.length, nullTestDocs.length);
+
+ aggResult.forEach(doc => {
+ assert.eq(doc.output, "NULL", "Unexpected result: _id = " + doc._id);
+ });
+
+ // Test that all nullish inputs result in the 'onNull' output _even_ if 'to' is nullish.
+ pipeline = [
+ {$project: {output: {$convert: {to: null, input: "$input", onNull: "NULL"}}}},
+ {$sort: {_id: 1}}
+ ];
+ var aggResult = coll.aggregate(pipeline).toArray();
+ assert.eq(aggResult.length, nullTestDocs.length);
+
+ aggResult.forEach(doc => {
+ assert.eq(doc.output, "NULL", "Unexpected result: _id = " + doc._id);
+ });
+}());