thoughts/node_modules/jsonwebtoken/node_modules/joi/test/index.js
node_modules/jsonwebtoken/node_modules/joi/test/index.jsRaw
1// Load modules
2
3var Lab = require('lab');
4var Code = require('code');
5var Joi = require('../lib');
6var Helper = require('./helper');
7
8
9// Declare internals
10
11var internals = {};
12
13
14// Test shortcuts
15
16var lab = exports.lab = Lab.script();
17var describe = lab.describe;
18var it = lab.it;
19var expect = Code.expect;
20
21
22describe('Joi', function () {
23
24 it('validates object', function (done) {
25
26 var schema = Joi.object({
27 a: Joi.number().min(0).max(3),
28 b: Joi.string().valid('a', 'b', 'c'),
29 c: Joi.string().email().optional()
30 }).without('a', 'none');
31
32 var obj = {
33 a: 1,
34 b: 'a',
35 c: 'joe@example.com'
36 };
37
38 schema.validate(obj, function (err, value) {
39
40 expect(err).to.not.exist();
41 done();
42 });
43 });
44
45 it('keeps schema immutable', function (done) {
46
47 var a = Joi.string();
48 var b = a.valid('b');
49
50 Helper.validate(a, [
51 ['a', true],
52 ['b', true],
53 [5, false]
54 ], function () {
55
56 Helper.validate(b, [
57 ['a', false],
58 ['b', true],
59 [5, false]
60 ], done);
61 });
62
63 });
64
65 it('validates null', function (done) {
66
67 Joi.string().validate(null, function (err, value) {
68
69 expect(err).to.exist();
70 expect(err.annotate()).to.equal('{\n \u001b[41m\"value\"\u001b[0m\u001b[31m [1]: -- missing --\u001b[0m\n}\n\u001b[31m\n[1] "value" must be a string\u001b[0m');
71 done();
72 });
73 });
74
75 it('validates null schema', function (done) {
76
77 Helper.validate(null, [
78 ['a', false],
79 [null, true]
80 ], done);
81 });
82
83 it('validates number literal', function (done) {
84
85 Helper.validate(5, [
86 [6, false],
87 [5, true]
88 ], done);
89 });
90
91 it('validates string literal', function (done) {
92
93 Helper.validate('5', [
94 ['6', false],
95 ['5', true]
96 ], done);
97 });
98
99 it('validates boolean literal', function (done) {
100
101 Helper.validate(true, [
102 [false, false],
103 [true, true]
104 ], done);
105 });
106
107 it('validates date literal', function (done) {
108
109 var now = Date.now();
110 Helper.validate(new Date(now), [
111 [new Date(now), true],
112 [now, true],
113 [now * 2, false]
114 ], done);
115 });
116
117 it('validates complex literal', function (done) {
118
119 var schema = ['key', 5, { a: true, b: [/^a/, 'boom'] }];
120 Helper.validate(schema, [
121 ['key', true],
122 [5, true],
123 ['other', false],
124 [6, false],
125 [{ c: 5 }, false],
126 [{}, true],
127 [{ b: 'abc' }, true],
128 [{ a: true, b: 'boom' }, true],
129 [{ a: 5, b: 'a' }, false]
130 ], done);
131 });
132
133 it('validates a compiled complex literal', function (done) {
134
135 var schema = Joi.compile(['key', 5, { a: true, b: [/^a/, 'boom'] }]);
136 Helper.validate(schema, [
137 ['key', true],
138 [5, true],
139 ['other', false],
140 [6, false],
141 [{ c: 5 }, false],
142 [{}, true],
143 [{ b: 'abc' }, true],
144 [{ a: true, b: 'boom' }, true],
145 [{ a: 5, b: 'a' }, false]
146 ], done);
147 });
148
149 it('validates regex directly', function (done) {
150
151 Joi.compile(/^5$/).validate('5', function (err, value) {
152
153 expect(err).to.not.exist();
154 Joi.compile(/.{2}/).validate('6', function (err2, value2) {
155
156 expect(err2).to.exist();
157 done();
158 });
159 });
160 });
161
162 it('validated with', function (done) {
163
164 var schema = Joi.object({
165 txt: Joi.string(),
166 upc: Joi.string()
167 }).with('txt', 'upc');
168
169 Joi.validate({ txt: 'a' }, schema, { abortEarly: false }, function (err, value) {
170
171 expect(err.message).to.equal('"txt" missing required peer "upc"');
172
173 Helper.validate(schema, [
174 [{ upc: 'test' }, true],
175 [{ txt: 'test' }, false],
176 [{ txt: 'test', upc: null }, false],
177 [{ txt: 'test', upc: '' }, false],
178 [{ txt: 'test', upc: undefined }, false],
179 [{ txt: 'test', upc: 'test' }, true]
180 ], done);
181 });
182 });
183
184 it('validated without', function (done) {
185
186 var schema = Joi.object({
187 txt: Joi.string(),
188 upc: Joi.string()
189 }).without('txt', 'upc');
190
191 Joi.validate({ txt: 'a', upc: 'b' }, schema, { abortEarly: false }, function (err, value) {
192
193 expect(err.message).to.equal('"txt" conflict with forbidden peer "upc"');
194
195 Helper.validate(schema, [
196 [{ upc: 'test' }, true],
197 [{ txt: 'test' }, true],
198 [{ txt: 'test', upc: null }, false],
199 [{ txt: 'test', upc: '' }, false],
200 [{ txt: 'test', upc: undefined }, true],
201 [{ txt: 'test', upc: 'test' }, false]
202 ], done);
203 });
204 });
205
206 it('validates xor', function (done) {
207
208 var schema = Joi.object({
209 txt: Joi.string(),
210 upc: Joi.string()
211 }).xor('txt', 'upc');
212
213 Joi.validate({}, schema, { abortEarly: false }, function (err, value) {
214
215 expect(err.message).to.equal('"value" must contain at least one of [txt, upc]');
216
217 Helper.validate(schema, [
218 [{ upc: null }, false],
219 [{ upc: 'test' }, true],
220 [{ txt: null }, false],
221 [{ txt: 'test' }, true],
222 [{ txt: 'test', upc: null }, false],
223 [{ txt: 'test', upc: '' }, false],
224 [{ txt: '', upc: 'test' }, false],
225 [{ txt: null, upc: 'test' }, false],
226 [{ txt: undefined, upc: 'test' }, true],
227 [{ txt: 'test', upc: undefined }, true],
228 [{ txt: 'test', upc: '' }, false],
229 [{ txt: 'test', upc: null }, false],
230 [{ txt: '', upc: undefined }, false],
231 [{ txt: '', upc: '' }, false],
232 [{ txt: 'test', upc: 'test' }, false]
233 ], done);
234 });
235 });
236
237 it('validates multiple peers xor', function (done) {
238
239 var schema = Joi.object({
240 txt: Joi.string(),
241 upc: Joi.string(),
242 code: Joi.string()
243 }).xor('txt', 'upc', 'code');
244
245 Helper.validate(schema, [
246 [{ upc: 'test' }, true],
247 [{ txt: 'test' }, true],
248 [{}, false]
249 ], done);
250 });
251
252 it('validates xor with number types', function (done) {
253
254 var schema = Joi.object({
255 code: Joi.number(),
256 upc: Joi.number()
257 }).xor('code', 'upc');
258
259 Helper.validate(schema, [
260 [{ upc: 123 }, true],
261 [{ code: 456 }, true],
262 [{ code: 456, upc: 123 }, false],
263 [{}, false]
264 ], done);
265 });
266
267 it('validates xor when empty value of peer allowed', function (done) {
268
269 var schema = Joi.object({
270 code: Joi.string(),
271 upc: Joi.string().allow('')
272 }).xor('code', 'upc');
273
274 Helper.validate(schema, [
275 [{ upc: '' }, true],
276 [{ upc: '123' }, true],
277 [{ code: '456' }, true],
278 [{ code: '456', upc: '' }, false],
279 [{}, false]
280 ], done);
281 });
282
283 it('validates or', function (done) {
284
285 var schema = Joi.object({
286 txt: Joi.string(),
287 upc: Joi.string().allow(null, ''),
288 code: Joi.number()
289 }).or('txt', 'upc', 'code');
290
291 Joi.validate({}, schema, { abortEarly: false }, function (err, value) {
292
293 expect(err.message).to.equal('"value" must contain at least one of [txt, upc, code]');
294
295 Helper.validate(schema, [
296 [{ upc: null }, true],
297 [{ upc: 'test' }, true],
298 [{ txt: null }, false],
299 [{ txt: 'test' }, true],
300 [{ code: null }, false],
301 [{ code: 123 }, true],
302 [{ txt: 'test', upc: null }, true],
303 [{ txt: 'test', upc: '' }, true],
304 [{ txt: '', upc: 'test' }, false],
305 [{ txt: null, upc: 'test' }, false],
306 [{ txt: undefined, upc: 'test' }, true],
307 [{ txt: 'test', upc: undefined }, true],
308 [{ txt: 'test', upc: '' }, true],
309 [{ txt: 'test', upc: null }, true],
310 [{ txt: '', upc: undefined }, false],
311 [{ txt: '', upc: undefined, code: 999 }, false],
312 [{ txt: '', upc: undefined, code: undefined }, false],
313 [{ txt: '', upc: '' }, false],
314 [{ txt: 'test', upc: 'test' }, true],
315 [{ txt: 'test', upc: 'test', code: 322 }, true]
316 ], done);
317 });
318 });
319
320 it('validates and', function (done) {
321
322 var schema = Joi.object({
323 txt: Joi.string(),
324 upc: Joi.string().allow(null, ''),
325 code: Joi.number()
326 }).and('txt', 'upc', 'code');
327
328 Joi.validate({ txt: 'x' }, schema, { abortEarly: false }, function (err, value) {
329
330 expect(err.message).to.equal('"value" contains [txt] without its required peers [upc, code]');
331
332 Helper.validate(schema, [
333 [{}, true],
334 [{ upc: null }, false],
335 [{ upc: 'test' }, false],
336 [{ txt: null }, false],
337 [{ txt: 'test' }, false],
338 [{ code: null }, false],
339 [{ code: 123 }, false],
340 [{ txt: 'test', upc: null }, false],
341 [{ txt: 'test', upc: '' }, false],
342 [{ txt: '', upc: 'test' }, false],
343 [{ txt: null, upc: 'test' }, false],
344 [{ txt: undefined, upc: 'test' }, false],
345 [{ txt: 'test', upc: undefined }, false],
346 [{ txt: 'test', upc: '' }, false],
347 [{ txt: 'test', upc: null }, false],
348 [{ txt: '', upc: undefined }, false],
349 [{ txt: '', upc: undefined, code: 999 }, false],
350 [{ txt: '', upc: undefined, code: undefined }, false],
351 [{ txt: '', upc: '' }, false],
352 [{ txt: 'test', upc: 'test' }, false],
353 [{ txt: 'test', upc: 'test', code: 322 }, true],
354 [{ txt: 'test', upc: null, code: 322 }, true]
355 ], done);
356 });
357 });
358
359 it('validates nand()', function (done) {
360
361 var schema = Joi.object({
362 txt: Joi.string(),
363 upc: Joi.string().allow(null, ''),
364 code: Joi.number()
365 }).nand('txt', 'upc', 'code');
366
367 Joi.validate({ txt: 'x', upc: 'y', code: 123 }, schema, { abortEarly: false }, function (err, value) {
368
369 expect(err.message).to.equal('"txt" must not exist simultaneously with [upc, code]');
370
371 Helper.validate(schema, [
372 [{}, true],
373 [{ upc: null }, true],
374 [{ upc: 'test' }, true],
375 [{ txt: 'test' }, true],
376 [{ code: 123 }, true],
377 [{ txt: 'test', upc: null }, true],
378 [{ txt: 'test', upc: '' }, true],
379 [{ txt: undefined, upc: 'test' }, true],
380 [{ txt: 'test', upc: undefined }, true],
381 [{ txt: 'test', upc: '' }, true],
382 [{ txt: 'test', upc: null }, true],
383 [{ txt: 'test', upc: undefined, code: 999 }, true],
384 [{ txt: 'test', upc: 'test' }, true],
385 [{ txt: 'test', upc: 'test', code: 322 }, false],
386 [{ txt: 'test', upc: null, code: 322 }, false]
387 ], done);
388 });
389 });
390
391 it('validates an array of valid types', function (done) {
392
393 var schema = Joi.object({
394 auth: [
395 Joi.object({
396 mode: Joi.string().valid('required', 'optional', 'try').allow(null)
397 }).allow(null),
398 Joi.string(),
399 Joi.boolean()
400 ]
401 });
402
403 schema.validate({ auth: { mode: 'none' } }, function (err, value) {
404
405 expect(err).to.exist();
406 expect(err.message).to.equal('child "auth" fails because [child "mode" fails because ["mode" must be one of [required, optional, try, null]], "auth" must be a string, "auth" must be a boolean]');
407
408 Helper.validate(schema, [
409 [{ auth: { mode: 'try' } }, true],
410 [{ something: undefined }, false],
411 [{ auth: { something: undefined } }, false],
412 [{ auth: null }, true],
413 [{ auth: undefined }, true],
414 [{}, true],
415 [{ auth: true }, true],
416 [{ auth: 123 }, false]
417 ], done);
418 });
419 });
420
421 it('validates alternatives', function (done) {
422
423 var schema = Joi.object({
424 auth: Joi.alternatives(
425 Joi.object({
426 mode: Joi.string().valid('required', 'optional', 'try').allow(null)
427 }).allow(null),
428 Joi.string(),
429 Joi.boolean()
430 )
431 });
432
433 schema.validate({ auth: { mode: 'none' } }, function (err, value) {
434
435 expect(err).to.exist();
436 expect(err.message).to.equal('child "auth" fails because [child "mode" fails because ["mode" must be one of [required, optional, try, null]], "auth" must be a string, "auth" must be a boolean]');
437
438 Helper.validate(schema, [
439 [{ auth: { mode: 'try' } }, true],
440 [{ something: undefined }, false],
441 [{ auth: { something: undefined } }, false],
442 [{ auth: null }, true],
443 [{ auth: undefined }, true],
444 [{}, true],
445 [{ auth: true }, true],
446 [{ auth: 123 }, false]
447 ], done);
448 });
449 });
450
451 it('validates required alternatives', function (done) {
452
453 var schema = {
454 a: Joi.alternatives(
455 Joi.string().required(),
456 Joi.boolean().required()
457 )
458 };
459
460 Helper.validate(schema, [
461 [{ a: null }, false],
462 [{ a: undefined }, true],
463 [{}, true],
464 [{ a: true }, true],
465 [{ a: 'true' }, true],
466 [{ a: 123 }, false],
467 [{ a: { c: 1 } }, false],
468 [{ b: undefined }, false]
469 ], done);
470 });
471
472 it('validates required [] alternatives', function (done) {
473
474 var schema = {
475 a: [
476 Joi.string().required(),
477 Joi.boolean().required()
478 ]
479 };
480
481 Helper.validate(schema, [
482 [{ a: null }, false],
483 [{ a: undefined }, true],
484 [{}, true],
485 [{ a: true }, true],
486 [{ a: 'true' }, true],
487 [{ a: 123 }, false],
488 [{ a: { c: 1 } }, false],
489 [{ b: undefined }, false]
490 ], done);
491 });
492
493 it('validates an array of string with valid', function (done) {
494
495 var schema = {
496 brand: Joi.array().items(Joi.string().valid('amex', 'visa'))
497 };
498
499 Helper.validate(schema, [
500 [{ brand: ['amex'] }, true],
501 [{ brand: ['visa', 'mc'] }, false]
502 ], done);
503 });
504
505 it('validates pre and post convert value', function (done) {
506
507 var schema = Joi.number().valid(5);
508
509 Helper.validate(schema, [
510 [5, true],
511 ['5', true]
512 ], done);
513 });
514
515 it('does not change object when validation fails', function (done) {
516
517 var schema = Joi.object({
518 a: Joi.number().valid(2)
519 });
520
521 var obj = {
522 a: '5'
523 };
524
525 schema.validate(obj, function (err, value) {
526
527 expect(err).to.exist();
528 expect(value.a).to.equal('5');
529 done();
530 });
531 });
532
533 it('does not set optional keys when missing', function (done) {
534
535 var schema = Joi.object({
536 a: Joi.number()
537 });
538
539 var obj = {};
540
541 schema.validate(obj, function (err, value) {
542
543 expect(err).to.not.exist();
544 expect(value.hasOwnProperty('a')).to.equal(false);
545 done();
546 });
547 });
548
549 it('invalidates pre and post convert value', function (done) {
550
551 var schema = Joi.number().invalid(5);
552
553 Helper.validate(schema, [
554 [5, false],
555 ['5', false]
556 ], done);
557 });
558
559 it('invalidates missing peers', function (done) {
560
561 var schema = Joi.object({
562 username: Joi.string(),
563 password: Joi.string()
564 }).with('username', 'password').without('password', 'access_token');
565
566 schema.validate({ username: 'bob' }, function (err, value) {
567
568 expect(err).to.exist();
569 done();
570 });
571 });
572
573 it('validates config where the root item is a joi type', function (done) {
574
575 Joi.boolean().allow(null).validate(true, function (err, value) {
576
577 expect(err).to.be.null();
578 Joi.object().validate({ auth: { mode: 'try' } }, function (err2, value2) {
579
580 expect(err2).to.be.null();
581
582 Joi.object().validate(true, function (err3, value3) {
583
584 expect(err3.message).to.contain('"value" must be an object');
585
586 Joi.string().validate(true, function (err4, value4) {
587
588 expect(err4.message).to.contain('"value" must be a string');
589
590 Joi.string().email().validate('test@test.com', function (err5, value5) {
591
592 expect(err5).to.be.null();
593 Joi.object({ param: Joi.string().required() }).validate({ param: 'item' }, function (err6, value6) {
594
595 expect(err6).to.be.null();
596 done();
597 });
598 });
599 });
600 });
601 });
602 });
603 });
604
605 it('converts string to number', function (done) {
606
607 var schema = Joi.object({
608 a: Joi.number()
609 });
610
611 var input = { a: '5' };
612 schema.validate(input, function (err, value) {
613
614 expect(err).to.be.null();
615 expect(value.a).to.equal(5);
616 expect(input.a).to.equal('5');
617 done();
618 });
619 });
620
621 it('allows unknown keys in objects if no schema was given', function (done) {
622
623 Joi.object().validate({ foo: 'bar' }, function (err, value) {
624
625 expect(err).to.not.exist();
626 done();
627 });
628 });
629
630 it('fails on unknown keys in objects if a schema was given', function (done) {
631
632 Joi.object({}).validate({ foo: 'bar' }, function (err, value) {
633
634 expect(err).to.exist();
635 expect(err.message).to.equal('"foo" is not allowed');
636
637 Joi.compile({}).validate({ foo: 'bar' }, function (err2, value2) {
638
639 expect(err2).to.exist();
640 expect(err2.message).to.equal('"foo" is not allowed');
641
642 Joi.compile({ other: Joi.number() }).validate({ foo: 'bar' }, function (err3, value3) {
643
644 expect(err3).to.exist();
645 expect(err3.message).to.equal('"foo" is not allowed');
646
647 done();
648 });
649 });
650 });
651 });
652
653 it('validates an unknown option', function (done) {
654
655 var config = {
656 auth: Joi.object({
657 mode: Joi.string().valid('required', 'optional', 'try').allow(null)
658 }).allow(null)
659 };
660
661 Joi.compile(config).validate({ auth: { unknown: true } }, function (err, value) {
662
663 expect(err).to.not.be.null();
664 expect(err.message).to.contain('"unknown" is not allowed');
665
666 Joi.compile(config).validate({ something: false }, function (err2, value2) {
667
668 expect(err2).to.not.be.null();
669 expect(err2.message).to.contain('"something" is not allowed');
670
671 done();
672 });
673 });
674 });
675
676 it('validates required key with multiple options', function (done) {
677
678 var config = {
679 module: Joi.alternatives([
680 Joi.object({
681 compile: Joi.func().required(),
682 execute: Joi.func()
683 }),
684 Joi.string()
685 ]).required()
686 };
687
688 Joi.compile(config).validate({}, function (err, value) {
689
690 expect(err).to.exist();
691 expect(err.message).to.contain('"module" is required');
692
693 Joi.compile(config).validate({ module: 'test' }, function (err2, value2) {
694
695 expect(err2).to.be.null();
696
697 Joi.compile(config).validate({ module: {} }, function (err3, value3) {
698
699 expect(err3).to.not.be.null();
700 expect(err3.message).to.contain('"compile" is required');
701 expect(err3.message).to.contain('"module" must be a string');
702
703 Joi.compile(config).validate({ module: { compile: function () { } } }, function (err4, value4) {
704
705 expect(err4).to.be.null();
706 done();
707 });
708 });
709 });
710 });
711 });
712
713 it('validates key with required alternatives', function (done) {
714
715 var config = {
716 module: Joi.alt().try(
717 Joi.object({
718 compile: Joi.func().required(),
719 execute: Joi.func()
720 }).required(),
721 Joi.string().required()
722 )
723 };
724
725 Joi.compile(config).validate({}, function (err, value) {
726
727 expect(err).to.not.exist();
728 done();
729 });
730 });
731
732 it('validates required key with alternatives', function (done) {
733
734 var config = {
735 module: Joi.alt().try(
736 Joi.object({
737 compile: Joi.func().required(),
738 execute: Joi.func()
739 }),
740 Joi.string()
741 ).required()
742 };
743
744 Joi.compile(config).validate({}, function (err, value) {
745
746 expect(err).to.exist();
747 expect(err.message).to.contain('"module" is required');
748 done();
749 });
750 });
751
752 it('does not require optional numbers', function (done) {
753
754 var config = {
755 position: Joi.number(),
756 suggestion: Joi.string()
757 };
758
759 Joi.compile(config).validate({ suggestion: 'something' }, function (err, value) {
760
761 expect(err).to.be.null();
762
763 Joi.compile(config).validate({ position: 1 }, function (err2, value2) {
764
765 expect(err2).to.be.null();
766 done();
767 });
768 });
769 });
770
771 it('does not require optional objects', function (done) {
772
773 var config = {
774 position: Joi.number(),
775 suggestion: Joi.object()
776 };
777
778 Joi.compile(config).validate({ suggestion: {} }, function (err, value) {
779
780 expect(err).to.be.null();
781
782 Joi.compile(config).validate({ position: 1 }, function (err2, value2) {
783
784 expect(err2).to.be.null();
785 done();
786 });
787 });
788 });
789
790 it('validates object successfully when config has an array of types', function (done) {
791
792 var schema = {
793 f: [Joi.number(), Joi.boolean()],
794 g: [Joi.string(), Joi.object()]
795 };
796
797 var obj = {
798 f: true,
799 g: 'test'
800 };
801
802 Joi.compile(schema).validate(obj, function (err, value) {
803
804 expect(err).to.not.exist();
805 done();
806 });
807 });
808
809 it('validates object successfully when config allows for optional key and key is missing', function (done) {
810
811 var schema = {
812 h: Joi.number(),
813 i: Joi.string(),
814 j: Joi.object()
815 };
816
817 var obj = {
818 h: 12,
819 i: 'test'
820 };
821
822 Joi.compile(schema).validate(obj, function (err, value) {
823
824 expect(err).to.not.exist();
825 done();
826 });
827 });
828
829 it('fails validation', function (done) {
830
831 var schema = {
832 a: Joi.number().min(0).max(3),
833 b: Joi.string().valid('a', 'b', 'c'),
834 c: Joi.string().email().optional()
835 };
836
837 var obj = {
838 a: 10,
839 b: 'a',
840 c: 'joe@example.com'
841 };
842
843 Joi.compile(schema).validate(obj, function (err, value) {
844
845 expect(err).to.exist();
846 done();
847 });
848 });
849
850 it('fails validation when the wrong types are supplied', function (done) {
851
852 var schema = {
853 a: Joi.number().min(0).max(3),
854 b: Joi.string().valid('a', 'b', 'c'),
855 c: Joi.string().email().optional()
856 };
857
858 var obj = {
859 a: 'a',
860 b: 'a',
861 c: 'joe@example.com'
862 };
863
864 Joi.compile(schema).validate(obj, function (err, value) {
865
866 expect(err).to.exist();
867 done();
868 });
869 });
870
871 it('fails validation when missing a required parameter', function (done) {
872
873 var obj = {
874 c: 10
875 };
876
877 Joi.compile({ a: Joi.string().required() }).validate(obj, function (err, value) {
878
879 expect(err).to.exist();
880 done();
881 });
882 });
883
884 it('fails validation when missing a required parameter within an object config', function (done) {
885
886 var obj = {
887 a: {}
888 };
889
890 Joi.compile({ a: Joi.object({ b: Joi.string().required() }) }).validate(obj, function (err, value) {
891
892 expect(err).to.exist();
893 done();
894 });
895 });
896
897 it('fails validation when parameter is required to be an object but is given as string', function (done) {
898
899 var obj = {
900 a: 'a string'
901 };
902
903 Joi.compile({ a: Joi.object({ b: Joi.string().required() }) }).validate(obj, function (err, value) {
904
905 expect(err).to.exist();
906 done();
907 });
908 });
909
910 it('validates when parameter is required to be an object and is given correctly as a json string', function (done) {
911
912 var schema = {
913 a: Joi.object({
914 b: Joi.string().required()
915 })
916 };
917
918 var input = {
919 a: '{"b":"string"}'
920 };
921
922 Joi.validate(input, schema, function (err, value) {
923
924 expect(err).to.not.exist();
925 expect(input.a).to.equal('{"b":"string"}');
926 expect(value.a.b).to.equal('string');
927 done();
928 });
929 });
930
931 it('fails validation when parameter is required to be an object but is given as a json string that is incorrect (number instead of string)', function (done) {
932
933 var obj = {
934 a: '{"b":2}'
935 };
936
937 Joi.object({ a: Joi.object({ b: Joi.string().required() }) }).validate(obj, function (err, value) {
938
939 expect(err).to.exist();
940 done();
941 });
942 });
943
944 it('fails validation when parameter is required to be an Array but is given as string', function (done) {
945
946 var obj = {
947 a: 'an array'
948 };
949
950 Joi.object({ a: Joi.array() }).validate(obj, function (err, value) {
951
952 expect(err).to.exist();
953 done();
954 });
955 });
956
957 it('validates when parameter is required to be an Array and is given correctly as a json string', function (done) {
958
959 var obj = {
960 a: '[1,2]'
961 };
962
963 Joi.object({ a: Joi.array() }).validate(obj, function (err, value) {
964
965 expect(err).to.be.null();
966 done();
967 });
968 });
969
970 it('fails validation when parameter is required to be an Array but is given as a json that is incorrect (object instead of array)', function (done) {
971
972 var obj = {
973 a: '{"b":2}'
974 };
975
976 Joi.object({ a: Joi.object({ b: Joi.string().required() }) }).validate(obj, function (err, value) {
977
978 expect(err).to.exist();
979 done();
980 });
981 });
982
983 it('fails validation when config is an array and fails', function (done) {
984
985 var schema = {
986 d: [Joi.string(), Joi.boolean()],
987 e: [Joi.number(), Joi.object()]
988 };
989
990 var obj = {
991 d: 10,
992 e: 'a'
993 };
994
995 Joi.compile(schema).validate(obj, function (err, value) {
996
997 expect(err).to.exist();
998 done();
999 });
1000 });
1001
1002 it('fails validation when config is an array and fails with extra keys', function (done) {
1003
1004 var schema = {
1005 d: [Joi.string(), Joi.boolean()],
1006 e: [Joi.number(), Joi.object()]
1007 };
1008
1009 var obj = {
1010 a: 10,
1011 b: 'a'
1012 };
1013
1014 Joi.compile(schema).validate(obj, function (err, value) {
1015
1016 expect(err).to.exist();
1017 done();
1018 });
1019 });
1020
1021 it('fails validation with extra keys', function (done) {
1022
1023 var schema = {
1024 a: Joi.number()
1025 };
1026
1027 var obj = {
1028 a: 1,
1029 b: 'a'
1030 };
1031
1032 Joi.compile(schema).validate(obj, function (err, value) {
1033
1034 expect(err).to.exist();
1035 done();
1036 });
1037 });
1038
1039 it('validates missing optional key with string condition', function (done) {
1040
1041 var schema = {
1042 key: Joi.string().alphanum(false).min(8)
1043 };
1044
1045 Joi.compile(schema).validate({}, function (err, value) {
1046
1047 expect(err).to.not.exist();
1048 done();
1049 });
1050 });
1051
1052 it('validates with extra keys and remove them when stripUnknown is set', function (done) {
1053
1054 var schema = {
1055 a: Joi.number().min(0).max(3),
1056 b: Joi.string().valid('a', 'b', 'c'),
1057 c: Joi.string().email().optional()
1058 };
1059
1060 var obj = {
1061 a: 1,
1062 b: 'a',
1063 d: 'c'
1064 };
1065
1066 Joi.validate(obj, schema, { stripUnknown: true, allowUnknown: true }, function (err, value) {
1067
1068 expect(err).to.be.null();
1069 expect(value).to.deep.equal({ a: 1, b: 'a' });
1070 done();
1071 });
1072 });
1073
1074 it('validates dependencies when stripUnknown is set', function (done) {
1075
1076 var schema = Joi.object({
1077 a: Joi.number(),
1078 b: Joi.string()
1079 }).and('a', 'b');
1080
1081 var obj = {
1082 a: 1,
1083 foo: 'bar'
1084 };
1085
1086 Joi.validate(obj, schema, { stripUnknown: true }, function (err, value) {
1087
1088 expect(err).to.exist();
1089 expect(err.message).to.equal('"value" contains [a] without its required peers [b]');
1090 done();
1091 });
1092 });
1093
1094 it('fails to validate with incorrect property when asked to strip unkown keys without aborting early', function (done) {
1095
1096 var schema = {
1097 a: Joi.number().min(0).max(3),
1098 b: Joi.string().valid('a', 'b', 'c'),
1099 c: Joi.string().email().optional()
1100 };
1101
1102 var obj = {
1103 a: 1,
1104 b: 'f',
1105 d: 'c'
1106 };
1107
1108 Joi.validate(obj, schema, { stripUnknown: true, abortEarly: false }, function (err, value) {
1109
1110 expect(err).to.exist();
1111 done();
1112 });
1113 });
1114
1115 it('should pass validation with extra keys when allowUnknown is set', function (done) {
1116
1117 var schema = {
1118 a: Joi.number().min(0).max(3),
1119 b: Joi.string().valid('a', 'b', 'c'),
1120 c: Joi.string().email().optional()
1121 };
1122
1123 var obj = {
1124 a: 1,
1125 b: 'a',
1126 d: 'c'
1127 };
1128
1129 Joi.validate(obj, schema, { allowUnknown: true }, function (err, value) {
1130
1131 expect(err).to.be.null();
1132 expect(value).to.deep.equal({ a: 1, b: 'a', d: 'c' });
1133 done();
1134 });
1135 });
1136
1137 it('should pass validation with extra keys set', function (done) {
1138
1139 var localConfig = Joi.object({
1140 a: Joi.number().min(0).max(3),
1141 b: Joi.string().valid('a', 'b', 'c')
1142 }).options({ allowUnknown: true });
1143
1144 var obj = {
1145 a: 1,
1146 b: 'a',
1147 d: 'c'
1148 };
1149
1150 localConfig.validate(obj, function (err, value) {
1151
1152 expect(err).to.be.null();
1153 expect(value).to.deep.equal({ a: 1, b: 'a', d: 'c' });
1154
1155 localConfig.validate(value, function (err2, value2) {
1156
1157 expect(err2).to.be.null();
1158 expect(value2).to.deep.equal({ a: 1, b: 'a', d: 'c' });
1159 done();
1160 });
1161 });
1162 });
1163
1164
1165 it('should pass validation with extra keys and remove them when skipExtraKeys is set locally', function (done) {
1166
1167 var localConfig = Joi.object({
1168 a: Joi.number().min(0).max(3),
1169 b: Joi.string().valid('a', 'b', 'c')
1170 }).options({ stripUnknown: true, allowUnknown: true });
1171
1172 var obj = {
1173 a: 1,
1174 b: 'a',
1175 d: 'c'
1176 };
1177
1178 localConfig.validate(obj, function (err, value) {
1179
1180 expect(err).to.be.null();
1181 expect(value).to.deep.equal({ a: 1, b: 'a' });
1182
1183 localConfig.validate(value, function (err2, value2) {
1184
1185 expect(err2).to.be.null();
1186 expect(value2).to.deep.equal({ a: 1, b: 'a' });
1187 done();
1188 });
1189 });
1190 });
1191
1192 it('should work when the skipFunctions setting is enabled', function (done) {
1193
1194 var schema = Joi.object({ username: Joi.string() }).options({ skipFunctions: true });
1195 var input = { username: 'test', func: function () { } };
1196 Joi.validate(input, schema, function (err, value) {
1197
1198 expect(err).to.not.exist();
1199 done();
1200 });
1201 });
1202
1203 it('should work when the skipFunctions setting is disabled', function (done) {
1204
1205 var schema = { username: Joi.string() };
1206 var input = { username: 'test', func: function () { } };
1207
1208 Joi.validate(input, schema, { skipFunctions: false }, function (err, value) {
1209
1210 expect(err).to.exist();
1211 expect(err.message).to.contain('"func" is not allowed');
1212 done();
1213 });
1214 });
1215
1216 it('should not convert values when convert is false', function (done) {
1217
1218 var schema = {
1219 arr: Joi.array().items(Joi.string())
1220 };
1221
1222 var input = { arr: 'foo' };
1223 Joi.validate(input, schema, { convert: false }, function (err, value) {
1224
1225 expect(err).to.exist();
1226 done();
1227 });
1228 });
1229
1230 it('full errors when abortEarly is false', function (done) {
1231
1232 var schema = {
1233 a: Joi.string(),
1234 b: Joi.string()
1235 };
1236
1237 var input = { a: 1, b: 2 };
1238
1239 Joi.validate(input, schema, function (errOne, valueOne) {
1240
1241 Joi.validate(input, schema, { abortEarly: false }, function (errFull, valueFull) {
1242
1243 expect(errOne).to.exist();
1244 expect(errFull).to.exist();
1245 expect(errFull.details.length).to.be.greaterThan(errOne.details.length);
1246 done();
1247 });
1248 });
1249 });
1250
1251 it('errors multiple times when abortEarly is false in a complex object', function (done) {
1252
1253 var schema = Joi.object({
1254 test: Joi.array().items(Joi.object().keys({
1255 foo: Joi.string().required().max(3),
1256 bar: Joi.string().max(5)
1257 })),
1258 test2: Joi.object({
1259 test3: Joi.array().items(Joi.object().keys({
1260 foo: Joi.string().required().max(3),
1261 bar: Joi.string().max(5),
1262 baz: Joi.object({
1263 test4: Joi.array().items(Joi.object().keys({
1264 foo: Joi.string().required().max(3),
1265 bar: Joi.string().max(5)
1266 }))
1267 })
1268 }))
1269 })
1270 });
1271
1272 var input = {
1273 test: [{
1274 foo: 'test1',
1275 bar: 'testfailed'
1276 }],
1277 test2: {
1278 test3: [{
1279 foo: '123'
1280 }, {
1281 foo: 'test1',
1282 bar: 'testfailed'
1283 }, {
1284 foo: '123',
1285 baz: {
1286 test4: [{
1287 foo: 'test1',
1288 baz: '123'
1289 }]
1290 }
1291 }]
1292 }
1293 };
1294
1295 Joi.validate(input, schema, { abortEarly: false }, function (err, value) {
1296
1297 expect(err).to.exist();
1298 expect(err.details).to.have.length(6);
1299 expect(err.details).to.deep.equal([{
1300 message: '"foo" length must be less than or equal to 3 characters long',
1301 path: 'test.0.foo',
1302 type: 'string.max',
1303 context: { limit: 3, value: 'test1', key: 'foo', encoding: undefined }
1304 }, {
1305 message: '"bar" length must be less than or equal to 5 characters long',
1306 path: 'test.0.bar',
1307 type: 'string.max',
1308 context: { limit: 5, value: 'testfailed', key: 'bar', encoding: undefined }
1309 }, {
1310 message: '"foo" length must be less than or equal to 3 characters long',
1311 path: 'test2.test3.1.foo',
1312 type: 'string.max',
1313 context: { limit: 3, value: 'test1', key: 'foo', encoding: undefined }
1314 }, {
1315 message: '"bar" length must be less than or equal to 5 characters long',
1316 path: 'test2.test3.1.bar',
1317 type: 'string.max',
1318 context: { limit: 5, value: 'testfailed', key: 'bar', encoding: undefined }
1319 }, {
1320 message: '"foo" length must be less than or equal to 3 characters long',
1321 path: 'test2.test3.2.baz.test4.0.foo',
1322 type: 'string.max',
1323 context: { limit: 3, value: 'test1', key: 'foo', encoding: undefined }
1324 }, {
1325 message: '"baz" is not allowed',
1326 path: 'test2.test3.2.baz.test4.0.baz',
1327 type: 'object.allowUnknown',
1328 context: { key: 'baz' }
1329 }]);
1330 done();
1331 });
1332 });
1333
1334 it('validates using the root any object', function (done) {
1335
1336 var any = Joi;
1337 any.validate('abc', function (err, value) {
1338
1339 expect(err).to.not.exist();
1340 done();
1341 });
1342 });
1343
1344 it('validates using the root any object (no callback)', function (done) {
1345
1346 var any = Joi;
1347 var result = any.validate('abc');
1348 expect(result.error).to.not.exist();
1349 expect(result.value).to.equal('abc');
1350 done();
1351 });
1352
1353 it('accepts no options', function (done) {
1354
1355 Joi.validate('test', Joi.string(), function (err, value) {
1356
1357 expect(err).to.not.exist();
1358 done();
1359 });
1360 });
1361
1362 it('accepts no options (no callback)', function (done) {
1363
1364 var result = Joi.validate('test', Joi.string());
1365 expect(result.error).to.not.exist();
1366 expect(result.value).to.equal('test');
1367 done();
1368 });
1369
1370 it('accepts options', function (done) {
1371
1372 Joi.validate('5', Joi.number(), { convert: false }, function (err, value) {
1373
1374 expect(err).to.exist();
1375 done();
1376 });
1377 });
1378
1379 it('accepts options (no callback)', function (done) {
1380
1381 var result = Joi.validate('5', Joi.number(), { convert: false });
1382 expect(result.error).to.exist();
1383 done();
1384 });
1385
1386 it('accepts null options', function (done) {
1387
1388 Joi.validate('test', Joi.string(), null, function (err, value) {
1389
1390 expect(err).to.not.exist();
1391 done();
1392 });
1393 });
1394
1395 it('accepts undefined options', function (done) {
1396
1397 Joi.validate('test', Joi.string(), undefined, function (err, value) {
1398
1399 expect(err).to.not.exist();
1400 done();
1401 });
1402 });
1403
1404 describe('#describe', function () {
1405
1406 var defaultFn = function () {
1407
1408 return 'test';
1409 };
1410 defaultFn.description = 'testing';
1411
1412 var defaultDescribedFn = function () {
1413
1414 return 'test';
1415 };
1416
1417 var defaultRef = Joi.ref('xor');
1418
1419 var schema = Joi.object({
1420 sub: {
1421 email: Joi.string().email(),
1422 date: Joi.date(),
1423 child: Joi.object({
1424 alphanum: Joi.string().alphanum()
1425 })
1426 },
1427 min: [Joi.number(), Joi.string().min(3)],
1428 max: Joi.string().max(3),
1429 required: Joi.string().required(),
1430 xor: Joi.string(),
1431 renamed: Joi.string().valid('456'),
1432 notEmpty: Joi.string().required().description('a').notes('b').tags('c'),
1433 empty: Joi.string().empty('').strip(),
1434 defaultRef: Joi.string().default(defaultRef, 'not here'),
1435 defaultFn: Joi.string().default(defaultFn, 'not here'),
1436 defaultDescribedFn: Joi.string().default(defaultDescribedFn, 'described test')
1437 }).rename('renamed', 'required').without('required', 'xor').without('xor', 'required');
1438
1439 var result = {
1440 type: 'object',
1441 children: {
1442 sub: {
1443 type: 'object',
1444 children: {
1445 email: {
1446 type: 'string',
1447 invalids: [''],
1448 rules: [{ name: 'email' }]
1449 },
1450 date: {
1451 type: 'date'
1452 },
1453 child: {
1454 type: 'object',
1455 children: {
1456 alphanum: {
1457 type: 'string',
1458 invalids: [''],
1459 rules: [{ name: 'alphanum' }]
1460 }
1461 }
1462 }
1463 }
1464 },
1465 min: {
1466 type: 'alternatives',
1467 alternatives: [
1468 {
1469 type: 'number',
1470 invalids: [Infinity, -Infinity]
1471 },
1472 {
1473 type: 'string',
1474 invalids: [''],
1475 rules: [{ name: 'min', arg: 3 }]
1476 }
1477 ]
1478 },
1479 max: {
1480 type: 'string',
1481 invalids: [''],
1482 rules: [{ name: 'max', arg: 3 }]
1483 },
1484 required: {
1485 type: 'string',
1486 flags: {
1487 presence: 'required'
1488 },
1489 invalids: ['']
1490 },
1491 xor: {
1492 type: 'string',
1493 invalids: ['']
1494 },
1495 renamed: {
1496 type: 'string',
1497 flags: {
1498 allowOnly: true
1499 },
1500 valids: ['456'],
1501 invalids: ['']
1502 },
1503 notEmpty: {
1504 type: 'string',
1505 flags: {
1506 presence: 'required'
1507 },
1508 description: 'a',
1509 notes: ['b'],
1510 tags: ['c'],
1511 invalids: ['']
1512 },
1513 empty: {
1514 type: 'string',
1515 flags: {
1516 empty: {
1517 type: 'string',
1518 flags: {
1519 allowOnly: true
1520 },
1521 valids: ['']
1522 },
1523 strip: true
1524 },
1525 invalids: ['']
1526 },
1527 defaultRef: {
1528 type: 'string',
1529 flags: {
1530 default: defaultRef
1531 },
1532 invalids: ['']
1533 },
1534 defaultFn: {
1535 type: 'string',
1536 flags: {
1537 default: defaultFn
1538 },
1539 invalids: ['']
1540 },
1541 defaultDescribedFn: {
1542 type: 'string',
1543 flags: {
1544 default: defaultDescribedFn
1545 },
1546 invalids: ['']
1547 }
1548 },
1549 dependencies: [
1550 {
1551 type: 'without',
1552 key: 'required',
1553 peers: ['xor']
1554 },
1555 {
1556 type: 'without',
1557 key: 'xor',
1558 peers: ['required']
1559 }
1560 ]
1561 };
1562
1563 it('describes schema (direct)', function (done) {
1564
1565 var description = schema.describe();
1566 expect(description).to.deep.equal(result);
1567 expect(description.children.defaultRef.flags.default.description).to.not.exist();
1568 expect(description.children.defaultFn.flags.default.description).to.equal('testing');
1569 expect(description.children.defaultDescribedFn.flags.default.description).to.equal('described test');
1570 done();
1571 });
1572
1573 it('describes schema (root)', function (done) {
1574
1575 var description = Joi.describe(schema);
1576 expect(description).to.deep.equal(result);
1577 done();
1578 });
1579
1580 it('describes schema (any)', function (done) {
1581
1582 var any = Joi;
1583 var description = any.describe();
1584 expect(description).to.deep.equal({
1585 type: 'any'
1586 });
1587 done();
1588 });
1589
1590 it('describes schema without invalids', function (done) {
1591
1592 var description = Joi.allow(null).describe();
1593 expect(description.invalids).to.not.exist();
1594 done();
1595 });
1596 });
1597
1598 describe('#assert', function () {
1599
1600 it('does not have a return value', function (done) {
1601
1602 var result;
1603 expect(function () {
1604
1605 result = Joi.assert('4', Joi.number());
1606 }).to.not.throw();
1607 expect(result).to.not.exist();
1608 done();
1609 });
1610 });
1611
1612 describe('#attempt', function () {
1613
1614 it('throws on invalid value', function (done) {
1615
1616 expect(function () {
1617
1618 Joi.attempt('x', Joi.number());
1619 }).to.throw('"x"\n\u001b[31m\n[1] "value" must be a number\u001b[0m');
1620 done();
1621 });
1622
1623 it('does not throw on valid value', function (done) {
1624
1625 expect(function () {
1626
1627 Joi.attempt('4', Joi.number());
1628 }).to.not.throw();
1629 done();
1630 });
1631
1632 it('returns validated structure', function (done) {
1633
1634 var valid;
1635 expect(function () {
1636
1637 valid = Joi.attempt('4', Joi.number());
1638 }).to.not.throw();
1639 expect(valid).to.equal(4);
1640 done();
1641 });
1642
1643 it('throws on invalid value with message', function (done) {
1644
1645 expect(function () {
1646
1647 Joi.attempt('x', Joi.number(), 'the reason is');
1648 }).to.throw('the reason is "x"\n\u001b[31m\n[1] "value" must be a number\u001b[0m');
1649 done();
1650 });
1651
1652 it('throws on invalid value with message as error', function (done) {
1653
1654 expect(function () {
1655
1656 Joi.attempt('x', Joi.number(), new Error('invalid value'));
1657 }).to.throw('invalid value');
1658 done();
1659 });
1660 });
1661
1662 describe('#compile', function () {
1663
1664 it('throws an error on invalid value', function (done) {
1665
1666 expect(function () {
1667
1668 Joi.compile(undefined);
1669 }).to.throw(Error, 'Invalid schema content: ');
1670 done();
1671 });
1672
1673 it('shows path to errors in object', function (done) {
1674
1675 var schema = {
1676 a: {
1677 b: {
1678 c: {
1679 d: undefined
1680 }
1681 }
1682 }
1683 };
1684
1685 expect(function () {
1686
1687 Joi.compile(schema);
1688 }).to.throw(Error, 'Invalid schema content: (a.b.c.d)');
1689 done();
1690 });
1691 });
1692});