Completed
Push — master ( 35b95e...b92b30 )
by Dmitry
07:36
created

DomainValidatorTest::u()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
namespace kdn\yii2\validators;
4
5
use kdn\yii2\validators\mocks\ModelMock;
6
use stdClass;
7
use Yii;
8
9
/**
10
 * Class DomainValidatorTest.
11
 * @package kdn\yii2\validators
12
 * @covers \kdn\yii2\validators\DomainValidator::init
13
 */
14
class DomainValidatorTest extends TestCase
15
{
16
    /**
17
     * @var DomainValidator
18
     */
19
    protected $validator;
20
21
    /**
22
     * {@inheritdoc}
23
     */
24
    protected function setUp()
25
    {
26
        parent::setUp();
27
        $this->validator = new DomainValidator(['labelNumberMin' => 1]);
28
    }
29
30
    public static function validDomainProvider()
31
    {
32
        return [
33
            'one domain name label' => ['localhost'],
34
            'two domain name labels' => ['example.com'],
35
            'domain name with trailing dot' => ['example.com.'],
36
            '127 levels, 253 characters and trailing dot' => [
37
                'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.' .
38
                'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.' .
39
                'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.' .
40
                'a.a.a.a.',
41
            ],
42
            'domain name labels with 63 characters' => [
43
                'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.' .
44
                'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.' .
45
                'example.com',
46
            ],
47
            'domain name with various symbols' => ['1.a-B.c2'],
48
            'Punycode, mixed domain name' => ['xn--e1afmkfd.test.xn--80akhbyknj4f'],
49
        ];
50
    }
51
52
    public static function validDomainInUrlProvider()
53
    {
54
        return [
55
            'HTTP, one domain name label' => ['http://localhost'],
56
            'HTTP, two domain name labels' => ['http://example.com/index.html'],
57
            'FTP, domain name with trailing dot' => ['ftp://example.com./img/dir/'],
58
            // todo it causes empty array in "idn_to_ascii" $idnaInfo
59
            'HTTPS, 127 levels, 253 characters and trailing dot' => [
60
                'https://a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.' .
61
                'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.' .
62
                'a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.' .
63
                'a.a.a.a./index.html',
64
            ],
65
            'missing scheme, domain name labels with 63 characters' => [
66
                '//aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.' .
67
                'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.' .
68
                'example.com',
69
            ],
70
            'complex URL, domain name with various symbols' => [
71
                'http://username:[email protected]:9090/path?a=b&c=d#anchor',
72
            ],
73
            'Punycode, FTP, mixed domain name' => ['ftp://xn--e1afmkfd.test.xn--80akhbyknj4f/img/dir/'],
74
        ];
75
    }
76
77
    public static function validDomainIdnProvider()
78
    {
79
        return [
80
            'IDN, one domain name label' => ['пример'],
81
            'IDN, two domain name labels' => ['пример.испытание'],
82
            'IDN, domain name with trailing dot' => ['пример.испытание.'],
83
            'IDN, mixed domain name' => ['пример.test.испытание'],
84
            'IDN, 34 levels, 253 characters (ф. == xn--t1a.) and trailing dot' => [
85
                'ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.s.s.s.',
86
            ],
87
            'IDN, domain name labels with 63 characters' => [
88
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
89
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
90
                'испытание',
91
            ],
92
            'IDN, domain name with various symbols' => ['1.a-B.cф2'],
93
            'IDN, hot beverage' => ['☕.us'],
94
            'IDN, full-width characters' => ['日本語。JP'],
95
            'IDN, box-drawing character' => ['ex╬ample.com'],
96
        ];
97
    }
98
99
    public static function validDomainIdnInUrlProvider()
100
    {
101
        return [
102
            'IDN, HTTP, one domain name label' => ['http://пример'],
103
            'IDN, HTTP, two domain name labels' => ['http://пример.испытание/index.html'],
104
            'IDN, FTP, domain name with trailing dot' => ['ftp://пример.испытание./img/dir/'],
105
            'IDN, FTP, mixed domain name' => ['ftp://пример.test.испытание/img/dir/'],
106
            // todo it causes empty array in "idn_to_ascii" $idnaInfo
107
            'IDN, HTTPS, 34 levels, 253 characters (ф. == xn--t1a.) and trailing dot' => [
108
                'https://ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.s.s.s./index.html',
109
            ],
110
            'IDN, missing scheme, domain name labels with 63 characters' => [
111
                '//ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
112
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
113
                'испытание',
114
            ],
115
            'IDN, complex URL, domain name with various symbols' => [
116
                'http://username:[email protected]ф2:9090/path?a=ф&c=d#-пример',
117
            ],
118
            'IDN, HTTP, hot beverage' => ['http://☕.us/index.html'],
119
            'IDN, HTTP, full-width characters' => ['http://日本語。JP/index.html'],
120
            'IDN, HTTP, box-drawing character' => ['http://ex╬ample.com/index.html'],
121
        ];
122
    }
123
124
    public static function validDomainAllWithoutIdnProvider()
125
    {
126
        return array_merge(
127
            static::validDomainProvider(),
128
            static::validDomainInUrlProvider()
129
        );
130
    }
131
132
    public static function validDomainAllOnlyIdnProvider()
133
    {
134
        return array_merge(
135
            static::validDomainIdnProvider(),
136
            static::validDomainIdnInUrlProvider()
137
        );
138
    }
139
140
    public static function validDomainAllProvider()
141
    {
142
        return array_merge(
143
            static::validDomainProvider(),
144
            static::validDomainInUrlProvider(),
145
            static::validDomainIdnProvider(),
146
            static::validDomainIdnInUrlProvider()
147
        );
148
    }
149
150
    /**
151
     * @param string $value
152
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
153
     * @dataProvider validDomainAllWithoutIdnProvider
154
     * @small
155
     */
156
    public function testValidDomain($value)
157
    {
158
        $this->assertTrue($this->validator->validate($value));
159
    }
160
161
    /**
162
     * @param string $value
163
     * @covers       \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
164
     * @covers       \kdn\yii2\validators\DomainValidator::getErrorMessage
165
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
166
     * @dataProvider validDomainAllOnlyIdnProvider
167
     * @small
168
     */
169
    public function testInvalidDomainWithDisabledIdn($value)
170
    {
171
        $this->assertFalse($this->validator->validate($value, $errorMessage));
172
        $this->assertEquals(
173
            'Each label of the input value can consist of only latin letters, numbers and hyphens.',
174
            $errorMessage
175
        );
176
    }
177
178
    /**
179
     * @param string $value
180
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
181
     * @dataProvider validDomainAllProvider
182
     * @small
183
     */
184
    public function testValidDomainWithEnabledIdn($value)
185
    {
186
        if (!function_exists('idn_to_ascii')) {
187
            $this->markTestSkipped('intl extension required.');
188
            return;
189
        }
190
191
        $this->validator->enableIDN = true;
192
        $this->testValidDomain($value);
193
    }
194
195
    /**
196
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
197
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
198
     * @covers \kdn\yii2\validators\DomainValidator::checkDNS
199
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
200
     * @medium
201
     */
202
    public function testDns()
203
    {
204
        $validator = $this->validator;
205
        $nonExistingDomain = 'non-existing-subdomain.example.com';
206
        $this->assertTrue($validator->validate($nonExistingDomain));
207
        $validator->checkDNS = true;
208
        $this->assertFalse($validator->validate($nonExistingDomain, $errorMessage));
209
        $this->assertEquals('DNS record corresponding to the input value not found.', $errorMessage);
210
211
        $data = [
212
            'google.com',
213
            'http://username:[email protected]:9090/path?a=b&c=d#anchor',
214
        ];
215
        foreach ($data as $value) {
216
            $this->assertTrue(
217
                $validator->validate($value),
218
                "Failed to validate \"$value\" (checkDNS = true)."
219
            );
220
        }
221
    }
222
223
    /**
224
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
225
     * @medium
226
     */
227
    public function testDnsCallable()
228
    {
229
        $validator = $this->validator;
230
        $nonExistingDomain = 'non-existing-subdomain.example.com';
231
        $this->assertTrue($validator->validate($nonExistingDomain));
232
        $customErrorMessage = 'test';
233
        $validator->checkDNS = function ($value) use ($nonExistingDomain, $customErrorMessage) {
234
            $records = @dns_get_record("$value.", DNS_MX); // @ is just for simplicity of test, avoid to use it
235
            if (empty($records)) {
236
                $this->assertEquals($nonExistingDomain, $value);
237
238
                return [$customErrorMessage, []];
239
            }
240
241
            return null;
242
        };
243
        $this->assertFalse($validator->validate($nonExistingDomain, $errorMessage));
244
        $this->assertEquals($customErrorMessage, $errorMessage);
245
246
        $data = [
247
            'gmail.com',
248
            'http://username:[email protected]:9090/path?a=b&c=d#anchor',
249
        ];
250
        foreach ($data as $value) {
251
            $this->assertTrue(
252
                $validator->validate($value),
253
                "Failed to validate \"$value\" (checkDNS = true)."
254
            );
255
        }
256
    }
257
258
    /**
259
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
260
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
261
     * @covers \kdn\yii2\validators\DomainValidator::checkDNS
262
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
263
     * @medium
264
     */
265
    public function testDnsWithEnabledIdn()
266
    {
267
        if (!function_exists('idn_to_ascii')) {
268
            $this->markTestSkipped('intl extension required.');
269
            return;
270
        }
271
272
        $validator = $this->validator;
273
        $validator->checkDNS = true;
274
        // enabling of IDN should not affect error message
275
        $validator->enableIDN = true;
276
        $this->assertFalse($validator->validate('non-existing-subdomain.example.com', $errorMessage));
277
        $this->assertEquals('DNS record corresponding to the input value not found.', $errorMessage);
278
279
        $data = [
280
            'google.com',
281
            'http://username:[email protected]:9090/path?a=b&c=d#anchor',
282
            'яндекс.рф',
283
            'http://username:password@яндекс.рф:9090/path?a=ф&c=d#-пример',
284
        ];
285
        foreach ($data as $value) {
286
            $this->assertTrue(
287
                $validator->validate($value),
288
                "Failed to validate \"$value\" (checkDNS = true, enableIDN = true)."
289
            );
290
        }
291
    }
292
293
    /**
294
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
295
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
296
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
297
     * @small
298
     */
299
    public function testUnderscore()
300
    {
301
        $validator = $this->validator;
302
        $validator->allowUnderscore = true;
303
304
        $data = [
305
            'ex_ample.com',
306
            'http://username:password@ex_ample.com:9090/path?a=b&c=d#anchor',
307
        ];
308
        foreach ($data as $value) {
309
            $this->assertTrue(
310
                $validator->validate($value),
311
                "Failed to validate \"$value\" (allowUnderscore = true)."
312
            );
313
        }
314
315
        $this->assertFalse($validator->validate('a_@_a', $errorMessage));
316
        if ($validator->enableIDN) {
317
            $expectedErrorMessage =
318
                'Each label of the input value can consist of only letters, numbers, hyphens and underscores.';
319
        } else {
320
            $expectedErrorMessage =
321
                'Each label of the input value can consist of only latin letters, numbers, hyphens and underscores.';
322
        }
323
        $this->assertEquals($expectedErrorMessage, $errorMessage);
324
    }
325
326
    /**
327
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
328
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
329
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
330
     * @small
331
     */
332
    public function testUnderscoreWithEnabledIdn()
333
    {
334
        if (!function_exists('idn_to_ascii')) {
335
            $this->markTestSkipped('intl extension required.');
336
            return;
337
        }
338
339
        $this->validator->enableIDN = true;
340
        $this->testUnderscore();
341
    }
342
343
    public static function urlNotAllowedProvider()
344
    {
345
        return array_merge(
346
            static::arrayAddColumn(static::validDomainProvider(), true),
347
            static::arrayAddColumn(static::validDomainInUrlProvider(), false)
348
        );
349
    }
350
351
    /**
352
     * @param string $value
353
     * @param bool $expectedResult
354
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
355
     * @uses         \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
356
     * @uses         \kdn\yii2\validators\DomainValidator::getErrorMessage
357
     * @dataProvider urlNotAllowedProvider
358
     * @small
359
     */
360
    public function testUrlNotAllowed($value, $expectedResult)
361
    {
362
        $validator = $this->validator;
363
        $validator->allowURL = false;
364
        $this->assertEquals($expectedResult, $validator->validate($value));
365
    }
366
367
    public static function urlNotAllowedProviderWithEnabledIdn()
368
    {
369
        return array_merge(
370
            static::urlNotAllowedProvider(),
371
            static::arrayAddColumn(static::validDomainIdnProvider(), true),
372
            static::arrayAddColumn(static::validDomainIdnInUrlProvider(), false)
373
        );
374
    }
375
376
    /**
377
     * @param string $value
378
     * @param bool $expectedResult
379
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
380
     * @uses         \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
381
     * @uses         \kdn\yii2\validators\DomainValidator::getErrorMessage
382
     * @dataProvider urlNotAllowedProviderWithEnabledIdn
383
     * @small
384
     */
385
    public function testUrlNotAllowedWithEnabledIdn($value, $expectedResult)
386
    {
387
        if (!function_exists('idn_to_ascii')) {
388
            $this->markTestSkipped('intl extension required.');
389
            return;
390
        }
391
392
        $this->validator->enableIDN = true;
393
        $this->testUrlNotAllowed($value, $expectedResult);
394
    }
395
396
    /**
397
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
398
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
399
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
400
     * @small
401
     */
402
    public function testLabelNumberMin()
403
    {
404
        $validator = $this->validator;
405
        $validator->labelNumberMin = 2;
406
        $this->assertFalse($validator->validate('localhost', $errorMessage));
407
        $this->assertEquals('the input value should consist of at least 2 labels separated by dot.', $errorMessage);
408
        $this->assertTrue($validator->validate('example.com'));
409
        $this->assertTrue($validator->validate('test.example.com'));
410
        $validator->labelNumberMin = 3;
411
        $this->assertFalse($validator->validate('example.com', $errorMessage));
412
        $this->assertEquals('the input value should consist of at least 3 labels separated by dots.', $errorMessage);
413
        $this->assertTrue($validator->validate('test.example.com'));
414
    }
415
416
    /**
417
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
418
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
419
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
420
     * @small
421
     */
422
    public function testLabelNumberMinWithEnabledIdn()
423
    {
424
        if (!function_exists('idn_to_ascii')) {
425
            $this->markTestSkipped('intl extension required.');
426
            return;
427
        }
428
429
        $this->validator->enableIDN = true;
430
        $this->testLabelNumberMin();
431
    }
432
433
    public static function invalidDomainProvider($testName)
434
    {
435
        if ($testName == 'testInvalidDomain') {
436
            $messageInvalidCharacter =
437
                'Each label of the input value can consist of only latin letters, numbers and hyphens.';
438
            $messageLabelStartEnd = 'Each label of the input value should start and end with latin letter or number.' .
439
                ' The rightmost label of the input value should start with latin letter.';
440
            $messageLabelTooLong = 'Each label of the input value should contain at most 63 characters.';
441
            $messageTooLong = 'the input value should contain at most 253 characters.';
442
        } else {
443
            $messageInvalidCharacter =
444
                'Each label of the input value can consist of only letters, numbers and hyphens.';
445
            $messageLabelStartEnd = 'Each label of the input value should start and end with letter or number.' .
446
                ' The rightmost label of the input value should start with letter.';
447
            $messageLabelTooLong = 'Label of the input value is too long.';
448
            $messageTooLong = 'the input value is too long.';
449
        }
450
        $messageLabelTooShort = 'Each label of the input value should contain at least 1 character.';
451
        $messageNotString = 'the input value must be a string.';
452
        $messageTooShort = 'the input value should contain at least 1 character.';
453
        return [
454
            'null' => [null, $messageNotString],
455
            'boolean' => [true, $messageNotString],
456
            'integer' => [1, $messageNotString],
457
            'float' => [1.2, $messageNotString],
458
            'array' => [[], $messageNotString],
459
            'object' => [new stdClass, $messageNotString],
460
461
            'domain name too long' => [str_repeat('a.', 126) . 'aa', $messageTooLong],
462
463
            'domain name too short' => ['', $messageTooShort],
464
465
            'first domain name label starts with hyphen' => ['-example.com', $messageLabelStartEnd],
466
            'first domain name label ends with hyphen' => ['example-.com', $messageLabelStartEnd],
467
            'last domain name label starts with hyphen' => ['example.-com', $messageLabelStartEnd],
468
            'last domain name label ends with hyphen' => ['example.com-', $messageLabelStartEnd],
469
470
            'IDN, first domain name label starts with hyphen' => ['-пример.испытание', $messageInvalidCharacter],
471
            'IDN, first domain name label ends with hyphen' => ['пример-.испытание', $messageInvalidCharacter],
472
            'IDN, last domain name label starts with hyphen' => ['пример.-испытание', $messageInvalidCharacter],
473
            'IDN, last domain name label ends with hyphen' => ['пример.испытание-', $messageInvalidCharacter],
474
475
            'IDN, HTTP, first domain name label starts with hyphen' => [
476
                'http://-пример.испытание/index.html',
477
                $messageInvalidCharacter,
478
            ],
479
            'IDN, HTTP, first domain name label ends with hyphen' => [
480
                'http://пример-.испытание/index.html',
481
                $messageInvalidCharacter,
482
            ],
483
            'IDN, HTTP, last domain name label starts with hyphen' => [
484
                'http://пример.-испытание/index.html',
485
                $messageInvalidCharacter,
486
            ],
487
            'IDN, HTTP, last domain name label ends with hyphen' => [
488
                'http://пример.испытание-/index.html',
489
                $messageInvalidCharacter,
490
            ],
491
492
            'last domain name label starts with number' => ['example.4om', $messageLabelStartEnd],
493
494
            'domain name label too long' => [str_repeat('a', 64), $messageLabelTooLong],
495
496
            'dot' => ['.', $messageLabelTooShort],
497
            'domain name starts with dot' => ['.example.com', $messageLabelTooShort],
498
            'domain name ends with two dots' => ['example.com..', $messageLabelTooShort],
499
            'domain name contains two dots in a row' => ['example..com', $messageLabelTooShort],
500
501
            'domain name contains underscore' => ['ex_ample.com', $messageInvalidCharacter],
502
            'domain name contains space' => ['ex ample.com', $messageInvalidCharacter],
503
            'domain name contains disallowed character' => ['a⒈com', $messageInvalidCharacter],
504
505
            'IDN, domain name too long' => [
506
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
507
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
508
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
509
                'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.',
510
                $messageInvalidCharacter,
511
            ],
512
            'IDN, domain name label too long' => [
513
                'фффффффффффффффффффффффффффффффффффффффффффффффффффффффффs',
514
                $messageInvalidCharacter,
515
            ],
516
517
            'invalid url with valid domain name' => ['http//example.com/index.html', $messageInvalidCharacter],
518
            'IDN, invalid url with valid domain name' => ['http//пример.com/index.html', $messageInvalidCharacter],
519
        ];
520
    }
521
522
    /**
523
     * @param string $value
524
     * @param string $expectedErrorMessage
525
     * @covers       \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
526
     * @covers       \kdn\yii2\validators\DomainValidator::getErrorMessage
527
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
528
     * @dataProvider invalidDomainProvider
529
     * @small
530
     */
531
    public function testInvalidDomain($value, $expectedErrorMessage)
532
    {
533
        $this->assertFalse($this->validator->validate($value, $errorMessage));
534
        $this->assertEquals($expectedErrorMessage, $errorMessage);
535
    }
536
537
    public static function invalidDomainWithEnabledIdnProvider()
538
    {
539
        $message = 'the input value is invalid.';
540
        $messageLabelStartEnd = 'Each label of the input value should start and end with letter or number.' .
541
            ' The rightmost label of the input value should start with letter.';
542
        $messageLabelTooLong = 'Label of the input value is too long.';
543
        $messageTooLong = 'the input value is too long.';
544
        return array_merge(
545
            static::invalidDomainProvider('testInvalidDomainWithEnabledIdn'),
546
            [
547
                /* todo it causes fatal error in PHP
548
                'IDN, domain name too long, fatal' => [
549
                    'ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.ф.s.s.s.s',
550
                    $messageTooLong,
551
                ],
552
                //*/
553
                /* todo it causes fatal error in PHP
554
                'IDN, domain name too long, fatal' => [
555
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
556
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
557
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
558
                    'фффффффффффффффффффффффффффффффффффффффффффффффффффффффф.',
559
                    $messageTooLong,
560
                ],
561
                //*/
562
563
                'IDN, first domain name label starts with hyphen' => ['-пример.испытание', $messageLabelStartEnd],
564
                'IDN, first domain name label ends with hyphen' => ['пример-.испытание', $messageLabelStartEnd],
565
                'IDN, last domain name label starts with hyphen' => ['пример.-испытание', $messageLabelStartEnd],
566
                'IDN, last domain name label ends with hyphen' => ['пример.испытание-', $messageLabelStartEnd],
567
568
                'IDN, HTTP, first domain name label starts with hyphen' => [
569
                    'http://-пример.испытание/index.html',
570
                    $messageLabelStartEnd,
571
                ],
572
                'IDN, HTTP, first domain name label ends with hyphen' => [
573
                    'http://пример-.испытание/index.html',
574
                    $messageLabelStartEnd,
575
                ],
576
                'IDN, HTTP, last domain name label starts with hyphen' => [
577
                    'http://пример.-испытание/index.html',
578
                    $messageLabelStartEnd,
579
                ],
580
                'IDN, HTTP, last domain name label ends with hyphen' => [
581
                    'http://пример.испытание-/index.html',
582
                    $messageLabelStartEnd,
583
                ],
584
585
                // todo it causes empty array in "idn_to_ascii" $idnaInfo
586
                'IDN, domain name too long' => [
587
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
588
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
589
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.' .
590
                    'ффффффффффффффффффффффффффффффффффффффффффффффффффффффффф.',
591
                    $messageTooLong,
592
                ],
593
                'IDN, domain name label too long' => [
594
                    'фффффффффффффффффффффффффффффффффффффффффффффффффффффффффs',
595
                    $messageLabelTooLong,
596
                ],
597
598
                'IDN, IDNA_ERROR_HYPHEN_3_4' => ['aa--a', $message],
599
                'IDN, IDNA_ERROR_LEADING_COMBINING_MARK' => [static::u('\u0308c'), $message],
600
                'IDN, IDNA_ERROR_PUNYCODE' => ['xn--0', $message],
601
                'IDN, IDNA_ERROR_INVALID_ACE_LABEL' => ['xn--a', $message],
602
                'IDN, IDNA_ERROR_BIDI' => [static::u('0A.\u05D0'), $message],
603
            ]
604
        );
605
    }
606
607
    /**
608
     * @param string $value
609
     * @param string $expectedErrorMessage
610
     * @covers       \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
611
     * @covers       \kdn\yii2\validators\DomainValidator::getErrorMessage
612
     * @covers       \kdn\yii2\validators\DomainValidator::validateValue
613
     * @dataProvider invalidDomainWithEnabledIdnProvider
614
     * @small
615
     */
616
    public function testInvalidDomainWithEnabledIdn($value, $expectedErrorMessage)
617
    {
618
        if (!function_exists('idn_to_ascii')) {
619
            $this->markTestSkipped('intl extension required.');
620
            return;
621
        }
622
623
        $this->validator->enableIDN = true;
624
        $this->testInvalidDomain($value, $expectedErrorMessage);
625
    }
626
627
    /**
628
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
629
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
630
     * @small
631
     */
632
    public function testCustomErrorMessage()
633
    {
634
        $validator = $this->validator;
635
        $messageNotString = 'test';
636
        $validator->messageNotString = $messageNotString;
637
        $this->assertFalse($validator->validate(null, $errorMessage));
638
        $this->assertEquals($messageNotString, $errorMessage);
639
    }
640
641
    /**
642
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
643
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
644
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
645
     * @small
646
     */
647
    public function testSimpleErrorMessage()
648
    {
649
        $validator = $this->validator;
650
        $validator->simpleErrorMessage = true;
651
        $this->assertFalse($validator->validate('-', $errorMessage));
652
        $this->assertEquals('the input value is invalid.', $errorMessage);
653
    }
654
655
    /**
656
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
657
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
658
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
659
     * @small
660
     */
661
    public function testValidateAttribute()
662
    {
663
        $model = new ModelMock(['domain' => 'example']);
664
        $validator = $this->validator;
665
666
        $validator->validateAttribute($model, 'domain');
667
        $this->assertFalse($model->hasErrors('domain'));
668
669
        $validator->labelNumberMin = 2;
670
        $validator->validateAttribute($model, 'domain');
671
        $this->assertTrue($model->hasErrors('domain'));
672
    }
673
674
    /**
675
     * @covers \kdn\yii2\validators\DomainValidator::getDefaultErrorMessages
676
     * @covers \kdn\yii2\validators\DomainValidator::getErrorMessage
677
     * @covers \kdn\yii2\validators\DomainValidator::validateValue
678
     * @small
679
     */
680
    public function testValidateAttributeAndI18n()
681
    {
682
        if (!function_exists('idn_to_ascii')) {
683
            $this->markTestSkipped('intl extension required.');
684
            return;
685
        }
686
687
        Yii::$app->language = 'ru-RU';
688
        $model = new ModelMock(['domain' => 'example']);
689
        $validator = $this->validator;
690
691
        $validator->validateAttribute($model, 'domain');
692
        $this->assertFalse($model->hasErrors('domain'));
693
694
        $validator->labelNumberMin = 2;
695
        $validator->validateAttribute($model, 'domain');
696
        $this->assertTrue($model->hasErrors('domain'));
697
        $this->assertEquals(
698
            'Значение «Доменное имя» должно состоять минимум из 2 меток, разделённых точкой.',
699
            $model->getFirstError('domain')
700
        );
701
702
        $model->clearErrors('domain');
703
704
        $validator->labelNumberMin = 21;
705
        $validator->validateAttribute($model, 'domain');
706
        $this->assertTrue($model->hasErrors('domain'));
707
        $this->assertEquals(
708
            'Значение «Доменное имя» должно состоять минимум из 21 метки, разделённых точками.',
709
            $model->getFirstError('domain')
710
        );
711
    }
712
713
    /**
714
     * IMPORTANT: this test should be executed last, because it can remove function "idn_to_ascii".
715
     * @covers \kdn\yii2\validators\DomainValidator::init
716
     * @expectedException \yii\base\InvalidConfigException
717
     * @expectedExceptionMessage In order to use IDN validation intl extension must be installed and enabled.
718
     * @small
719
     */
720
    public function testInitIdnIntlException()
721
    {
722
        if (!function_exists('runkit_function_remove') || !ini_get('runkit.internal_override')) {
723
            $this->markTestSkipped('runkit extension required. runkit.internal_override should be set to "On".');
724
            return;
725
        }
726
727
        runkit_function_remove('idn_to_ascii');
728
        new DomainValidator(['enableIDN' => true]);
729
    }
730
731
    /**
732
     * Add column to array.
733
     * @param array $array
734
     * @param mixed $value
735
     * @return array
736
     */
737
    protected static function arrayAddColumn($array, $value)
738
    {
739
        return array_map(
740
            function ($data) use ($value) {
741
                $data[] = $value;
742
                return $data;
743
            },
744
            $array
745
        );
746
    }
747
748
    /**
749
     * \u escape sequence for PHP.
750
     * @param string $text
751
     * @return string
752
     */
753
    protected static function u($text)
754
    {
755
        return json_decode("\"$text\"");
756
    }
757
}
758