Completed
Push — master ( af922d...0bf27c )
by Dmitry
02:54
created

testValidDomainWithEnabledIdn()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

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