Skip to content

Commit b43add6

Browse files
authored
Merge pull request #5146 from Automattic/3064
feat(validation): include failed paths in error message and inspect output
2 parents acc6ac5 + 68a94d0 commit b43add6

6 files changed

+51
-22
lines changed

lib/error/validation.js

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ var MongooseError = require('../error.js');
1414

1515
function ValidationError(instance) {
1616
this.errors = {};
17+
this._message = '';
1718
if (instance && instance.constructor.name === 'model') {
18-
MongooseError.call(this, instance.constructor.modelName + ' validation failed');
19+
this._message = instance.constructor.modelName + ' validation failed';
20+
MongooseError.call(this, this._message);
1921
} else {
20-
MongooseError.call(this, 'Validation failed');
22+
this._message = 'Validation failed';
23+
MongooseError.call(this, this._message);
2124
}
2225
if (Error.captureStackTrace) {
2326
Error.captureStackTrace(this);
@@ -37,24 +40,49 @@ function ValidationError(instance) {
3740
ValidationError.prototype = Object.create(MongooseError.prototype);
3841
ValidationError.prototype.constructor = MongooseError;
3942

43+
Object.defineProperty(ValidationError.prototype, 'message', {
44+
get: function() {
45+
return this._message + ': ' + _generateMessage(this);
46+
},
47+
enumerable: true
48+
});
4049

4150
/**
4251
* Console.log helper
4352
*/
4453

4554
ValidationError.prototype.toString = function() {
46-
var ret = this.name + ': ';
55+
return this.name + ': ' + _generateMessage(this);
56+
};
57+
58+
/*!
59+
* inspect helper
60+
*/
61+
62+
ValidationError.prototype.inspect = function() {
63+
return Object.assign(new Error(this.message), this);
64+
};
65+
66+
/*!
67+
* ignore
68+
*/
69+
70+
function _generateMessage(err) {
71+
var keys = Object.keys(err.errors || {});
72+
var len = keys.length;
4773
var msgs = [];
74+
var key;
4875

49-
Object.keys(this.errors || {}).forEach(function(key) {
50-
if (this === this.errors[key]) {
51-
return;
76+
for (var i = 0; i < len; ++i) {
77+
key = keys[i];
78+
if (err === err.errors[key]) {
79+
continue;
5280
}
53-
msgs.push(String(this.errors[key]));
54-
}, this);
81+
msgs.push(key + ': ' + err.errors[key].message);
82+
}
5583

56-
return ret + msgs.join(', ');
57-
};
84+
return msgs.join(', ');
85+
}
5886

5987
/*!
6088
* Module exports

test/document.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,7 @@ describe('document', function() {
12721272
var m = new M({name: 'gh1109-2', arr: [1]});
12731273
assert.equal(called, false);
12741274
m.save(function(err) {
1275-
assert.equal(String(err), 'ValidationError: BAM');
1275+
assert.equal(String(err), 'ValidationError: arr: BAM');
12761276
assert.equal(called, true);
12771277
m.arr.push(2);
12781278
called = false;
@@ -1301,7 +1301,7 @@ describe('document', function() {
13011301
assert.equal(err.errors.arr.message, 'Path `arr` is required.');
13021302
m.arr.push({nice: true});
13031303
m.save(function(err) {
1304-
assert.equal(String(err), 'ValidationError: BAM');
1304+
assert.equal(String(err), 'ValidationError: arr: BAM');
13051305
m.arr.push(95);
13061306
m.save(function(err) {
13071307
assert.ifError(err);

test/model.populate.test.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2023,7 +2023,8 @@ describe('model: populate:', function() {
20232023
});
20242024

20252025
comment.save(function(err) {
2026-
assert.equal('CommentWithRequiredField validation failed', err && err.message);
2026+
assert.ok(err);
2027+
assert.ok(err.message.indexOf('CommentWithRequiredField validation failed') === 0, err.message);
20272028
assert.ok('num' in err.errors);
20282029
assert.ok('str' in err.errors);
20292030
assert.ok('user' in err.errors);

test/model.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1139,7 +1139,7 @@ describe('Model', function() {
11391139
db.close();
11401140
assert.equal(err.errors.name.message, 'Name cannot be greater than 1 character for path "name" with value `hi`');
11411141
assert.equal(err.name, 'ValidationError');
1142-
assert.equal(err.message, 'IntrospectionValidation validation failed');
1142+
assert.ok(err.message.indexOf('IntrospectionValidation validation failed') !== -1, err.message);
11431143
done();
11441144
});
11451145
});

test/schema.validation.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,7 @@ describe('schema', function() {
10571057
bad.foods = 'waffles';
10581058
bad.validate(function(error) {
10591059
assert.ok(error);
1060-
var errorMessage = 'CastError: Cast to Object failed for value ' +
1060+
var errorMessage = 'foods: Cast to Object failed for value ' +
10611061
'"waffles" at path "foods"';
10621062
assert.ok(error.toString().indexOf(errorMessage) !== -1, error.toString());
10631063
done();
@@ -1071,7 +1071,7 @@ describe('schema', function() {
10711071
var bad = new Breakfast({});
10721072
bad.validate(function(error) {
10731073
assert.ok(error);
1074-
var errorMessage = 'ValidationError: Path `description` is required.';
1074+
var errorMessage = 'ValidationError: description: Path `description` is required.';
10751075
assert.equal(errorMessage, error.toString());
10761076
done();
10771077
});
@@ -1085,7 +1085,7 @@ describe('schema', function() {
10851085
var error = bad.validateSync();
10861086

10871087
assert.ok(error);
1088-
var errorMessage = 'ValidationError: Path `description` is required.';
1088+
var errorMessage = 'ValidationError: description: Path `description` is required.';
10891089
assert.equal(errorMessage, error.toString());
10901090
done();
10911091
});
@@ -1114,7 +1114,7 @@ describe('schema', function() {
11141114
var bad = new Breakfast({});
11151115
bad.validate(function(error) {
11161116
assert.ok(error);
1117-
var errorMessage = 'ValidationError: Path `description` is required.';
1117+
var errorMessage = 'ValidationError: description: Path `description` is required.';
11181118
assert.equal(errorMessage, error.toString());
11191119
done();
11201120
});
@@ -1132,7 +1132,7 @@ describe('schema', function() {
11321132
var Breakfast = mongoose.model('gh2832', breakfast, 'gh2832');
11331133
Breakfast.create({description: undefined}, function(error) {
11341134
assert.ok(error);
1135-
var errorMessage = 'ValidationError: CastError: Cast to String failed for value "undefined" at path "description"';
1135+
var errorMessage = 'ValidationError: description: Cast to String failed for value "undefined" at path "description"';
11361136
assert.equal(errorMessage, error.toString());
11371137
assert.ok(error.errors.description);
11381138
assert.equal(error.errors.description.reason.toString(), 'Error: oops');

test/types.buffer.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ describe('types.buffer', function() {
6969
});
7070

7171
t.validate(function(err) {
72-
assert.equal(err.message, 'UserBuffer validation failed');
72+
assert.ok(err.message.indexOf('UserBuffer validation failed') === 0, err.message);
7373
assert.equal(err.errors.required.kind, 'required');
7474
t.required = {x: [20]};
7575
t.save(function(err) {
@@ -83,11 +83,11 @@ describe('types.buffer', function() {
8383

8484
t.sub.push({name: 'Friday Friday'});
8585
t.save(function(err) {
86-
assert.equal(err.message, 'UserBuffer validation failed');
86+
assert.ok(err.message.indexOf('UserBuffer validation failed') === 0, err.message);
8787
assert.equal(err.errors['sub.0.buf'].kind, 'required');
8888
t.sub[0].buf = new Buffer('well well');
8989
t.save(function(err) {
90-
assert.equal(err.message, 'UserBuffer validation failed');
90+
assert.ok(err.message.indexOf('UserBuffer validation failed') === 0, err.message);
9191
assert.equal(err.errors['sub.0.buf'].kind, 'user defined');
9292
assert.equal(err.errors['sub.0.buf'].message, 'valid failed');
9393

0 commit comments

Comments
 (0)