* Module dependencies.
*/
'use strict';
const start = require('./common');
const ValidationError = require('../lib/error/validation');
const assert = require('assert');
const mongoose = start.mongoose;
const Schema = mongoose.Schema;
const SchemaType = mongoose.SchemaType;
const ValidatorError = SchemaType.ValidatorError;
describe('ValidationError', function() {
describe('#infiniteRecursion', function() {
it('does not cause RangeError (gh-1834)', async function() {
const SubSchema = new Schema({
name: { type: String, required: true },
contents: [
new Schema(
{
key: { type: String, required: true },
value: { type: String, required: true }
},
{ _id: false }
)
]
});
const M = mongoose.model('SubSchema', SubSchema);
const model = new M({
name: 'Model',
contents: [
{ key: 'foo' }
]
});
const err = await model.validate().then(() => null, err => err);
assert.doesNotThrow(function() {
JSON.stringify(err);
});
});
});
describe('#minDate', function() {
it('causes a validation error', async function() {
const MinSchema = new Schema({
appointmentDate: { type: Date, min: Date.now }
});
const M = mongoose.model('MinSchema', MinSchema);
const model = new M({
appointmentDate: new Date(Date.now().valueOf() - 10000)
});
const err = await model.validate().then(() => null, err => err);
assert.notEqual(err, null, 'min Date validation failed.');
assert.ok(err.message.startsWith('MinSchema validation failed'));
model.appointmentDate = new Date(Date.now().valueOf() + 10000);
await model.validate();
});
});
describe('#maxDate', function() {
it('causes a validation error', async function() {
const MaxSchema = new Schema({
birthdate: { type: Date, max: Date.now }
});
const M = mongoose.model('MaxSchema', MaxSchema);
const model = new M({
birthdate: new Date(Date.now().valueOf() + 2000)
});
const err = await model.validate().then(() => null, err => err);
assert.notEqual(err, null, 'max Date validation failed');
assert.ok(err.message.startsWith('MaxSchema validation failed'));
model.birthdate = Date.now();
await model.validate();
});
});
describe('#minLength', function() {
it('causes a validation error', async function() {
const AddressSchema = new Schema({
postalCode: { type: String, minlength: 5 },
zipCode: { type: String, minLength: 5 }
});
const Address = mongoose.model('MinLengthAddress', AddressSchema);
const model = new Address({
postalCode: '9512',
zipCode: '9512'
});
const err = await model.validate().then(() => null, err => err);
assert.notEqual(err, null, 'String minLength validation failed.');
assert.ok(err.message.startsWith('MinLengthAddress validation failed'));
model.postalCode = '95125';
model.zipCode = '95125';
await model.validate();
});
it('with correct error message (gh-4207)', async function() {
const old = mongoose.Error.messages;
mongoose.Error.messages = {
String: {
minlength: 'woops!'
}
};
const AddressSchema = new Schema({
postalCode: { type: String, minlength: 5 },
zipCode: { type: String, minLength: 5 }
});
const Address = mongoose.model('gh4207', AddressSchema);
const model = new Address({
postalCode: '9512',
zipCode: '9512'
});
const err = await model.validate().then(() => null, err => err);
assert.equal(err.errors['postalCode'].message, 'woops!');
assert.ok(err.message.startsWith('gh4207 validation failed'));
mongoose.Error.messages = old;
});
});
describe('#maxLength', function() {
it('causes a validation error', async function() {
const AddressSchema = new Schema({
postalCode: { type: String, maxlength: 10 },
zipCode: { type: String, maxLength: 10 }
});
const Address = mongoose.model('MaxLengthAddress', AddressSchema);
const model = new Address({
postalCode: '95125012345',
zipCode: '95125012345'
});
const err = await model.validate().then(() => null, err => err);
assert.notEqual(err, null, 'String maxLength validation failed.');
assert.ok(err.message.startsWith('MaxLengthAddress validation failed'));
model.postalCode = '95125';
model.zipCode = '95125';
await model.validate();
});
it('can include custom error message with string length', async function() {
const AddressSchema = new Schema({
postalCode: { type: String, maxlength: [10, 'Postal code must be at most 10 characters, got {LENGTH}'] }
});
const Address = mongoose.model('gh15550', AddressSchema);
const model = new Address({
postalCode: '95125012345'
});
const err = await model.validate().then(() => null, err => err);
assert.notEqual(err, null, 'String maxLength validation failed.');
assert.ok(err.message.endsWith('Postal code must be at most 10 characters, got 11'), err.message);
});
it('trims long strings in error message', async function() {
const AddressSchema = new Schema({
name: { type: String, maxlength: 10 }
});
const Address = mongoose.model('gh15550_2', AddressSchema);
const model = new Address({
name: 'John Jacob Jingleheimer Schmidt'
});
const err = await model.validate().then(() => null, err => err);
assert.notEqual(err, null, 'String maxLength validation failed.');
assert.equal(err.errors.name.value, 'John Jacob Jingleheimer Schmidt');
assert.equal(err.errors.name.properties.value, 'John Jacob Jingleheimer Schmidt');
assert.ok(
err.message.endsWith('Path `name` (`John Jacob Jingleheimer Schmid...`, length 31) is longer than the maximum allowed length (10).'),
err.message
);
});
});
describe('#toString', function() {
it('does not cause RangeError (gh-1296)', async function() {
const ASchema = new Schema({
key: { type: String, required: true },
value: { type: String, required: true }
});
const BSchema = new Schema({
contents: [ASchema]
});
const M = mongoose.model('A', BSchema);
const m = new M();
m.contents.push({ key: 'asdf' });
const err = await m.validate().then(() => null, err => err);
assert.doesNotThrow(function() {
String(err);
});
});
});
describe('formatMessage', function() {
it('replaces properties in a message', function() {
const props = { base: 'eggs', topping: 'bacon' };
const message = 'I had {BASE} and {TOPPING} for breakfast';
const result = ValidatorError.prototype.formatMessage(message, props);
assert.equal(result, 'I had eggs and bacon for breakfast');
});
});
it('JSON.stringify() with message (gh-5309) (gh-9296)', function() {
model.modelName = 'TestClass';
const err = new ValidationError(new model());
err.addError('test', new ValidatorError({ message: 'Fail' }));
const obj = JSON.parse(JSON.stringify(err));
assert.ok(obj.message.indexOf('TestClass validation failed') !== -1,
obj.message);
assert.ok(obj.message.indexOf('test: Fail') !== -1,
obj.message);
assert.ok(obj.errors['test'].message);
function model() {}
});
it('default error message', function() {
const err = new ValidationError();
assert.equal(err.message, 'Validation failed');
});
describe('when user code defines a r/o Error#toJSON', function() {
it('should not fail', function(done) {
if (typeof Deno !== 'undefined') {
return this.skip();
}
this.timeout(10000);
const err = [];
const child = require('child_process')
.fork('./test/isolated/project-has-error.toJSON.js', ['--no-warnings'], { silent: true });
child.stderr.on('data', function(buf) { err.push(buf); });
child.on('exit', function(code) {
const stderr = err.filter(line => !line.includes('Warning: ')).join('');
assert.equal(stderr, '');
assert.equal(code, 0);
done();
});
});
});
it('should have error name in Cast error gh-10166', async function() {
const zetaSchema = new Schema({
text: { type: String, required: [true, 'Text is required'] },
number: { type: Number, required: [true, 'Number is required'] }
});
const Zeta = mongoose.model('Zeta', zetaSchema);
const entry = new Zeta({ text: false, number: 'fsfsf' });
const error = await entry.validate().then(() => null, err => err);
assert.ok(JSON.parse(JSON.stringify(error.errors.number.message)));
assert.ok(JSON.parse(JSON.stringify(error.errors.number.name)));
});
});