joi 8.4.2 → 9.0.0-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/test/string.js ADDED
@@ -0,0 +1,3206 @@
1
+ 'use strict';
2
+
3
+ // Load modules
4
+
5
+ const Lab = require('lab');
6
+ const Code = require('code');
7
+ const Joi = require('../lib');
8
+ const Helper = require('./helper');
9
+
10
+
11
+ // Declare internals
12
+
13
+ const internals = {};
14
+
15
+
16
+ // Test shortcuts
17
+
18
+ const lab = exports.lab = Lab.script();
19
+ const describe = lab.describe;
20
+ const it = lab.it;
21
+ const expect = Code.expect;
22
+
23
+
24
+ describe('string', () => {
25
+
26
+ it('fails on boolean', (done) => {
27
+
28
+ const schema = Joi.string();
29
+ Helper.validate(schema, [
30
+ [true, false],
31
+ [false, false]
32
+ ], done);
33
+ });
34
+
35
+ it('fails on integer', (done) => {
36
+
37
+ const schema = Joi.string();
38
+ Helper.validate(schema, [
39
+ [123, false],
40
+ [0, false],
41
+ ['123', true],
42
+ ['0', true]
43
+ ], done);
44
+ });
45
+
46
+ describe('valid()', () => {
47
+
48
+ it('validates case sensitive values', (done) => {
49
+
50
+ Helper.validate(Joi.string().valid('a', 'b'), [
51
+ ['a', true],
52
+ ['b', true],
53
+ ['A', false],
54
+ ['B', false]
55
+ ], done);
56
+ });
57
+
58
+ it('validates case insensitive values', (done) => {
59
+
60
+ Helper.validate(Joi.string().valid('a', 'b').insensitive(), [
61
+ ['a', true],
62
+ ['b', true],
63
+ ['A', true],
64
+ ['B', true],
65
+ [4, false]
66
+ ], done);
67
+ });
68
+
69
+ it('validates case insensitive values with non-strings', (done) => {
70
+
71
+ Helper.validate(Joi.string().valid('a', 'b', 5).insensitive(), [
72
+ ['a', true],
73
+ ['b', true],
74
+ ['A', true],
75
+ ['B', true],
76
+ [4, false],
77
+ [5, true]
78
+ ], done);
79
+ });
80
+ });
81
+
82
+ describe('invalid()', () => {
83
+
84
+ it('invalidates case sensitive values', (done) => {
85
+
86
+ Helper.validate(Joi.string().invalid('a', 'b'), [
87
+ ['a', false],
88
+ ['b', false],
89
+ ['A', true],
90
+ ['B', true]
91
+ ], done);
92
+ });
93
+
94
+ it('invalidates case insensitive values', (done) => {
95
+
96
+ Helper.validate(Joi.string().invalid('a', 'b').insensitive(), [
97
+ ['a', false],
98
+ ['b', false],
99
+ ['A', false],
100
+ ['B', false]
101
+ ], done);
102
+ });
103
+ });
104
+
105
+ describe('min()', () => {
106
+
107
+ it('throws when limit is not a number', (done) => {
108
+
109
+ expect(() => {
110
+
111
+ Joi.string().min('a');
112
+ }).to.throw('limit must be a positive integer or reference');
113
+ done();
114
+ });
115
+
116
+ it('throws when limit is not an integer', (done) => {
117
+
118
+ expect(() => {
119
+
120
+ Joi.string().min(1.2);
121
+ }).to.throw('limit must be a positive integer or reference');
122
+ done();
123
+ });
124
+
125
+ it('throws when limit is not a positive integer', (done) => {
126
+
127
+ expect(() => {
128
+
129
+ Joi.string().min(-1);
130
+ }).to.throw('limit must be a positive integer or reference');
131
+ done();
132
+ });
133
+
134
+ it('enforces a limit using byte count', (done) => {
135
+
136
+ const schema = Joi.string().min(2, 'utf8');
137
+ Helper.validate(schema, [
138
+ ['\u00bd', true],
139
+ ['a', false]
140
+ ], done);
141
+ });
142
+
143
+ it('accepts references as min length', (done) => {
144
+
145
+ const schema = Joi.object({ a: Joi.number(), b: Joi.string().min(Joi.ref('a'), 'utf8') });
146
+ Helper.validate(schema, [
147
+ [{ a: 2, b: '\u00bd' }, true],
148
+ [{ a: 2, b: 'a' }, false],
149
+ [{ a: 2, b: 'a' }, false, null, 'child "b" fails because ["b" length must be at least 2 characters long]']
150
+ ], done);
151
+ });
152
+
153
+ it('accepts context references as min length', (done) => {
154
+
155
+ const schema = Joi.object({ b: Joi.string().min(Joi.ref('$a'), 'utf8') });
156
+ Helper.validate(schema, [
157
+ [{ b: '\u00bd' }, true, { context: { a: 2 } }],
158
+ [{ b: 'a' }, false, { context: { a: 2 } }],
159
+ [{ b: 'a' }, false, { context: { a: 2 } }, 'child "b" fails because ["b" length must be at least 2 characters long]']
160
+ ], done);
161
+ });
162
+
163
+ it('errors if reference is not a number', (done) => {
164
+
165
+ const schema = Joi.object({ a: Joi.any(), b: Joi.string().min(Joi.ref('a'), 'utf8') });
166
+
167
+ Helper.validate(schema, [
168
+ [{ a: 'Hi there', b: '\u00bd' }, false, null, 'child "b" fails because ["b" references "a" which is not a number]']
169
+ ], done);
170
+ });
171
+
172
+ it('errors if context reference is not a number', (done) => {
173
+
174
+ const schema = Joi.object({ b: Joi.string().min(Joi.ref('$a'), 'utf8') });
175
+
176
+ Helper.validate(schema, [
177
+ [{ b: '\u00bd' }, false, { context: { a: 'Hi there' } }, 'child "b" fails because ["b" references "a" which is not a number]']
178
+ ], done);
179
+ });
180
+ });
181
+
182
+ describe('max()', () => {
183
+
184
+ it('throws when limit is not a number', (done) => {
185
+
186
+ expect(() => {
187
+
188
+ Joi.string().max('a');
189
+ }).to.throw('limit must be a positive integer or reference');
190
+ done();
191
+ });
192
+
193
+ it('throws when limit is not an integer', (done) => {
194
+
195
+ expect(() => {
196
+
197
+ Joi.string().max(1.2);
198
+ }).to.throw('limit must be a positive integer or reference');
199
+ done();
200
+ });
201
+
202
+ it('throws when limit is not a positive integer', (done) => {
203
+
204
+ expect(() => {
205
+
206
+ Joi.string().max(-1);
207
+ }).to.throw('limit must be a positive integer or reference');
208
+ done();
209
+ });
210
+
211
+ it('enforces a limit using byte count', (done) => {
212
+
213
+ const schema = Joi.string().max(1, 'utf8');
214
+ Helper.validate(schema, [
215
+ ['\u00bd', false],
216
+ ['a', true]
217
+ ], done);
218
+ });
219
+
220
+ it('accepts references as min length', (done) => {
221
+
222
+ const schema = Joi.object({ a: Joi.number(), b: Joi.string().max(Joi.ref('a'), 'utf8') });
223
+ Helper.validate(schema, [
224
+ [{ a: 2, b: '\u00bd' }, true],
225
+ [{ a: 2, b: 'three' }, false],
226
+ [{ a: 2, b: 'three' }, false, null, 'child "b" fails because ["b" length must be less than or equal to 2 characters long]']
227
+ ], done);
228
+ });
229
+
230
+ it('accepts context references as min length', (done) => {
231
+
232
+ const schema = Joi.object({ b: Joi.string().max(Joi.ref('$a'), 'utf8') });
233
+ Helper.validate(schema, [
234
+ [{ b: '\u00bd' }, true, { context: { a: 2 } }],
235
+ [{ b: 'three' }, false, { context: { a: 2 } }],
236
+ [{ b: 'three' }, false, { context: { a: 2 } }, 'child "b" fails because ["b" length must be less than or equal to 2 characters long]']
237
+ ], done);
238
+ });
239
+
240
+ it('errors if reference is not a number', (done) => {
241
+
242
+ const schema = Joi.object({ a: Joi.any(), b: Joi.string().max(Joi.ref('a'), 'utf8') });
243
+
244
+ Helper.validate(schema, [
245
+ [{ a: 'Hi there', b: '\u00bd' }, false, null, 'child "b" fails because ["b" references "a" which is not a number]']
246
+ ], done);
247
+ });
248
+
249
+ it('errors if context reference is not a number', (done) => {
250
+
251
+ const schema = Joi.object({ b: Joi.string().max(Joi.ref('$a'), 'utf8') });
252
+
253
+ Helper.validate(schema, [
254
+ [{ b: '\u00bd' }, false, { context: { a: 'Hi there' } }, 'child "b" fails because ["b" references "a" which is not a number]']
255
+ ], done);
256
+ });
257
+ });
258
+
259
+ describe('creditCard()', () => {
260
+
261
+ it('should validate credit card', (done) => {
262
+
263
+ const t = Joi.string().creditCard();
264
+ t.validate('4111111111111112', (err, value) => {
265
+
266
+ expect(err.message).to.equal('"value" must be a credit card');
267
+
268
+ Helper.validate(t, [
269
+ ['378734493671000', true], // american express
270
+ ['371449635398431', true], // american express
271
+ ['378282246310005', true], // american express
272
+ ['341111111111111', true], // american express
273
+ ['5610591081018250', true], // australian bank
274
+ ['5019717010103742', true], // dankort pbs
275
+ ['38520000023237', true], // diners club
276
+ ['30569309025904', true], // diners club
277
+ ['6011000990139424', true], // discover
278
+ ['6011111111111117', true], // discover
279
+ ['6011601160116611', true], // discover
280
+ ['3566002020360505', true], // jbc
281
+ ['3530111333300000', true], // jbc
282
+ ['5105105105105100', true], // mastercard
283
+ ['5555555555554444', true], // mastercard
284
+ ['5431111111111111', true], // mastercard
285
+ ['6331101999990016', true], // switch/solo paymentech
286
+ ['4222222222222', true], // visa
287
+ ['4012888888881881', true], // visa
288
+ ['4111111111111111', true], // visa
289
+ ['4111111111111112', false],
290
+ [null, false]
291
+ ], done);
292
+ });
293
+ });
294
+ });
295
+
296
+ describe('length()', () => {
297
+
298
+ it('throws when limit is not a number', (done) => {
299
+
300
+ expect(() => {
301
+
302
+ Joi.string().length('a');
303
+ }).to.throw('limit must be a positive integer or reference');
304
+ done();
305
+ });
306
+
307
+ it('throws when limit is not an integer', (done) => {
308
+
309
+ expect(() => {
310
+
311
+ Joi.string().length(1.2);
312
+ }).to.throw('limit must be a positive integer or reference');
313
+ done();
314
+ });
315
+
316
+ it('throws when limit is not a positive integer', (done) => {
317
+
318
+ expect(() => {
319
+
320
+ Joi.string().length(-42);
321
+ }).to.throw('limit must be a positive integer or reference');
322
+ done();
323
+ });
324
+
325
+ it('enforces a limit using byte count', (done) => {
326
+
327
+ const schema = Joi.string().length(2, 'utf8');
328
+ Helper.validate(schema, [
329
+ ['\u00bd', true],
330
+ ['a', false]
331
+ ], done);
332
+ });
333
+
334
+ it('accepts references as length', (done) => {
335
+
336
+ const schema = Joi.object({ a: Joi.number(), b: Joi.string().length(Joi.ref('a'), 'utf8') });
337
+ Helper.validate(schema, [
338
+ [{ a: 2, b: '\u00bd' }, true],
339
+ [{ a: 2, b: 'a' }, false],
340
+ [{ a: 2, b: 'a' }, false, null, 'child "b" fails because ["b" length must be 2 characters long]']
341
+ ], done);
342
+ });
343
+
344
+ it('accepts context references as length', (done) => {
345
+
346
+ const schema = Joi.object({ b: Joi.string().length(Joi.ref('$a'), 'utf8') });
347
+ Helper.validate(schema, [
348
+ [{ b: '\u00bd' }, true, { context: { a: 2 } }],
349
+ [{ b: 'a' }, false, { context: { a: 2 } }],
350
+ [{ b: 'a' }, false, { context: { a: 2 } }, 'child "b" fails because ["b" length must be 2 characters long]']
351
+ ], done);
352
+ });
353
+
354
+ it('errors if reference is not a number', (done) => {
355
+
356
+ const schema = Joi.object({ a: Joi.any(), b: Joi.string().length(Joi.ref('a'), 'utf8') });
357
+
358
+ Helper.validate(schema, [
359
+ [{ a: 'Hi there', b: '\u00bd' }, false, null, 'child "b" fails because ["b" references "a" which is not a number]']
360
+ ], done);
361
+ });
362
+
363
+ it('errors if context reference is not a number', (done) => {
364
+
365
+ const schema = Joi.object({ a: Joi.any(), b: Joi.string().length(Joi.ref('$a'), 'utf8') });
366
+
367
+ Helper.validate(schema, [
368
+ [{ b: '\u00bd' }, false, { context: { a: 'Hi there' } }, 'child "b" fails because ["b" references "a" which is not a number]']
369
+ ], done);
370
+ });
371
+ });
372
+
373
+ describe('email()', () => {
374
+
375
+ it('throws when options are not an object', (done) => {
376
+
377
+ expect(() => {
378
+
379
+ const emailOptions = true;
380
+ Joi.string().email(emailOptions);
381
+ }).to.throw('email options must be an object');
382
+ done();
383
+ });
384
+
385
+ it('throws when checkDNS option is enabled', (done) => {
386
+
387
+ expect(() => {
388
+
389
+ const emailOptions = { checkDNS: true };
390
+ Joi.string().email(emailOptions);
391
+ }).to.throw('checkDNS option is not supported');
392
+ done();
393
+ });
394
+
395
+ it('throws when tldWhitelist is not an array or object', (done) => {
396
+
397
+ expect(() => {
398
+
399
+ const emailOptions = { tldWhitelist: 'domain.tld' };
400
+ Joi.string().email(emailOptions);
401
+ }).to.throw('tldWhitelist must be an array or object');
402
+ done();
403
+ });
404
+
405
+ it('throws when minDomainAtoms is not a number', (done) => {
406
+
407
+ expect(() => {
408
+
409
+ const emailOptions = { minDomainAtoms: '1' };
410
+ Joi.string().email(emailOptions);
411
+ }).to.throw('minDomainAtoms must be a positive integer');
412
+ done();
413
+ });
414
+
415
+ it('throws when minDomainAtoms is not an integer', (done) => {
416
+
417
+ expect(() => {
418
+
419
+ const emailOptions = { minDomainAtoms: 1.2 };
420
+ Joi.string().email(emailOptions);
421
+ }).to.throw('minDomainAtoms must be a positive integer');
422
+ done();
423
+ });
424
+
425
+ it('throws when minDomainAtoms is not positive', (done) => {
426
+
427
+ expect(() => {
428
+
429
+ const emailOptions = { minDomainAtoms: 0 };
430
+ Joi.string().email(emailOptions);
431
+ }).to.throw('minDomainAtoms must be a positive integer');
432
+ done();
433
+ });
434
+
435
+ it('does not throw when minDomainAtoms is a positive integer', (done) => {
436
+
437
+ expect(() => {
438
+
439
+ const emailOptions = { minDomainAtoms: 1 };
440
+ Joi.string().email(emailOptions);
441
+ }).to.not.throw();
442
+ done();
443
+ });
444
+
445
+ it('throws when errorLevel is not an integer or boolean', (done) => {
446
+
447
+ expect(() => {
448
+
449
+ const emailOptions = { errorLevel: 1.2 };
450
+ Joi.string().email(emailOptions);
451
+ }).to.throw('errorLevel must be a non-negative integer or boolean');
452
+ done();
453
+ });
454
+
455
+ it('throws when errorLevel is negative', (done) => {
456
+
457
+ expect(() => {
458
+
459
+ const emailOptions = { errorLevel: -1 };
460
+ Joi.string().email(emailOptions);
461
+ }).to.throw('errorLevel must be a non-negative integer or boolean');
462
+ done();
463
+ });
464
+
465
+ it('does not throw when errorLevel is 0', (done) => {
466
+
467
+ expect(() => {
468
+
469
+ const emailOptions = { errorLevel: 0 };
470
+ Joi.string().email(emailOptions);
471
+ }).to.not.throw();
472
+ done();
473
+ });
474
+
475
+ it('validates email', (done) => {
476
+
477
+ const schema = Joi.string().email();
478
+ Helper.validate(schema, [
479
+ ['joe@example.com', true],
480
+ ['"joe"@example.com', true],
481
+ ['@iaminvalid.com', false],
482
+ ['joe@[IPv6:2a00:1450:4001:c02::1b]', true],
483
+ ['12345678901234567890123456789012345678901234567890123456789012345@walmartlabs.com', false],
484
+ ['123456789012345678901234567890123456789012345678901234567890@12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345678901234567890123456789012345678901234567890123456789.12345.toolong.com', false]
485
+ ], done);
486
+ });
487
+
488
+ it('validates email with tldWhitelist as array', (done) => {
489
+
490
+ const schema = Joi.string().email({ tldWhitelist: ['com', 'org'] });
491
+ Helper.validate(schema, [
492
+ ['joe@example.com', true],
493
+ ['joe@example.org', true],
494
+ ['joe@example.edu', false]
495
+ ], done);
496
+ });
497
+
498
+ it('validates email with tldWhitelist as object', (done) => {
499
+
500
+ const schema = Joi.string().email({ tldWhitelist: { com: true, org: true } });
501
+ Helper.validate(schema, [
502
+ ['joe@example.com', true],
503
+ ['joe@example.org', true],
504
+ ['joe@example.edu', false]
505
+ ], done);
506
+ });
507
+
508
+ it('validates email with minDomainAtoms', (done) => {
509
+
510
+ const schema = Joi.string().email({ minDomainAtoms: 4 });
511
+ Helper.validate(schema, [
512
+ ['joe@example.com', false],
513
+ ['joe@www.example.com', false],
514
+ ['joe@sub.www.example.com', true]
515
+ ], done);
516
+ });
517
+
518
+ it('validates email with errorLevel as boolean', (done) => {
519
+
520
+ let schema = Joi.string().email({ errorLevel: false });
521
+ Helper.validate(schema, [
522
+ ['joe@example.com', true],
523
+ ['joe@www.example.com', true],
524
+ ['joe@localhost', true],
525
+ ['joe', false]
526
+ ]);
527
+
528
+ schema = Joi.string().email({ errorLevel: true });
529
+ Helper.validate(schema, [
530
+ ['joe@example.com', true],
531
+ ['joe@www.example.com', true],
532
+ ['joe@localhost', false],
533
+ ['joe', false]
534
+ ], done);
535
+ });
536
+
537
+ it('validates email with errorLevel as integer', (done) => {
538
+
539
+ const schema = Joi.string().email({ errorLevel: 10 });
540
+ Helper.validate(schema, [
541
+ ['joe@example.com', true],
542
+ ['joe@www.example.com', true],
543
+ ['joe@localhost', true],
544
+ ['joe', false]
545
+ ], done);
546
+ });
547
+
548
+ it('validates email with a friendly error message', (done) => {
549
+
550
+ const schema = { item: Joi.string().email() };
551
+ Joi.compile(schema).validate({ item: 'something' }, (err, value) => {
552
+
553
+ expect(err.message).to.contain('must be a valid email');
554
+ done();
555
+ });
556
+ });
557
+ });
558
+
559
+ describe('hostname()', () => {
560
+
561
+ it('validates hostnames', (done) => {
562
+
563
+ const schema = Joi.string().hostname();
564
+ Helper.validate(schema, [
565
+ ['www.example.com', true],
566
+ ['domain.local', true],
567
+ ['3domain.local', true],
568
+ ['hostname', true],
569
+ ['host:name', false],
570
+ ['-', false],
571
+ ['2387628', true],
572
+ ['01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', false],
573
+ ['::1', true],
574
+ ['0:0:0:0:0:0:0:1', true],
575
+ ['0:?:0:0:0:0:0:1', false]
576
+ ], done);
577
+ });
578
+ });
579
+
580
+ describe('lowercase()', () => {
581
+
582
+ it('only allows strings that are entirely lowercase', (done) => {
583
+
584
+ const schema = Joi.string().lowercase();
585
+ Helper.validateOptions(schema, [
586
+ ['this is all lowercase', true],
587
+ ['5', true],
588
+ ['lower\tcase', true],
589
+ ['Uppercase', false],
590
+ ['MixEd cAsE', false],
591
+ [1, false]
592
+ ], { convert: false }, done);
593
+ });
594
+
595
+ it('coerce string to lowercase before validation', (done) => {
596
+
597
+ const schema = Joi.string().lowercase();
598
+ schema.validate('UPPER TO LOWER', (err, value) => {
599
+
600
+ expect(err).to.not.exist();
601
+ expect(value).to.equal('upper to lower');
602
+ done();
603
+ });
604
+ });
605
+
606
+ it('should work in combination with a trim', (done) => {
607
+
608
+ const schema = Joi.string().lowercase().trim();
609
+ Helper.validate(schema, [
610
+ [' abc', true],
611
+ [' ABC', true],
612
+ ['ABC', true],
613
+ [1, false]
614
+ ], done);
615
+ });
616
+
617
+ it('should work in combination with a replacement', (done) => {
618
+
619
+ const schema = Joi.string().lowercase().replace(/\s+/g, ' ');
620
+ Helper.validate(schema, [
621
+ ['a\r b\n c', true, null, 'a b c'],
622
+ ['A\t B C', true, null, 'a b c'],
623
+ ['ABC', true, null, 'abc'],
624
+ [1, false]
625
+ ], done);
626
+ });
627
+ });
628
+
629
+ describe('uppercase()', () => {
630
+
631
+ it('only allow strings that are entirely uppercase', (done) => {
632
+
633
+ const schema = Joi.string().uppercase();
634
+ Helper.validateOptions(schema, [
635
+ ['THIS IS ALL UPPERCASE', true],
636
+ ['5', true],
637
+ ['UPPER\nCASE', true],
638
+ ['lOWERCASE', false],
639
+ ['MixEd cAsE', false],
640
+ [1, false]
641
+ ], { convert: false }, done);
642
+ });
643
+
644
+ it('coerce string to uppercase before validation', (done) => {
645
+
646
+ const schema = Joi.string().uppercase();
647
+ schema.validate('lower to upper', (err, value) => {
648
+
649
+ expect(err).to.not.exist();
650
+ expect(value).to.equal('LOWER TO UPPER');
651
+ done();
652
+ });
653
+ });
654
+
655
+ it('works in combination with a forced trim', (done) => {
656
+
657
+ const schema = Joi.string().uppercase().trim();
658
+ Helper.validate(schema, [
659
+ [' abc', true],
660
+ [' ABC', true],
661
+ ['ABC', true],
662
+ [1, false]
663
+ ], done);
664
+ });
665
+
666
+ it('works in combination with a forced replacement', (done) => {
667
+
668
+ const schema = Joi.string().uppercase().replace(/\s+/g, ' ');
669
+ Helper.validate(schema, [
670
+ ['a\r b\n c', true, null, 'A B C'],
671
+ ['A\t B C', true, null, 'A B C'],
672
+ ['ABC', true, null, 'ABC'],
673
+ [1, false]
674
+ ], done);
675
+ });
676
+ });
677
+
678
+ describe('trim()', () => {
679
+
680
+ it('only allow strings that have no leading or trailing whitespace', (done) => {
681
+
682
+ const schema = Joi.string().trim();
683
+ Helper.validateOptions(schema, [
684
+ [' something', false],
685
+ ['something ', false],
686
+ ['something\n', false],
687
+ ['some thing', true],
688
+ ['something', true]
689
+ ], { convert: false }, done);
690
+ });
691
+
692
+ it('removes leading and trailing whitespace before validation', (done) => {
693
+
694
+ const schema = Joi.string().trim();
695
+ schema.validate(' trim this ', (err, value) => {
696
+
697
+ expect(err).to.not.exist();
698
+ expect(value).to.equal('trim this');
699
+ done();
700
+ });
701
+ });
702
+
703
+ it('removes leading and trailing whitespace before validation', (done) => {
704
+
705
+ const schema = Joi.string().trim().allow('');
706
+ schema.validate(' ', (err, value) => {
707
+
708
+ expect(err).to.not.exist();
709
+ expect(value).to.equal('');
710
+ done();
711
+ });
712
+ });
713
+
714
+ it('should work in combination with min', (done) => {
715
+
716
+ const schema = Joi.string().min(4).trim();
717
+ Helper.validate(schema, [
718
+ [' a ', false],
719
+ ['abc ', false],
720
+ ['abcd ', true]
721
+ ], done);
722
+ });
723
+
724
+ it('should work in combination with max', (done) => {
725
+
726
+ const schema = Joi.string().max(4).trim();
727
+ Helper.validate(schema, [
728
+ [' abcde ', false],
729
+ ['abc ', true],
730
+ ['abcd ', true]
731
+ ], done);
732
+ });
733
+
734
+ it('should work in combination with length', (done) => {
735
+
736
+ const schema = Joi.string().length(4).trim();
737
+ Helper.validate(schema, [
738
+ [' ab ', false],
739
+ ['abc ', false],
740
+ ['abcd ', true]
741
+ ], done);
742
+ });
743
+
744
+ it('should work in combination with a case change', (done) => {
745
+
746
+ const schema = Joi.string().trim().lowercase();
747
+ Helper.validate(schema, [
748
+ [' abc', true],
749
+ [' ABC', true],
750
+ ['ABC', true]
751
+ ], done);
752
+ });
753
+ });
754
+
755
+ describe('replace()', () => {
756
+
757
+ it('successfully replaces the first occurrence of the expression', (done) => {
758
+
759
+ const schema = Joi.string().replace(/\s+/, ''); // no "g" flag
760
+ Helper.validateOptions(schema, [
761
+ ['\tsomething', true, null, 'something'],
762
+ ['something\r', true, null, 'something'],
763
+ ['something ', true, null, 'something'],
764
+ ['some thing', true, null, 'something'],
765
+ ['so me thing', true, null, 'some thing'] // first occurrence!
766
+ ], { convert: true }, done);
767
+ });
768
+
769
+ it('successfully replaces all occurrences of the expression', (done) => {
770
+
771
+ const schema = Joi.string().replace(/\s+/g, ''); // has "g" flag
772
+ Helper.validateOptions(schema, [
773
+ ['\tsomething', true, null, 'something'],
774
+ ['something\r', true, null, 'something'],
775
+ ['something ', true, null, 'something'],
776
+ ['some thing', true, null, 'something'],
777
+ ['so me thing', true, null, 'something']
778
+ ], { convert: true }, done);
779
+ });
780
+
781
+ it('successfully replaces all occurrences of a string pattern', (done) => {
782
+
783
+ const schema = Joi.string().replace('foo', 'X'); // has "g" flag
784
+ Helper.validateOptions(schema, [
785
+ ['foobarfoobazfoo', true, null, 'XbarXbazX']
786
+ ], { convert: true }, done);
787
+ });
788
+
789
+ it('successfully replaces multiple times', (done) => {
790
+
791
+ const schema = Joi.string().replace(/a/g, 'b').replace(/b/g, 'c');
792
+ schema.validate('a quick brown fox', (err, value) => {
793
+
794
+ expect(err).to.not.exist();
795
+ expect(value).to.equal('c quick crown fox');
796
+ done();
797
+ });
798
+ });
799
+
800
+ it('should work in combination with trim', (done) => {
801
+
802
+ // The string below is the name "Yamada Tarou" separated by a
803
+ // carriage return, a "full width" ideographic space and a newline
804
+
805
+ const schema = Joi.string().trim().replace(/\s+/g, ' ');
806
+ schema.validate(' \u5C71\u7530\r\u3000\n\u592A\u90CE ', (err, value) => {
807
+
808
+ expect(err).to.not.exist();
809
+ expect(value).to.equal('\u5C71\u7530 \u592A\u90CE');
810
+ done();
811
+ });
812
+ });
813
+
814
+ it('should work in combination with min', (done) => {
815
+
816
+ const schema = Joi.string().min(4).replace(/\s+/g, ' ');
817
+ Helper.validate(schema, [
818
+ [' a ', false],
819
+ ['abc ', true, null, 'abc '],
820
+ ['a\t\rbc', true, null, 'a bc']
821
+ ], done);
822
+ });
823
+
824
+ it('should work in combination with max', (done) => {
825
+
826
+ const schema = Joi.string().max(5).replace(/ CHANGE ME /g, '-b-');
827
+ Helper.validate(schema, [
828
+ ['a CHANGE ME c', true, null, 'a-b-c'],
829
+ ['a-b-c', true, null, 'a-b-c'] // nothing changes here!
830
+ ], done);
831
+ });
832
+
833
+ it('should work in combination with length', (done) => {
834
+
835
+ const schema = Joi.string().length(5).replace(/\s+/g, ' ');
836
+ Helper.validate(schema, [
837
+ ['a bc', false],
838
+ ['a\tb\nc', true, null, 'a b c']
839
+ ], done);
840
+ });
841
+
842
+ });
843
+
844
+ describe('regex()', () => {
845
+
846
+ it('should not include a pattern name by default', (done) => {
847
+
848
+ const schema = Joi.string().regex(/[a-z]+/).regex(/[0-9]+/);
849
+ schema.validate('abcd', (err, value) => {
850
+
851
+ expect(err.message).to.contain('required pattern');
852
+ done();
853
+ });
854
+ });
855
+
856
+ it('should include a pattern name if specified', (done) => {
857
+
858
+ const schema = Joi.string().regex(/[a-z]+/, 'letters').regex(/[0-9]+/, 'numbers');
859
+ schema.validate('abcd', (err, value) => {
860
+
861
+ expect(err.message).to.contain('numbers pattern');
862
+ done();
863
+ });
864
+ });
865
+ });
866
+
867
+ describe('ip()', () => {
868
+
869
+ const invalidIPs = [
870
+ ['ASDF', false],
871
+ ['192.0.2.16:80/30', false],
872
+ ['192.0.2.16a', false],
873
+ ['qwerty', false],
874
+ ['127.0.0.1:8000', false],
875
+ ['ftp://www.example.com', false],
876
+ ['Bananas in pajamas are coming down the stairs', false]
877
+ ];
878
+
879
+ const invalidIPv4s = [
880
+ ['0.0.0.0/33', false],
881
+ ['256.0.0.0/0', false],
882
+ ['255.255.255.256/32', false],
883
+ ['256.0.0.0', false],
884
+ ['255.255.255.256', false]
885
+ ];
886
+
887
+ const invalidIPv6s = [
888
+ ['2001:db8::7/33', false],
889
+ ['1080:0:0:0:8:800:200C:417G', false]
890
+ ];
891
+
892
+ const invalidIPvFutures = [
893
+ ['v1.09azAZ-._~!$&\'()*+,;=:/33', false],
894
+ ['v1.09#', false]
895
+ ];
896
+
897
+ const validIPv4sWithCidr = function (success) {
898
+
899
+ return [
900
+ ['0.0.0.0/32', success],
901
+ ['255.255.255.255/0', success],
902
+ ['127.0.0.1/0', success],
903
+ ['192.168.2.1/0', success],
904
+ ['0.0.0.3/2', success],
905
+ ['0.0.0.7/3', success],
906
+ ['0.0.0.15/4', success],
907
+ ['0.0.0.31/5', success],
908
+ ['0.0.0.63/6', success],
909
+ ['0.0.0.127/7', success],
910
+ ['01.020.030.100/7', success],
911
+ ['0.0.0.0/0', success],
912
+ ['00.00.00.00/0', success],
913
+ ['000.000.000.000/32', success]
914
+ ];
915
+ };
916
+
917
+ const validIPv4sWithoutCidr = function (success) {
918
+
919
+ return [
920
+ ['0.0.0.0', success],
921
+ ['255.255.255.255', success],
922
+ ['127.0.0.1', success],
923
+ ['192.168.2.1', success],
924
+ ['0.0.0.3', success],
925
+ ['0.0.0.7', success],
926
+ ['0.0.0.15', success],
927
+ ['0.0.0.31', success],
928
+ ['0.0.0.63', success],
929
+ ['0.0.0.127', success],
930
+ ['01.020.030.100', success],
931
+ ['0.0.0.0', success],
932
+ ['00.00.00.00', success],
933
+ ['000.000.000.000', success]
934
+ ];
935
+ };
936
+
937
+ const validIPv6sWithCidr = function (success) {
938
+
939
+ return [
940
+ ['2001:db8::7/32', success],
941
+ ['a:b:c:d:e::1.2.3.4/13', success],
942
+ ['FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/0', success],
943
+ ['FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/32', success],
944
+ ['1080:0:0:0:8:800:200C:417A/27', success]
945
+ ];
946
+ };
947
+
948
+ const validIPv6sWithoutCidr = function (success) {
949
+
950
+ return [
951
+ ['2001:db8::7', success],
952
+ ['a:b:c:d:e::1.2.3.4', success],
953
+ ['FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', success],
954
+ ['FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', success],
955
+ ['1080:0:0:0:8:800:200C:417A', success],
956
+ ['::1:2:3:4:5:6:7', success],
957
+ ['::1:2:3:4:5:6', success],
958
+ ['1::1:2:3:4:5:6', success],
959
+ ['::1:2:3:4:5', success],
960
+ ['1::1:2:3:4:5', success],
961
+ ['2:1::1:2:3:4:5', success],
962
+ ['::1:2:3:4', success],
963
+ ['1::1:2:3:4', success],
964
+ ['2:1::1:2:3:4', success],
965
+ ['3:2:1::1:2:3:4', success],
966
+ ['::1:2:3', success],
967
+ ['1::1:2:3', success],
968
+ ['2:1::1:2:3', success],
969
+ ['3:2:1::1:2:3', success],
970
+ ['4:3:2:1::1:2:3', success],
971
+ ['::1:2', success],
972
+ ['1::1:2', success],
973
+ ['2:1::1:2', success],
974
+ ['3:2:1::1:2', success],
975
+ ['4:3:2:1::1:2', success],
976
+ ['5:4:3:2:1::1:2', success],
977
+ ['::1', success],
978
+ ['1::1', success],
979
+ ['2:1::1', success],
980
+ ['3:2:1::1', success],
981
+ ['4:3:2:1::1', success],
982
+ ['5:4:3:2:1::1', success],
983
+ ['6:5:4:3:2:1::1', success],
984
+ ['::', success],
985
+ ['1::', success],
986
+ ['2:1::', success],
987
+ ['3:2:1::', success],
988
+ ['4:3:2:1::', success],
989
+ ['5:4:3:2:1::', success],
990
+ ['6:5:4:3:2:1::', success],
991
+ ['7:6:5:4:3:2:1::', success]
992
+ ];
993
+ };
994
+
995
+ const validIPvFuturesWithCidr = function (success) {
996
+
997
+ return [
998
+ ['v1.09azAZ-._~!$&\'()*+,;=:/32', success]
999
+ ];
1000
+ };
1001
+
1002
+ const validIPvFuturesWithoutCidr = function (success) {
1003
+
1004
+ return [
1005
+ ['v1.09azAZ-._~!$&\'()*+,;=:', success]
1006
+ ];
1007
+ };
1008
+
1009
+ it('should validate all ip addresses with optional CIDR by default', (done) => {
1010
+
1011
+ const schema = Joi.string().ip();
1012
+ Helper.validate(schema, []
1013
+ .concat(validIPv4sWithCidr(true))
1014
+ .concat(validIPv4sWithoutCidr(true))
1015
+ .concat(validIPv6sWithCidr(true))
1016
+ .concat(validIPv6sWithoutCidr(true))
1017
+ .concat(validIPvFuturesWithCidr(true))
1018
+ .concat(validIPvFuturesWithoutCidr(true))
1019
+ .concat(invalidIPs)
1020
+ .concat(invalidIPv4s)
1021
+ .concat(invalidIPv6s)
1022
+ .concat(invalidIPvFutures), done);
1023
+ });
1024
+
1025
+ it('should validate all ip addresses with an optional CIDR', (done) => {
1026
+
1027
+ const schema = Joi.string().ip({ cidr: 'optional' });
1028
+ Helper.validate(schema, []
1029
+ .concat(validIPv4sWithCidr(true))
1030
+ .concat(validIPv4sWithoutCidr(true))
1031
+ .concat(validIPv6sWithCidr(true))
1032
+ .concat(validIPv6sWithoutCidr(true))
1033
+ .concat(validIPvFuturesWithCidr(true))
1034
+ .concat(validIPvFuturesWithoutCidr(true))
1035
+ .concat(invalidIPs)
1036
+ .concat(invalidIPv4s)
1037
+ .concat(invalidIPv6s)
1038
+ .concat(invalidIPvFutures), done);
1039
+ });
1040
+
1041
+ it('should validate all ip addresses with a required CIDR', (done) => {
1042
+
1043
+ const schema = Joi.string().ip({ cidr: 'required' });
1044
+ Helper.validate(schema, []
1045
+ .concat(validIPv4sWithCidr(true))
1046
+ .concat(validIPv4sWithoutCidr(false))
1047
+ .concat(validIPv6sWithCidr(true))
1048
+ .concat(validIPv6sWithoutCidr(false))
1049
+ .concat(validIPvFuturesWithCidr(true))
1050
+ .concat(validIPvFuturesWithoutCidr(false))
1051
+ .concat(invalidIPs)
1052
+ .concat(invalidIPv4s)
1053
+ .concat(invalidIPv6s)
1054
+ .concat(invalidIPvFutures), done);
1055
+ });
1056
+
1057
+ it('should validate all ip addresses with a forbidden CIDR', (done) => {
1058
+
1059
+ const schema = Joi.string().ip({ cidr: 'forbidden' });
1060
+ Helper.validate(schema, []
1061
+ .concat(validIPv4sWithCidr(false))
1062
+ .concat(validIPv4sWithoutCidr(true))
1063
+ .concat(validIPv6sWithCidr(false))
1064
+ .concat(validIPv6sWithoutCidr(true))
1065
+ .concat(validIPvFuturesWithCidr(false))
1066
+ .concat(validIPvFuturesWithoutCidr(true))
1067
+ .concat(invalidIPs)
1068
+ .concat(invalidIPv4s)
1069
+ .concat(invalidIPv6s)
1070
+ .concat(invalidIPvFutures), done);
1071
+ });
1072
+
1073
+ it('throws when options is not an object', (done) => {
1074
+
1075
+ expect(() => {
1076
+
1077
+ Joi.string().ip(42);
1078
+ }).to.throw('options must be an object');
1079
+ done();
1080
+ });
1081
+
1082
+ it('throws when options.cidr is not a string', (done) => {
1083
+
1084
+ expect(() => {
1085
+
1086
+ Joi.string().ip({ cidr: 42 });
1087
+ }).to.throw('cidr must be a string');
1088
+ done();
1089
+ });
1090
+
1091
+ it('throws when options.cidr is not a valid value', (done) => {
1092
+
1093
+ expect(() => {
1094
+
1095
+ Joi.string().ip({ cidr: '42' });
1096
+ }).to.throw('cidr must be one of required, optional, forbidden');
1097
+ done();
1098
+ });
1099
+
1100
+ it('throws when options.version is an empty array', (done) => {
1101
+
1102
+ expect(() => {
1103
+
1104
+ Joi.string().ip({ version: [] });
1105
+ }).to.throw('version must have at least 1 version specified');
1106
+ done();
1107
+ });
1108
+
1109
+ it('throws when options.version is not a string', (done) => {
1110
+
1111
+ expect(() => {
1112
+
1113
+ Joi.string().ip({ version: 42 });
1114
+ }).to.throw('version at position 0 must be a string');
1115
+ done();
1116
+ });
1117
+
1118
+ it('throws when options.version is not a valid value', (done) => {
1119
+
1120
+ expect(() => {
1121
+
1122
+ Joi.string().ip({ version: '42' });
1123
+ }).to.throw('version at position 0 must be one of ipv4, ipv6, ipvfuture');
1124
+ done();
1125
+ });
1126
+
1127
+ it('validates ip with a friendly error message', (done) => {
1128
+
1129
+ const schema = { item: Joi.string().ip() };
1130
+ Joi.compile(schema).validate({ item: 'something' }, (err, value) => {
1131
+
1132
+ expect(err.message).to.contain('must be a valid ip address');
1133
+ done();
1134
+ });
1135
+ });
1136
+
1137
+ it('validates ip and cidr presence with a friendly error message', (done) => {
1138
+
1139
+ const schema = { item: Joi.string().ip({ cidr: 'required' }) };
1140
+ Joi.compile(schema).validate({ item: 'something' }, (err, value) => {
1141
+
1142
+ expect(err.message).to.contain('must be a valid ip address with a required CIDR');
1143
+ done();
1144
+ });
1145
+ });
1146
+
1147
+ it('validates custom ip version and cidr presence with a friendly error message', (done) => {
1148
+
1149
+ const schema = { item: Joi.string().ip({ version: 'ipv4', cidr: 'required' }) };
1150
+ Joi.compile(schema).validate({ item: 'something' }, (err, value) => {
1151
+
1152
+ expect(err.message).to.contain('child "item" fails because ["item" must be a valid ip address of one of the following versions [ipv4] with a required CIDR]');
1153
+ done();
1154
+ });
1155
+ });
1156
+
1157
+ describe('ip({ version: "ipv4" })', () => {
1158
+
1159
+ it('should validate all ipv4 addresses with a default CIDR strategy', (done) => {
1160
+
1161
+ const schema = Joi.string().ip({ version: 'ipv4' });
1162
+ Helper.validate(schema, []
1163
+ .concat(validIPv4sWithCidr(true))
1164
+ .concat(validIPv4sWithoutCidr(true))
1165
+ .concat(validIPv6sWithCidr(false))
1166
+ .concat(validIPv6sWithoutCidr(false))
1167
+ .concat(validIPvFuturesWithCidr(false))
1168
+ .concat(validIPvFuturesWithoutCidr(false))
1169
+ .concat(invalidIPs)
1170
+ .concat(invalidIPv4s)
1171
+ .concat(invalidIPv6s)
1172
+ .concat(invalidIPvFutures), done);
1173
+ });
1174
+
1175
+ it('should validate all ipv4 addresses with an optional CIDR', (done) => {
1176
+
1177
+ const schema = Joi.string().ip({ version: 'ipv4', cidr: 'optional' });
1178
+ Helper.validate(schema, []
1179
+ .concat(validIPv4sWithCidr(true))
1180
+ .concat(validIPv4sWithoutCidr(true))
1181
+ .concat(validIPv6sWithCidr(false))
1182
+ .concat(validIPv6sWithoutCidr(false))
1183
+ .concat(validIPvFuturesWithCidr(false))
1184
+ .concat(validIPvFuturesWithoutCidr(false))
1185
+ .concat(invalidIPs)
1186
+ .concat(invalidIPv4s)
1187
+ .concat(invalidIPv6s)
1188
+ .concat(invalidIPvFutures), done);
1189
+ });
1190
+
1191
+ it('should validate all ipv4 addresses with a required CIDR', (done) => {
1192
+
1193
+ const schema = Joi.string().ip({ version: 'ipv4', cidr: 'required' });
1194
+ Helper.validate(schema, []
1195
+ .concat(validIPv4sWithCidr(true))
1196
+ .concat(validIPv4sWithoutCidr(false))
1197
+ .concat(validIPv6sWithCidr(false))
1198
+ .concat(validIPv6sWithoutCidr(false))
1199
+ .concat(validIPvFuturesWithCidr(false))
1200
+ .concat(validIPvFuturesWithoutCidr(false))
1201
+ .concat(invalidIPs)
1202
+ .concat(invalidIPv4s)
1203
+ .concat(invalidIPv6s)
1204
+ .concat(invalidIPvFutures), done);
1205
+ });
1206
+
1207
+ it('should validate all ipv4 addresses with a forbidden CIDR', (done) => {
1208
+
1209
+ const schema = Joi.string().ip({ version: 'ipv4', cidr: 'forbidden' });
1210
+ Helper.validate(schema, []
1211
+ .concat(validIPv4sWithCidr(false))
1212
+ .concat(validIPv4sWithoutCidr(true))
1213
+ .concat(validIPv6sWithCidr(false))
1214
+ .concat(validIPv6sWithoutCidr(false))
1215
+ .concat(validIPvFuturesWithCidr(false))
1216
+ .concat(validIPvFuturesWithoutCidr(false))
1217
+ .concat(invalidIPs)
1218
+ .concat(invalidIPv4s)
1219
+ .concat(invalidIPv6s)
1220
+ .concat(invalidIPvFutures), done);
1221
+ });
1222
+ });
1223
+
1224
+ describe('ip({ version: "ipv6" })', () => {
1225
+
1226
+ it('should validate all ipv6 addresses with a default CIDR strategy', (done) => {
1227
+
1228
+ const schema = Joi.string().ip({ version: 'ipv6' });
1229
+ Helper.validate(schema, []
1230
+ .concat(validIPv4sWithCidr(false))
1231
+ .concat(validIPv4sWithoutCidr(false))
1232
+ .concat(validIPv6sWithCidr(true))
1233
+ .concat(validIPv6sWithoutCidr(true))
1234
+ .concat(validIPvFuturesWithCidr(false))
1235
+ .concat(validIPvFuturesWithoutCidr(false))
1236
+ .concat(invalidIPs)
1237
+ .concat(invalidIPv4s)
1238
+ .concat(invalidIPv6s)
1239
+ .concat(invalidIPvFutures), done);
1240
+ });
1241
+
1242
+ it('should validate all ipv6 addresses with an optional CIDR', (done) => {
1243
+
1244
+ const schema = Joi.string().ip({ version: 'ipv6', cidr: 'optional' });
1245
+ Helper.validate(schema, []
1246
+ .concat(validIPv4sWithCidr(false))
1247
+ .concat(validIPv4sWithoutCidr(false))
1248
+ .concat(validIPv6sWithCidr(true))
1249
+ .concat(validIPv6sWithoutCidr(true))
1250
+ .concat(validIPvFuturesWithCidr(false))
1251
+ .concat(validIPvFuturesWithoutCidr(false))
1252
+ .concat(invalidIPs)
1253
+ .concat(invalidIPv4s)
1254
+ .concat(invalidIPv6s)
1255
+ .concat(invalidIPvFutures), done);
1256
+ });
1257
+
1258
+ it('should validate all ipv6 addresses with a required CIDR', (done) => {
1259
+
1260
+ const schema = Joi.string().ip({ version: 'ipv6', cidr: 'required' });
1261
+ Helper.validate(schema, []
1262
+ .concat(validIPv4sWithCidr(false))
1263
+ .concat(validIPv4sWithoutCidr(false))
1264
+ .concat(validIPv6sWithCidr(true))
1265
+ .concat(validIPv6sWithoutCidr(false))
1266
+ .concat(validIPvFuturesWithCidr(false))
1267
+ .concat(validIPvFuturesWithoutCidr(false))
1268
+ .concat(invalidIPs)
1269
+ .concat(invalidIPv4s)
1270
+ .concat(invalidIPv6s)
1271
+ .concat(invalidIPvFutures), done);
1272
+ });
1273
+
1274
+ it('should validate all ipv6 addresses with a forbidden CIDR', (done) => {
1275
+
1276
+ const schema = Joi.string().ip({ version: 'ipv6', cidr: 'forbidden' });
1277
+ Helper.validate(schema, []
1278
+ .concat(validIPv4sWithCidr(false))
1279
+ .concat(validIPv4sWithoutCidr(false))
1280
+ .concat(validIPv6sWithCidr(false))
1281
+ .concat(validIPv6sWithoutCidr(true))
1282
+ .concat(validIPvFuturesWithCidr(false))
1283
+ .concat(validIPvFuturesWithoutCidr(false))
1284
+ .concat(invalidIPs)
1285
+ .concat(invalidIPv4s)
1286
+ .concat(invalidIPv6s)
1287
+ .concat(invalidIPvFutures), done);
1288
+ });
1289
+ });
1290
+
1291
+ describe('ip({ version: "ipvfuture" })', () => {
1292
+
1293
+ it('should validate all ipvfuture addresses with a default CIDR strategy', (done) => {
1294
+
1295
+ const schema = Joi.string().ip({ version: 'ipvfuture' });
1296
+ Helper.validate(schema, []
1297
+ .concat(validIPv4sWithCidr(false))
1298
+ .concat(validIPv4sWithoutCidr(false))
1299
+ .concat(validIPv6sWithCidr(false))
1300
+ .concat(validIPv6sWithoutCidr(false))
1301
+ .concat(validIPvFuturesWithCidr(true))
1302
+ .concat(validIPvFuturesWithoutCidr(true))
1303
+ .concat(invalidIPs)
1304
+ .concat(invalidIPv4s)
1305
+ .concat(invalidIPv6s)
1306
+ .concat(invalidIPvFutures), done);
1307
+ });
1308
+
1309
+ it('should validate all ipvfuture addresses with an optional CIDR', (done) => {
1310
+
1311
+ const schema = Joi.string().ip({ version: 'ipvfuture', cidr: 'optional' });
1312
+ Helper.validate(schema, []
1313
+ .concat(validIPv4sWithCidr(false))
1314
+ .concat(validIPv4sWithoutCidr(false))
1315
+ .concat(validIPv6sWithCidr(false))
1316
+ .concat(validIPv6sWithoutCidr(false))
1317
+ .concat(validIPvFuturesWithCidr(true))
1318
+ .concat(validIPvFuturesWithoutCidr(true))
1319
+ .concat(invalidIPs)
1320
+ .concat(invalidIPv4s)
1321
+ .concat(invalidIPv6s)
1322
+ .concat(invalidIPvFutures), done);
1323
+ });
1324
+
1325
+ it('should validate all ipvfuture addresses with a required CIDR', (done) => {
1326
+
1327
+ const schema = Joi.string().ip({ version: 'ipvfuture', cidr: 'required' });
1328
+ Helper.validate(schema, []
1329
+ .concat(validIPv4sWithCidr(false))
1330
+ .concat(validIPv4sWithoutCidr(false))
1331
+ .concat(validIPv6sWithCidr(false))
1332
+ .concat(validIPv6sWithoutCidr(false))
1333
+ .concat(validIPvFuturesWithCidr(true))
1334
+ .concat(validIPvFuturesWithoutCidr(false))
1335
+ .concat(invalidIPs)
1336
+ .concat(invalidIPv4s)
1337
+ .concat(invalidIPv6s)
1338
+ .concat(invalidIPvFutures), done);
1339
+ });
1340
+
1341
+ it('should validate all ipvfuture addresses with a forbidden CIDR', (done) => {
1342
+
1343
+ const schema = Joi.string().ip({ version: 'ipvfuture', cidr: 'forbidden' });
1344
+ Helper.validate(schema, []
1345
+ .concat(validIPv4sWithCidr(false))
1346
+ .concat(validIPv4sWithoutCidr(false))
1347
+ .concat(validIPv6sWithCidr(false))
1348
+ .concat(validIPv6sWithoutCidr(false))
1349
+ .concat(validIPvFuturesWithCidr(false))
1350
+ .concat(validIPvFuturesWithoutCidr(true))
1351
+ .concat(invalidIPs)
1352
+ .concat(invalidIPv4s)
1353
+ .concat(invalidIPv6s)
1354
+ .concat(invalidIPvFutures), done);
1355
+ });
1356
+ });
1357
+
1358
+ describe('ip({ version: [ "ipv4", "ipv6" ] })', () => {
1359
+
1360
+ it('should validate all ipv4 and ipv6 addresses with a default CIDR strategy', (done) => {
1361
+
1362
+ const schema = Joi.string().ip({ version: ['ipv4', 'ipv6'] });
1363
+ Helper.validate(schema, []
1364
+ .concat(validIPv4sWithCidr(true))
1365
+ .concat(validIPv4sWithoutCidr(true))
1366
+ .concat(validIPv6sWithCidr(true))
1367
+ .concat(validIPv6sWithoutCidr(true))
1368
+ .concat(validIPvFuturesWithCidr(false))
1369
+ .concat(validIPvFuturesWithoutCidr(false))
1370
+ .concat(invalidIPs)
1371
+ .concat(invalidIPv4s)
1372
+ .concat(invalidIPv6s)
1373
+ .concat(invalidIPvFutures), done);
1374
+ });
1375
+
1376
+ it('should validate all ipv4 and ipv6 addresses with an optional CIDR', (done) => {
1377
+
1378
+ const schema = Joi.string().ip({ version: ['ipv4', 'ipv6'], cidr: 'optional' });
1379
+ Helper.validate(schema, []
1380
+ .concat(validIPv4sWithCidr(true))
1381
+ .concat(validIPv4sWithoutCidr(true))
1382
+ .concat(validIPv6sWithCidr(true))
1383
+ .concat(validIPv6sWithoutCidr(true))
1384
+ .concat(validIPvFuturesWithCidr(false))
1385
+ .concat(validIPvFuturesWithoutCidr(false))
1386
+ .concat(invalidIPs)
1387
+ .concat(invalidIPv4s)
1388
+ .concat(invalidIPv6s)
1389
+ .concat(invalidIPvFutures), done);
1390
+ });
1391
+
1392
+ it('should validate all ipv4 and ipv6 addresses with a required CIDR', (done) => {
1393
+
1394
+ const schema = Joi.string().ip({ version: ['ipv4', 'ipv6'], cidr: 'required' });
1395
+ Helper.validate(schema, []
1396
+ .concat(validIPv4sWithCidr(true))
1397
+ .concat(validIPv4sWithoutCidr(false))
1398
+ .concat(validIPv6sWithCidr(true))
1399
+ .concat(validIPv6sWithoutCidr(false))
1400
+ .concat(validIPvFuturesWithCidr(false))
1401
+ .concat(validIPvFuturesWithoutCidr(false))
1402
+ .concat(invalidIPs)
1403
+ .concat(invalidIPv4s)
1404
+ .concat(invalidIPv6s)
1405
+ .concat(invalidIPvFutures), done);
1406
+ });
1407
+
1408
+ it('should validate all ipv4 and ipv6 addresses with a forbidden CIDR', (done) => {
1409
+
1410
+ const schema = Joi.string().ip({ version: ['ipv4', 'ipv6'], cidr: 'forbidden' });
1411
+ Helper.validate(schema, []
1412
+ .concat(validIPv4sWithCidr(false))
1413
+ .concat(validIPv4sWithoutCidr(true))
1414
+ .concat(validIPv6sWithCidr(false))
1415
+ .concat(validIPv6sWithoutCidr(true))
1416
+ .concat(validIPvFuturesWithCidr(false))
1417
+ .concat(validIPvFuturesWithoutCidr(false))
1418
+ .concat(invalidIPs)
1419
+ .concat(invalidIPv4s)
1420
+ .concat(invalidIPv6s)
1421
+ .concat(invalidIPvFutures), done);
1422
+ });
1423
+ });
1424
+ });
1425
+
1426
+ describe('uri()', () => {
1427
+
1428
+ it('validates uri', (done) => {
1429
+
1430
+ // Handful of tests taken from Node: https://github.com/joyent/node/blob/cfcb1de130867197cbc9c6012b7e84e08e53d032/test/simple/test-url.js
1431
+ // Also includes examples from RFC 8936: http://tools.ietf.org/html/rfc3986#page-7
1432
+ const schema = Joi.string().uri();
1433
+
1434
+ Helper.validate(schema, [
1435
+ ['foo://example.com:8042/over/there?name=ferret#nose', true],
1436
+ ['urn:example:animal:ferret:nose', true],
1437
+ ['ftp://ftp.is.co.za/rfc/rfc1808.txt', true],
1438
+ ['http://www.ietf.org/rfc/rfc2396.txt', true],
1439
+ ['ldap://[2001:db8::7]/c=GB?objectClass?one', true],
1440
+ ['ldap://2001:db8::7/c=GB?objectClass?one', false],
1441
+ ['mailto:John.Doe@example.com', true],
1442
+ ['news:comp.infosystems.www.servers.unix', true],
1443
+ ['tel:+1-816-555-1212', true],
1444
+ ['telnet://192.0.2.16:80/', true],
1445
+ ['urn:oasis:names:specification:docbook:dtd:xml:4.1.2', true],
1446
+ ['file:///example.txt', true],
1447
+ ['http://asdf:qw%20er@localhost:8000?asdf=12345&asda=fc%2F#bacon', true],
1448
+ ['http://asdf@localhost:8000', true],
1449
+ ['http://[v1.09azAZ-._~!$&\'()*+,;=:]', true],
1450
+ ['http://[a:b:c:d:e::1.2.3.4]', true],
1451
+ ['coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]', true],
1452
+ ['http://[1080:0:0:0:8:800:200C:417A]', true],
1453
+ ['http://v1.09azAZ-._~!$&\'()*+,;=:', true], // This doesn't look valid, but it is. The `v1.09azAZ-._~!$&\'()*+,;=` part is a valid registered name as it has no invalid characters
1454
+ ['http://a:b:c:d:e::1.2.3.4', false],
1455
+ ['coap://FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', false],
1456
+ ['http://1080:0:0:0:8:800:200C:417A', false],
1457
+ ['http://127.0.0.1:8000/foo?bar', true],
1458
+ ['http://asdf:qwer@localhost:8000', true],
1459
+ ['http://user:pass%3A@localhost:80', true],
1460
+ ['http://localhost:123', true],
1461
+ ['https://localhost:123', true],
1462
+ ['file:///whatever', true],
1463
+ ['mailto:asdf@asdf.com', true],
1464
+ ['ftp://www.example.com', true],
1465
+ ['javascript:alert(\'hello\');', true], // eslint-disable-line no-script-url
1466
+ ['xmpp:isaacschlueter@jabber.org', true],
1467
+ ['f://some.host/path', true],
1468
+ ['http://localhost:18/asdf', true],
1469
+ ['http://localhost:42/asdf?qwer=zxcv', true],
1470
+ ['HTTP://www.example.com/', true],
1471
+ ['HTTP://www.example.com', true],
1472
+ ['http://www.ExAmPlE.com/', true],
1473
+ ['http://user:pw@www.ExAmPlE.com/', true],
1474
+ ['http://USER:PW@www.ExAmPlE.com/', true],
1475
+ ['http://user@www.example.com/', true],
1476
+ ['http://user%3Apw@www.example.com/', true],
1477
+ ['http://x.com/path?that%27s#all,%20folks', true],
1478
+ ['HTTP://X.COM/Y', true],
1479
+ ['http://www.narwhaljs.org/blog/categories?id=news', true],
1480
+ ['http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', true],
1481
+ ['http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', true],
1482
+ ['http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', true],
1483
+ ['http://_jabber._tcp.google.com:80/test', true],
1484
+ ['http://user:pass@_jabber._tcp.google.com:80/test', true],
1485
+ ['http://[fe80::1]/a/b?a=b#abc', true],
1486
+ ['http://fe80::1/a/b?a=b#abc', false],
1487
+ ['http://user:password@[3ffe:2a00:100:7031::1]:8080', true],
1488
+ ['coap://[1080:0:0:0:8:800:200C:417A]:61616/', true],
1489
+ ['coap://1080:0:0:0:8:800:200C:417A:61616/', false],
1490
+ ['git+http://github.com/joyent/node.git', true],
1491
+ ['http://bucket_name.s3.amazonaws.com/image.jpg', true],
1492
+ ['dot.test://foo/bar', true],
1493
+ ['svn+ssh://foo/bar', true],
1494
+ ['dash-test://foo/bar', true],
1495
+ ['xmpp:isaacschlueter@jabber.org', true],
1496
+ ['http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', true],
1497
+ ['javascript:alert(\'hello\');', true], // eslint-disable-line no-script-url
1498
+ ['file://localhost/etc/node/', true],
1499
+ ['file:///etc/node/', true],
1500
+ ['http://USER:PW@www.ExAmPlE.com/', true],
1501
+ ['mailto:local1@domain1?query1', true],
1502
+ ['http://example/a/b?c/../d', true],
1503
+ ['http://example/x%2Fabc', true],
1504
+ ['http://a/b/c/d;p=1/g;x=1/y', true],
1505
+ ['http://a/b/c/g#s/../x', true],
1506
+ ['http://a/b/c/.foo', true],
1507
+ ['http://example.com/b//c//d;p?q#blarg', true],
1508
+ ['g:h', true],
1509
+ ['http://a/b/c/g', true],
1510
+ ['http://a/b/c/g/', true],
1511
+ ['http://a/g', true],
1512
+ ['http://g', true],
1513
+ ['http://a/b/c/d;p?y', true],
1514
+ ['http://a/b/c/g?y', true],
1515
+ ['http://a/b/c/d;p?q#s', true],
1516
+ ['http://a/b/c/g#s', true],
1517
+ ['http://a/b/c/g?y#s', true],
1518
+ ['http://a/b/c/;x', true],
1519
+ ['http://a/b/c/g;x', true],
1520
+ ['http://a/b/c/g;x?y#s', true],
1521
+ ['http://a/b/c/d;p?q', true],
1522
+ ['http://a/b/c/', true],
1523
+ ['http://a/b/', true],
1524
+ ['http://a/b/g', true],
1525
+ ['http://a/', true],
1526
+ ['http://a/g', true],
1527
+ ['http://a/g', true],
1528
+ ['file:/asda', true],
1529
+ ['qwerty', false],
1530
+ ['invalid uri', false],
1531
+ ['1http://google.com', false],
1532
+ ['http://testdomain`,.<>/?\'";{}][++\\|~!@#$%^&*().org', false],
1533
+ ['', false],
1534
+ ['(╯°□°)╯︵ ┻━┻', false],
1535
+ ['one/two/three?value=abc&value2=123#david-rules', false],
1536
+ ['//username:password@test.example.com/one/two/three?value=abc&value2=123#david-rules', false],
1537
+ ['http://a\r" \t\n<\'b:b@c\r\nd/e?f', false],
1538
+ ['/absolute', false]
1539
+ ], done);
1540
+ });
1541
+
1542
+ it('validates uri with a single scheme provided', (done) => {
1543
+
1544
+ const schema = Joi.string().uri({
1545
+ scheme: 'http'
1546
+ });
1547
+
1548
+ Helper.validate(schema, [
1549
+ ['http://google.com', true],
1550
+ ['https://google.com', false],
1551
+ ['ftp://google.com', false],
1552
+ ['file:/asdf', false],
1553
+ ['/path?query=value#hash', false]
1554
+ ], done);
1555
+ });
1556
+
1557
+ it('validates uri with a single regex scheme provided', (done) => {
1558
+
1559
+ const schema = Joi.string().uri({
1560
+ scheme: /https?/
1561
+ });
1562
+
1563
+ Helper.validate(schema, [
1564
+ ['http://google.com', true],
1565
+ ['https://google.com', true],
1566
+ ['ftp://google.com', false],
1567
+ ['file:/asdf', false],
1568
+ ['/path?query=value#hash', false]
1569
+ ], done);
1570
+ });
1571
+
1572
+ it('validates uri with multiple schemes provided', (done) => {
1573
+
1574
+ const schema = Joi.string().uri({
1575
+ scheme: [/https?/, 'ftp', 'file', 'git+http']
1576
+ });
1577
+
1578
+ Helper.validate(schema, [
1579
+ ['http://google.com', true],
1580
+ ['https://google.com', true],
1581
+ ['ftp://google.com', true],
1582
+ ['file:/asdf', true],
1583
+ ['git+http://github.com/hapijs/joi', true],
1584
+ ['/path?query=value#hash', false]
1585
+ ], done);
1586
+ });
1587
+
1588
+ it('validates uri with a friendly error message', (done) => {
1589
+
1590
+ const schema = { item: Joi.string().uri() };
1591
+
1592
+ Joi.compile(schema).validate({ item: 'something invalid' }, (err, value) => {
1593
+
1594
+ expect(err.message).to.contain('must be a valid uri');
1595
+ done();
1596
+ });
1597
+ });
1598
+
1599
+ it('validates uri with a custom scheme with a friendly error message', (done) => {
1600
+
1601
+ const schema = {
1602
+ item: Joi.string().uri({
1603
+ scheme: 'http'
1604
+ })
1605
+ };
1606
+
1607
+ Joi.compile(schema).validate({ item: 'something invalid' }, (err, value) => {
1608
+
1609
+ expect(err.message).to.contain('must be a valid uri with a scheme matching the http pattern');
1610
+ done();
1611
+ });
1612
+ });
1613
+
1614
+ it('validates uri with a custom array of schemes with a friendly error message', (done) => {
1615
+
1616
+ const schema = {
1617
+ item: Joi.string().uri({
1618
+ scheme: ['http', /https?/]
1619
+ })
1620
+ };
1621
+
1622
+ Joi.compile(schema).validate({ item: 'something invalid' }, (err, value) => {
1623
+
1624
+ expect(err.message).to.contain('must be a valid uri with a scheme matching the http|https? pattern');
1625
+ done();
1626
+ });
1627
+ });
1628
+
1629
+ it('validates uri treats scheme as optional', (done) => {
1630
+
1631
+ expect(() => {
1632
+
1633
+ Joi.string().uri({});
1634
+ }).to.not.throw();
1635
+
1636
+ done();
1637
+ });
1638
+
1639
+ it('validates uri requires uriOptions as an object with a friendly error message', (done) => {
1640
+
1641
+ expect(() => {
1642
+
1643
+ Joi.string().uri('http');
1644
+ }).to.throw(Error, 'options must be an object');
1645
+
1646
+ done();
1647
+ });
1648
+
1649
+ it('validates uri requires scheme to be a RegExp, String, or Array with a friendly error message', (done) => {
1650
+
1651
+ expect(() => {
1652
+
1653
+ Joi.string().uri({
1654
+ scheme: {}
1655
+ });
1656
+ }).to.throw(Error, 'scheme must be a RegExp, String, or Array');
1657
+
1658
+ done();
1659
+ });
1660
+
1661
+ it('validates uri requires scheme to not be an empty array', (done) => {
1662
+
1663
+ expect(() => {
1664
+
1665
+ Joi.string().uri({
1666
+ scheme: []
1667
+ });
1668
+ }).to.throw(Error, 'scheme must have at least 1 scheme specified');
1669
+
1670
+ done();
1671
+ });
1672
+
1673
+ it('validates uri requires scheme to be an Array of schemes to all be valid schemes with a friendly error message', (done) => {
1674
+
1675
+ expect(() => {
1676
+
1677
+ Joi.string().uri({
1678
+ scheme: [
1679
+ 'http',
1680
+ '~!@#$%^&*()_'
1681
+ ]
1682
+ });
1683
+ }).to.throw(Error, 'scheme at position 1 must be a valid scheme');
1684
+
1685
+ done();
1686
+ });
1687
+
1688
+ it('validates uri requires scheme to be an Array of schemes to be strings or RegExp', (done) => {
1689
+
1690
+ expect(() => {
1691
+
1692
+ Joi.string().uri({
1693
+ scheme: [
1694
+ 'http',
1695
+ {}
1696
+ ]
1697
+ });
1698
+ }).to.throw(Error, 'scheme at position 1 must be a RegExp or String');
1699
+
1700
+ done();
1701
+ });
1702
+
1703
+ it('validates uri requires scheme to be a valid String scheme with a friendly error message', (done) => {
1704
+
1705
+ expect(() => {
1706
+
1707
+ Joi.string().uri({
1708
+ scheme: '~!@#$%^&*()_'
1709
+ });
1710
+ }).to.throw(Error, 'scheme at position 0 must be a valid scheme');
1711
+
1712
+ done();
1713
+ });
1714
+
1715
+ it('validates relative uri', (done) => {
1716
+
1717
+ const schema = Joi.string().uri({ allowRelative: true });
1718
+ Helper.validate(schema, [
1719
+ ['foo://example.com:8042/over/there?name=ferret#nose', true],
1720
+ ['urn:example:animal:ferret:nose', true],
1721
+ ['ftp://ftp.is.co.za/rfc/rfc1808.txt', true],
1722
+ ['http://www.ietf.org/rfc/rfc2396.txt', true],
1723
+ ['ldap://[2001:db8::7]/c=GB?objectClass?one', true],
1724
+ ['mailto:John.Doe@example.com', true],
1725
+ ['news:comp.infosystems.www.servers.unix', true],
1726
+ ['tel:+1-816-555-1212', true],
1727
+ ['telnet://192.0.2.16:80/', true],
1728
+ ['urn:oasis:names:specification:docbook:dtd:xml:4.1.2', true],
1729
+ ['file:///example.txt', true],
1730
+ ['http://asdf:qw%20er@localhost:8000?asdf=12345&asda=fc%2F#bacon', true],
1731
+ ['http://asdf@localhost:8000', true],
1732
+ ['http://[v1.09azAZ-._~!$&\'()*+,;=:]', true],
1733
+ ['http://[a:b:c:d:e::1.2.3.4]', true],
1734
+ ['coap://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]', true],
1735
+ ['http://[1080:0:0:0:8:800:200C:417A]', true],
1736
+ ['http://127.0.0.1:8000/foo?bar', true],
1737
+ ['http://asdf:qwer@localhost:8000', true],
1738
+ ['http://user:pass%3A@localhost:80', true],
1739
+ ['http://localhost:123', true],
1740
+ ['https://localhost:123', true],
1741
+ ['file:///whatever', true],
1742
+ ['mailto:asdf@asdf.com', true],
1743
+ ['ftp://www.example.com', true],
1744
+ ['javascript:alert(\'hello\');', true], // eslint-disable-line no-script-url
1745
+ ['xmpp:isaacschlueter@jabber.org', true],
1746
+ ['f://some.host/path', true],
1747
+ ['http://localhost:18/asdf', true],
1748
+ ['http://localhost:42/asdf?qwer=zxcv', true],
1749
+ ['HTTP://www.example.com/', true],
1750
+ ['HTTP://www.example.com', true],
1751
+ ['http://www.ExAmPlE.com/', true],
1752
+ ['http://user:pw@www.ExAmPlE.com/', true],
1753
+ ['http://USER:PW@www.ExAmPlE.com/', true],
1754
+ ['http://user@www.example.com/', true],
1755
+ ['http://user%3Apw@www.example.com/', true],
1756
+ ['http://x.com/path?that%27s#all,%20folks', true],
1757
+ ['HTTP://X.COM/Y', true],
1758
+ ['http://www.narwhaljs.org/blog/categories?id=news', true],
1759
+ ['http://mt0.google.com/vt/lyrs=m@114&hl=en&src=api&x=2&y=2&z=3&s=', true],
1760
+ ['http://mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', true],
1761
+ ['http://user:pass@mt0.google.com/vt/lyrs=m@114???&hl=en&src=api&x=2&y=2&z=3&s=', true],
1762
+ ['http://_jabber._tcp.google.com:80/test', true],
1763
+ ['http://user:pass@_jabber._tcp.google.com:80/test', true],
1764
+ ['http://[fe80::1]/a/b?a=b#abc', true],
1765
+ ['http://user:password@[3ffe:2a00:100:7031::1]:8080', true],
1766
+ ['coap://[1080:0:0:0:8:800:200C:417A]:61616/', true],
1767
+ ['git+http://github.com/joyent/node.git', true],
1768
+ ['http://bucket_name.s3.amazonaws.com/image.jpg', true],
1769
+ ['dot.test://foo/bar', true],
1770
+ ['svn+ssh://foo/bar', true],
1771
+ ['dash-test://foo/bar', true],
1772
+ ['xmpp:isaacschlueter@jabber.org', true],
1773
+ ['http://atpass:foo%40bar@127.0.0.1:8080/path?search=foo#bar', true],
1774
+ ['javascript:alert(\'hello\');', true], // eslint-disable-line no-script-url
1775
+ ['file://localhost/etc/node/', true],
1776
+ ['file:///etc/node/', true],
1777
+ ['http://USER:PW@www.ExAmPlE.com/', true],
1778
+ ['mailto:local1@domain1?query1', true],
1779
+ ['http://example/a/b?c/../d', true],
1780
+ ['http://example/x%2Fabc', true],
1781
+ ['http://a/b/c/d;p=1/g;x=1/y', true],
1782
+ ['http://a/b/c/g#s/../x', true],
1783
+ ['http://a/b/c/.foo', true],
1784
+ ['http://example.com/b//c//d;p?q#blarg', true],
1785
+ ['g:h', true],
1786
+ ['http://a/b/c/g', true],
1787
+ ['http://a/b/c/g/', true],
1788
+ ['http://a/g', true],
1789
+ ['http://g', true],
1790
+ ['http://a/b/c/d;p?y', true],
1791
+ ['http://a/b/c/g?y', true],
1792
+ ['http://a/b/c/d;p?q#s', true],
1793
+ ['http://a/b/c/g#s', true],
1794
+ ['http://a/b/c/g?y#s', true],
1795
+ ['http://a/b/c/;x', true],
1796
+ ['http://a/b/c/g;x', true],
1797
+ ['http://a/b/c/g;x?y#s', true],
1798
+ ['http://a/b/c/d;p?q', true],
1799
+ ['http://a/b/c/', true],
1800
+ ['http://a/b/', true],
1801
+ ['http://a/b/g', true],
1802
+ ['http://a/', true],
1803
+ ['http://a/g', true],
1804
+ ['http://a/g', true],
1805
+ ['file:/asda', true],
1806
+ ['qwerty', true],
1807
+ ['invalid uri', false],
1808
+ ['1http://google.com', false],
1809
+ ['http://testdomain`,.<>/?\'";{}][++\\|~!@#$%^&*().org', false],
1810
+ ['', false],
1811
+ ['(╯°□°)╯︵ ┻━┻', false],
1812
+ ['one/two/three?value=abc&value2=123#david-rules', true],
1813
+ ['//username:password@test.example.com/one/two/three?value=abc&value2=123#david-rules', true],
1814
+ ['http://a\r" \t\n<\'b:b@c\r\nd/e?f', false],
1815
+ ['/absolute', true]
1816
+ ], done);
1817
+ });
1818
+ });
1819
+
1820
+ describe('validate()', () => {
1821
+
1822
+ it('should, by default, allow undefined, deny empty string', (done) => {
1823
+
1824
+ Helper.validate(Joi.string(), [
1825
+ [undefined, true],
1826
+ ['', false]
1827
+ ], done);
1828
+ });
1829
+
1830
+ it('should, when .required(), deny undefined, deny empty string', (done) => {
1831
+
1832
+ Helper.validate(Joi.string().required(), [
1833
+ [undefined, false],
1834
+ ['', false]
1835
+ ], done);
1836
+ });
1837
+
1838
+ it('should, when .required(), print a friend error message for an empty string', (done) => {
1839
+
1840
+ const schema = Joi.string().required();
1841
+ Joi.compile(schema).validate('', (err, value) => {
1842
+
1843
+ expect(err.message).to.contain('be empty');
1844
+ done();
1845
+ });
1846
+ });
1847
+
1848
+ it('should, when .required(), print a friendly error message for trimmed whitespace', (done) => {
1849
+
1850
+ const schema = Joi.string().trim().required();
1851
+
1852
+ Joi.compile(schema).validate(' ', (err) => {
1853
+
1854
+ expect(err.message).to.contain('be empty');
1855
+ done();
1856
+ });
1857
+ });
1858
+
1859
+ it('should, when .required(), validate non-empty strings', (done) => {
1860
+
1861
+ const schema = Joi.string().required();
1862
+ Helper.validate(schema, [
1863
+ ['test', true],
1864
+ ['0', true],
1865
+ [null, false]
1866
+ ], done);
1867
+ });
1868
+
1869
+ it('validates invalid values', (done) => {
1870
+
1871
+ const schema = Joi.string().invalid('a', 'b', 'c');
1872
+ Helper.validate(schema, [
1873
+ ['x', true],
1874
+ ['a', false],
1875
+ ['c', false]
1876
+ ], done);
1877
+ });
1878
+
1879
+ it('should invalidate invalid values', (done) => {
1880
+
1881
+ const schema = Joi.string().valid('a', 'b', 'c');
1882
+ Helper.validate(schema, [
1883
+ ['x', false],
1884
+ ['a', true],
1885
+ ['c', true]
1886
+ ], done);
1887
+ });
1888
+
1889
+ it('validates array arguments correctly', (done) => {
1890
+
1891
+ const schema = Joi.string().valid(['a', 'b', 'c']);
1892
+ Helper.validate(schema, [
1893
+ ['x', false],
1894
+ ['a', true],
1895
+ ['c', true]
1896
+ ], done);
1897
+ });
1898
+
1899
+ it('validates minimum length when min is used', (done) => {
1900
+
1901
+ const schema = Joi.string().min(3);
1902
+ Helper.validate(schema, [
1903
+ ['test', true],
1904
+ ['0', false],
1905
+ [null, false]
1906
+ ], done);
1907
+ });
1908
+
1909
+ it('validates minimum length when min is 0', (done) => {
1910
+
1911
+ const schema = Joi.string().min(0).required();
1912
+ Helper.validate(schema, [
1913
+ ['0', true],
1914
+ [null, false],
1915
+ [undefined, false]
1916
+ ], done);
1917
+ });
1918
+
1919
+ it('should return false with minimum length and a null value passed in', (done) => {
1920
+
1921
+ const schema = Joi.string().min(3);
1922
+ Helper.validate(schema, [
1923
+ [null, false]
1924
+ ], done);
1925
+ });
1926
+
1927
+ it('null allowed overrides min length requirement', (done) => {
1928
+
1929
+ const schema = Joi.string().min(3).allow(null);
1930
+ Helper.validate(schema, [
1931
+ [null, true]
1932
+ ], done);
1933
+ });
1934
+
1935
+ it('validates maximum length when max is used', (done) => {
1936
+
1937
+ const schema = Joi.string().max(3);
1938
+ Helper.validate(schema, [
1939
+ ['test', false],
1940
+ ['0', true],
1941
+ [null, false]
1942
+ ], done);
1943
+ });
1944
+
1945
+ it('should return true with max and not required when value is undefined', (done) => {
1946
+
1947
+ const schema = Joi.string().max(3);
1948
+ Helper.validate(schema, [
1949
+ [undefined, true]
1950
+ ], done);
1951
+ });
1952
+
1953
+ it('validates length requirements', (done) => {
1954
+
1955
+ const schema = Joi.string().length(3);
1956
+ Helper.validate(schema, [
1957
+ ['test', false],
1958
+ ['0', false],
1959
+ [null, false],
1960
+ ['abc', true]
1961
+ ], done);
1962
+ });
1963
+
1964
+ it('validates regex', (done) => {
1965
+
1966
+ const schema = Joi.string().regex(/^[0-9][-][a-z]+$/);
1967
+ Helper.validate(schema, [
1968
+ ['van', false],
1969
+ ['0-www', true]
1970
+ ], done);
1971
+ });
1972
+
1973
+ it('validates regex (ignoring global flag)', (done) => {
1974
+
1975
+ const schema = Joi.string().regex(/a/g);
1976
+ Helper.validate(schema, [
1977
+ ['ab', true],
1978
+ ['ac', true]
1979
+ ], done);
1980
+ });
1981
+
1982
+ it('validates token', (done) => {
1983
+
1984
+ const schema = Joi.string().token();
1985
+ Helper.validate(schema, [
1986
+ ['w0rld_of_w4lm4rtl4bs', true],
1987
+ ['w0rld of_w4lm4rtl4bs', false],
1988
+ ['abcd#f?h1j orly?', false]
1989
+ ], done);
1990
+ });
1991
+
1992
+ it('validates alphanum', (done) => {
1993
+
1994
+ const schema = Joi.string().alphanum();
1995
+ Helper.validate(schema, [
1996
+ ['w0rld of w4lm4rtl4bs', false],
1997
+ ['w0rldofw4lm4rtl4bs', true],
1998
+ ['abcd#f?h1j orly?', false]
1999
+ ], done);
2000
+ });
2001
+
2002
+ it('should return false for denied value', (done) => {
2003
+
2004
+ const text = Joi.string().invalid('joi');
2005
+ text.validate('joi', (err, value) => {
2006
+
2007
+ expect(err).to.exist();
2008
+ done();
2009
+ });
2010
+ });
2011
+
2012
+ it('should return true for allowed value', (done) => {
2013
+
2014
+ const text = Joi.string().allow('hapi');
2015
+ text.validate('result', (err, value) => {
2016
+
2017
+ expect(err).to.not.exist();
2018
+ done();
2019
+ });
2020
+ });
2021
+
2022
+ it('validates with one validator (min)', (done) => {
2023
+
2024
+ const text = Joi.string().min(3);
2025
+ text.validate('joi', (err, value) => {
2026
+
2027
+ expect(err).to.not.exist();
2028
+ done();
2029
+ });
2030
+ });
2031
+
2032
+ it('validates with two validators (min, required)', (done) => {
2033
+
2034
+ const text = Joi.string().min(3).required();
2035
+ text.validate('joi', (err, value) => {
2036
+
2037
+ expect(err).to.not.exist();
2038
+
2039
+ text.validate('', (err2, value2) => {
2040
+
2041
+ expect(err2).to.exist();
2042
+ done();
2043
+ });
2044
+ });
2045
+ });
2046
+
2047
+ it('validates null with allow(null)', (done) => {
2048
+
2049
+ Helper.validate(Joi.string().allow(null), [
2050
+ [null, true]
2051
+ ], done);
2052
+ });
2053
+
2054
+ it('validates "" (empty string) with allow(\'\')', (done) => {
2055
+
2056
+ Helper.validate(Joi.string().allow(''), [
2057
+ ['', true],
2058
+ ['', true]
2059
+ ], done);
2060
+ });
2061
+
2062
+ it('validates combination of required and min', (done) => {
2063
+
2064
+ const rule = Joi.string().required().min(3);
2065
+ Helper.validate(rule, [
2066
+ ['x', false],
2067
+ ['123', true],
2068
+ ['', false],
2069
+ [null, false]
2070
+ ], done);
2071
+ });
2072
+
2073
+ it('validates combination of required and max', (done) => {
2074
+
2075
+ const rule = Joi.string().required().max(3);
2076
+ Helper.validate(rule, [
2077
+ ['x', true],
2078
+ ['123', true],
2079
+ ['1234', false],
2080
+ ['', false],
2081
+ [null, false]
2082
+ ], done);
2083
+ });
2084
+
2085
+ it('validates combination of allow(\'\') and min', (done) => {
2086
+
2087
+ const rule = Joi.string().allow('').min(3);
2088
+ Helper.validate(rule, [
2089
+ ['x', false],
2090
+ ['123', true],
2091
+ ['1234', true],
2092
+ ['', true],
2093
+ [null, false]
2094
+ ], done);
2095
+ });
2096
+
2097
+ it('validates combination of allow(\'\') and max', (done) => {
2098
+
2099
+ const rule = Joi.string().allow('').max(3);
2100
+ Helper.validate(rule, [
2101
+ ['x', true],
2102
+ ['123', true],
2103
+ ['1234', false],
2104
+ ['', true],
2105
+ [null, false]
2106
+ ], done);
2107
+ });
2108
+
2109
+ it('validates combination of null allowed and max', (done) => {
2110
+
2111
+ const rule = Joi.string().allow(null).max(3);
2112
+ Helper.validate(rule, [
2113
+ ['x', true],
2114
+ ['123', true],
2115
+ ['1234', false],
2116
+ ['', false],
2117
+ [null, true]
2118
+ ], done);
2119
+ });
2120
+
2121
+ it('validates combination of min and max', (done) => {
2122
+
2123
+ const rule = Joi.string().min(2).max(3);
2124
+ Helper.validate(rule, [
2125
+ ['x', false],
2126
+ ['123', true],
2127
+ ['1234', false],
2128
+ ['12', true],
2129
+ ['', false],
2130
+ [null, false]
2131
+ ], done);
2132
+ });
2133
+
2134
+ it('validates combination of min, max, and allow(\'\')', (done) => {
2135
+
2136
+ const rule = Joi.string().min(2).max(3).allow('');
2137
+ Helper.validate(rule, [
2138
+ ['x', false],
2139
+ ['123', true],
2140
+ ['1234', false],
2141
+ ['12', true],
2142
+ ['', true],
2143
+ [null, false]
2144
+ ], done);
2145
+ });
2146
+
2147
+ it('validates combination of min, max, and required', (done) => {
2148
+
2149
+ const rule = Joi.string().min(2).max(3).required();
2150
+ Helper.validate(rule, [
2151
+ ['x', false],
2152
+ ['123', true],
2153
+ ['1234', false],
2154
+ ['12', true],
2155
+ ['', false],
2156
+ [null, false]
2157
+ ], done);
2158
+ });
2159
+
2160
+ it('validates combination of min, max, and regex', (done) => {
2161
+
2162
+ const rule = Joi.string().min(2).max(3).regex(/^a/);
2163
+ Helper.validate(rule, [
2164
+ ['x', false],
2165
+ ['123', false],
2166
+ ['1234', false],
2167
+ ['12', false],
2168
+ ['ab', true],
2169
+ ['abc', true],
2170
+ ['abcd', false],
2171
+ ['', false],
2172
+ [null, false]
2173
+ ], done);
2174
+ });
2175
+
2176
+ it('validates combination of min, max, regex, and allow(\'\')', (done) => {
2177
+
2178
+ const rule = Joi.string().min(2).max(3).regex(/^a/).allow('');
2179
+ Helper.validate(rule, [
2180
+ ['x', false],
2181
+ ['123', false],
2182
+ ['1234', false],
2183
+ ['12', false],
2184
+ ['ab', true],
2185
+ ['abc', true],
2186
+ ['abcd', false],
2187
+ ['', true],
2188
+ [null, false]
2189
+ ], done);
2190
+ });
2191
+
2192
+ it('validates combination of min, max, regex, and required', (done) => {
2193
+
2194
+ const rule = Joi.string().min(2).max(3).regex(/^a/).required();
2195
+ Helper.validate(rule, [
2196
+ ['x', false],
2197
+ ['123', false],
2198
+ ['1234', false],
2199
+ ['12', false],
2200
+ ['ab', true],
2201
+ ['abc', true],
2202
+ ['abcd', false],
2203
+ ['', false],
2204
+ [null, false]
2205
+ ], done);
2206
+ });
2207
+
2208
+ it('validates combination of min, max, and alphanum', (done) => {
2209
+
2210
+ const rule = Joi.string().min(2).max(3).alphanum();
2211
+ Helper.validate(rule, [
2212
+ ['x', false],
2213
+ ['123', true],
2214
+ ['1234', false],
2215
+ ['12', true],
2216
+ ['ab', true],
2217
+ ['abc', true],
2218
+ ['abcd', false],
2219
+ ['*ab', false],
2220
+ ['', false],
2221
+ [null, false]
2222
+ ], done);
2223
+ });
2224
+
2225
+ it('validates combination of min, max, alphanum, and allow(\'\')', (done) => {
2226
+
2227
+ const rule = Joi.string().min(2).max(3).alphanum().allow('');
2228
+ Helper.validate(rule, [
2229
+ ['x', false],
2230
+ ['123', true],
2231
+ ['1234', false],
2232
+ ['12', true],
2233
+ ['ab', true],
2234
+ ['abc', true],
2235
+ ['abcd', false],
2236
+ ['*ab', false],
2237
+ ['', true],
2238
+ [null, false]
2239
+ ], done);
2240
+ });
2241
+
2242
+ it('validates combination of min, max, alphanum, and required', (done) => {
2243
+
2244
+ const rule = Joi.string().min(2).max(3).alphanum().required();
2245
+ Helper.validate(rule, [
2246
+ ['x', false],
2247
+ ['123', true],
2248
+ ['1234', false],
2249
+ ['12', true],
2250
+ ['ab', true],
2251
+ ['abc', true],
2252
+ ['abcd', false],
2253
+ ['*ab', false],
2254
+ ['', false],
2255
+ [null, false]
2256
+ ], done);
2257
+ });
2258
+
2259
+ it('validates combination of min, max, alphanum, and regex', (done) => {
2260
+
2261
+ const rule = Joi.string().min(2).max(3).alphanum().regex(/^a/);
2262
+ Helper.validate(rule, [
2263
+ ['x', false],
2264
+ ['123', false],
2265
+ ['1234', false],
2266
+ ['12', false],
2267
+ ['ab', true],
2268
+ ['abc', true],
2269
+ ['a2c', true],
2270
+ ['abcd', false],
2271
+ ['*ab', false],
2272
+ ['', false],
2273
+ [null, false]
2274
+ ], done);
2275
+ });
2276
+
2277
+ it('validates combination of min, max, alphanum, required, and regex', (done) => {
2278
+
2279
+ const rule = Joi.string().min(2).max(3).alphanum().required().regex(/^a/);
2280
+ Helper.validate(rule, [
2281
+ ['x', false],
2282
+ ['123', false],
2283
+ ['1234', false],
2284
+ ['12', false],
2285
+ ['ab', true],
2286
+ ['abc', true],
2287
+ ['a2c', true],
2288
+ ['abcd', false],
2289
+ ['*ab', false],
2290
+ ['', false],
2291
+ [null, false]
2292
+ ], done);
2293
+ });
2294
+
2295
+ it('validates combination of min, max, alphanum, allow(\'\'), and regex', (done) => {
2296
+
2297
+ const rule = Joi.string().min(2).max(3).alphanum().allow('').regex(/^a/);
2298
+ Helper.validate(rule, [
2299
+ ['x', false],
2300
+ ['123', false],
2301
+ ['1234', false],
2302
+ ['12', false],
2303
+ ['ab', true],
2304
+ ['abc', true],
2305
+ ['a2c', true],
2306
+ ['abcd', false],
2307
+ ['*ab', false],
2308
+ ['', true],
2309
+ [null, false]
2310
+ ], done);
2311
+ });
2312
+
2313
+ it('validates combination of email and min', (done) => {
2314
+
2315
+ const rule = Joi.string().email().min(8);
2316
+ Helper.validate(rule, [
2317
+ ['x@x.com', false],
2318
+ ['123@x.com', true],
2319
+ ['', false],
2320
+ [null, false]
2321
+ ], done);
2322
+ });
2323
+
2324
+ it('validates combination of email, min, and max', (done) => {
2325
+
2326
+ const rule = Joi.string().email().min(8).max(10);
2327
+ Helper.validate(rule, [
2328
+ ['x@x.com', false],
2329
+ ['123@x.com', true],
2330
+ ['1234@x.com', true],
2331
+ ['12345@x.com', false],
2332
+ ['', false],
2333
+ [null, false]
2334
+ ], done);
2335
+ });
2336
+
2337
+ it('validates combination of email, min, max, and invalid', (done) => {
2338
+
2339
+ const rule = Joi.string().email().min(8).max(10).invalid('123@x.com');
2340
+ Helper.validate(rule, [
2341
+ ['x@x.com', false],
2342
+ ['123@x.com', false],
2343
+ ['1234@x.com', true],
2344
+ ['12345@x.com', false],
2345
+ ['', false],
2346
+ [null, false]
2347
+ ], done);
2348
+ });
2349
+
2350
+ it('validates combination of email, min, max, and allow', (done) => {
2351
+
2352
+ const rule = Joi.string().email().min(8).max(10).allow('x@x.com');
2353
+ Helper.validate(rule, [
2354
+ ['x@x.com', true],
2355
+ ['123@x.com', true],
2356
+ ['1234@x.com', true],
2357
+ ['12345@x.com', false],
2358
+ ['', false],
2359
+ [null, false]
2360
+ ], done);
2361
+ });
2362
+
2363
+ it('validates combination of email, min, max, allow, and invalid', (done) => {
2364
+
2365
+ const rule = Joi.string().email().min(8).max(10).allow('x@x.com').invalid('123@x.com');
2366
+ Helper.validate(rule, [
2367
+ ['x@x.com', true],
2368
+ ['123@x.com', false],
2369
+ ['1234@x.com', true],
2370
+ ['12345@x.com', false],
2371
+ ['', false],
2372
+ [null, false]
2373
+ ], done);
2374
+ });
2375
+
2376
+ it('validates combination of email, min, max, allow, invalid, and allow(\'\')', (done) => {
2377
+
2378
+ const rule = Joi.string().email().min(8).max(10).allow('x@x.com').invalid('123@x.com').allow('');
2379
+ Helper.validate(rule, [
2380
+ ['x@x.com', true],
2381
+ ['123@x.com', false],
2382
+ ['1234@x.com', true],
2383
+ ['12345@x.com', false],
2384
+ ['', true],
2385
+ [null, false]
2386
+ ], done);
2387
+ });
2388
+
2389
+ it('validates combination of email, min, max, allow, and allow(\'\')', (done) => {
2390
+
2391
+ const rule = Joi.string().email().min(8).max(10).allow('x@x.com').allow('');
2392
+ Helper.validate(rule, [
2393
+ ['x@x.com', true],
2394
+ ['123@x.com', true],
2395
+ ['1234@x.com', true],
2396
+ ['12345@x.com', false],
2397
+ ['', true],
2398
+ [null, false]
2399
+ ], done);
2400
+ });
2401
+
2402
+ it('validates combination of email, min, max, allow, invalid, and regex', (done) => {
2403
+
2404
+ const rule = Joi.string().email().min(8).max(10).allow('x@x.com').invalid('123@x.com').regex(/^1/);
2405
+ Helper.validate(rule, [
2406
+ ['x@x.com', true],
2407
+ ['123@x.com', false],
2408
+ ['1234@x.com', true],
2409
+ ['12345@x.com', false],
2410
+ ['', false],
2411
+ [null, false]
2412
+ ], done);
2413
+ });
2414
+
2415
+ it('validates combination of email, min, max, allow, invalid, regex, and allow(\'\')', (done) => {
2416
+
2417
+ const rule = Joi.string().email().min(8).max(10).allow('x@x.com').invalid('123@x.com').regex(/^1/).allow('');
2418
+ Helper.validate(rule, [
2419
+ ['x@x.com', true],
2420
+ ['123@x.com', false],
2421
+ ['1234@x.com', true],
2422
+ ['12345@x.com', false],
2423
+ ['', true],
2424
+ [null, false]
2425
+ ], done);
2426
+ });
2427
+
2428
+ it('validates combination of email, min, max, and allow(\'\')', (done) => {
2429
+
2430
+ const rule = Joi.string().email().min(8).max(10).allow('');
2431
+ Helper.validate(rule, [
2432
+ ['x@x.com', false],
2433
+ ['123@x.com', true],
2434
+ ['1234@x.com', true],
2435
+ ['12345@x.com', false],
2436
+ ['', true],
2437
+ [null, false]
2438
+ ], done);
2439
+ });
2440
+
2441
+ it('validates combination of email, min, max, and regex', (done) => {
2442
+
2443
+ const rule = Joi.string().email().min(8).max(10).regex(/^1234/);
2444
+ Helper.validate(rule, [
2445
+ ['x@x.com', false],
2446
+ ['123@x.com', false],
2447
+ ['1234@x.com', true],
2448
+ ['12345@x.com', false],
2449
+ ['', false],
2450
+ [null, false]
2451
+ ], done);
2452
+ });
2453
+
2454
+ it('validates combination of email, min, max, regex, and allow(\'\')', (done) => {
2455
+
2456
+ const rule = Joi.string().email().min(8).max(10).regex(/^1234/).allow('');
2457
+ Helper.validate(rule, [
2458
+ ['x@x.com', false],
2459
+ ['123@x.com', false],
2460
+ ['1234@x.com', true],
2461
+ ['12345@x.com', false],
2462
+ ['', true],
2463
+ [null, false]
2464
+ ], done);
2465
+ });
2466
+
2467
+ it('validates combination of email, min, max, regex, and required', (done) => {
2468
+
2469
+ const rule = Joi.string().email().min(8).max(10).regex(/^1234/).required();
2470
+ Helper.validate(rule, [
2471
+ ['x@x.com', false],
2472
+ ['123@x.com', false],
2473
+ ['1234@x.com', true],
2474
+ ['12345@x.com', false],
2475
+ ['', false],
2476
+ [null, false]
2477
+ ], done);
2478
+ });
2479
+
2480
+ it('validates isoDate', (done) => {
2481
+
2482
+ Helper.validate(Joi.string().isoDate(), [
2483
+ ['2013-06-07T14:21:46.295Z', true],
2484
+ ['2013-06-07T14:21:46.295Z0', false],
2485
+ ['2013-06-07T14:21:46.295+07:00', true],
2486
+ ['2013-06-07T14:21:46.295+07:000', false],
2487
+ ['2013-06-07T14:21:46.295-07:00', true],
2488
+ ['2013-06-07T14:21:46Z', true],
2489
+ ['2013-06-07T14:21:46Z0', false],
2490
+ ['2013-06-07T14:21:46+07:00', true],
2491
+ ['2013-06-07T14:21:46+07:000', false],
2492
+ ['2013-06-07T14:21:46-07:00', true],
2493
+ ['2013-06-07T14:21Z', true],
2494
+ ['2013-06-07T14:21+07:00', true],
2495
+ ['2013-06-07T14:21+07:000', false],
2496
+ ['2013-06-07T14:21-07:00', true],
2497
+ ['2013-06-07T14:21Z+7:00', false],
2498
+ ['2013-06-07', true],
2499
+ ['2013-06-07T', false],
2500
+ ['2013-06-07T14:21', true],
2501
+ ['1-1-2013', false],
2502
+ ['2013-06-07T14.2334,4', true],
2503
+ ['2013-06-07T14,23:34', false],
2504
+ ['2013-06-07T24', false],
2505
+ ['2013-06-07T24:00', true],
2506
+ ['2013-06-07T24:21', false],
2507
+ ['2013-06-07 142146.295', true],
2508
+ ['2013-06-07 146946.295', false],
2509
+ ['2013-06-07 1421,44', true],
2510
+ ['2013-W23', true],
2511
+ ['2013-W23-1', true],
2512
+ ['2013-W2311', false],
2513
+ ['2013-W231', true],
2514
+ ['2013-M231', false],
2515
+ ['2013-W23-1T14:21', true],
2516
+ ['2013-W23-1T14:21:', false],
2517
+ ['2013-W23-1T14:21:46+07:00', true],
2518
+ ['2013-W23-1T14:21:46+07:000', false],
2519
+ ['2013-W23-1T14:21:46-07:00', true],
2520
+ ['2013-184', true],
2521
+ ['2013-1841', false]
2522
+ ], done);
2523
+ });
2524
+
2525
+ it('validates isoDate with a friendly error message', (done) => {
2526
+
2527
+ const schema = { item: Joi.string().isoDate() };
2528
+ Joi.compile(schema).validate({ item: 'something' }, (err, value) => {
2529
+
2530
+ expect(err.message).to.contain('must be a valid ISO 8601 date');
2531
+ done();
2532
+ });
2533
+ });
2534
+
2535
+ it('validates combination of isoDate and min', (done) => {
2536
+
2537
+ const rule = Joi.string().isoDate().min(23);
2538
+ Helper.validate(rule, [
2539
+ ['2013-06-07T14:21:46.295Z', true],
2540
+ ['2013-06-07T14:21:46.295Z0', false],
2541
+ ['2013-06-07T14:21:46.295+07:00', true],
2542
+ ['2013-06-07T14:21:46.295+07:000', false],
2543
+ ['2013-06-07T14:21:46.295-07:00', true],
2544
+ ['2013-06-07T14:21:46Z', false],
2545
+ ['2013-06-07T14:21:46Z0', false],
2546
+ ['2013-06-07T14:21:46+07:00', true],
2547
+ ['2013-06-07T14:21:46-07:00', true],
2548
+ ['2013-06-07T14:21Z', false],
2549
+ ['2013-06-07T14:21+07:00', false],
2550
+ ['2013-06-07T14:21+07:000', false],
2551
+ ['2013-06-07T14:21-07:00', false],
2552
+ ['2013-06-07T14:21Z+7:00', false],
2553
+ ['2013-06-07', false],
2554
+ ['2013-06-07T', false],
2555
+ ['2013-06-07T14:21', false],
2556
+ ['1-1-2013', false],
2557
+ ['', false],
2558
+ [null, false]
2559
+ ], done);
2560
+ });
2561
+
2562
+ it('validates combination of isoDate, min and max', (done) => {
2563
+
2564
+ const rule = Joi.string().isoDate().min(17).max(23);
2565
+ Helper.validate(rule, [
2566
+ ['2013-06-07T14:21:46.295Z', false],
2567
+ ['2013-06-07T14:21:46.295Z0', false],
2568
+ ['2013-06-07T14:21:46.295+07:00', false],
2569
+ ['2013-06-07T14:21:46.295+07:000', false],
2570
+ ['2013-06-07T14:21:46.295-07:00', false],
2571
+ ['2013-06-07T14:21:46Z', true],
2572
+ ['2013-06-07T14:21:46Z0', false],
2573
+ ['2013-06-07T14:21:46+07:00', false],
2574
+ ['2013-06-07T14:21:46-07:00', false],
2575
+ ['2013-06-07T14:21Z', true],
2576
+ ['2013-06-07T14:21+07:00', true],
2577
+ ['2013-06-07T14:21+07:000', false],
2578
+ ['2013-06-07T14:21-07:00', true],
2579
+ ['2013-06-07T14:21Z+7:00', false],
2580
+ ['2013-06-07', false],
2581
+ ['2013-06-07T', false],
2582
+ ['2013-06-07T14:21', false],
2583
+ ['1-1-2013', false],
2584
+ ['', false],
2585
+ [null, false]
2586
+ ], done);
2587
+ });
2588
+
2589
+ it('validates combination of isoDate, min, max and invalid', (done) => {
2590
+
2591
+ const rule = Joi.string().isoDate().min(17).max(23).invalid('2013-06-07T14:21+07:00');
2592
+ Helper.validate(rule, [
2593
+ ['2013-06-07T14:21:46.295Z', false],
2594
+ ['2013-06-07T14:21:46.295Z0', false],
2595
+ ['2013-06-07T14:21:46.295+07:00', false],
2596
+ ['2013-06-07T14:21:46.295+07:000', false],
2597
+ ['2013-06-07T14:21:46.295-07:00', false],
2598
+ ['2013-06-07T14:21:46Z', true],
2599
+ ['2013-06-07T14:21:46Z0', false],
2600
+ ['2013-06-07T14:21:46+07:00', false],
2601
+ ['2013-06-07T14:21:46-07:00', false],
2602
+ ['2013-06-07T14:21Z', true],
2603
+ ['2013-06-07T14:21+07:00', false],
2604
+ ['2013-06-07T14:21+07:000', false],
2605
+ ['2013-06-07T14:21-07:00', true],
2606
+ ['2013-06-07T14:21Z+7:00', false],
2607
+ ['2013-06-07', false],
2608
+ ['2013-06-07T', false],
2609
+ ['2013-06-07T14:21', false],
2610
+ ['1-1-2013', false],
2611
+ ['', false],
2612
+ [null, false]
2613
+ ], done);
2614
+ });
2615
+
2616
+ it('validates combination of isoDate, min, max and allow', (done) => {
2617
+
2618
+ const rule = Joi.string().isoDate().min(17).max(23).allow('2013-06-07T14:21:46.295+07:00');
2619
+ Helper.validate(rule, [
2620
+ ['2013-06-07T14:21:46.295Z', false],
2621
+ ['2013-06-07T14:21:46.295Z0', false],
2622
+ ['2013-06-07T14:21:46.295+07:00', true],
2623
+ ['2013-06-07T14:21:46.295+07:000', false],
2624
+ ['2013-06-07T14:21:46.295-07:00', false],
2625
+ ['2013-06-07T14:21:46Z', true],
2626
+ ['2013-06-07T14:21:46Z0', false],
2627
+ ['2013-06-07T14:21:46+07:00', false],
2628
+ ['2013-06-07T14:21:46-07:00', false],
2629
+ ['2013-06-07T14:21Z', true],
2630
+ ['2013-06-07T14:21+07:00', true],
2631
+ ['2013-06-07T14:21+07:000', false],
2632
+ ['2013-06-07T14:21-07:00', true],
2633
+ ['2013-06-07T14:21Z+7:00', false],
2634
+ ['2013-06-07', false],
2635
+ ['2013-06-07T', false],
2636
+ ['2013-06-07T14:21', false],
2637
+ ['1-1-2013', false],
2638
+ ['', false],
2639
+ [null, false]
2640
+ ], done);
2641
+ });
2642
+
2643
+ it('validates combination of isoDate, min, max, allow and invalid', (done) => {
2644
+
2645
+ const rule = Joi.string().isoDate().min(17).max(23).allow('2013-06-07T14:21:46.295+07:00').invalid('2013-06-07T14:21+07:00');
2646
+ Helper.validate(rule, [
2647
+ ['2013-06-07T14:21:46.295Z', false],
2648
+ ['2013-06-07T14:21:46.295Z0', false],
2649
+ ['2013-06-07T14:21:46.295+07:00', true],
2650
+ ['2013-06-07T14:21:46.295+07:000', false],
2651
+ ['2013-06-07T14:21:46.295-07:00', false],
2652
+ ['2013-06-07T14:21:46Z', true],
2653
+ ['2013-06-07T14:21:46Z0', false],
2654
+ ['2013-06-07T14:21:46+07:00', false],
2655
+ ['2013-06-07T14:21:46-07:00', false],
2656
+ ['2013-06-07T14:21Z', true],
2657
+ ['2013-06-07T14:21+07:00', false],
2658
+ ['2013-06-07T14:21+07:000', false],
2659
+ ['2013-06-07T14:21-07:00', true],
2660
+ ['2013-06-07T14:21Z+7:00', false],
2661
+ ['2013-06-07', false],
2662
+ ['2013-06-07T', false],
2663
+ ['2013-06-07T14:21', false],
2664
+ ['1-1-2013', false],
2665
+ ['', false],
2666
+ [null, false]
2667
+ ], done);
2668
+ });
2669
+
2670
+ it('validates combination of isoDate, min, max, allow, invalid and allow(\'\')', (done) => {
2671
+
2672
+ const rule = Joi.string().isoDate().min(17).max(23).allow('2013-06-07T14:21:46.295+07:00').invalid('2013-06-07T14:21+07:00').allow('');
2673
+ Helper.validate(rule, [
2674
+ ['2013-06-07T14:21:46.295Z', false],
2675
+ ['2013-06-07T14:21:46.295Z0', false],
2676
+ ['2013-06-07T14:21:46.295+07:00', true],
2677
+ ['2013-06-07T14:21:46.295+07:000', false],
2678
+ ['2013-06-07T14:21:46.295-07:00', false],
2679
+ ['2013-06-07T14:21:46Z', true],
2680
+ ['2013-06-07T14:21:46Z0', false],
2681
+ ['2013-06-07T14:21:46+07:00', false],
2682
+ ['2013-06-07T14:21:46-07:00', false],
2683
+ ['2013-06-07T14:21Z', true],
2684
+ ['2013-06-07T14:21+07:00', false],
2685
+ ['2013-06-07T14:21+07:000', false],
2686
+ ['2013-06-07T14:21-07:00', true],
2687
+ ['2013-06-07T14:21Z+7:00', false],
2688
+ ['2013-06-07', false],
2689
+ ['2013-06-07T', false],
2690
+ ['2013-06-07T14:21', false],
2691
+ ['1-1-2013', false],
2692
+ ['', true],
2693
+ [null, false]
2694
+ ], done);
2695
+ });
2696
+
2697
+ it('validates combination of isoDate, min, max, allow, invalid and allow(\'\')', (done) => {
2698
+
2699
+ const rule = Joi.string().isoDate().min(17).max(23).allow('2013-06-07T14:21:46.295+07:00').allow('');
2700
+ Helper.validate(rule, [
2701
+ ['2013-06-07T14:21:46.295Z', false],
2702
+ ['2013-06-07T14:21:46.295Z0', false],
2703
+ ['2013-06-07T14:21:46.295+07:00', true],
2704
+ ['2013-06-07T14:21:46.295+07:000', false],
2705
+ ['2013-06-07T14:21:46.295-07:00', false],
2706
+ ['2013-06-07T14:21:46Z', true],
2707
+ ['2013-06-07T14:21:46Z0', false],
2708
+ ['2013-06-07T14:21:46+07:00', false],
2709
+ ['2013-06-07T14:21:46-07:00', false],
2710
+ ['2013-06-07T14:21Z', true],
2711
+ ['2013-06-07T14:21+07:00', true],
2712
+ ['2013-06-07T14:21+07:000', false],
2713
+ ['2013-06-07T14:21-07:00', true],
2714
+ ['2013-06-07T14:21Z+7:00', false],
2715
+ ['2013-06-07', false],
2716
+ ['2013-06-07T', false],
2717
+ ['2013-06-07T14:21', false],
2718
+ ['1-1-2013', false],
2719
+ ['', true],
2720
+ [null, false]
2721
+ ], done);
2722
+ });
2723
+
2724
+ it('validates combination of isoDate, min, max, allow, invalid and regex', (done) => {
2725
+
2726
+ const rule = Joi.string().isoDate().min(17).max(23).allow('2013-06-07T14:21:46.295+07:00').invalid('2013-06-07T14:21Z').regex(/Z$/);
2727
+ Helper.validate(rule, [
2728
+ ['2013-06-07T14:21:46.295Z', false],
2729
+ ['2013-06-07T14:21:46.295Z0', false],
2730
+ ['2013-06-07T14:21:46.295+07:00', true],
2731
+ ['2013-06-07T14:21:46.295+07:000', false],
2732
+ ['2013-06-07T14:21:46.295-07:00', false],
2733
+ ['2013-06-07T14:21:46Z', true],
2734
+ ['2013-06-07T14:21:46Z0', false],
2735
+ ['2013-06-07T14:21:46+07:00', false],
2736
+ ['2013-06-07T14:21:46-07:00', false],
2737
+ ['2013-06-07T14:21Z', false],
2738
+ ['2013-06-07T14:21+07:00', false],
2739
+ ['2013-06-07T14:21+07:000', false],
2740
+ ['2013-06-07T14:21-07:00', false],
2741
+ ['2013-06-07T14:21Z+7:00', false],
2742
+ ['2013-06-07', false],
2743
+ ['2013-06-07T', false],
2744
+ ['2013-06-07T14:21', false],
2745
+ ['1-1-2013', false],
2746
+ ['', false],
2747
+ [null, false]
2748
+ ], done);
2749
+ });
2750
+
2751
+ it('validates combination of isoDate, min, max, allow, invalid, regex and allow(\'\')', (done) => {
2752
+
2753
+ const rule = Joi.string().isoDate().min(17).max(23).allow('2013-06-07T14:21:46.295+07:00').invalid('2013-06-07T14:21Z').regex(/Z$/).allow('');
2754
+ Helper.validate(rule, [
2755
+ ['2013-06-07T14:21:46.295Z', false],
2756
+ ['2013-06-07T14:21:46.295Z0', false],
2757
+ ['2013-06-07T14:21:46.295+07:00', true],
2758
+ ['2013-06-07T14:21:46.295+07:000', false],
2759
+ ['2013-06-07T14:21:46.295-07:00', false],
2760
+ ['2013-06-07T14:21:46Z', true],
2761
+ ['2013-06-07T14:21:46Z0', false],
2762
+ ['2013-06-07T14:21:46+07:00', false],
2763
+ ['2013-06-07T14:21:46-07:00', false],
2764
+ ['2013-06-07T14:21Z', false],
2765
+ ['2013-06-07T14:21+07:00', false],
2766
+ ['2013-06-07T14:21+07:000', false],
2767
+ ['2013-06-07T14:21-07:00', false],
2768
+ ['2013-06-07T14:21Z+7:00', false],
2769
+ ['2013-06-07', false],
2770
+ ['2013-06-07T', false],
2771
+ ['2013-06-07T14:21', false],
2772
+ ['1-1-2013', false],
2773
+ ['', true],
2774
+ [null, false]
2775
+ ], done);
2776
+ });
2777
+
2778
+ it('validates combination of isoDate, min, max and allow(\'\')', (done) => {
2779
+
2780
+ const rule = Joi.string().isoDate().min(17).max(23).allow('');
2781
+ Helper.validate(rule, [
2782
+ ['2013-06-07T14:21:46.295Z', false],
2783
+ ['2013-06-07T14:21:46.295Z0', false],
2784
+ ['2013-06-07T14:21:46.295+07:00', false],
2785
+ ['2013-06-07T14:21:46.295+07:000', false],
2786
+ ['2013-06-07T14:21:46.295-07:00', false],
2787
+ ['2013-06-07T14:21:46Z', true],
2788
+ ['2013-06-07T14:21:46Z0', false],
2789
+ ['2013-06-07T14:21:46+07:00', false],
2790
+ ['2013-06-07T14:21:46-07:00', false],
2791
+ ['2013-06-07T14:21Z', true],
2792
+ ['2013-06-07T14:21+07:00', true],
2793
+ ['2013-06-07T14:21+07:000', false],
2794
+ ['2013-06-07T14:21-07:00', true],
2795
+ ['2013-06-07T14:21Z+7:00', false],
2796
+ ['2013-06-07', false],
2797
+ ['2013-06-07T', false],
2798
+ ['2013-06-07T14:21', false],
2799
+ ['1-1-2013', false],
2800
+ ['', true],
2801
+ [null, false]
2802
+ ], done);
2803
+ });
2804
+
2805
+ it('validates combination of isoDate, min, max and regex', (done) => {
2806
+
2807
+ const rule = Joi.string().isoDate().min(17).max(23).regex(/Z$/);
2808
+ Helper.validate(rule, [
2809
+ ['2013-06-07T14:21:46.295Z', false],
2810
+ ['2013-06-07T14:21:46.295Z0', false],
2811
+ ['2013-06-07T14:21:46.295+07:00', false],
2812
+ ['2013-06-07T14:21:46.295+07:000', false],
2813
+ ['2013-06-07T14:21:46.295-07:00', false],
2814
+ ['2013-06-07T14:21:46Z', true],
2815
+ ['2013-06-07T14:21:46Z0', false],
2816
+ ['2013-06-07T14:21:46+07:00', false],
2817
+ ['2013-06-07T14:21:46-07:00', false],
2818
+ ['2013-06-07T14:21Z', true],
2819
+ ['2013-06-07T14:21+07:00', false],
2820
+ ['2013-06-07T14:21+07:000', false],
2821
+ ['2013-06-07T14:21-07:00', false],
2822
+ ['2013-06-07T14:21Z+7:00', false],
2823
+ ['2013-06-07', false],
2824
+ ['2013-06-07T', false],
2825
+ ['2013-06-07T14:21', false],
2826
+ ['1-1-2013', false],
2827
+ ['', false],
2828
+ [null, false]
2829
+ ], done);
2830
+ });
2831
+
2832
+ it('validates combination of isoDate, min, max, regex and allow(\'\')', (done) => {
2833
+
2834
+ const rule = Joi.string().isoDate().min(17).max(23).regex(/Z$/).allow('');
2835
+ Helper.validate(rule, [
2836
+ ['2013-06-07T14:21:46.295Z', false],
2837
+ ['2013-06-07T14:21:46.295Z0', false],
2838
+ ['2013-06-07T14:21:46.295+07:00', false],
2839
+ ['2013-06-07T14:21:46.295+07:000', false],
2840
+ ['2013-06-07T14:21:46.295-07:00', false],
2841
+ ['2013-06-07T14:21:46Z', true],
2842
+ ['2013-06-07T14:21:46Z0', false],
2843
+ ['2013-06-07T14:21:46+07:00', false],
2844
+ ['2013-06-07T14:21:46-07:00', false],
2845
+ ['2013-06-07T14:21Z', true],
2846
+ ['2013-06-07T14:21+07:00', false],
2847
+ ['2013-06-07T14:21+07:000', false],
2848
+ ['2013-06-07T14:21-07:00', false],
2849
+ ['2013-06-07T14:21Z+7:00', false],
2850
+ ['2013-06-07', false],
2851
+ ['2013-06-07T', false],
2852
+ ['2013-06-07T14:21', false],
2853
+ ['1-1-2013', false],
2854
+ ['', true],
2855
+ [null, false]
2856
+ ], done);
2857
+ });
2858
+
2859
+ it('validates combination of isoDate, min, max, regex and required', (done) => {
2860
+
2861
+ const rule = Joi.string().isoDate().min(17).max(23).regex(/Z$/).required();
2862
+ Helper.validate(rule, [
2863
+ ['2013-06-07T14:21:46.295Z', false],
2864
+ ['2013-06-07T14:21:46.295Z0', false],
2865
+ ['2013-06-07T14:21:46.295+07:00', false],
2866
+ ['2013-06-07T14:21:46.295+07:000', false],
2867
+ ['2013-06-07T14:21:46.295-07:00', false],
2868
+ ['2013-06-07T14:21:46Z', true],
2869
+ ['2013-06-07T14:21:46Z0', false],
2870
+ ['2013-06-07T14:21:46+07:00', false],
2871
+ ['2013-06-07T14:21:46-07:00', false],
2872
+ ['2013-06-07T14:21Z', true],
2873
+ ['2013-06-07T14:21+07:00', false],
2874
+ ['2013-06-07T14:21+07:000', false],
2875
+ ['2013-06-07T14:21-07:00', false],
2876
+ ['2013-06-07T14:21Z+7:00', false],
2877
+ ['2013-06-07', false],
2878
+ ['2013-06-07T', false],
2879
+ ['2013-06-07T14:21', false],
2880
+ ['1-1-2013', false],
2881
+ ['', false],
2882
+ [null, false]
2883
+ ], done);
2884
+ });
2885
+
2886
+ it('validates guid', (done) => {
2887
+
2888
+ Helper.validate(Joi.string().guid(), [
2889
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', true],
2890
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
2891
+ ['69593D62-71EA-4548-85E4-04FC71357423', true],
2892
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
2893
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', true],
2894
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
2895
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', true],
2896
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', true],
2897
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
2898
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
2899
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false]
2900
+ ], done);
2901
+ });
2902
+
2903
+ it('validates guid with a friendly error message', (done) => {
2904
+
2905
+ const schema = { item: Joi.string().guid() };
2906
+ Joi.compile(schema).validate({ item: 'something' }, (err, value) => {
2907
+
2908
+ expect(err.message).to.contain('must be a valid GUID');
2909
+ done();
2910
+ });
2911
+ });
2912
+
2913
+ it('validates combination of guid and min', (done) => {
2914
+
2915
+ const rule = Joi.string().guid().min(36);
2916
+ Helper.validate(rule, [
2917
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', true],
2918
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', false],
2919
+ ['69593D62-71EA-4548-85E4-04FC71357423', true],
2920
+ ['677E2553DD4D43B09DA77414DB1EB8EA', false],
2921
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', true],
2922
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', false],
2923
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', true],
2924
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
2925
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
2926
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
2927
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2928
+ ['', false],
2929
+ [null, false]
2930
+ ], done);
2931
+ });
2932
+
2933
+ it('validates combination of guid, min and max', (done) => {
2934
+
2935
+ const rule = Joi.string().guid().min(32).max(34);
2936
+ Helper.validate(rule, [
2937
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2938
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
2939
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
2940
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
2941
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
2942
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
2943
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
2944
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', true],
2945
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
2946
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
2947
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2948
+ ['', false],
2949
+ [null, false]
2950
+ ], done);
2951
+ });
2952
+
2953
+ it('validates combination of guid, min, max and invalid', (done) => {
2954
+
2955
+ const rule = Joi.string().guid().min(32).max(34).invalid('b4b2fb69c6244e5eb0698e0c6ec66618');
2956
+ Helper.validate(rule, [
2957
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2958
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
2959
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
2960
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
2961
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
2962
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
2963
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
2964
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
2965
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
2966
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
2967
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2968
+ ['', false],
2969
+ [null, false]
2970
+ ], done);
2971
+ });
2972
+
2973
+ it('validates combination of guid, min, max and allow', (done) => {
2974
+
2975
+ const rule = Joi.string().guid().min(32).max(34).allow('{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D');
2976
+ Helper.validate(rule, [
2977
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2978
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
2979
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
2980
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
2981
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
2982
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
2983
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
2984
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', true],
2985
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
2986
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', true],
2987
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2988
+ ['', false],
2989
+ [null, false]
2990
+ ], done);
2991
+ });
2992
+
2993
+ it('validates combination of guid, min, max, allow and invalid', (done) => {
2994
+
2995
+ const rule = Joi.string().guid().min(32).max(34).allow('{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D').invalid('b4b2fb69c6244e5eb0698e0c6ec66618');
2996
+ Helper.validate(rule, [
2997
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
2998
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
2999
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3000
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
3001
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3002
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3003
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3004
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3005
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3006
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', true],
3007
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3008
+ ['', false],
3009
+ [null, false]
3010
+ ], done);
3011
+ });
3012
+
3013
+ it('validates combination of guid, min, max, allow, invalid and allow(\'\')', (done) => {
3014
+
3015
+ const rule = Joi.string().guid().min(32).max(34).allow('{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D').invalid('b4b2fb69c6244e5eb0698e0c6ec66618').allow('');
3016
+ Helper.validate(rule, [
3017
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3018
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
3019
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3020
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
3021
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3022
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3023
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3024
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3025
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3026
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', true],
3027
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3028
+ ['', true],
3029
+ [null, false]
3030
+ ], done);
3031
+ });
3032
+
3033
+ it('validates combination of guid, min, max, allow and allow(\'\')', (done) => {
3034
+
3035
+ const rule = Joi.string().guid().min(32).max(34).allow('{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D').allow('');
3036
+ Helper.validate(rule, [
3037
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3038
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
3039
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3040
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
3041
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3042
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3043
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3044
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', true],
3045
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3046
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', true],
3047
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3048
+ ['', true],
3049
+ [null, false]
3050
+ ], done);
3051
+ });
3052
+
3053
+ it('validates combination of guid, min, max, allow, invalid and regex', (done) => {
3054
+
3055
+ const rule = Joi.string().guid().min(32).max(34).allow('{D1A5279D-B27D-4CD4-A05E-EFDD53D08').invalid('b4b2fb69c6244e5eb0698e0c6ec66618').regex(/^{7e908/);
3056
+ Helper.validate(rule, [
3057
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3058
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', false],
3059
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3060
+ ['677E2553DD4D43B09DA77414DB1EB8EA', false],
3061
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3062
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3063
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3064
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3065
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3066
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08', true],
3067
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3068
+ ['', false],
3069
+ [null, false]
3070
+ ], done);
3071
+ });
3072
+
3073
+ it('validates combination of guid, min, max, allow, invalid, regex and allow(\'\')', (done) => {
3074
+
3075
+ const rule = Joi.string().guid().min(32).max(34).allow('{D1A5279D-B27D-4CD4-A05E-EFDD53D08').invalid('b4b2fb69c6244e5eb0698e0c6ec66618').regex(/^{7e908/).allow('');
3076
+ Helper.validate(rule, [
3077
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3078
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', false],
3079
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3080
+ ['677E2553DD4D43B09DA77414DB1EB8EA', false],
3081
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3082
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3083
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3084
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3085
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3086
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08', true],
3087
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3088
+ ['', true],
3089
+ [null, false]
3090
+ ], done);
3091
+ });
3092
+
3093
+ it('validates combination of guid, min, max and allow(\'\')', (done) => {
3094
+
3095
+ const rule = Joi.string().guid().min(32).max(34).allow('');
3096
+ Helper.validate(rule, [
3097
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3098
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', true],
3099
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3100
+ ['677E2553DD4D43B09DA77414DB1EB8EA', true],
3101
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3102
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3103
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3104
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', true],
3105
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3106
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
3107
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3108
+ ['', true],
3109
+ [null, false]
3110
+ ], done);
3111
+ });
3112
+
3113
+ it('validates combination of guid, min, max and regex', (done) => {
3114
+
3115
+ const rule = Joi.string().guid().min(32).max(34).regex(/^{7e9081/i);
3116
+ Helper.validate(rule, [
3117
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3118
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', false],
3119
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3120
+ ['677E2553DD4D43B09DA77414DB1EB8EA', false],
3121
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3122
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3123
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3124
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3125
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3126
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
3127
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3128
+ ['', false],
3129
+ [null, false]
3130
+ ], done);
3131
+ });
3132
+
3133
+ it('validates combination of guid, min, max, regex and allow(\'\')', (done) => {
3134
+
3135
+ const rule = Joi.string().guid().min(32).max(34).regex(/^{7e9081/i).allow('');
3136
+ Helper.validate(rule, [
3137
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3138
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', false],
3139
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3140
+ ['677E2553DD4D43B09DA77414DB1EB8EA', false],
3141
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3142
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3143
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3144
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3145
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3146
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
3147
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3148
+ ['', true],
3149
+ [null, false]
3150
+ ], done);
3151
+ });
3152
+
3153
+ it('validates combination of guid, min, max, regex and required', (done) => {
3154
+
3155
+ const rule = Joi.string().guid().min(32).max(34).regex(/^{7e9081/i).required();
3156
+ Helper.validate(rule, [
3157
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3158
+ ['{B59511BD6A5F4DF09ECF562A108D8A2E}', false],
3159
+ ['69593D62-71EA-4548-85E4-04FC71357423', false],
3160
+ ['677E2553DD4D43B09DA77414DB1EB8EA', false],
3161
+ ['{5ba3bba3-729a-4717-88c1-b7c4b7ba80db}', false],
3162
+ ['{7e9081b59a6d4cc1a8c347f69fb4198d}', true],
3163
+ ['0c74f13f-fa83-4c48-9b33-68921dd72463', false],
3164
+ ['b4b2fb69c6244e5eb0698e0c6ec66618', false],
3165
+ ['{283B67B2-430F-4E6F-97E6-19041992-C1B0}', false],
3166
+ ['{D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D', false],
3167
+ ['D1A5279D-B27D-4CD4-A05E-EFDD53D08E8D}', false],
3168
+ ['', false],
3169
+ [null, false]
3170
+ ], done);
3171
+ });
3172
+
3173
+ it('validates an hexadecimal string', (done) => {
3174
+
3175
+ const rule = Joi.string().hex();
3176
+ Helper.validate(rule, [
3177
+ ['123456789abcdef', true],
3178
+ ['123456789AbCdEf', true],
3179
+ ['123afg', false, null, '"value" must only contain hexadecimal characters']
3180
+ ], done);
3181
+ });
3182
+
3183
+ it('validates combination of uppercase, min, max, alphanum and valid', (done) => {
3184
+
3185
+ const rule = Joi.string().uppercase().min(2).max(3).alphanum().valid('AB', 'BC');
3186
+ Helper.validate(rule, [
3187
+ ['x', false],
3188
+ ['123', false],
3189
+ ['1234', false],
3190
+ ['12', false],
3191
+ ['ab', true],
3192
+ ['abc', false],
3193
+ ['a2c', false],
3194
+ ['abcd', false],
3195
+ ['*ab', false],
3196
+ ['', false],
3197
+ ['bc', true],
3198
+ ['BC', true],
3199
+ ['de', false],
3200
+ ['ABc', false],
3201
+ ['AB', true],
3202
+ [null, false]
3203
+ ], done);
3204
+ });
3205
+ });
3206
+ });