summaryrefslogtreecommitdiff
path: root/jstests/core/doc_validation.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/doc_validation.js')
-rw-r--r--jstests/core/doc_validation.js515
1 files changed, 256 insertions, 259 deletions
diff --git a/jstests/core/doc_validation.js b/jstests/core/doc_validation.js
index 9acfffae4e3..57f99adf48c 100644
--- a/jstests/core/doc_validation.js
+++ b/jstests/core/doc_validation.js
@@ -9,278 +9,275 @@
// Test basic inserts and updates with document validation.
(function() {
- "use strict";
+"use strict";
- function assertFailsValidation(res) {
- if (res instanceof WriteResult) {
- assert.writeErrorWithCode(res, ErrorCodes.DocumentValidationFailure, tojson(res));
- } else {
- assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure, tojson(res));
- }
+function assertFailsValidation(res) {
+ if (res instanceof WriteResult) {
+ assert.writeErrorWithCode(res, ErrorCodes.DocumentValidationFailure, tojson(res));
+ } else {
+ assert.commandFailedWithCode(res, ErrorCodes.DocumentValidationFailure, tojson(res));
}
+}
- const array = [];
- for (let i = 0; i < 2048; i++) {
- array.push({arbitrary: i});
- }
+const array = [];
+for (let i = 0; i < 2048; i++) {
+ array.push({arbitrary: i});
+}
- const collName = "doc_validation";
- const coll = db[collName];
-
- /**
- * Runs a series of document validation tests using the validator 'validator', which should
- * enforce the existence of a field "a".
- */
- function runInsertUpdateValidationTest(validator) {
- coll.drop();
-
- // Create a collection with document validator 'validator'.
- assert.commandWorked(db.createCollection(collName, {validator: validator}));
-
- // Insert and upsert documents that will pass validation.
- assert.writeOK(coll.insert({_id: "valid1", a: 1}));
- assert.writeOK(coll.update({_id: "valid2"}, {_id: "valid2", a: 2}, {upsert: true}));
- assert.writeOK(coll.runCommand(
- "findAndModify", {query: {_id: "valid3"}, update: {$set: {a: 3}}, upsert: true}));
-
- // Insert and upsert documents that will not pass validation.
- assertFailsValidation(coll.insert({_id: "invalid3", b: 1}));
- assertFailsValidation(
- coll.update({_id: "invalid4"}, {_id: "invalid4", b: 2}, {upsert: true}));
- assertFailsValidation(coll.runCommand(
- "findAndModify", {query: {_id: "invalid4"}, update: {$set: {b: 3}}, upsert: true}));
-
- // Assert that we can remove the document that passed validation.
- assert.writeOK(coll.remove({_id: "valid1"}));
-
- // Check that we can only update documents that pass validation. We insert a valid and an
- // invalid document, then set the validator.
- coll.drop();
- assert.writeOK(coll.insert({_id: "valid1", a: 1}));
- assert.writeOK(coll.insert({_id: "invalid2", b: 1}));
- assert.commandWorked(coll.runCommand("collMod", {validator: validator}));
-
- // Assert that updates on a conforming document succeed when they affect fields not involved
- // in validator.
- // Add a new field.
- assert.writeOK(coll.update({_id: "valid1"}, {$set: {z: 1}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {y: 2}}}));
- // In-place update.
- assert.writeOK(coll.update({_id: "valid1"}, {$inc: {z: 1}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$inc: {y: 1}}}));
- // Out-of-place update.
- assert.writeOK(coll.update({_id: "valid1"}, {$set: {z: array}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {y: array}}}));
- // No-op update.
- assert.writeOK(coll.update({_id: "valid1"}, {a: 1}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {a: 1}}}));
-
- // Verify those same updates will fail on non-conforming document.
- assertFailsValidation(coll.update({_id: "invalid2"}, {$set: {z: 1}}));
- assertFailsValidation(coll.update({_id: "invalid2"}, {$inc: {z: 1}}));
- assertFailsValidation(coll.update({_id: "invalid2"}, {$set: {z: array}}));
- assertFailsValidation(
- coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {y: 2}}}));
- assertFailsValidation(
- coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$inc: {y: 1}}}));
- assertFailsValidation(coll.runCommand(
- "findAndModify", {query: {_id: "invalid2"}, update: {$set: {y: array}}}));
-
- // A no-op update of an invalid doc will succeed.
- assert.writeOK(coll.update({_id: "invalid2"}, {$set: {b: 1}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {b: 1}}}));
-
- // Verify that we can't make a conforming document fail validation, but can update a
- // non-conforming document to pass validation.
- coll.drop();
- assert.writeOK(coll.insert({_id: "valid1", a: 1}));
- assert.writeOK(coll.insert({_id: "invalid2", b: 1}));
- assert.writeOK(coll.insert({_id: "invalid3", b: 1}));
- assert.commandWorked(coll.runCommand("collMod", {validator: validator}));
-
- assertFailsValidation(coll.update({_id: "valid1"}, {$unset: {a: 1}}));
- assert.writeOK(coll.update({_id: "invalid2"}, {$set: {a: 1}}));
- assertFailsValidation(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$unset: {a: 1}}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "invalid3"}, update: {$set: {a: 1}}}));
-
- // Modify the collection to remove the document validator.
- assert.commandWorked(coll.runCommand("collMod", {validator: {}}));
-
- // Verify that no validation is applied to updates.
- assert.writeOK(coll.update({_id: "valid1"}, {$set: {z: 1}}));
- assert.writeOK(coll.update({_id: "invalid2"}, {$set: {z: 1}}));
- assert.writeOK(coll.update({_id: "valid1"}, {$unset: {a: 1}}));
- assert.writeOK(coll.update({_id: "invalid2"}, {$set: {a: 1}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {z: 2}}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {z: 2}}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$unset: {a: 1}}}));
- assert.writeOK(
- coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {a: 1}}}));
- }
+const collName = "doc_validation";
+const coll = db[collName];
- // Run the test with a normal validator.
- runInsertUpdateValidationTest({a: {$exists: true}});
-
- // Run the test again with an equivalent JSON Schema.
- runInsertUpdateValidationTest({$jsonSchema: {required: ["a"]}});
-
- /**
- * Run a series of document validation tests involving collation using the validator
- * 'validator', which should enforce that the field "a" has the value "xyz".
- */
- function runCollationValidationTest(validator) {
- coll.drop();
- assert.commandWorked(db.createCollection(
- collName, {validator: validator, collation: {locale: "en_US", strength: 2}}));
-
- // An insert that matches the validator should succeed.
- assert.writeOK(coll.insert({_id: 0, a: "xyz", b: "foo"}));
-
- const isJSONSchema = validator.hasOwnProperty("$jsonSchema");
-
- // A normal validator should respect the collation and the inserts should succeed. A JSON
- // Schema validator ignores the collation and the inserts should fail.
- const assertCorrectResult =
- isJSONSchema ? res => assertFailsValidation(res) : res => assert.writeOK(res);
- assertCorrectResult(coll.insert({a: "XYZ"}));
- assertCorrectResult(coll.insert({a: "XyZ", b: "foo"}));
- assertCorrectResult(coll.update({_id: 0}, {a: "xyZ", b: "foo"}));
- assertCorrectResult(coll.update({_id: 0}, {$set: {a: "Xyz"}}));
- assertCorrectResult(
- coll.runCommand("findAndModify", {query: {_id: 0}, update: {a: "xyZ", b: "foo"}}));
- assertCorrectResult(
- coll.runCommand("findAndModify", {query: {_id: 0}, update: {$set: {a: "Xyz"}}}));
-
- // Test an insert and an update that should always fail.
- assertFailsValidation(coll.insert({a: "not xyz"}));
- assertFailsValidation(coll.update({_id: 0}, {$set: {a: "xyzz"}}));
- assertFailsValidation(
- coll.runCommand("findAndModify", {query: {_id: 0}, update: {$set: {a: "xyzz"}}}));
-
- // A normal validator expands leaf arrays, such that if "a" is an array containing "xyz", it
- // matches {a: "xyz"}. A JSON Schema validator does not expand leaf arrays and treats arrays
- // as a single array value.
- assertCorrectResult(coll.insert({a: ["xyz"]}));
- assertCorrectResult(coll.insert({a: ["XYZ"]}));
- assertCorrectResult(coll.insert({a: ["XyZ"], b: "foo"}));
- }
+/**
+ * Runs a series of document validation tests using the validator 'validator', which should
+ * enforce the existence of a field "a".
+ */
+function runInsertUpdateValidationTest(validator) {
+ coll.drop();
- runCollationValidationTest({a: "xyz"});
- runCollationValidationTest({$jsonSchema: {properties: {a: {enum: ["xyz"]}}}});
+ // Create a collection with document validator 'validator'.
+ assert.commandWorked(db.createCollection(collName, {validator: validator}));
- // The validator is allowed to contain $expr.
- coll.drop();
- assert.commandWorked(db.createCollection(collName, {validator: {$expr: {$eq: ["$a", 5]}}}));
- assert.writeOK(coll.insert({a: 5}));
- assertFailsValidation(coll.insert({a: 4}));
- assert.commandWorked(
- db.runCommand({"collMod": collName, "validator": {$expr: {$eq: ["$a", 4]}}}));
- assert.writeOK(coll.insert({a: 4}));
- assertFailsValidation(coll.insert({a: 5}));
-
- // The validator supports $expr with the date extraction expressions (with a timezone
- // specified).
- coll.drop();
- assert.commandWorked(db.createCollection(collName, {
- validator:
- {$expr: {$eq: [1, {$dayOfMonth: {date: "$a", timezone: "America/New_York"}}]}}
- }));
- assert.writeOK(coll.insert({a: ISODate("2017-10-01T22:00:00")}));
- assertFailsValidation(coll.insert({a: ISODate("2017-10-01T00:00:00")}));
-
- // The validator supports $expr with a $dateToParts expression.
- coll.drop();
- assert.commandWorked(db.createCollection(collName, {
- validator: {
- $expr: {
- $eq: [
- {
- "year": 2017,
- "month": 10,
- "day": 1,
- "hour": 18,
- "minute": 0,
- "second": 0,
- "millisecond": 0
- },
- {$dateToParts: {date: "$a", timezone: "America/New_York"}}
- ]
- }
- }
- }));
- assert.writeOK(coll.insert({a: ISODate("2017-10-01T22:00:00")}));
- assertFailsValidation(coll.insert({a: ISODate("2017-10-01T00:00:00")}));
+ // Insert and upsert documents that will pass validation.
+ assert.writeOK(coll.insert({_id: "valid1", a: 1}));
+ assert.writeOK(coll.update({_id: "valid2"}, {_id: "valid2", a: 2}, {upsert: true}));
+ assert.writeOK(coll.runCommand("findAndModify",
+ {query: {_id: "valid3"}, update: {$set: {a: 3}}, upsert: true}));
- // The validator supports $expr with $dateToString expression.
- coll.drop();
- assert.commandWorked(db.createCollection(collName, {
- validator: {
- $expr: {
- $eq: [
- "2017-07-04 14:56:42 +0000 (0 minutes)",
- {
- $dateToString: {
- format: "%Y-%m-%d %H:%M:%S %z (%Z minutes)",
- date: "$date",
- timezone: "$tz"
- }
- }
- ]
- }
- }
- }));
- assert.writeOK(coll.insert({date: new ISODate("2017-07-04T14:56:42.911Z"), tz: "UTC"}));
- assertFailsValidation(
- coll.insert({date: new ISODate("2017-07-04T14:56:42.911Z"), tz: "America/New_York"}));
+ // Insert and upsert documents that will not pass validation.
+ assertFailsValidation(coll.insert({_id: "invalid3", b: 1}));
+ assertFailsValidation(coll.update({_id: "invalid4"}, {_id: "invalid4", b: 2}, {upsert: true}));
+ assertFailsValidation(coll.runCommand(
+ "findAndModify", {query: {_id: "invalid4"}, update: {$set: {b: 3}}, upsert: true}));
+
+ // Assert that we can remove the document that passed validation.
+ assert.writeOK(coll.remove({_id: "valid1"}));
- // The validator supports $expr with $dateFromParts expression.
+ // Check that we can only update documents that pass validation. We insert a valid and an
+ // invalid document, then set the validator.
coll.drop();
- assert.commandWorked(db.createCollection(collName, {
- validator: {
- $expr: {
- $eq: [
- ISODate("2016-12-31T15:00:00Z"),
- {'$dateFromParts': {year: "$year", "timezone": "$timezone"}}
- ]
- }
- }
- }));
- assert.writeOK(coll.insert({_id: 0, year: 2017, month: 6, day: 19, timezone: "Asia/Tokyo"}));
+ assert.writeOK(coll.insert({_id: "valid1", a: 1}));
+ assert.writeOK(coll.insert({_id: "invalid2", b: 1}));
+ assert.commandWorked(coll.runCommand("collMod", {validator: validator}));
+
+ // Assert that updates on a conforming document succeed when they affect fields not involved
+ // in validator.
+ // Add a new field.
+ assert.writeOK(coll.update({_id: "valid1"}, {$set: {z: 1}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {y: 2}}}));
+ // In-place update.
+ assert.writeOK(coll.update({_id: "valid1"}, {$inc: {z: 1}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$inc: {y: 1}}}));
+ // Out-of-place update.
+ assert.writeOK(coll.update({_id: "valid1"}, {$set: {z: array}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {y: array}}}));
+ // No-op update.
+ assert.writeOK(coll.update({_id: "valid1"}, {a: 1}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {a: 1}}}));
+
+ // Verify those same updates will fail on non-conforming document.
+ assertFailsValidation(coll.update({_id: "invalid2"}, {$set: {z: 1}}));
+ assertFailsValidation(coll.update({_id: "invalid2"}, {$inc: {z: 1}}));
+ assertFailsValidation(coll.update({_id: "invalid2"}, {$set: {z: array}}));
assertFailsValidation(
- coll.insert({_id: 1, year: 2022, month: 1, day: 1, timezone: "America/New_York"}));
+ coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {y: 2}}}));
+ assertFailsValidation(
+ coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$inc: {y: 1}}}));
+ assertFailsValidation(
+ coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {y: array}}}));
- // The validator supports $expr with $dateFromString expression.
+ // A no-op update of an invalid doc will succeed.
+ assert.writeOK(coll.update({_id: "invalid2"}, {$set: {b: 1}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {b: 1}}}));
+
+ // Verify that we can't make a conforming document fail validation, but can update a
+ // non-conforming document to pass validation.
coll.drop();
- assert.commandWorked(db.createCollection(collName, {
- validator: {
- $expr: {
- $eq: [
- ISODate("2017-07-04T15:56:02Z"),
- {'$dateFromString': {dateString: "$date", timezone: 'America/New_York'}}
- ]
- }
- }
- }));
- assert.writeOK(coll.insert({_id: 0, date: "2017-07-04T11:56:02"}));
- assertFailsValidation(coll.insert({_id: 1, date: "2015-02-02T11:00:00"}));
+ assert.writeOK(coll.insert({_id: "valid1", a: 1}));
+ assert.writeOK(coll.insert({_id: "invalid2", b: 1}));
+ assert.writeOK(coll.insert({_id: "invalid3", b: 1}));
+ assert.commandWorked(coll.runCommand("collMod", {validator: validator}));
- // The validator can contain an $expr that may throw at runtime.
+ assertFailsValidation(coll.update({_id: "valid1"}, {$unset: {a: 1}}));
+ assert.writeOK(coll.update({_id: "invalid2"}, {$set: {a: 1}}));
+ assertFailsValidation(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$unset: {a: 1}}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "invalid3"}, update: {$set: {a: 1}}}));
+
+ // Modify the collection to remove the document validator.
+ assert.commandWorked(coll.runCommand("collMod", {validator: {}}));
+
+ // Verify that no validation is applied to updates.
+ assert.writeOK(coll.update({_id: "valid1"}, {$set: {z: 1}}));
+ assert.writeOK(coll.update({_id: "invalid2"}, {$set: {z: 1}}));
+ assert.writeOK(coll.update({_id: "valid1"}, {$unset: {a: 1}}));
+ assert.writeOK(coll.update({_id: "invalid2"}, {$set: {a: 1}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$set: {z: 2}}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {z: 2}}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "valid1"}, update: {$unset: {a: 1}}}));
+ assert.writeOK(
+ coll.runCommand("findAndModify", {query: {_id: "invalid2"}, update: {$set: {a: 1}}}));
+}
+
+// Run the test with a normal validator.
+runInsertUpdateValidationTest({a: {$exists: true}});
+
+// Run the test again with an equivalent JSON Schema.
+runInsertUpdateValidationTest({$jsonSchema: {required: ["a"]}});
+
+/**
+ * Run a series of document validation tests involving collation using the validator
+ * 'validator', which should enforce that the field "a" has the value "xyz".
+ */
+function runCollationValidationTest(validator) {
coll.drop();
- assert.commandWorked(
- db.createCollection(collName, {validator: {$expr: {$eq: ["$a", {$divide: [1, "$b"]}]}}}));
- assert.writeOK(coll.insert({a: 1, b: 1}));
- let res = coll.insert({a: 1, b: 0});
- assert.writeError(res);
- assert.eq(res.getWriteError().code, 16608);
- assert.writeOK(coll.insert({a: -1, b: -1}));
+ assert.commandWorked(db.createCollection(
+ collName, {validator: validator, collation: {locale: "en_US", strength: 2}}));
+
+ // An insert that matches the validator should succeed.
+ assert.writeOK(coll.insert({_id: 0, a: "xyz", b: "foo"}));
+
+ const isJSONSchema = validator.hasOwnProperty("$jsonSchema");
+
+ // A normal validator should respect the collation and the inserts should succeed. A JSON
+ // Schema validator ignores the collation and the inserts should fail.
+ const assertCorrectResult =
+ isJSONSchema ? res => assertFailsValidation(res) : res => assert.writeOK(res);
+ assertCorrectResult(coll.insert({a: "XYZ"}));
+ assertCorrectResult(coll.insert({a: "XyZ", b: "foo"}));
+ assertCorrectResult(coll.update({_id: 0}, {a: "xyZ", b: "foo"}));
+ assertCorrectResult(coll.update({_id: 0}, {$set: {a: "Xyz"}}));
+ assertCorrectResult(
+ coll.runCommand("findAndModify", {query: {_id: 0}, update: {a: "xyZ", b: "foo"}}));
+ assertCorrectResult(
+ coll.runCommand("findAndModify", {query: {_id: 0}, update: {$set: {a: "Xyz"}}}));
+
+ // Test an insert and an update that should always fail.
+ assertFailsValidation(coll.insert({a: "not xyz"}));
+ assertFailsValidation(coll.update({_id: 0}, {$set: {a: "xyzz"}}));
+ assertFailsValidation(
+ coll.runCommand("findAndModify", {query: {_id: 0}, update: {$set: {a: "xyzz"}}}));
+
+ // A normal validator expands leaf arrays, such that if "a" is an array containing "xyz", it
+ // matches {a: "xyz"}. A JSON Schema validator does not expand leaf arrays and treats arrays
+ // as a single array value.
+ assertCorrectResult(coll.insert({a: ["xyz"]}));
+ assertCorrectResult(coll.insert({a: ["XYZ"]}));
+ assertCorrectResult(coll.insert({a: ["XyZ"], b: "foo"}));
+}
+
+runCollationValidationTest({a: "xyz"});
+runCollationValidationTest({$jsonSchema: {properties: {a: {enum: ["xyz"]}}}});
+
+// The validator is allowed to contain $expr.
+coll.drop();
+assert.commandWorked(db.createCollection(collName, {validator: {$expr: {$eq: ["$a", 5]}}}));
+assert.writeOK(coll.insert({a: 5}));
+assertFailsValidation(coll.insert({a: 4}));
+assert.commandWorked(db.runCommand({"collMod": collName, "validator": {$expr: {$eq: ["$a", 4]}}}));
+assert.writeOK(coll.insert({a: 4}));
+assertFailsValidation(coll.insert({a: 5}));
+
+// The validator supports $expr with the date extraction expressions (with a timezone
+// specified).
+coll.drop();
+assert.commandWorked(db.createCollection(
+ collName,
+ {validator: {$expr: {$eq: [1, {$dayOfMonth: {date: "$a", timezone: "America/New_York"}}]}}}));
+assert.writeOK(coll.insert({a: ISODate("2017-10-01T22:00:00")}));
+assertFailsValidation(coll.insert({a: ISODate("2017-10-01T00:00:00")}));
+
+// The validator supports $expr with a $dateToParts expression.
+coll.drop();
+assert.commandWorked(db.createCollection(collName, {
+ validator: {
+ $expr: {
+ $eq: [
+ {
+ "year": 2017,
+ "month": 10,
+ "day": 1,
+ "hour": 18,
+ "minute": 0,
+ "second": 0,
+ "millisecond": 0
+ },
+ {$dateToParts: {date: "$a", timezone: "America/New_York"}}
+ ]
+ }
+ }
+}));
+assert.writeOK(coll.insert({a: ISODate("2017-10-01T22:00:00")}));
+assertFailsValidation(coll.insert({a: ISODate("2017-10-01T00:00:00")}));
+
+// The validator supports $expr with $dateToString expression.
+coll.drop();
+assert.commandWorked(db.createCollection(collName, {
+ validator: {
+ $expr: {
+ $eq: [
+ "2017-07-04 14:56:42 +0000 (0 minutes)",
+ {
+ $dateToString: {
+ format: "%Y-%m-%d %H:%M:%S %z (%Z minutes)",
+ date: "$date",
+ timezone: "$tz"
+ }
+ }
+ ]
+ }
+ }
+}));
+assert.writeOK(coll.insert({date: new ISODate("2017-07-04T14:56:42.911Z"), tz: "UTC"}));
+assertFailsValidation(
+ coll.insert({date: new ISODate("2017-07-04T14:56:42.911Z"), tz: "America/New_York"}));
+
+// The validator supports $expr with $dateFromParts expression.
+coll.drop();
+assert.commandWorked(db.createCollection(collName, {
+ validator: {
+ $expr: {
+ $eq: [
+ ISODate("2016-12-31T15:00:00Z"),
+ {'$dateFromParts': {year: "$year", "timezone": "$timezone"}}
+ ]
+ }
+ }
+}));
+assert.writeOK(coll.insert({_id: 0, year: 2017, month: 6, day: 19, timezone: "Asia/Tokyo"}));
+assertFailsValidation(
+ coll.insert({_id: 1, year: 2022, month: 1, day: 1, timezone: "America/New_York"}));
+
+// The validator supports $expr with $dateFromString expression.
+coll.drop();
+assert.commandWorked(db.createCollection(collName, {
+ validator: {
+ $expr: {
+ $eq: [
+ ISODate("2017-07-04T15:56:02Z"),
+ {'$dateFromString': {dateString: "$date", timezone: 'America/New_York'}}
+ ]
+ }
+ }
+}));
+assert.writeOK(coll.insert({_id: 0, date: "2017-07-04T11:56:02"}));
+assertFailsValidation(coll.insert({_id: 1, date: "2015-02-02T11:00:00"}));
+
+// The validator can contain an $expr that may throw at runtime.
+coll.drop();
+assert.commandWorked(
+ db.createCollection(collName, {validator: {$expr: {$eq: ["$a", {$divide: [1, "$b"]}]}}}));
+assert.writeOK(coll.insert({a: 1, b: 1}));
+let res = coll.insert({a: 1, b: 0});
+assert.writeError(res);
+assert.eq(res.getWriteError().code, 16608);
+assert.writeOK(coll.insert({a: -1, b: -1}));
})();