1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | 'use strict';
|
8 |
|
9 |
|
10 | const should = require('./init.js');
|
11 | const async = require('async');
|
12 |
|
13 | const j = require('../');
|
14 | let db, User;
|
15 | const ValidationError = j.ValidationError;
|
16 |
|
17 | function getValidAttributes() {
|
18 | return {
|
19 | name: 'Anatoliy',
|
20 | email: 'email@example.com',
|
21 | state: '',
|
22 | age: 26,
|
23 | gender: 'male',
|
24 | createdByAdmin: false,
|
25 | createdByScript: true,
|
26 | };
|
27 | }
|
28 |
|
29 | describe('validations', function() {
|
30 | let User, Entry, Employee;
|
31 |
|
32 | before(function(done) {
|
33 | db = getSchema();
|
34 | User = db.define('User', {
|
35 | email: String,
|
36 | name: String,
|
37 | password: String,
|
38 | state: String,
|
39 | age: Number,
|
40 | gender: String,
|
41 | domain: String,
|
42 | pendingPeriod: Number,
|
43 | createdByAdmin: Boolean,
|
44 | createdByScript: Boolean,
|
45 | updatedAt: Date,
|
46 | });
|
47 | Entry = db.define('Entry', {
|
48 | id: {type: 'string', id: true, generated: false},
|
49 | name: {type: 'string'},
|
50 | });
|
51 | Employee = db.define('Employee', {
|
52 | id: {type: Number, id: true, generated: false},
|
53 | name: {type: String},
|
54 | age: {type: Number},
|
55 | }, {
|
56 | validateUpdate: true,
|
57 | });
|
58 | Entry.validatesUniquenessOf('id');
|
59 | db.automigrate(function(err) {
|
60 | should.not.exist(err);
|
61 | Employee.create(empData, done);
|
62 | });
|
63 | });
|
64 |
|
65 | beforeEach(function(done) {
|
66 | User.destroyAll(function() {
|
67 | delete User.validations;
|
68 | done();
|
69 | });
|
70 | });
|
71 |
|
72 | after(function(done) {
|
73 | Employee.destroyAll(done);
|
74 | });
|
75 |
|
76 | describe('commons', function() {
|
77 | describe('skipping', function() {
|
78 | it('should NOT skip when `if` is fulfilled', function() {
|
79 | User.validatesPresenceOf('pendingPeriod', {if: 'createdByAdmin'});
|
80 | const user = new User;
|
81 | user.createdByAdmin = true;
|
82 | user.isValid().should.be.false();
|
83 | user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
84 | user.pendingPeriod = 1;
|
85 | user.isValid().should.be.true();
|
86 | });
|
87 |
|
88 | it('should skip when `if` is NOT fulfilled', function() {
|
89 | User.validatesPresenceOf('pendingPeriod', {if: 'createdByAdmin'});
|
90 | const user = new User;
|
91 | user.createdByAdmin = false;
|
92 | user.isValid().should.be.true();
|
93 | user.errors.should.be.false();
|
94 | user.pendingPeriod = 1;
|
95 | user.isValid().should.be.true();
|
96 | });
|
97 |
|
98 | it('should NOT skip when `unless` is fulfilled', function() {
|
99 | User.validatesPresenceOf('pendingPeriod', {unless: 'createdByAdmin'});
|
100 | const user = new User;
|
101 | user.createdByAdmin = false;
|
102 | user.isValid().should.be.false();
|
103 | user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
104 | user.pendingPeriod = 1;
|
105 | user.isValid().should.be.true();
|
106 | });
|
107 |
|
108 | it('should skip when `unless` is NOT fulfilled', function() {
|
109 | User.validatesPresenceOf('pendingPeriod', {unless: 'createdByAdmin'});
|
110 | const user = new User;
|
111 | user.createdByAdmin = true;
|
112 | user.isValid().should.be.true();
|
113 | user.errors.should.be.false();
|
114 | user.pendingPeriod = 1;
|
115 | user.isValid().should.be.true();
|
116 | });
|
117 | });
|
118 |
|
119 | describe('skipping in async validation', function() {
|
120 | it('should skip when `if` is NOT fulfilled', function(done) {
|
121 | User.validateAsync('pendingPeriod', function(err, done) {
|
122 | if (!this.pendingPeriod) err();
|
123 | done();
|
124 | }, {if: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
125 | const user = new User;
|
126 | user.createdByAdmin = false;
|
127 | user.isValid(function(valid) {
|
128 | valid.should.be.true();
|
129 | user.errors.should.be.false();
|
130 | done();
|
131 | });
|
132 | });
|
133 |
|
134 | it('should NOT skip when `if` is fulfilled', function(done) {
|
135 | User.validateAsync('pendingPeriod', function(err, done) {
|
136 | if (!this.pendingPeriod) err();
|
137 | done();
|
138 | }, {if: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
139 | const user = new User;
|
140 | user.createdByAdmin = true;
|
141 | user.isValid(function(valid) {
|
142 | valid.should.be.false();
|
143 | user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
144 | done();
|
145 | });
|
146 | });
|
147 |
|
148 | it('should skip when `unless` is NOT fulfilled', function(done) {
|
149 | User.validateAsync('pendingPeriod', function(err, done) {
|
150 | if (!this.pendingPeriod) err();
|
151 | done();
|
152 | }, {unless: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
153 | const user = new User;
|
154 | user.createdByAdmin = true;
|
155 | user.isValid(function(valid) {
|
156 | valid.should.be.true();
|
157 | user.errors.should.be.false();
|
158 | done();
|
159 | });
|
160 | });
|
161 |
|
162 | it('should NOT skip when `unless` is fulfilled', function(done) {
|
163 | User.validateAsync('pendingPeriod', function(err, done) {
|
164 | if (!this.pendingPeriod) err();
|
165 | done();
|
166 | }, {unless: 'createdByAdmin', code: 'presence', message: 'can\'t be blank'});
|
167 | const user = new User;
|
168 | user.createdByAdmin = false;
|
169 | user.isValid(function(valid) {
|
170 | valid.should.be.false();
|
171 | user.errors.pendingPeriod.should.eql(['can\'t be blank']);
|
172 | done();
|
173 | });
|
174 | });
|
175 | });
|
176 |
|
177 | describe('lifecycle', function() {
|
178 | it('should work on create', function(done) {
|
179 | delete User.validations;
|
180 | User.validatesPresenceOf('name');
|
181 | User.create(function(e, u) {
|
182 | should.exist(e);
|
183 | User.create({name: 'Valid'}, function(e, d) {
|
184 | should.not.exist(e);
|
185 | done();
|
186 | });
|
187 | });
|
188 | });
|
189 |
|
190 | it('should work on update', function(done) {
|
191 | delete User.validations;
|
192 | User.validatesPresenceOf('name');
|
193 | User.create({name: 'Valid'}, function(e, d) {
|
194 | d.updateAttribute('name', null, function(e) {
|
195 | should.exist(e);
|
196 | e.should.be.instanceOf(Error);
|
197 | e.should.be.instanceOf(ValidationError);
|
198 | d.updateAttribute('name', 'Vasiliy', function(e) {
|
199 | should.not.exist(e);
|
200 | done();
|
201 | });
|
202 | });
|
203 | });
|
204 | });
|
205 |
|
206 | it('should ignore errors on upsert by default', function(done) {
|
207 | delete User.validations;
|
208 | User.validatesPresenceOf('name');
|
209 |
|
210 |
|
211 | User.create({name: 'a-name'}, (err, u) => {
|
212 | if (err) return done(err);
|
213 | User.updateOrCreate({id: u.id}, done);
|
214 | });
|
215 | });
|
216 |
|
217 | it('should be skipped by upsert when disabled via settings', function(done) {
|
218 | const Customer = User.extend('Customer');
|
219 | Customer.attachTo(db);
|
220 | db.autoupdate(function(err) {
|
221 | if (err) return done(err);
|
222 |
|
223 |
|
224 | Customer.create({name: 'a-name'}, (err, u) => {
|
225 | if (err) return done(err);
|
226 |
|
227 | Customer.prototype.isValid = function() {
|
228 | throw new Error('isValid() should not be called at all');
|
229 | };
|
230 | Customer.settings.validateUpsert = false;
|
231 |
|
232 | Customer.updateOrCreate({id: u.id, name: ''}, done);
|
233 | });
|
234 | });
|
235 | });
|
236 |
|
237 | it('should work on upsert when enabled via settings', function(done) {
|
238 | User.validatesPresenceOf('name');
|
239 | User.settings.validateUpsert = true;
|
240 |
|
241 |
|
242 | User.create({name: 'a-name'}, (err, u) => {
|
243 | if (err) return done(err);
|
244 | User.upsert({id: u.id, name: ''}, function(err, u) {
|
245 | if (!err) return done(new Error('Validation should have failed.'));
|
246 | err.should.be.instanceOf(ValidationError);
|
247 | done();
|
248 | });
|
249 | });
|
250 | });
|
251 |
|
252 | it('should return error code', function(done) {
|
253 | delete User.validations;
|
254 | User.validatesPresenceOf('name');
|
255 | User.create(function(e, u) {
|
256 | should.exist(e);
|
257 | e.details.codes.name.should.eql(['presence']);
|
258 | done();
|
259 | });
|
260 | });
|
261 |
|
262 | it('should allow to modify error after validation', function(done) {
|
263 | User.afterValidate = function(next) {
|
264 | next();
|
265 | };
|
266 | done();
|
267 | });
|
268 |
|
269 | it('should include validation messages in err.message', function(done) {
|
270 | delete User.validations;
|
271 | User.validatesPresenceOf('name');
|
272 | User.create(function(e, u) {
|
273 | should.exist(e);
|
274 | e.message.should.match(/`name` can't be blank/);
|
275 | done();
|
276 | });
|
277 | });
|
278 |
|
279 | it('should include property value in err.message', function(done) {
|
280 | delete User.validations;
|
281 | User.validatesPresenceOf('name');
|
282 | User.create(function(e, u) {
|
283 | should.exist(e);
|
284 | e.message.should.match(/`name` can't be blank \(value: undefined\)/);
|
285 | done();
|
286 | });
|
287 | });
|
288 |
|
289 | it('should include model name in err.message', function(done) {
|
290 | delete User.validations;
|
291 | User.validatesPresenceOf('name');
|
292 | User.create(function(e, u) {
|
293 | should.exist(e);
|
294 | e.message.should.match(/`User` instance/i);
|
295 | done();
|
296 | });
|
297 | });
|
298 |
|
299 | it('should return validation metadata', function() {
|
300 | const expected = {name: [{validation: 'presence', options: {}}]};
|
301 | delete User.validations;
|
302 | User.validatesPresenceOf('name');
|
303 | const validations = User.validations;
|
304 | validations.should.eql(expected);
|
305 | });
|
306 | });
|
307 | });
|
308 |
|
309 | describe('validation with or without options', function() {
|
310 | it('should work on update with options', function(done) {
|
311 | delete User.validations;
|
312 | User.validatesPresenceOf('name');
|
313 | User.create({name: 'Valid'}, function(e, d) {
|
314 | d.updateAttribute('name', null, {options: 'options'}, function(e) {
|
315 | should.exist(e);
|
316 | e.should.be.instanceOf(Error);
|
317 | e.should.be.instanceOf(ValidationError);
|
318 | d.updateAttribute('name', 'Vasiliy', {options: 'options'}, err => {
|
319 | if (err) return done(err);
|
320 |
|
321 | done();
|
322 | });
|
323 | });
|
324 | });
|
325 | });
|
326 |
|
327 | it('passes options to custom sync validator', done => {
|
328 | delete User.validations;
|
329 | User.validate('name', function(err, options) {
|
330 | if (options.testFlag !== 'someValue') err();
|
331 | });
|
332 | User.create({name: 'Valid'}, {testFlag: 'someValue'}, function(e, d) {
|
333 | d.updateAttribute('name', null, {testFlag: 'otherValue'}, function(e) {
|
334 | should.exist(e);
|
335 | e.should.be.instanceOf(ValidationError);
|
336 | d.updateAttribute('name', 'Vasiliy', {testFlag: 'someValue'}, err => {
|
337 | if (err) return done(err);
|
338 |
|
339 | done();
|
340 | });
|
341 | });
|
342 | });
|
343 | });
|
344 |
|
345 | it('passes options to async validator', done => {
|
346 | delete User.validations;
|
347 | User.validateAsync('name', function(err, options, done) {
|
348 | if (options.testFlag !== 'someValue') {
|
349 | console.error(
|
350 | 'Unexpected validation options: %j Expected %j',
|
351 | options, {testFlag: 'someValue'},
|
352 | );
|
353 | err();
|
354 | }
|
355 | process.nextTick(function() { done(); });
|
356 | });
|
357 | User.create({name: 'Valid'}, {testFlag: 'someValue'}, function(e, d) {
|
358 | if (e) return done(e);
|
359 | d.updateAttribute('name', null, {testFlag: 'otherValue'}, function(e) {
|
360 | should.exist(e);
|
361 | e.should.be.instanceOf(ValidationError);
|
362 | d.updateAttribute('name', 'Vasiliy', {testFlag: 'someValue'}, err => {
|
363 | if (err) return done(err);
|
364 |
|
365 | done();
|
366 | });
|
367 | });
|
368 | });
|
369 | });
|
370 |
|
371 | it('should work on update without options', function(done) {
|
372 | delete User.validations;
|
373 | User.validatesPresenceOf('name');
|
374 | User.create({name: 'Valid'}, function(e, d) {
|
375 | d.updateAttribute('name', null, function(e) {
|
376 | should.exist(e);
|
377 | e.should.be.instanceOf(Error);
|
378 | e.should.be.instanceOf(ValidationError);
|
379 | d.updateAttribute('name', 'Vasiliy', function(e) {
|
380 | should.not.exist(e);
|
381 | done();
|
382 | });
|
383 | });
|
384 | });
|
385 | });
|
386 |
|
387 | it('should work on create with options', function(done) {
|
388 | delete User.validations;
|
389 | User.validatesPresenceOf('name');
|
390 | User.create(function(e, u) {
|
391 | should.exist(e);
|
392 | User.create({name: 'Valid'}, {options: 'options'}, function(e, d) {
|
393 | should.not.exist(e);
|
394 | done();
|
395 | });
|
396 | });
|
397 | });
|
398 |
|
399 | it('should work on create without options', function(done) {
|
400 | delete User.validations;
|
401 | User.validatesPresenceOf('name');
|
402 | User.create(function(e, u) {
|
403 | should.exist(e);
|
404 | User.create({name: 'Valid'}, function(e, d) {
|
405 | should.not.exist(e);
|
406 | done();
|
407 | });
|
408 | });
|
409 | });
|
410 | });
|
411 |
|
412 | describe('presence', function() {
|
413 | it('should validate presence', function() {
|
414 | User.validatesPresenceOf('name', 'email');
|
415 |
|
416 | const validations = User.validations;
|
417 | validations.name.should.eql([{validation: 'presence', options: {}}]);
|
418 | validations.email.should.eql([{validation: 'presence', options: {}}]);
|
419 |
|
420 | const u = new User;
|
421 | u.isValid().should.not.be.true();
|
422 | u.name = 1;
|
423 | u.isValid().should.not.be.true();
|
424 | u.email = 2;
|
425 | u.isValid().should.be.true();
|
426 | });
|
427 |
|
428 | it('should reject NaN value as a number', function() {
|
429 | User.validatesPresenceOf('age');
|
430 | const u = new User();
|
431 | u.isValid().should.be.false();
|
432 | u.age = NaN;
|
433 | u.isValid().should.be.false();
|
434 | u.age = 1;
|
435 | u.isValid().should.be.true();
|
436 | });
|
437 |
|
438 | it('should allow "NaN" value as a string', function() {
|
439 | User.validatesPresenceOf('name');
|
440 | const u = new User();
|
441 | u.isValid().should.be.false();
|
442 | u.name = 'NaN';
|
443 | u.isValid().should.be.true();
|
444 | });
|
445 |
|
446 | it('should skip validation by property (if/unless)', function() {
|
447 | User.validatesPresenceOf('domain', {unless: 'createdByScript'});
|
448 |
|
449 | const user = new User(getValidAttributes());
|
450 | user.isValid().should.be.true();
|
451 |
|
452 | user.createdByScript = false;
|
453 | user.isValid().should.be.false();
|
454 | user.errors.domain.should.eql(['can\'t be blank']);
|
455 |
|
456 | user.domain = 'domain';
|
457 | user.isValid().should.be.true();
|
458 | });
|
459 |
|
460 | describe('validate presence on update', function() {
|
461 | before(function(done) {
|
462 | Employee.destroyAll(function(err) {
|
463 | should.not.exist(err);
|
464 | delete Employee.validations;
|
465 | db.automigrate('Employee', function(err) {
|
466 | should.not.exist(err);
|
467 | Employee.create(empData, function(err, inst) {
|
468 | should.not.exist(err);
|
469 | should.exist(inst);
|
470 | Employee.validatesPresenceOf('name', 'age');
|
471 | done();
|
472 | });
|
473 | });
|
474 | });
|
475 | });
|
476 |
|
477 | it('succeeds when validate condition is met', function(done) {
|
478 | const data = {name: 'Foo-new', age: 5};
|
479 | Employee.updateAll({id: 1}, data,
|
480 | function(err, emp) {
|
481 | should.not.exist(err);
|
482 | should.exist(emp);
|
483 | should.equal(emp.count, 1);
|
484 | Employee.find({where: {id: 1}}, function(err, emp) {
|
485 | should.not.exist(err);
|
486 | should.exist(emp);
|
487 | data.id = 1;
|
488 | should.deepEqual(data, emp[0].toObject());
|
489 | done();
|
490 | });
|
491 | });
|
492 | });
|
493 |
|
494 | it('throws err when validate condition is not met', function(done) {
|
495 | Employee.updateAll({where: {id: 1}}, {name: 'Foo-new'},
|
496 | function(err, emp) {
|
497 | should.exist(err);
|
498 | should.not.exist(emp);
|
499 | should.equal(err.statusCode, 422);
|
500 | should.equal(err.details.messages.age[0], 'can\'t be blank');
|
501 | done();
|
502 | });
|
503 | });
|
504 | });
|
505 | });
|
506 |
|
507 | describe('absence', function() {
|
508 | it('should validate absence', function() {
|
509 | User.validatesAbsenceOf('reserved', {if: 'locked'});
|
510 | let u = new User({reserved: 'foo', locked: true});
|
511 | u.isValid().should.not.be.true();
|
512 | u.reserved = null;
|
513 | u.isValid().should.be.true();
|
514 | u = new User({reserved: 'foo', locked: false});
|
515 | u.isValid().should.be.true();
|
516 | });
|
517 |
|
518 | describe('validate absence on update', function() {
|
519 | before(function(done) {
|
520 | Employee.destroyAll(function(err) {
|
521 | should.not.exist(err);
|
522 | delete Employee.validations;
|
523 | db.automigrate('Employee', function(err) {
|
524 | should.not.exist(err);
|
525 | Employee.create(empData, function(err, inst) {
|
526 | should.not.exist(err);
|
527 | should.exist(inst);
|
528 | Employee.validatesAbsenceOf('name');
|
529 | done();
|
530 | });
|
531 | });
|
532 | });
|
533 | });
|
534 |
|
535 | it('succeeds when validate condition is met', function(done) {
|
536 | const data = {age: 5};
|
537 | Employee.updateAll({id: 1}, data,
|
538 | function(err, emp) {
|
539 | should.not.exist(err);
|
540 | should.exist(emp);
|
541 | should.equal(emp.count, 1);
|
542 | Employee.find({where: {id: 1}}, function(err, emp) {
|
543 | should.not.exist(err);
|
544 | should.exist(emp);
|
545 | data.id = 1;
|
546 | data.name = 'Foo';
|
547 | should.deepEqual(data, emp[0].toObject());
|
548 | done();
|
549 | });
|
550 | });
|
551 | });
|
552 |
|
553 | it('throws err when validate condition is not met', function(done) {
|
554 | Employee.updateAll({where: {id: 1}}, {name: 'Foo-new', age: 5},
|
555 | function(err, emp) {
|
556 | should.exist(err);
|
557 | should.not.exist(emp);
|
558 | should.equal(err.statusCode, 422);
|
559 | should.equal(err.details.messages.name[0], 'can\'t be set');
|
560 | done();
|
561 | });
|
562 | });
|
563 | });
|
564 | });
|
565 |
|
566 | describe('uniqueness', function() {
|
567 | it('should validate uniqueness', function(done) {
|
568 | User.validatesUniquenessOf('email');
|
569 | const u = new User({email: 'hey'});
|
570 | Boolean(u.isValid(function(valid) {
|
571 | valid.should.be.true();
|
572 | u.save(function() {
|
573 | const u2 = new User({email: 'hey'});
|
574 | u2.isValid(function(valid) {
|
575 | valid.should.be.false();
|
576 | done();
|
577 | });
|
578 | });
|
579 | })).should.be.false();
|
580 | });
|
581 |
|
582 | it('should handle same object modification', function(done) {
|
583 | User.validatesUniquenessOf('email');
|
584 | const u = new User({email: 'hey'});
|
585 | Boolean(u.isValid(function(valid) {
|
586 | valid.should.be.true();
|
587 | u.save(function() {
|
588 | u.name = 'Goghi';
|
589 | u.isValid(function(valid) {
|
590 | valid.should.be.true();
|
591 | u.save(done);
|
592 | });
|
593 | });
|
594 |
|
595 | })).should.not.be.ok;
|
596 | });
|
597 |
|
598 | it('should support multi-key constraint', function(done) {
|
599 | const EMAIL = 'user@xample.com';
|
600 | const SiteUser = db.define('SiteUser', {
|
601 | siteId: String,
|
602 | email: String,
|
603 | });
|
604 | SiteUser.validatesUniquenessOf('email', {scopedTo: ['siteId']});
|
605 | async.waterfall([
|
606 | function automigrate(next) {
|
607 | db.automigrate(next);
|
608 | },
|
609 | function createSite1User(next) {
|
610 | SiteUser.create(
|
611 | {siteId: 1, email: EMAIL},
|
612 | next,
|
613 | );
|
614 | },
|
615 | function createSite2User(user1, next) {
|
616 | SiteUser.create(
|
617 | {siteId: 2, email: EMAIL},
|
618 | next,
|
619 | );
|
620 | },
|
621 | function validateDuplicateUser(user2, next) {
|
622 | const user3 = new SiteUser({siteId: 1, email: EMAIL});
|
623 | user3.isValid(function(valid) {
|
624 | valid.should.be.false();
|
625 | next();
|
626 | });
|
627 | },
|
628 | ], function(err) {
|
629 | if (err && err.name == 'ValidationError') {
|
630 | console.error('ValidationError:', err.details.messages);
|
631 | }
|
632 | done(err);
|
633 | });
|
634 | });
|
635 |
|
636 | it('should skip blank values', function(done) {
|
637 | User.validatesUniquenessOf('email');
|
638 | const u = new User({email: ' '});
|
639 | Boolean(u.isValid(function(valid) {
|
640 | valid.should.be.true();
|
641 | u.save(function() {
|
642 | const u2 = new User({email: null});
|
643 | u2.isValid(function(valid) {
|
644 | valid.should.be.true();
|
645 | done();
|
646 | });
|
647 | });
|
648 | })).should.be.false();
|
649 | });
|
650 |
|
651 | it('should work with if/unless', function(done) {
|
652 | User.validatesUniquenessOf('email', {
|
653 | if: function() { return true; },
|
654 | unless: function() { return false; },
|
655 | });
|
656 | const u = new User({email: 'hello'});
|
657 | Boolean(u.isValid(function(valid) {
|
658 | valid.should.be.true();
|
659 | done();
|
660 | })).should.be.false();
|
661 | });
|
662 |
|
663 | it('should work with id property on create', function(done) {
|
664 | Entry.create({id: 'entry'}, function(err, entry) {
|
665 | const e = new Entry({id: 'entry'});
|
666 | Boolean(e.isValid(function(valid) {
|
667 | valid.should.be.false();
|
668 | done();
|
669 | })).should.be.false();
|
670 | });
|
671 | });
|
672 |
|
673 | it('should work with id property after create', function(done) {
|
674 | Entry.findById('entry', function(err, e) {
|
675 | Boolean(e.isValid(function(valid) {
|
676 | valid.should.be.true();
|
677 | done();
|
678 | })).should.be.false();
|
679 | });
|
680 | });
|
681 |
|
682 | it('passes case insensitive validation', function(done) {
|
683 | User.validatesUniquenessOf('email', {ignoreCase: true});
|
684 | const u = new User({email: 'hey'});
|
685 | Boolean(u.isValid(function(valid) {
|
686 | valid.should.be.true();
|
687 | u.save(function(err) {
|
688 | if (err) return done(err);
|
689 | const u2 = new User({email: 'HEY'});
|
690 | u2.isValid(function(valid) {
|
691 | valid.should.be.false();
|
692 | done();
|
693 | });
|
694 | });
|
695 | })).should.be.false();
|
696 | });
|
697 |
|
698 | it('passed case sensitive validation', function(done) {
|
699 | User.validatesUniquenessOf('email', {ignoreCase: false});
|
700 | const u = new User({email: 'hey'});
|
701 | Boolean(u.isValid(function(valid) {
|
702 | valid.should.be.true();
|
703 | u.save(function(err) {
|
704 | if (err) return done(err);
|
705 | const u2 = new User({email: 'HEY'});
|
706 | u2.isValid(function(valid) {
|
707 | valid.should.be.true();
|
708 | done();
|
709 | });
|
710 | });
|
711 | })).should.be.false();
|
712 | });
|
713 |
|
714 | it('passes case insensitive validation with string that needs escaping', function(done) {
|
715 | User.validatesUniquenessOf('email', {ignoreCase: true});
|
716 | const u = new User({email: 'me+me@my.com'});
|
717 | Boolean(u.isValid(function(valid) {
|
718 | valid.should.be.true();
|
719 | u.save(function(err) {
|
720 | if (err) return done(err);
|
721 | const u2 = new User({email: 'ME+ME@MY.COM'});
|
722 | u2.isValid(function(valid) {
|
723 | valid.should.be.false();
|
724 | done();
|
725 | });
|
726 | });
|
727 | })).should.be.false();
|
728 | });
|
729 |
|
730 | it('passed case sensitive validation with string that needs escaping', function(done) {
|
731 | User.validatesUniquenessOf('email', {ignoreCase: false});
|
732 | const u = new User({email: 'me+me@my.com'});
|
733 | Boolean(u.isValid(function(valid) {
|
734 | valid.should.be.true();
|
735 | u.save(function(err) {
|
736 | if (err) return done(err);
|
737 | const u2 = new User({email: 'ME+ME@MY.COM'});
|
738 | u2.isValid(function(valid) {
|
739 | valid.should.be.true();
|
740 | done();
|
741 | });
|
742 | });
|
743 | })).should.be.false();
|
744 | });
|
745 |
|
746 | it('passes partial case insensitive validation with string that needs escaping', function(done) {
|
747 | User.validatesUniquenessOf('email', {ignoreCase: true});
|
748 | const u = new User({email: 'also+me@my.com'});
|
749 | Boolean(u.isValid(function(valid) {
|
750 | valid.should.be.true();
|
751 | u.save(function(err) {
|
752 | if (err) return done(err);
|
753 | const u2 = new User({email: 'Me@My.com'});
|
754 | u2.isValid(function(valid) {
|
755 | valid.should.be.true();
|
756 | done();
|
757 | });
|
758 | });
|
759 | })).should.be.false();
|
760 | });
|
761 |
|
762 | it('passes partial case sensitive validation with string that needs escaping', function(done) {
|
763 | User.validatesUniquenessOf('email', {ignoreCase: false});
|
764 | const u = new User({email: 'also+me@my.com'});
|
765 | Boolean(u.isValid(function(valid) {
|
766 | valid.should.be.true();
|
767 | u.save(function(err) {
|
768 | if (err) return done(err);
|
769 | const u2 = new User({email: 'Me@My.com'});
|
770 | u2.isValid(function(valid) {
|
771 | valid.should.be.true();
|
772 | done();
|
773 | });
|
774 | });
|
775 | })).should.be.false();
|
776 | });
|
777 |
|
778 | describe('validate uniqueness on update', function() {
|
779 | before(function(done) {
|
780 | Employee.destroyAll(function(err) {
|
781 | should.not.exist(err);
|
782 | delete Employee.validations;
|
783 | db.automigrate('Employee', function(err) {
|
784 | should.not.exist(err);
|
785 | Employee.create(empData, function(err, inst) {
|
786 | should.not.exist(err);
|
787 | should.exist(inst);
|
788 | Employee.validatesUniquenessOf('name');
|
789 | done();
|
790 | });
|
791 | });
|
792 | });
|
793 | });
|
794 |
|
795 | it('succeeds when validate condition is met', function(done) {
|
796 | const data = {name: 'Foo-new', age: 5};
|
797 | Employee.updateAll({id: 1}, data,
|
798 | function(err, emp) {
|
799 | should.not.exist(err);
|
800 | should.exist(emp);
|
801 | should.equal(emp.count, 1);
|
802 | Employee.find({where: {id: 1}}, function(err, emp) {
|
803 | should.not.exist(err);
|
804 | should.exist(emp);
|
805 | data.id = 1;
|
806 | should.deepEqual(data, emp[0].toObject());
|
807 | done();
|
808 | });
|
809 | });
|
810 | });
|
811 |
|
812 | it('throws err when validate condition is not met', function(done) {
|
813 | Employee.updateAll({where: {id: 1}}, {name: 'Bar', age: 5},
|
814 | function(err, emp) {
|
815 | should.exist(err);
|
816 | should.not.exist(emp);
|
817 | should.equal(err.statusCode, 422);
|
818 | should.equal(err.details.messages.name[0], 'is not unique');
|
819 | done();
|
820 | });
|
821 | });
|
822 | });
|
823 | });
|
824 |
|
825 | describe('format', function() {
|
826 | it('should validate the format of valid strings', function() {
|
827 | User.validatesFormatOf('name', {with: /[a-z][A-Z]*$/});
|
828 | const u = new User({name: 'valid name'});
|
829 | u.isValid().should.be.true();
|
830 | });
|
831 |
|
832 | it('should validate the format of invalid strings', function() {
|
833 | User.validatesFormatOf('name', {with: /[a-z][A-Z]*$/});
|
834 | const u = new User({name: 'invalid name!'});
|
835 | u.isValid().should.be.false();
|
836 | });
|
837 |
|
838 | it('should validate the format of valid numbers', function() {
|
839 | User.validatesFormatOf('age', {with: /^\d+$/});
|
840 | const u = new User({age: 30});
|
841 | u.isValid().should.be.true();
|
842 | });
|
843 |
|
844 | it('should validate the format of invalid numbers', function() {
|
845 | User.validatesFormatOf('age', {with: /^\d+$/});
|
846 | const u = new User({age: 'thirty'});
|
847 | u.isValid().should.be.false();
|
848 | });
|
849 |
|
850 | it('should overwrite default blank message with custom format message', function() {
|
851 | const CUSTOM_MESSAGE = 'custom validation message';
|
852 | User.validatesFormatOf('name', {with: /[a-z][A-Z]*$/, message: CUSTOM_MESSAGE});
|
853 | const u = new User({name: 'invalid name string 123'});
|
854 | u.isValid().should.be.false();
|
855 | u.errors.should.containEql({
|
856 | name: [CUSTOM_MESSAGE],
|
857 | codes: {
|
858 | name: ['format'],
|
859 | },
|
860 | });
|
861 | });
|
862 |
|
863 | it('should skip missing values when allowing blank', function() {
|
864 | User.validatesFormatOf('email', {with: /^\S+@\S+\.\S+$/, allowBlank: true});
|
865 | const u = new User({});
|
866 | u.isValid().should.be.true();
|
867 | });
|
868 |
|
869 | it('should skip null values when allowing null', function() {
|
870 | User.validatesFormatOf('email', {with: /^\S+@\S+\.\S+$/, allowNull: true});
|
871 | const u = new User({email: null});
|
872 | u.isValid().should.be.true();
|
873 | });
|
874 |
|
875 | it('should not skip missing values', function() {
|
876 | User.validatesFormatOf('email', {with: /^\S+@\S+\.\S+$/});
|
877 | const u = new User({});
|
878 | u.isValid().should.be.false();
|
879 | });
|
880 |
|
881 | it('should not skip null values', function() {
|
882 | User.validatesFormatOf('email', {with: /^\S+@\S+\.\S+$/});
|
883 | const u = new User({email: null});
|
884 | u.isValid().should.be.false();
|
885 | });
|
886 |
|
887 | describe('validate format correctly on bulk creation with global flag enabled in RegExp', function() {
|
888 | before(function(done) {
|
889 | Employee.destroyAll(function(err) {
|
890 | should.not.exist(err);
|
891 | delete Employee.validations;
|
892 | db.automigrate('Employee', function(err) {
|
893 | should.not.exist(err);
|
894 | Employee.create(empData, function(err, inst) {
|
895 | should.not.exist(err);
|
896 | should.exist(inst);
|
897 | Employee.validatesFormatOf('name', {with: /^[a-z]+$/g, allowNull: false});
|
898 | done();
|
899 | });
|
900 | });
|
901 | });
|
902 | });
|
903 |
|
904 | it('succeeds when validate condition is met for all items', function(done) {
|
905 | Employee.create([
|
906 | {name: 'test'},
|
907 | {name: 'test'},
|
908 | {name: 'test'},
|
909 | {name: 'test'},
|
910 | {name: 'test'},
|
911 | {name: 'test'},
|
912 | ], (err, instances) => {
|
913 | should.not.exist(err);
|
914 | should.exist(instances);
|
915 | instances.should.have.lengthOf(6);
|
916 | done();
|
917 | });
|
918 | });
|
919 | });
|
920 |
|
921 | describe('validate format on update', function() {
|
922 | before(function(done) {
|
923 | Employee.destroyAll(function(err) {
|
924 | should.not.exist(err);
|
925 | delete Employee.validations;
|
926 | db.automigrate('Employee', function(err) {
|
927 | should.not.exist(err);
|
928 | Employee.create(empData, function(err, inst) {
|
929 | should.not.exist(err);
|
930 | should.exist(inst);
|
931 | Employee.validatesFormatOf('name', {with: /^\w+\s\w+$/, allowNull: false});
|
932 | done();
|
933 | });
|
934 | });
|
935 | });
|
936 | });
|
937 |
|
938 | it('succeeds when validate condition is met', function(done) {
|
939 | const data = {name: 'Foo Mo', age: 5};
|
940 | Employee.updateAll({id: 1}, data,
|
941 | function(err, emp) {
|
942 | should.not.exist(err);
|
943 | should.exist(emp);
|
944 | should.equal(emp.count, 1);
|
945 | Employee.find({where: {id: 1}}, function(err, emp) {
|
946 | should.not.exist(err);
|
947 | should.exist(emp);
|
948 | data.id = 1;
|
949 | should.deepEqual(data, emp[0].toObject());
|
950 | done();
|
951 | });
|
952 | });
|
953 | });
|
954 |
|
955 | it('throws err when validate condition is not met', function(done) {
|
956 | Employee.updateAll({where: {id: 1}}, {name: '45foo', age: 5},
|
957 | function(err, emp) {
|
958 | should.exist(err);
|
959 | should.not.exist(emp);
|
960 | should.equal(err.statusCode, 422);
|
961 | should.equal(err.details.messages.name[0], 'is invalid');
|
962 | done();
|
963 | });
|
964 | });
|
965 | });
|
966 | });
|
967 |
|
968 | describe('numericality', function() {
|
969 | it('passes when given numeric values', function() {
|
970 | User.validatesNumericalityOf('age');
|
971 | const user = new User({age: 10});
|
972 | user.isValid().should.be.true();
|
973 | });
|
974 |
|
975 | it('fails when given non-numeric values', function() {
|
976 | User.validatesNumericalityOf('age');
|
977 | const user = new User({age: 'notanumber'});
|
978 | user.isValid().should.be.false();
|
979 | user.errors.should.containEql({age: ['is not a number']});
|
980 | });
|
981 |
|
982 | it('fails when given undefined values', function() {
|
983 | User.validatesNumericalityOf('age');
|
984 | const user = new User({});
|
985 | user.isValid().should.be.false();
|
986 | user.errors.should.containEql({age: ['is blank']});
|
987 | });
|
988 |
|
989 | it('skips undefined values when allowBlank option is true', function() {
|
990 | User.validatesNumericalityOf('age', {allowBlank: true});
|
991 | const user = new User({});
|
992 | user.isValid().should.be.true();
|
993 | });
|
994 |
|
995 | it('fails when given non-numeric values when allowBlank option is true', function() {
|
996 | User.validatesNumericalityOf('age', {allowBlank: true});
|
997 | const user = new User({age: 'test'});
|
998 | user.isValid().should.be.false();
|
999 | user.errors.should.containEql({age: ['is not a number']});
|
1000 | });
|
1001 |
|
1002 | it('fails when given null values', function() {
|
1003 | User.validatesNumericalityOf('age');
|
1004 | const user = new User({age: null});
|
1005 | user.isValid().should.be.false();
|
1006 | user.errors.should.containEql({age: ['is null']});
|
1007 | });
|
1008 |
|
1009 | it('passes when given null values when allowNull option is true', function() {
|
1010 | User.validatesNumericalityOf('age', {allowNull: true});
|
1011 | const user = new User({age: null});
|
1012 | user.isValid().should.be.true();
|
1013 | });
|
1014 |
|
1015 | it('passes when given float values', function() {
|
1016 | User.validatesNumericalityOf('age');
|
1017 | const user = new User({age: 13.37});
|
1018 | user.isValid().should.be.true();
|
1019 | });
|
1020 |
|
1021 | it('fails when given non-integer values when int option is true', function() {
|
1022 | User.validatesNumericalityOf('age', {int: true});
|
1023 | const user = new User({age: 13.37});
|
1024 | user.isValid().should.be.false();
|
1025 | user.errors.should.match({age: /is not an integer/});
|
1026 | });
|
1027 |
|
1028 | describe('validate numericality on update', function() {
|
1029 | before(function(done) {
|
1030 | Employee.destroyAll(function(err) {
|
1031 | should.not.exist(err);
|
1032 | delete Employee.validations;
|
1033 | db.automigrate('Employee', function(err) {
|
1034 | should.not.exist(err);
|
1035 | Employee.create(empData, function(err, inst) {
|
1036 | should.not.exist(err);
|
1037 | should.exist(inst);
|
1038 | Employee.validatesNumericalityOf('age');
|
1039 | done();
|
1040 | });
|
1041 | });
|
1042 | });
|
1043 | });
|
1044 |
|
1045 | it('succeeds when validate condition is met', function(done) {
|
1046 | const data = {name: 'Foo-new', age: 5};
|
1047 | Employee.updateAll({id: 1}, data,
|
1048 | function(err, emp) {
|
1049 | should.not.exist(err);
|
1050 | should.exist(emp);
|
1051 | should.equal(emp.count, 1);
|
1052 | Employee.find({where: {id: 1}}, function(err, emp) {
|
1053 | should.not.exist(err);
|
1054 | should.exist(emp);
|
1055 | data.id = 1;
|
1056 | should.deepEqual(data, emp[0].toObject());
|
1057 | done();
|
1058 | });
|
1059 | });
|
1060 | });
|
1061 |
|
1062 | it('throws err when validate condition is not met', function(done) {
|
1063 | Employee.updateAll({where: {id: 1}}, {age: {someAge: 5}},
|
1064 | function(err, emp) {
|
1065 | should.exist(err);
|
1066 | should.not.exist(emp);
|
1067 | should.equal(err.statusCode, 422);
|
1068 | should.equal(err.details.messages.age[0], 'is not a number');
|
1069 | done();
|
1070 | });
|
1071 | });
|
1072 | });
|
1073 | });
|
1074 |
|
1075 | describe('inclusion', function() {
|
1076 | it('fails when included value is not used for property', function(done) {
|
1077 | User.validatesInclusionOf('name', {in: ['bob', 'john']});
|
1078 | User.create({name: 'bobby'}, function(err) {
|
1079 | err.should.be.instanceof(Error);
|
1080 | err.details.messages.should.match({name: /is not included in the list/});
|
1081 | done();
|
1082 | });
|
1083 | });
|
1084 |
|
1085 | it('passes when included value is used for property', function(done) {
|
1086 | User.validatesInclusionOf('name', {in: ['bob', 'john']});
|
1087 | User.create({name: 'bob'}, function(err, user) {
|
1088 | if (err) return done(err);
|
1089 | user.name.should.eql('bob');
|
1090 | done();
|
1091 | });
|
1092 | });
|
1093 |
|
1094 | it('fails with a custom error message', function(done) {
|
1095 | User.validatesInclusionOf('name', {in: ['bob', 'john'], message: 'not used'});
|
1096 | User.create({name: 'dude'}, function(err) {
|
1097 | err.should.be.instanceof(Error);
|
1098 | err.details.messages.should.match({name: /not used/});
|
1099 | done();
|
1100 | });
|
1101 | });
|
1102 |
|
1103 | it('fails with a null value when allowNull is false', function(done) {
|
1104 | User.validatesInclusionOf('name', {in: ['bob'], allowNull: false});
|
1105 | User.create({name: null}, function(err) {
|
1106 | err.should.be.instanceof(Error);
|
1107 | err.details.messages.should.match({name: /is null/});
|
1108 | done();
|
1109 | });
|
1110 | });
|
1111 |
|
1112 | it('passes with a null value when allowNull is true', function(done) {
|
1113 | User.validatesInclusionOf('name', {in: ['bob'], allowNull: true});
|
1114 | User.create({name: null}, done);
|
1115 | });
|
1116 |
|
1117 | it('fails if value is used for integer property', function(done) {
|
1118 | User.validatesInclusionOf('age', {in: [123, 456]});
|
1119 | User.create({age: 789}, function(err) {
|
1120 | err.should.be.instanceof(Error);
|
1121 | err.details.messages.should.match({age: /is not included in the list/});
|
1122 | done();
|
1123 | });
|
1124 | });
|
1125 |
|
1126 | it('passes with an empty value when allowBlank option is true', function(done) {
|
1127 | User.validatesInclusionOf('gender', {in: ['male', 'female'], allowBlank: true});
|
1128 | User.create({gender: ''}, done);
|
1129 | });
|
1130 |
|
1131 | it('fails with an empty value when allowBlank option is false', function(done) {
|
1132 | User.validatesInclusionOf('gender', {in: ['male', 'female'], allowBlank: false});
|
1133 | User.create({gender: ''}, function(err) {
|
1134 | err.should.be.instanceOf(ValidationError);
|
1135 | getErrorDetails(err)
|
1136 | .should.equal('`gender` is blank (value: "").');
|
1137 | done();
|
1138 | });
|
1139 | });
|
1140 |
|
1141 | function getErrorDetails(err) {
|
1142 | return err.message.replace(/^.*Details: /, '');
|
1143 | }
|
1144 |
|
1145 | describe('validate inclusion on update', function() {
|
1146 | before(function(done) {
|
1147 | Employee.destroyAll(function(err) {
|
1148 | should.not.exist(err);
|
1149 | delete Employee.validations;
|
1150 | db.automigrate('Employee', function(err) {
|
1151 | should.not.exist(err);
|
1152 | Employee.create(empData, function(err, inst) {
|
1153 | should.not.exist(err);
|
1154 | should.exist(inst);
|
1155 | Employee.validatesInclusionOf('name', {in: ['Foo-new']});
|
1156 | done();
|
1157 | });
|
1158 | });
|
1159 | });
|
1160 | });
|
1161 |
|
1162 | it('succeeds when validate condition is met', function(done) {
|
1163 | const data = {name: 'Foo-new', age: 5};
|
1164 | Employee.updateAll({id: 1}, data,
|
1165 | function(err, emp) {
|
1166 | should.not.exist(err);
|
1167 | should.exist(emp);
|
1168 | should.equal(emp.count, 1);
|
1169 | Employee.find({where: {id: 1}}, function(err, emp) {
|
1170 | should.not.exist(err);
|
1171 | should.exist(emp);
|
1172 | data.id = 1;
|
1173 | should.deepEqual(data, emp[0].toObject());
|
1174 | done();
|
1175 | });
|
1176 | });
|
1177 | });
|
1178 |
|
1179 | it('throws err when validate condition is not met', function(done) {
|
1180 | Employee.updateAll({where: {id: 1}}, {name: 'Foo-new2', age: 5},
|
1181 | function(err, emp) {
|
1182 | should.exist(err);
|
1183 | should.not.exist(emp);
|
1184 | should.equal(err.statusCode, 422);
|
1185 | should.equal(err.details.messages.name[0], 'is not included in ' +
|
1186 | 'the list');
|
1187 | done();
|
1188 | });
|
1189 | });
|
1190 | });
|
1191 | });
|
1192 |
|
1193 | describe('exclusion', function() {
|
1194 | it('fails when excluded value is used for property', function(done) {
|
1195 | User.validatesExclusionOf('name', {in: ['bob']});
|
1196 | User.create({name: 'bob'}, function(err, user) {
|
1197 | err.should.be.instanceof(Error);
|
1198 | err.details.messages.should.match({name: /is reserved/});
|
1199 | done();
|
1200 | });
|
1201 | });
|
1202 |
|
1203 | it('passes when excluded value not found for property', function(done) {
|
1204 | User.validatesExclusionOf('name', {in: ['dude']});
|
1205 | User.create({name: 'bob'}, function(err, user) {
|
1206 | if (err) return done(err);
|
1207 | user.name.should.eql('bob');
|
1208 | done();
|
1209 | });
|
1210 | });
|
1211 |
|
1212 | it('fails with a custom error message', function(done) {
|
1213 | User.validatesExclusionOf('name', {in: ['bob'], message: 'cannot use this'});
|
1214 | User.create({name: 'bob'}, function(err) {
|
1215 | err.should.be.instanceof(Error);
|
1216 | err.details.messages.should.match({name: /cannot use this/});
|
1217 | done();
|
1218 | });
|
1219 | });
|
1220 |
|
1221 | it('fails with a null value when allowNull is false', function(done) {
|
1222 | User.validatesExclusionOf('name', {in: ['bob'], allowNull: false});
|
1223 | User.create({name: null}, function(err) {
|
1224 | err.should.be.instanceof(Error);
|
1225 | err.details.messages.should.match({name: /is null/});
|
1226 | done();
|
1227 | });
|
1228 | });
|
1229 |
|
1230 | it('passes with a null value when allowNull is true', function(done) {
|
1231 | User.validatesExclusionOf('name', {in: ['bob'], allowNull: true});
|
1232 | User.create({name: null}, done);
|
1233 | });
|
1234 |
|
1235 | it('fails if value is used for integer property', function(done) {
|
1236 | User.validatesExclusionOf('age', {in: [123, 456]});
|
1237 | User.create({age: 123}, function(err) {
|
1238 | err.should.be.instanceof(Error);
|
1239 | err.details.messages.should.match({age: /is reserved/});
|
1240 | done();
|
1241 | });
|
1242 | });
|
1243 |
|
1244 | describe('validate exclusion on update', function() {
|
1245 | before(function(done) {
|
1246 | Employee.destroyAll(function(err) {
|
1247 | should.not.exist(err);
|
1248 | delete Employee.validations;
|
1249 | db.automigrate('Employee', function(err) {
|
1250 | should.not.exist(err);
|
1251 | Employee.create(empData, function(err, inst) {
|
1252 | should.not.exist(err);
|
1253 | should.exist(inst);
|
1254 | Employee.validatesExclusionOf('name', {in: ['Bob']});
|
1255 | done();
|
1256 | });
|
1257 | });
|
1258 | });
|
1259 | });
|
1260 |
|
1261 | it('succeeds when validate condition is met', function(done) {
|
1262 | const data = {name: 'Foo-new', age: 5};
|
1263 | Employee.updateAll({id: 1}, data,
|
1264 | function(err, emp) {
|
1265 | should.not.exist(err);
|
1266 | should.exist(emp);
|
1267 | should.equal(emp.count, 1);
|
1268 | Employee.find({where: {id: 1}}, function(err, emp) {
|
1269 | should.not.exist(err);
|
1270 | should.exist(emp);
|
1271 | data.id = 1;
|
1272 | should.deepEqual(data, emp[0].toObject());
|
1273 | done();
|
1274 | });
|
1275 | });
|
1276 | });
|
1277 |
|
1278 | it('throws err when validate condition is not met', function(done) {
|
1279 | Employee.updateAll({where: {id: 1}}, {name: 'Bob', age: 5},
|
1280 | function(err, emp) {
|
1281 | should.exist(err);
|
1282 | should.not.exist(emp);
|
1283 | should.equal(err.statusCode, 422);
|
1284 | should.equal(err.details.messages.name[0], 'is reserved');
|
1285 | done();
|
1286 | });
|
1287 | });
|
1288 | });
|
1289 | });
|
1290 |
|
1291 | describe('length', function() {
|
1292 | it('should validate length');
|
1293 |
|
1294 | describe('validate length on update', function() {
|
1295 | before(function(done) {
|
1296 | Employee.destroyAll(function(err) {
|
1297 | should.not.exist(err);
|
1298 | delete Employee.validations;
|
1299 | db.automigrate('Employee', function(err) {
|
1300 | should.not.exist(err);
|
1301 | Employee.create(empData, function(err, inst) {
|
1302 | should.not.exist(err);
|
1303 | should.exist(inst);
|
1304 | Employee.validatesLengthOf('name', {min: 5});
|
1305 | done();
|
1306 | });
|
1307 | });
|
1308 | });
|
1309 | });
|
1310 |
|
1311 | it('succeeds when validate condition is met', function(done) {
|
1312 | const data = {name: 'Foo-new', age: 5};
|
1313 | Employee.updateAll({id: 1}, data,
|
1314 | function(err, emp) {
|
1315 | should.not.exist(err);
|
1316 | should.exist(emp);
|
1317 | should.equal(emp.count, 1);
|
1318 | Employee.find({where: {id: 1}}, function(err, emp) {
|
1319 | should.not.exist(err);
|
1320 | should.exist(emp);
|
1321 | data.id = 1;
|
1322 | should.deepEqual(data, emp[0].toObject());
|
1323 | done();
|
1324 | });
|
1325 | });
|
1326 | });
|
1327 |
|
1328 | it('throws err when validate condition is not met', function(done) {
|
1329 | Employee.updateAll({where: {id: 1}}, {name: 'Bob', age: 5},
|
1330 | function(err, emp) {
|
1331 | should.exist(err);
|
1332 | should.not.exist(emp);
|
1333 | should.equal(err.statusCode, 422);
|
1334 | should.equal(err.details.messages.name[0], 'too short');
|
1335 | done();
|
1336 | });
|
1337 | });
|
1338 | });
|
1339 | });
|
1340 |
|
1341 | describe('custom', function() {
|
1342 | it('should validate using custom sync validation', function() {
|
1343 | User.validate('email', function(err) {
|
1344 | if (this.email === 'hello') err();
|
1345 | }, {code: 'invalid-email'});
|
1346 | const u = new User({email: 'hello'});
|
1347 | Boolean(u.isValid()).should.be.false();
|
1348 | u.errors.codes.should.eql({email: ['invalid-email']});
|
1349 | });
|
1350 |
|
1351 | it('should validate and return detailed error messages', function() {
|
1352 | User.validate('global', function(err) {
|
1353 | if (this.email === 'hello' || this.email === 'hey') {
|
1354 | this.errors.add('email', 'Cannot be `' + this.email + '`', 'invalid-email');
|
1355 | err(false);
|
1356 | }
|
1357 | });
|
1358 | const u = new User({email: 'hello'});
|
1359 | Boolean(u.isValid()).should.be.false();
|
1360 | u.errors.should.containEql({email: ['Cannot be `hello`']});
|
1361 | u.errors.codes.should.eql({email: ['invalid-email']});
|
1362 | });
|
1363 |
|
1364 | it('should validate using custom async validation', function(done) {
|
1365 | User.validateAsync('email', function(err, next) {
|
1366 | process.nextTick(next);
|
1367 | }, {
|
1368 | if: function() { return true; },
|
1369 | unless: function() { return false; },
|
1370 | });
|
1371 | const u = new User({email: 'hello'});
|
1372 | Boolean(u.isValid(function(valid) {
|
1373 | valid.should.be.true();
|
1374 | done();
|
1375 | })).should.be.false();
|
1376 | });
|
1377 | });
|
1378 |
|
1379 | describe('invalid value formatting', function() {
|
1380 | let origMaxLen;
|
1381 | beforeEach(function saveAndSetMaxLen() {
|
1382 | origMaxLen = ValidationError.maxPropertyStringLength;
|
1383 | });
|
1384 |
|
1385 | afterEach(function restoreMaxLen() {
|
1386 | ValidationError.maxPropertyStringLength = origMaxLen;
|
1387 | });
|
1388 |
|
1389 | it('should truncate long strings', function() {
|
1390 | ValidationError.maxPropertyStringLength = 9;
|
1391 | const err = givenValidationError('prop', '1234567890abc', 'is invalid');
|
1392 | getErrorDetails(err)
|
1393 | .should.equal('`prop` is invalid (value: "12...abc").');
|
1394 | });
|
1395 |
|
1396 | it('should truncate long objects', function() {
|
1397 | ValidationError.maxPropertyStringLength = 12;
|
1398 | const err = givenValidationError('prop', {foo: 'bar'}, 'is invalid');
|
1399 | getErrorDetails(err)
|
1400 | .should.equal('`prop` is invalid (value: { foo:... }).');
|
1401 | });
|
1402 |
|
1403 | it('should truncate long arrays', function() {
|
1404 | ValidationError.maxPropertyStringLength = 12;
|
1405 | const err = givenValidationError('prop', [{a: 1, b: 2}], 'is invalid');
|
1406 | getErrorDetails(err)
|
1407 | .should.equal('`prop` is invalid (value: [ { a...} ]).');
|
1408 | });
|
1409 |
|
1410 | it('should print only top-level object properties', function() {
|
1411 | const err = givenValidationError('prop', {a: {b: 'c'}}, 'is invalid');
|
1412 | getErrorDetails(err)
|
1413 | .should.equal('`prop` is invalid (value: { a: [Object] }).');
|
1414 | });
|
1415 |
|
1416 | it('should print only top-level props of objects in array', function() {
|
1417 | const err = givenValidationError('prop', [{a: {b: 'c'}}], 'is invalid');
|
1418 | getErrorDetails(err)
|
1419 | .should.equal('`prop` is invalid (value: [ { a: [Object] } ]).');
|
1420 | });
|
1421 |
|
1422 | it('should exclude colors from Model values', function() {
|
1423 | const obj = new User();
|
1424 | obj.email = 'test@example.com';
|
1425 | const err = givenValidationError('user', obj, 'is invalid');
|
1426 | getErrorDetails(err).should.equal(
|
1427 | '`user` is invalid (value: { email: \'test@example.com\' }).',
|
1428 | );
|
1429 | });
|
1430 |
|
1431 | function givenValidationError(propertyName, propertyValue, errorMessage) {
|
1432 | const jsonVal = {};
|
1433 | jsonVal[propertyName] = propertyValue;
|
1434 | const errorVal = {};
|
1435 | errorVal[propertyName] = [errorMessage];
|
1436 |
|
1437 | const obj = {
|
1438 | errors: errorVal,
|
1439 | toJSON: function() { return jsonVal; },
|
1440 | };
|
1441 | return new ValidationError(obj);
|
1442 | }
|
1443 |
|
1444 | function getErrorDetails(err) {
|
1445 | return err.message.replace(/^.*Details: /, '');
|
1446 | }
|
1447 | });
|
1448 |
|
1449 | describe('date', function() {
|
1450 | it('should validate a date object', function() {
|
1451 | User.validatesDateOf('updatedAt');
|
1452 | const u = new User({updatedAt: new Date()});
|
1453 | u.isValid().should.be.true();
|
1454 | });
|
1455 |
|
1456 | it('should validate a date string', function() {
|
1457 | User.validatesDateOf('updatedAt');
|
1458 | const u = new User({updatedAt: '2000-01-01'});
|
1459 | u.isValid().should.be.true();
|
1460 | });
|
1461 |
|
1462 | it('should validate a null date', function() {
|
1463 | User.validatesDateOf('updatedAt');
|
1464 | const u = new User({updatedAt: null});
|
1465 | u.isValid().should.be.true();
|
1466 | });
|
1467 |
|
1468 | it('should validate an undefined date', function() {
|
1469 | User.validatesDateOf('updatedAt');
|
1470 | const u = new User({updatedAt: undefined});
|
1471 | u.isValid().should.be.true();
|
1472 | });
|
1473 |
|
1474 | it('should validate an invalid date string', function() {
|
1475 | User.validatesDateOf('updatedAt');
|
1476 | const u = new User({updatedAt: 'invalid date string'});
|
1477 | u.isValid().should.not.be.true();
|
1478 | u.errors.should.containEql({
|
1479 | updatedAt: ['is not a valid date'],
|
1480 | codes: {
|
1481 | updatedAt: ['date'],
|
1482 | },
|
1483 | });
|
1484 | });
|
1485 |
|
1486 | it('should attach validation by default to all date properties', function() {
|
1487 | const AnotherUser = db.define('User', {
|
1488 | email: String,
|
1489 | name: String,
|
1490 | password: String,
|
1491 | state: String,
|
1492 | age: Number,
|
1493 | gender: String,
|
1494 | domain: String,
|
1495 | pendingPeriod: Number,
|
1496 | createdByAdmin: Boolean,
|
1497 | createdByScript: Boolean,
|
1498 | updatedAt: Date,
|
1499 | });
|
1500 | const u = new AnotherUser({updatedAt: 'invalid date string'});
|
1501 | u.isValid().should.not.be.true();
|
1502 | u.errors.should.containEql({
|
1503 | updatedAt: ['is not a valid date'],
|
1504 | codes: {
|
1505 | updatedAt: ['date'],
|
1506 | },
|
1507 | });
|
1508 | });
|
1509 |
|
1510 | it('should overwrite default blank message with custom format message', function() {
|
1511 | const CUSTOM_MESSAGE = 'custom validation message';
|
1512 | User.validatesDateOf('updatedAt', {message: CUSTOM_MESSAGE});
|
1513 | const u = new User({updatedAt: 'invalid date string'});
|
1514 | u.isValid().should.not.be.true();
|
1515 | u.errors.should.containEql({
|
1516 | updatedAt: [CUSTOM_MESSAGE],
|
1517 | codes: {
|
1518 | updatedAt: ['date'],
|
1519 | },
|
1520 | });
|
1521 | });
|
1522 | });
|
1523 | });
|
1524 |
|
1525 | const empData = [{
|
1526 | id: 1,
|
1527 | name: 'Foo',
|
1528 | age: 1,
|
1529 | }, {
|
1530 | id: 2,
|
1531 | name: 'Bar',
|
1532 | age: 2,
|
1533 | }, {
|
1534 | id: 3,
|
1535 | name: 'Baz',
|
1536 | age: 3,
|
1537 | }];
|