IpTest   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 874
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
eloc 608
c 3
b 0
f 0
dl 0
loc 874
rs 9.992
wmc 13

13 Methods

Rating   Name   Duplication   Size   Complexity  
B dataValidationFailed() 0 266 1
B dataOptions() 0 386 1
A testWhen() 0 4 1
A testGetName() 0 4 1
A testSkipOnError() 0 3 1
A testDisableBothIpv4AndIpv6() 0 5 1
A getDifferentRuleInHandlerItems() 0 3 1
A testGetNetworks() 0 4 1
A dataRangesForSubstitution() 0 31 1
A testRangesForSubstitution() 0 4 1
B dataValidationPassed() 0 80 1
A getNetworksData() 0 28 1
A testNetworkAliasException() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Tests\Rule;
6
7
use InvalidArgumentException;
8
use Yiisoft\Validator\Rule\Ip;
9
use Yiisoft\Validator\Rule\IpHandler;
10
use Yiisoft\Validator\Tests\Rule\Base\DifferentRuleInHandlerTestTrait;
11
use Yiisoft\Validator\Tests\Rule\Base\RuleTestCase;
12
use Yiisoft\Validator\Tests\Rule\Base\RuleWithOptionsTestTrait;
13
use Yiisoft\Validator\Tests\Rule\Base\SkipOnErrorTestTrait;
14
use Yiisoft\Validator\Tests\Rule\Base\WhenTestTrait;
15
16
final class IpTest extends RuleTestCase
17
{
18
    use DifferentRuleInHandlerTestTrait;
19
    use RuleWithOptionsTestTrait;
20
    use SkipOnErrorTestTrait;
21
    use WhenTestTrait;
22
23
    public function testGetName(): void
24
    {
25
        $rule = new Ip();
26
        $this->assertSame(Ip::class, $rule->getName());
27
    }
28
29
    public function getNetworksData(): array
30
    {
31
        return [
32
            'default' => [
33
                [],
34
                [
35
                    '*' => ['any'],
36
                    'any' => ['0.0.0.0/0', '::/0'],
37
                    'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
38
                    'multicast' => ['224.0.0.0/4', 'ff00::/8'],
39
                    'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
40
                    'localhost' => ['127.0.0.0/8', '::1'],
41
                    'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
42
                    'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
43
                ],
44
            ],
45
            'custom' => [
46
                ['custom' => ['1.1.1.1/1', '2.2.2.2/2']],
47
                [
48
                    '*' => ['any'],
49
                    'any' => ['0.0.0.0/0', '::/0'],
50
                    'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
51
                    'multicast' => ['224.0.0.0/4', 'ff00::/8'],
52
                    'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
53
                    'localhost' => ['127.0.0.0/8', '::1'],
54
                    'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
55
                    'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
56
                    'custom' => ['1.1.1.1/1', '2.2.2.2/2'],
57
                ],
58
            ],
59
        ];
60
    }
61
62
    /**
63
     * @dataProvider getNetworksData
64
     */
65
    public function testGetNetworks(array $networksArgument, array $expectedNetworks): void
66
    {
67
        $rule = new Ip(networks: $networksArgument);
68
        $this->assertSame($expectedNetworks, $rule->getNetworks());
69
    }
70
71
    public function dataOptions(): array
72
    {
73
        return [
74
            [
75
                new Ip(),
76
                [
77
                    'networks' => [
78
                        '*' => ['any'],
79
                        'any' => ['0.0.0.0/0', '::/0'],
80
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
81
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
82
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
83
                        'localhost' => ['127.0.0.0/8', '::1'],
84
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
85
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
86
                    ],
87
                    'allowIpv4' => true,
88
                    'allowIpv6' => true,
89
                    'allowSubnet' => false,
90
                    'requireSubnet' => false,
91
                    'allowNegation' => false,
92
                    'incorrectInputMessage' => [
93
                        'template' => 'The value must be a string.',
94
                        'parameters' => [],
95
                    ],
96
                    'message' => [
97
                        'template' => 'Must be a valid IP address.',
98
                        'parameters' => [],
99
                    ],
100
                    'ipv4NotAllowedMessage' => [
101
                        'template' => 'Must not be an IPv4 address.',
102
                        'parameters' => [],
103
                    ],
104
                    'ipv6NotAllowedMessage' => [
105
                        'template' => 'Must not be an IPv6 address.',
106
                        'parameters' => [],
107
                    ],
108
                    'wrongCidrMessage' => [
109
                        'template' => 'Contains wrong subnet mask.',
110
                        'parameters' => [],
111
                    ],
112
                    'noSubnetMessage' => [
113
                        'template' => 'Must be an IP address with specified subnet.',
114
                        'parameters' => [],
115
                    ],
116
                    'hasSubnetMessage' => [
117
                        'template' => 'Must not be a subnet.',
118
                        'parameters' => [],
119
                    ],
120
                    'notInRangeMessage' => [
121
                        'template' => 'Is not in the allowed range.',
122
                        'parameters' => [],
123
                    ],
124
                    'ranges' => [],
125
                    'skipOnEmpty' => false,
126
                    'skipOnError' => false,
127
                ],
128
            ],
129
            [
130
                new Ip(allowIpv4: false),
131
                [
132
                    'networks' => [
133
                        '*' => ['any'],
134
                        'any' => ['0.0.0.0/0', '::/0'],
135
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
136
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
137
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
138
                        'localhost' => ['127.0.0.0/8', '::1'],
139
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
140
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
141
                    ],
142
                    'allowIpv4' => false,
143
                    'allowIpv6' => true,
144
                    'allowSubnet' => false,
145
                    'requireSubnet' => false,
146
                    'allowNegation' => false,
147
                    'incorrectInputMessage' => [
148
                        'template' => 'The value must be a string.',
149
                        'parameters' => [],
150
                    ],
151
                    'message' => [
152
                        'template' => 'Must be a valid IP address.',
153
                        'parameters' => [],
154
                    ],
155
                    'ipv4NotAllowedMessage' => [
156
                        'template' => 'Must not be an IPv4 address.',
157
                        'parameters' => [],
158
                    ],
159
                    'ipv6NotAllowedMessage' => [
160
                        'template' => 'Must not be an IPv6 address.',
161
                        'parameters' => [],
162
                    ],
163
                    'wrongCidrMessage' => [
164
                        'template' => 'Contains wrong subnet mask.',
165
                        'parameters' => [],
166
                    ],
167
                    'noSubnetMessage' => [
168
                        'template' => 'Must be an IP address with specified subnet.',
169
                        'parameters' => [],
170
                    ],
171
                    'hasSubnetMessage' => [
172
                        'template' => 'Must not be a subnet.',
173
                        'parameters' => [],
174
                    ],
175
                    'notInRangeMessage' => [
176
                        'template' => 'Is not in the allowed range.',
177
                        'parameters' => [],
178
                    ],
179
                    'ranges' => [],
180
                    'skipOnEmpty' => false,
181
                    'skipOnError' => false,
182
                ],
183
            ],
184
            [
185
                new Ip(allowIpv6: false),
186
                [
187
                    'networks' => [
188
                        '*' => ['any'],
189
                        'any' => ['0.0.0.0/0', '::/0'],
190
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
191
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
192
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
193
                        'localhost' => ['127.0.0.0/8', '::1'],
194
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
195
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
196
                    ],
197
                    'allowIpv4' => true,
198
                    'allowIpv6' => false,
199
                    'allowSubnet' => false,
200
                    'requireSubnet' => false,
201
                    'allowNegation' => false,
202
                    'incorrectInputMessage' => [
203
                        'template' => 'The value must be a string.',
204
                        'parameters' => [],
205
                    ],
206
                    'message' => [
207
                        'template' => 'Must be a valid IP address.',
208
                        'parameters' => [],
209
                    ],
210
                    'ipv4NotAllowedMessage' => [
211
                        'template' => 'Must not be an IPv4 address.',
212
                        'parameters' => [],
213
                    ],
214
                    'ipv6NotAllowedMessage' => [
215
                        'template' => 'Must not be an IPv6 address.',
216
                        'parameters' => [],
217
                    ],
218
                    'wrongCidrMessage' => [
219
                        'template' => 'Contains wrong subnet mask.',
220
                        'parameters' => [],
221
                    ],
222
                    'noSubnetMessage' => [
223
                        'template' => 'Must be an IP address with specified subnet.',
224
                        'parameters' => [],
225
                    ],
226
                    'hasSubnetMessage' => [
227
                        'template' => 'Must not be a subnet.',
228
                        'parameters' => [],
229
                    ],
230
                    'notInRangeMessage' => [
231
                        'template' => 'Is not in the allowed range.',
232
                        'parameters' => [],
233
                    ],
234
                    'ranges' => [],
235
                    'skipOnEmpty' => false,
236
                    'skipOnError' => false,
237
                ],
238
            ],
239
            [
240
                new Ip(allowSubnet: true),
241
                [
242
                    'networks' => [
243
                        '*' => ['any'],
244
                        'any' => ['0.0.0.0/0', '::/0'],
245
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
246
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
247
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
248
                        'localhost' => ['127.0.0.0/8', '::1'],
249
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
250
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
251
                    ],
252
                    'allowIpv4' => true,
253
                    'allowIpv6' => true,
254
                    'allowSubnet' => true,
255
                    'requireSubnet' => false,
256
                    'allowNegation' => false,
257
                    'incorrectInputMessage' => [
258
                        'template' => 'The value must be a string.',
259
                        'parameters' => [],
260
                    ],
261
                    'message' => [
262
                        'template' => 'Must be a valid IP address.',
263
                        'parameters' => [],
264
                    ],
265
                    'ipv4NotAllowedMessage' => [
266
                        'template' => 'Must not be an IPv4 address.',
267
                        'parameters' => [],
268
                    ],
269
                    'ipv6NotAllowedMessage' => [
270
                        'template' => 'Must not be an IPv6 address.',
271
                        'parameters' => [],
272
                    ],
273
                    'wrongCidrMessage' => [
274
                        'template' => 'Contains wrong subnet mask.',
275
                        'parameters' => [],
276
                    ],
277
                    'noSubnetMessage' => [
278
                        'template' => 'Must be an IP address with specified subnet.',
279
                        'parameters' => [],
280
                    ],
281
                    'hasSubnetMessage' => [
282
                        'template' => 'Must not be a subnet.',
283
                        'parameters' => [],
284
                    ],
285
                    'notInRangeMessage' => [
286
                        'template' => 'Is not in the allowed range.',
287
                        'parameters' => [],
288
                    ],
289
                    'ranges' => [],
290
                    'skipOnEmpty' => false,
291
                    'skipOnError' => false,
292
                ],
293
            ],
294
            [
295
                new Ip(requireSubnet: true),
296
                [
297
                    'networks' => [
298
                        '*' => ['any'],
299
                        'any' => ['0.0.0.0/0', '::/0'],
300
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
301
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
302
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
303
                        'localhost' => ['127.0.0.0/8', '::1'],
304
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
305
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
306
                    ],
307
                    'allowIpv4' => true,
308
                    'allowIpv6' => true,
309
                    'allowSubnet' => true,
310
                    'requireSubnet' => true,
311
                    'allowNegation' => false,
312
                    'incorrectInputMessage' => [
313
                        'template' => 'The value must be a string.',
314
                        'parameters' => [],
315
                    ],
316
                    'message' => [
317
                        'template' => 'Must be a valid IP address.',
318
                        'parameters' => [],
319
                    ],
320
                    'ipv4NotAllowedMessage' => [
321
                        'template' => 'Must not be an IPv4 address.',
322
                        'parameters' => [],
323
                    ],
324
                    'ipv6NotAllowedMessage' => [
325
                        'template' => 'Must not be an IPv6 address.',
326
                        'parameters' => [],
327
                    ],
328
                    'wrongCidrMessage' => [
329
                        'template' => 'Contains wrong subnet mask.',
330
                        'parameters' => [],
331
                    ],
332
                    'noSubnetMessage' => [
333
                        'template' => 'Must be an IP address with specified subnet.',
334
                        'parameters' => [],
335
                    ],
336
                    'hasSubnetMessage' => [
337
                        'template' => 'Must not be a subnet.',
338
                        'parameters' => [],
339
                    ],
340
                    'notInRangeMessage' => [
341
                        'template' => 'Is not in the allowed range.',
342
                        'parameters' => [],
343
                    ],
344
                    'ranges' => [],
345
                    'skipOnEmpty' => false,
346
                    'skipOnError' => false,
347
                ],
348
            ],
349
            [
350
                new Ip(allowNegation: true),
351
                [
352
                    'networks' => [
353
                        '*' => ['any'],
354
                        'any' => ['0.0.0.0/0', '::/0'],
355
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
356
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
357
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
358
                        'localhost' => ['127.0.0.0/8', '::1'],
359
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
360
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
361
                    ],
362
                    'allowIpv4' => true,
363
                    'allowIpv6' => true,
364
                    'allowSubnet' => false,
365
                    'requireSubnet' => false,
366
                    'allowNegation' => true,
367
                    'incorrectInputMessage' => [
368
                        'template' => 'The value must be a string.',
369
                        'parameters' => [],
370
                    ],
371
                    'message' => [
372
                        'template' => 'Must be a valid IP address.',
373
                        'parameters' => [],
374
                    ],
375
                    'ipv4NotAllowedMessage' => [
376
                        'template' => 'Must not be an IPv4 address.',
377
                        'parameters' => [],
378
                    ],
379
                    'ipv6NotAllowedMessage' => [
380
                        'template' => 'Must not be an IPv6 address.',
381
                        'parameters' => [],
382
                    ],
383
                    'wrongCidrMessage' => [
384
                        'template' => 'Contains wrong subnet mask.',
385
                        'parameters' => [],
386
                    ],
387
                    'noSubnetMessage' => [
388
                        'template' => 'Must be an IP address with specified subnet.',
389
                        'parameters' => [],
390
                    ],
391
                    'hasSubnetMessage' => [
392
                        'template' => 'Must not be a subnet.',
393
                        'parameters' => [],
394
                    ],
395
                    'notInRangeMessage' => [
396
                        'template' => 'Is not in the allowed range.',
397
                        'parameters' => [],
398
                    ],
399
                    'ranges' => [],
400
                    'skipOnEmpty' => false,
401
                    'skipOnError' => false,
402
                ],
403
            ],
404
            [
405
                new Ip(ranges: ['private']),
406
                [
407
                    'networks' => [
408
                        '*' => ['any'],
409
                        'any' => ['0.0.0.0/0', '::/0'],
410
                        'private' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
411
                        'multicast' => ['224.0.0.0/4', 'ff00::/8'],
412
                        'linklocal' => ['169.254.0.0/16', 'fe80::/10'],
413
                        'localhost' => ['127.0.0.0/8', '::1'],
414
                        'documentation' => ['192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', '2001:db8::/32'],
415
                        'system' => ['multicast', 'linklocal', 'localhost', 'documentation'],
416
                    ],
417
                    'allowIpv4' => true,
418
                    'allowIpv6' => true,
419
                    'allowSubnet' => false,
420
                    'requireSubnet' => false,
421
                    'allowNegation' => false,
422
                    'incorrectInputMessage' => [
423
                        'template' => 'The value must be a string.',
424
                        'parameters' => [],
425
                    ],
426
                    'message' => [
427
                        'template' => 'Must be a valid IP address.',
428
                        'parameters' => [],
429
                    ],
430
                    'ipv4NotAllowedMessage' => [
431
                        'template' => 'Must not be an IPv4 address.',
432
                        'parameters' => [],
433
                    ],
434
                    'ipv6NotAllowedMessage' => [
435
                        'template' => 'Must not be an IPv6 address.',
436
                        'parameters' => [],
437
                    ],
438
                    'wrongCidrMessage' => [
439
                        'template' => 'Contains wrong subnet mask.',
440
                        'parameters' => [],
441
                    ],
442
                    'noSubnetMessage' => [
443
                        'template' => 'Must be an IP address with specified subnet.',
444
                        'parameters' => [],
445
                    ],
446
                    'hasSubnetMessage' => [
447
                        'template' => 'Must not be a subnet.',
448
                        'parameters' => [],
449
                    ],
450
                    'notInRangeMessage' => [
451
                        'template' => 'Is not in the allowed range.',
452
                        'parameters' => [],
453
                    ],
454
                    'ranges' => ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8'],
455
                    'skipOnEmpty' => false,
456
                    'skipOnError' => false,
457
                ],
458
            ],
459
        ];
460
    }
461
462
    public function dataValidationPassed(): array
463
    {
464
        return [
465
            ['192.168.10.11', [new Ip()]],
466
467
            ['10.0.0.1', [new Ip(ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'])]],
468
            ['192.168.5.101', [new Ip(ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'])]],
469
            ['cafe::babe', [new Ip(ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'])]],
470
471
            ['192.168.5.32/11', [new Ip(allowSubnet: true)]],
472
            ['192.168.5.32/32', [new Ip(allowSubnet: true)]],
473
            ['0.0.0.0/0', [new Ip(allowSubnet: true)]],
474
475
            ['10.0.0.1/24', [new Ip(requireSubnet: true)]],
476
            ['10.0.0.1/0', [new Ip(requireSubnet: true)]],
477
            ['!192.168.5.32/32', [new Ip(requireSubnet: true, allowNegation: true)]],
478
479
            ['2008:fa::1', [new Ip()]],
480
            ['2008:00fa::0001', [new Ip()]],
481
            ['2008:fa::1', [new Ip(allowIpv4: false)]],
482
            ['2008:fa::0:1/64', [new Ip(allowIpv4: false, allowSubnet: true)]],
483
            ['2008:fa::0:1/128', [new Ip(allowIpv4: false, allowSubnet: true)]],
484
            ['2008:fa::0:1/0', [new Ip(allowIpv4: false, allowSubnet: true)]],
485
            ['2008:db0::1/64', [new Ip(allowIpv4: false, requireSubnet: true)]],
486
            ['!2008:fa::0:1/64', [new Ip(allowIpv4: false, requireSubnet: true, allowNegation: true)]],
487
488
            ['192.168.10.11', [new Ip()]],
489
            ['2008:fa::1', [new Ip()]],
490
            ['2008:00fa::0001', [new Ip()]],
491
492
            ['2008:fa::1', [new Ip(allowIpv4: false)]],
493
            ['192.168.10.11', [new Ip(allowIpv6: false)]],
494
495
            ['192.168.5.32/11', [new Ip(requireSubnet: true)]],
496
            ['192.168.5.32/32', [new Ip(requireSubnet: true)]],
497
            ['0.0.0.0/0', [new Ip(requireSubnet: true)]],
498
            ['2008:fa::0:1/64', [new Ip(requireSubnet: true)]],
499
            ['2008:fa::0:1/128', [new Ip(requireSubnet: true)]],
500
            ['2008:fa::0:1/0', [new Ip(requireSubnet: true)]],
501
            ['10.0.0.1/24', [new Ip(requireSubnet: true)]],
502
            ['10.0.0.1/0', [new Ip(requireSubnet: true)]],
503
            ['2008:db0::1/64', [new Ip(requireSubnet: true)]],
504
505
            ['!192.168.5.32/32', [new Ip(requireSubnet: true, allowNegation: true)]],
506
            ['!2008:fa::0:1/64', [new Ip(requireSubnet: true, allowNegation: true)]],
507
508
            ['10.0.1.2', [new Ip(ranges: ['10.0.1.0/24'])]],
509
            ['10.0.1.2', [new Ip(ranges: ['10.0.1.0/24'])]],
510
            ['127.0.0.1', [new Ip(ranges: ['!10.0.1.0/24', '10.0.0.0/8', 'localhost'])]],
511
            ['10.0.1.2', [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '!10.0.0.0/8', 'localhost'])]],
512
            ['127.0.0.1', [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '!10.0.0.0/8', 'localhost'])]],
513
            ['10.0.1.28/28', [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '!10.0.0.0/8', 'localhost'])]],
514
515
            ['2001:db0:1:1::6', [new Ip(ranges: ['2001:db0:1:1::/64'])]],
516
            ['2001:db0:1:2::7', [new Ip(ranges: ['2001:db0:1:2::/64'])]],
517
            ['2001:db0:1:2::7', [new Ip(allowSubnet: true, ranges: ['2001:db0:1:2::/64', '!2001:db0::/32'])]],
518
519
            ['10.0.1.2', [new Ip(ranges: ['10.0.1.0/24'])]],
520
            ['2001:db0:1:2::7', [new Ip(ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', '127.0.0.1'])]],
521
            ['10.0.1.2', [new Ip(ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', '127.0.0.1'])]],
522
            ['8.8.8.8', [new Ip(ranges: ['!system', 'any'])]],
523
            [
524
                '10.0.1.2',
525
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', 'localhost', '!any'])],
526
            ],
527
            [
528
                '2001:db0:1:2::7',
529
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', 'localhost', '!any'])],
530
            ],
531
            [
532
                '127.0.0.1',
533
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', 'localhost', '!any'])],
534
            ],
535
            [
536
                '10.0.1.28/28',
537
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', 'localhost', '!any'])],
538
            ],
539
540
            ['1.2.3.4', [new Ip(networks: ['myNetworkEu' => ['1.2.3.4/10', '5.6.7.8']], ranges: ['myNetworkEu'])]],
541
            ['5.6.7.8', [new Ip(networks: ['myNetworkEu' => ['1.2.3.4/10', '5.6.7.8']], ranges: ['myNetworkEu'])]],
542
        ];
543
    }
544
545
    public function dataValidationFailed(): array
546
    {
547
        $incorrectInputMessage = 'The value must be a string.';
548
        $message = 'Must be a valid IP address.';
549
        $hasSubnetMessage = 'Must not be a subnet.';
550
        $notInRangeMessage = 'Is not in the allowed range.';
551
        $ipv4NotAllowedMessage = 'Must not be an IPv4 address.';
552
        $wrongCidrMessage = 'Contains wrong subnet mask.';
553
        $noSubnetMessage = 'Must be an IP address with specified subnet.';
554
        $ipv6NotAllowedMessage = 'Must not be an IPv6 address.';
555
556
        return [
557
            'incorrect input, array' => [['what an array', '??'], [new Ip()], ['' => [$incorrectInputMessage]]],
558
            'incorrect input, integer' => [123456, [new Ip()], ['' => [$incorrectInputMessage]]],
559
            'incorrect input, boolean (true)' => [true, [new Ip()], ['' => [$incorrectInputMessage]]],
560
            'incorrect input, boolean (false)' => [false, [new Ip()], ['' => [$incorrectInputMessage]]],
561
            'incorrect input, null' => [null, [new Ip()], ['' => [$incorrectInputMessage]]],
562
            'custom incorrect input message' => [
563
                1,
564
                [new Ip(incorrectInputMessage: 'Custom incorrect input message.')],
565
                ['' => ['Custom incorrect input message.']],
566
            ],
567
            'custom incorrect input message with parameters' => [
568
                1,
569
                [new Ip(incorrectInputMessage: 'Attribute - {attribute}, type - {type}.')],
570
                ['' => ['Attribute - , type - int.']],
571
            ],
572
            'custom incorrect input message with parameters, attribute set' => [
573
                ['data' => 1],
574
                ['data' => new Ip(incorrectInputMessage: 'Attribute - {attribute}, type - {type}.')],
575
                ['data' => ['Attribute - data, type - int.']],
576
            ],
577
578
            // Small length
579
            ['1', [new Ip()], ['' => [$message]]],
580
            ['1.1.1.', [new Ip()], ['' => [$message]]],
581
            ['1.1.1', [new Ip()], ['' => [$message]]],
582
583
            ['not.an.ip', [new Ip()], ['' => [$message]]],
584
            ['bad:forSure', [new Ip()], ['' => [$message]]],
585
586
            ['2008:fz::0', [new Ip()], ['' => [$message]]],
587
            ['2008:fa::0::1', [new Ip()], ['' => [$message]]],
588
            ['!2008:fa::0::1', [new Ip()], ['' => [$message]]],
589
            ['2008:fa::0:1/64', [new Ip()], ['' => [$hasSubnetMessage]]],
590
            'custom has subnet message' => [
591
                '2008:fa::0:1/64',
592
                [new Ip(hasSubnetMessage: 'Custom has subnet message.')],
593
                ['' => ['Custom has subnet message.']],
594
            ],
595
            'custom has subnet message with parameters' => [
596
                '2008:fa::0:1/64',
597
                [new Ip(hasSubnetMessage: 'Attribute - {attribute}, value - {value}.')],
598
                ['' => ['Attribute - , value - 2008:fa::0:1/64.']],
599
            ],
600
            'custom has subnet message with parameters, attribute set' => [
601
                ['data' => '2008:fa::0:1/64'],
602
                ['data' => new Ip(hasSubnetMessage: 'Attribute - {attribute}, value - {value}.')],
603
                ['data' => ['Attribute - data, value - 2008:fa::0:1/64.']],
604
            ],
605
606
            [
607
                'babe::cafe',
608
                [new Ip(ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'])],
609
                ['' => [$notInRangeMessage]],
610
            ],
611
            [
612
                '10.0.0.2',
613
                [new Ip(ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'])],
614
                ['' => [$notInRangeMessage]],
615
            ],
616
            'custom not in range message' => [
617
                '10.0.0.2',
618
                [
619
                    new Ip(
620
                        notInRangeMessage: 'Custom not in range message.',
621
                        ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'],
622
                    ),
623
                ],
624
                ['' => ['Custom not in range message.']],
625
            ],
626
            'custom not in range message with parameters' => [
627
                '10.0.0.2',
628
                [
629
                    new Ip(
630
                        notInRangeMessage: 'Attribute - {attribute}, value - {value}.',
631
                        ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'],
632
                    ),
633
                ],
634
                ['' => ['Attribute - , value - 10.0.0.2.']],
635
            ],
636
            'custom not in range message with parameters, attribute set' => [
637
                ['data' => '10.0.0.2'],
638
                [
639
                    'data' => new Ip(
640
                        notInRangeMessage: 'Attribute - {attribute}, value - {value}.',
641
                        ranges: ['10.0.0.1', '!10.0.0.0/8', '!babe::/8', 'any'],
642
                    ),
643
                ],
644
                ['data' => ['Attribute - data, value - 10.0.0.2.']],
645
            ],
646
647
            'leading zeroes' => ['192.168.005.001', [new Ip()], ['' => [$message]]],
648
            ['192.168.5.321', [new Ip()], ['' => [$message]]],
649
            ['!192.168.5.32', [new Ip()], ['' => [$message]]],
650
            ['192.168.5.32/11', [new Ip()], ['' => [$hasSubnetMessage]]],
651
            ['192.168.10.11', [new Ip(allowIpv4: false)], ['' => [$ipv4NotAllowedMessage]]],
652
            'custom IPv4 not allowed message' => [
653
                '192.168.10.11',
654
                [new Ip(allowIpv4: false, ipv4NotAllowedMessage: 'Custom IPv4 not allowed message.')],
655
                ['' => ['Custom IPv4 not allowed message.']],
656
            ],
657
            'custom IPv4 not allowed message with parameters' => [
658
                '192.168.10.11',
659
                [new Ip(allowIpv4: false, ipv4NotAllowedMessage: 'Attribute - {attribute}, value - {value}.')],
660
                ['' => ['Attribute - , value - 192.168.10.11.']],
661
            ],
662
            'custom IPv4 not allowed message with parameters, attribute set' => [
663
                ['data' => '192.168.10.11'],
664
                [
665
                    'data' => new Ip(
666
                        allowIpv4: false,
667
                        ipv4NotAllowedMessage: 'Attribute - {attribute}, value - {value}.',
668
                    ),
669
                ],
670
                ['data' => ['Attribute - data, value - 192.168.10.11.']],
671
            ],
672
673
            ['192.168.5.32/33', [new Ip(allowSubnet: true)], ['' => [$wrongCidrMessage]]],
674
            'custom wrong CIDR message' => [
675
                '192.168.5.32/33',
676
                [new Ip(allowSubnet: true, wrongCidrMessage: 'Custom wrong CIDR message.')],
677
                ['' => ['Custom wrong CIDR message.']],
678
            ],
679
            'custom wrong CIDR message with parameters' => [
680
                '192.168.5.32/33',
681
                [new Ip(allowSubnet: true, wrongCidrMessage: 'Attribute - {attribute}, value - {value}.')],
682
                ['' => ['Attribute - , value - 192.168.5.32/33.']],
683
            ],
684
            'custom wrong CIDR message with parameters, attribute set' => [
685
                ['data' => '192.168.5.32/33'],
686
                ['data' => new Ip(allowSubnet: true, wrongCidrMessage: 'Attribute - {attribute}, value - {value}.')],
687
                ['data' => ['Attribute - data, value - 192.168.5.32/33.']],
688
            ],
689
690
            ['192.168.5.32/af', [new Ip(allowSubnet: true)], ['' => [$message]]],
691
            ['192.168.5.32/11/12', [new Ip(allowSubnet: true)], ['' => [$message]]],
692
            ['10.0.0.1', [new Ip(requireSubnet: true)], ['' => [$noSubnetMessage]]],
693
            'custom no subnet message' => [
694
                '10.0.0.1',
695
                [new Ip(requireSubnet: true, noSubnetMessage: 'Custom no subnet message.')],
696
                ['' => ['Custom no subnet message.']],
697
            ],
698
            'custom no subnet message with parameters' => [
699
                '10.0.0.1',
700
                [new Ip(requireSubnet: true, noSubnetMessage: 'Attribute - {attribute}, value - {value}.')],
701
                ['' => ['Attribute - , value - 10.0.0.1.']],
702
            ],
703
            'custom no subnet message with parameters, attribute set' => [
704
                ['data' => '10.0.0.1'],
705
                ['data' => new Ip(requireSubnet: true, noSubnetMessage: 'Attribute - {attribute}, value - {value}.')],
706
                ['data' => ['Attribute - data, value - 10.0.0.1.']],
707
            ],
708
709
            ['!!192.168.5.32/32', [new Ip(requireSubnet: true, allowNegation: true)], ['' => [$message]]],
710
711
            ['!2008:fa::0:1/0', [new Ip(allowIpv4: false, allowSubnet: true)], ['' => [$message]]],
712
            ['2008:fz::0/129', [new Ip(allowIpv4: false, allowSubnet: true)], ['' => [$message]]],
713
            ['2008:db0::1', [new Ip(allowIpv4: false, requireSubnet: true)], ['' => [$noSubnetMessage]]],
714
            [
715
                '!!2008:fa::0:1/64',
716
                [new Ip(allowIpv4: false, requireSubnet: true, allowNegation: true)],
717
                ['' => [$message]],
718
            ],
719
720
            ['2008:fa::1', [new Ip(allowIpv6: false)], ['' => [$ipv6NotAllowedMessage]]],
721
            'custom IPv6 not allowed message' => [
722
                '2008:fa::1',
723
                [new Ip(allowIpv6: false, ipv6NotAllowedMessage: 'Custom IPv6 not allowed message.')],
724
                ['' => ['Custom IPv6 not allowed message.']],
725
            ],
726
            'custom IPv6 not allowed message with parameters' => [
727
                '2008:fa::1',
728
                [new Ip(allowIpv6: false, ipv6NotAllowedMessage: 'Attribute - {attribute}, value - {value}.')],
729
                ['' => ['Attribute - , value - 2008:fa::1.']],
730
            ],
731
            'custom IPv6 not allowed message with parameters, attribute set' => [
732
                ['data' => '2008:fa::1'],
733
                [
734
                    'data' => new Ip(
735
                        allowIpv6: false,
736
                        ipv6NotAllowedMessage: 'Attribute - {attribute}, value - {value}.',
737
                    ),
738
                ],
739
                ['data' => ['Attribute - data, value - 2008:fa::1.']],
740
            ],
741
742
            ['!2008:fa::0:1/0', [new Ip(requireSubnet: true)], ['' => [$message]]],
743
            ['2008:fz::0/129', [new Ip(requireSubnet: true)], ['' => [$message]]],
744
            ['192.168.5.32/33', [new Ip(requireSubnet: true)], ['' => [$wrongCidrMessage]]],
745
            ['192.168.5.32/af', [new Ip(requireSubnet: true)], ['' => [$message]]],
746
            ['192.168.5.32/11/12', [new Ip(requireSubnet: true)], ['' => [$message]]],
747
            ['2008:db0::1', [new Ip(requireSubnet: true)], ['' => [$noSubnetMessage]]],
748
            ['10.0.0.1', [new Ip(requireSubnet: true)], ['' => [$noSubnetMessage]]],
749
            ['!!192.168.5.32/32', [new Ip(requireSubnet: true, allowNegation: true)], ['' => [$message]]],
750
            ['!!2008:fa::0:1/64', [new Ip(requireSubnet: true, allowNegation: true)], ['' => [$message]]],
751
752
            ['192.5.1.1', [new Ip(ranges: ['10.0.1.0/24'])], ['' => [$notInRangeMessage]]],
753
            ['10.0.3.2', [new Ip(ranges: ['10.0.1.0/24'])], ['' => [$notInRangeMessage]]],
754
            ['10.0.1.2', [new Ip(ranges: ['!10.0.1.0/24', '10.0.0.0/8', 'localhost'])], ['' => [$notInRangeMessage]]],
755
            [
756
                '10.2.2.2',
757
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '!10.0.0.0/8', 'localhost'])],
758
                ['' => [$notInRangeMessage]],
759
            ],
760
            [
761
                '10.0.1.1/22',
762
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '!10.0.0.0/8', 'localhost'])],
763
                ['' => [$notInRangeMessage]],
764
            ],
765
            ['2001:db0:1:2::7', [new Ip(ranges: ['2001:db0:1:1::/64'])], ['' => [$notInRangeMessage]]],
766
            [
767
                '2001:db0:1:2::7',
768
                [new Ip(ranges: ['!2001:db0::/32', '2001:db0:1:2::/64'])],
769
                ['' => [$notInRangeMessage]],
770
            ],
771
772
            ['192.5.1.1', [new Ip(ranges: ['10.0.1.0/24'])], ['' => [$notInRangeMessage]]],
773
            ['2001:db0:1:2::7', [new Ip(ranges: ['10.0.1.0/24'])], ['' => [$notInRangeMessage]]],
774
            [
775
                '10.0.3.2',
776
                [new Ip(ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', '127.0.0.1'])],
777
                ['' => [$notInRangeMessage]],
778
            ],
779
            ['127.0.0.1', [new Ip(ranges: ['!system', 'any'])], ['' => [$notInRangeMessage]]],
780
            ['fe80::face', [new Ip(ranges: ['!system', 'any'])], ['' => [$notInRangeMessage]]],
781
782
            [
783
                '10.2.2.2',
784
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', 'localhost', '!any'])],
785
                ['' => [$notInRangeMessage]],
786
            ],
787
            [
788
                '10.0.1.1/22',
789
                [new Ip(allowSubnet: true, ranges: ['10.0.1.0/24', '2001:db0:1:2::/64', 'localhost', '!any'])],
790
                ['' => [$notInRangeMessage]],
791
            ],
792
793
            ['01.01.01.01', [new Ip()], ['' => [$message]]],
794
            ['010.010.010.010', [new Ip()], ['' => [$message]]],
795
            ['001.001.001.001', [new Ip()], ['' => [$message]]],
796
797
            'custom message' => [
798
                '192.168.5.32/af',
799
                [new Ip(allowSubnet: true, message: 'Custom message.')],
800
                ['' => ['Custom message.']],
801
            ],
802
            'custom message with parameters' => [
803
                '192.168.5.32/af',
804
                [new Ip(allowSubnet: true, message: 'Attribute - {attribute}, value - {value}.')],
805
                ['' => ['Attribute - , value - 192.168.5.32/af.']],
806
            ],
807
            'custom message with parameters, attribute set' => [
808
                ['data' => '192.168.5.32/af'],
809
                ['data' => new Ip(allowSubnet: true, message: 'Attribute - {attribute}, value - {value}.')],
810
                ['data' => ['Attribute - data, value - 192.168.5.32/af.']],
811
            ],
812
        ];
813
    }
814
815
    public function testNetworkAliasException(): void
816
    {
817
        $this->expectException(InvalidArgumentException::class);
818
        $this->expectExceptionMessage('Network alias "*" already set as default');
819
        new Ip(networks: ['*' => ['wrong']], ranges: ['*']);
820
    }
821
822
    public function dataRangesForSubstitution(): array
823
    {
824
        return [
825
            'ipv4' => [['10.0.0.1'], ['10.0.0.1']],
826
            'any' => [['192.168.0.32', 'fa::/32', 'any'], ['192.168.0.32', 'fa::/32', '0.0.0.0/0', '::/0']],
827
            'ipv4+!private' => [
828
                ['10.0.0.1', '!private'],
829
                ['10.0.0.1', '!10.0.0.0/8', '!172.16.0.0/12', '!192.168.0.0/16', '!fd00::/8'],
830
            ],
831
            'private+!system' => [
832
                ['private', '!system'],
833
                [
834
                    '10.0.0.0/8',
835
                    '172.16.0.0/12',
836
                    '192.168.0.0/16',
837
                    'fd00::/8',
838
                    '!224.0.0.0/4',
839
                    '!ff00::/8',
840
                    '!169.254.0.0/16',
841
                    '!fe80::/10',
842
                    '!127.0.0.0/8',
843
                    '!::1',
844
                    '!192.0.2.0/24',
845
                    '!198.51.100.0/24',
846
                    '!203.0.113.0/24',
847
                    '!2001:db8::/32',
848
                ],
849
            ],
850
            'containing duplicates' => [
851
                ['10.0.0.1', '10.0.0.2', '10.0.0.2', '10.0.0.3'],
852
                ['10.0.0.1', '10.0.0.2', 3 => '10.0.0.3'],
853
            ],
854
        ];
855
    }
856
857
    /**
858
     * @param string[] $ranges
859
     * @param string[] $expectedRanges
860
     *
861
     * @dataProvider dataRangesForSubstitution
862
     */
863
    public function testRangesForSubstitution(array $ranges, array $expectedRanges): void
864
    {
865
        $rule = new Ip(ranges: $ranges);
866
        $this->assertSame($expectedRanges, $rule->getRanges());
867
    }
868
869
    public function testDisableBothIpv4AndIpv6(): void
870
    {
871
        $this->expectException(InvalidArgumentException::class);
872
        $this->expectExceptionMessage('Both IPv4 and IPv6 checks can not be disabled at the same time');
873
        new Ip(allowIpv4: false, allowIpv6: false);
874
    }
875
876
    public function testSkipOnError(): void
877
    {
878
        $this->testSkipOnErrorInternal(new Ip(), new Ip(skipOnError: true));
879
    }
880
881
    public function testWhen(): void
882
    {
883
        $when = static fn (mixed $value): bool => $value !== null;
884
        $this->testWhenInternal(new Ip(), new Ip(when: $when));
885
    }
886
887
    protected function getDifferentRuleInHandlerItems(): array
888
    {
889
        return [Ip::class, IpHandler::class];
890
    }
891
}
892