Passed
Pull Request — master (#521)
by
unknown
03:08
created

CompareTest::testValidationFailed()

Size

Total Lines 7
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 7
c 0
b 0
f 0
eloc 1
nc 1
nop 3
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Tests\Rule;
6
7
use DateTime;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use stdClass;
11
use Stringable;
12
use Yiisoft\Validator\DataSetInterface;
13
use Yiisoft\Validator\DataWrapperInterface;
14
use Yiisoft\Validator\Rule\Compare;
15
use Yiisoft\Validator\Rule\CompareType;
16
use Yiisoft\Validator\RuleInterface;
17
use Yiisoft\Validator\Tests\Rule\Base\RuleTestCase;
18
use Yiisoft\Validator\Tests\Rule\Base\RuleWithOptionsTestTrait;
19
use Yiisoft\Validator\Tests\Rule\Base\SkipOnErrorTestTrait;
20
use Yiisoft\Validator\Tests\Rule\Base\WhenTestTrait;
21
22
use function is_string;
23
24
final class CompareTest extends RuleTestCase
25
{
26
    use RuleWithOptionsTestTrait;
27
    use SkipOnErrorTestTrait;
28
    use WhenTestTrait;
29
30
    public function testInitWithWrongType(): void
31
    {
32
        $this->expectException(InvalidArgumentException::class);
33
        $message = 'Type "float" is not supported. The valid types are: "original", "string", "number".';
34
        $this->expectExceptionMessage($message);
35
36
        new Compare(type: 'float');
37
    }
38
39
    public function testInitWithWrongOperator(): void
40
    {
41
        $this->expectException(InvalidArgumentException::class);
42
        $message = 'Operator "=" is not supported. The valid operators are: "==", "===", "!=", "!==", ">", ">=", ' .
43
            '"<", "<=".';
44
        $this->expectExceptionMessage($message);
45
46
        new Compare(1, operator: '=');
47
    }
48
49
    public function testGetName(): void
50
    {
51
        $rule = new Compare();
52
        $this->assertSame('compare', $rule->getName());
53
    }
54
55
    public function dataOptions(): array
56
    {
57
        return [
58
            [
59
                new Compare(1),
60
                [
61
                    'targetValue' => 1,
62
                    'targetAttribute' => null,
63
                    'incorrectInputMessage' => [
64
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
65
                        'parameters' => [
66
                            'targetValue' => 1,
67
                            'targetAttribute' => null,
68
                            'targetValueOrAttribute' => 1,
69
                        ],
70
                    ],
71
                    'incorrectDataSetTypeMessage' => [
72
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
73
                        'parameters' => [
74
                            'targetValue' => 1,
75
                            'targetAttribute' => null,
76
                            'targetValueOrAttribute' => 1,
77
                        ],
78
                    ],
79
                    'message' => [
80
                        'template' => 'Value must be equal to "{targetValueOrAttribute}".',
81
                        'parameters' => [
82
                            'targetValue' => 1,
83
                            'targetAttribute' => null,
84
                            'targetValueOrAttribute' => 1,
85
                        ],
86
                    ],
87
                    'type' => 'string',
88
                    'operator' => '==',
89
                    'skipOnEmpty' => false,
90
                    'skipOnError' => false,
91
                ],
92
            ],
93
            [
94
                new Compare(1, type: CompareType::NUMBER),
95
                [
96
                    'targetValue' => 1,
97
                    'targetAttribute' => null,
98
                    'incorrectInputMessage' => [
99
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
100
                        'parameters' => [
101
                            'targetValue' => 1,
102
                            'targetAttribute' => null,
103
                            'targetValueOrAttribute' => 1,
104
                        ],
105
                    ],
106
                    'incorrectDataSetTypeMessage' => [
107
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
108
                        'parameters' => [
109
                            'targetValue' => 1,
110
                            'targetAttribute' => null,
111
                            'targetValueOrAttribute' => 1,
112
                        ],
113
                    ],
114
                    'message' => [
115
                        'template' => 'Value must be equal to "{targetValueOrAttribute}".',
116
                        'parameters' => [
117
                            'targetValue' => 1,
118
                            'targetAttribute' => null,
119
                            'targetValueOrAttribute' => 1,
120
                        ],
121
                    ],
122
                    'type' => 'number',
123
                    'operator' => '==',
124
                    'skipOnEmpty' => false,
125
                    'skipOnError' => false,
126
                ],
127
            ],
128
            [
129
                new Compare(1, type: CompareType::NUMBER, operator: '>='),
130
                [
131
                    'targetValue' => 1,
132
                    'targetAttribute' => null,
133
                    'incorrectInputMessage' => [
134
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
135
                        'parameters' => [
136
                            'targetValue' => 1,
137
                            'targetAttribute' => null,
138
                            'targetValueOrAttribute' => 1,
139
                        ],
140
                    ],
141
                    'incorrectDataSetTypeMessage' => [
142
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
143
                        'parameters' => [
144
                            'targetValue' => 1,
145
                            'targetAttribute' => null,
146
                            'targetValueOrAttribute' => 1,
147
                        ],
148
                    ],
149
                    'message' => [
150
                        'template' => 'Value must be greater than or equal to "{targetValueOrAttribute}".',
151
                        'parameters' => [
152
                            'targetValue' => 1,
153
                            'targetAttribute' => null,
154
                            'targetValueOrAttribute' => 1,
155
                        ],
156
                    ],
157
                    'type' => 'number',
158
                    'operator' => '>=',
159
                    'skipOnEmpty' => false,
160
                    'skipOnError' => false,
161
                ],
162
            ],
163
            [
164
                new Compare('YES'),
165
                [
166
                    'targetValue' => 'YES',
167
                    'targetAttribute' => null,
168
                    'incorrectInputMessage' => [
169
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
170
                        'parameters' => [
171
                            'targetValue' => 'YES',
172
                            'targetAttribute' => null,
173
                            'targetValueOrAttribute' => 'YES',
174
                        ],
175
                    ],
176
                    'incorrectDataSetTypeMessage' => [
177
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
178
                        'parameters' => [
179
                            'targetValue' => 'YES',
180
                            'targetAttribute' => null,
181
                            'targetValueOrAttribute' => 'YES',
182
                        ],
183
                    ],
184
                    'message' => [
185
                        'template' => 'Value must be equal to "{targetValueOrAttribute}".',
186
                        'parameters' => [
187
                            'targetValue' => 'YES',
188
                            'targetAttribute' => null,
189
                            'targetValueOrAttribute' => 'YES',
190
                        ],
191
                    ],
192
                    'type' => 'string',
193
                    'operator' => '==',
194
                    'skipOnEmpty' => false,
195
                    'skipOnError' => false,
196
                ],
197
            ],
198
            [
199
                new Compare('YES', skipOnEmpty: true),
200
                [
201
                    'targetValue' => 'YES',
202
                    'targetAttribute' => null,
203
                    'incorrectInputMessage' => [
204
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
205
                        'parameters' => [
206
                            'targetValue' => 'YES',
207
                            'targetAttribute' => null,
208
                            'targetValueOrAttribute' => 'YES',
209
                        ],
210
                    ],
211
                    'incorrectDataSetTypeMessage' => [
212
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
213
                        'parameters' => [
214
                            'targetValue' => 'YES',
215
                            'targetAttribute' => null,
216
                            'targetValueOrAttribute' => 'YES',
217
                        ],
218
                    ],
219
                    'message' => [
220
                        'template' => 'Value must be equal to "{targetValueOrAttribute}".',
221
                        'parameters' => [
222
                            'targetValue' => 'YES',
223
                            'targetAttribute' => null,
224
                            'targetValueOrAttribute' => 'YES',
225
                        ],
226
                    ],
227
                    'type' => 'string',
228
                    'operator' => '==',
229
                    'skipOnEmpty' => true,
230
                    'skipOnError' => false,
231
                ],
232
            ],
233
            [
234
                new Compare('YES', operator: '!=='),
235
                [
236
                    'targetValue' => 'YES',
237
                    'targetAttribute' => null,
238
                    'incorrectInputMessage' => [
239
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
240
                        'parameters' => [
241
                            'targetValue' => 'YES',
242
                            'targetAttribute' => null,
243
                            'targetValueOrAttribute' => 'YES',
244
                        ],
245
                    ],
246
                    'incorrectDataSetTypeMessage' => [
247
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
248
                        'parameters' => [
249
                            'targetValue' => 'YES',
250
                            'targetAttribute' => null,
251
                            'targetValueOrAttribute' => 'YES',
252
                        ],
253
                    ],
254
                    'message' => [
255
                        'template' => 'Value must not be equal to "{targetValueOrAttribute}".',
256
                        'parameters' => [
257
                            'targetValue' => 'YES',
258
                            'targetAttribute' => null,
259
                            'targetValueOrAttribute' => 'YES',
260
                        ],
261
                    ],
262
                    'type' => 'string',
263
                    'operator' => '!==',
264
                    'skipOnEmpty' => false,
265
                    'skipOnError' => false,
266
                ],
267
            ],
268
            [
269
                new Compare('YES', message: 'Custom message for {targetValueOrAttribute}.'),
270
                [
271
                    'targetValue' => 'YES',
272
                    'targetAttribute' => null,
273
                    'incorrectInputMessage' => [
274
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
275
                        'parameters' => [
276
                            'targetValue' => 'YES',
277
                            'targetAttribute' => null,
278
                            'targetValueOrAttribute' => 'YES',
279
                        ],
280
                    ],
281
                    'incorrectDataSetTypeMessage' => [
282
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
283
                        'parameters' => [
284
                            'targetValue' => 'YES',
285
                            'targetAttribute' => null,
286
                            'targetValueOrAttribute' => 'YES',
287
                        ],
288
                    ],
289
                    'message' => [
290
                        'template' => 'Custom message for {targetValueOrAttribute}.',
291
                        'parameters' => [
292
                            'targetValue' => 'YES',
293
                            'targetAttribute' => null,
294
                            'targetValueOrAttribute' => 'YES',
295
                        ],
296
                    ],
297
                    'type' => 'string',
298
                    'operator' => '==',
299
                    'skipOnEmpty' => false,
300
                    'skipOnError' => false,
301
                ],
302
            ],
303
            [
304
                new Compare(null, 'test'),
305
                [
306
                    'targetValue' => null,
307
                    'targetAttribute' => 'test',
308
                    'incorrectInputMessage' => [
309
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
310
                        'parameters' => [
311
                            'targetValue' => null,
312
                            'targetAttribute' => 'test',
313
                            'targetValueOrAttribute' => 'test',
314
                        ],
315
                    ],
316
                    'incorrectDataSetTypeMessage' => [
317
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
318
                        'parameters' => [
319
                            'targetValue' => null,
320
                            'targetAttribute' => 'test',
321
                            'targetValueOrAttribute' => 'test',
322
                        ],
323
                    ],
324
                    'message' => [
325
                        'template' => 'Value must be equal to "{targetValueOrAttribute}".',
326
                        'parameters' => [
327
                            'targetValue' => null,
328
                            'targetAttribute' => 'test',
329
                            'targetValueOrAttribute' => 'test',
330
                        ],
331
                    ],
332
                    'type' => 'string',
333
                    'operator' => '==',
334
                    'skipOnEmpty' => false,
335
                    'skipOnError' => false,
336
                ],
337
            ],
338
            [
339
                new Compare(
340
                    null,
341
                    'test',
342
                    incorrectInputMessage: 'Custom message 1.',
343
                    incorrectDataSetTypeMessage: 'Custom message 2.',
344
                    message: 'Custom message 3.',
345
                ),
346
                [
347
                    'targetValue' => null,
348
                    'targetAttribute' => 'test',
349
                    'incorrectInputMessage' => [
350
                        'template' => 'Custom message 1.',
351
                        'parameters' => [
352
                            'targetValue' => null,
353
                            'targetAttribute' => 'test',
354
                            'targetValueOrAttribute' => 'test',
355
                        ],
356
                    ],
357
                    'incorrectDataSetTypeMessage' => [
358
                        'template' => 'Custom message 2.',
359
                        'parameters' => [
360
                            'targetValue' => null,
361
                            'targetAttribute' => 'test',
362
                            'targetValueOrAttribute' => 'test',
363
                        ],
364
                    ],
365
                    'message' => [
366
                        'template' => 'Custom message 3.',
367
                        'parameters' => [
368
                            'targetValue' => null,
369
                            'targetAttribute' => 'test',
370
                            'targetValueOrAttribute' => 'test',
371
                        ],
372
                    ],
373
                    'type' => 'string',
374
                    'operator' => '==',
375
                    'skipOnEmpty' => false,
376
                    'skipOnError' => false,
377
                ],
378
            ],
379
            [
380
                new Compare(1, 'test'),
381
                [
382
                    'targetValue' => 1,
383
                    'targetAttribute' => 'test',
384
                    'incorrectInputMessage' => [
385
                        'template' => 'The allowed types are integer, float, string, boolean and null.',
386
                        'parameters' => [
387
                            'targetValue' => 1,
388
                            'targetAttribute' => 'test',
389
                            'targetValueOrAttribute' => 1,
390
                        ],
391
                    ],
392
                    'incorrectDataSetTypeMessage' => [
393
                        'template' => 'The attribute value returned from a custom data set must have a scalar type or be null.',
394
                        'parameters' => [
395
                            'targetValue' => 1,
396
                            'targetAttribute' => 'test',
397
                            'targetValueOrAttribute' => 1,
398
                        ],
399
                    ],
400
                    'message' => [
401
                        'template' => 'Value must be equal to "{targetValueOrAttribute}".',
402
                        'parameters' => [
403
                            'targetValue' => 1,
404
                            'targetAttribute' => 'test',
405
                            'targetValueOrAttribute' => 1,
406
                        ],
407
                    ],
408
                    'type' => 'string',
409
                    'operator' => '==',
410
                    'skipOnEmpty' => false,
411
                    'skipOnError' => false,
412
                ],
413
            ],
414
        ];
415
    }
416
417
    public function dataValidationPassed(): array
418
    {
419
        $targetStringableFloat = new class () implements Stringable {
420
            public function __toString(): string
421
            {
422
                return '100.5';
423
            }
424
        };
425
        $stringableFloat = new class () implements Stringable
426
        {
427
            public function __toString(): string
428
            {
429
                return '100.50';
430
            }
431
        };
432
        $targetStringableUuid = new class () implements Stringable {
433
            public function __toString(): string
434
            {
435
                return '3b98a689-7d49-48bb-8741-7e27f220b69a';
436
            }
437
        };
438
        $stringableUuid = new class () implements Stringable
439
        {
440
            public function __toString(): string
441
            {
442
                return 'd62f2b3f-707f-451a-8819-046ff8436a4f';
443
            }
444
        };
445
446
        return [
447
            // Number specific, expressions
448
449
            'target value: float, value: float with the same value as expression result, type: number, operator: ==' => [
450
                1 - 0.83,
451
                [new Compare(0.17, type: CompareType::NUMBER)],
452
            ],
453
            'target value: float, value: float with the same value as expression result, type: number, operator: ===' => [
454
                1 - 0.83,
455
                [new Compare(0.17, type: CompareType::NUMBER, operator: '===')],
456
            ],
457
            'target value: float, value: float with the same value as expression result, type: number, operator: >=' => [
458
                1 - 0.83,
459
                [new Compare(0.17, type: CompareType::NUMBER, operator: '>=')],
460
            ],
461
462
            // Number / original specific, decimal places, directly provided values
463
464
            'target value: string float, value: string float with the same value, but extra decimal place (0), type: number, operator: ==' => [
465
                '100.50',
466
                [new Compare('100.5', type: CompareType::NUMBER)],
467
            ],
468
            'target value: float, value: string float with the same value, but extra decimal place (0), type: number, operator: ==' => [
469
                '100.50',
470
                [new Compare(100.5, type: CompareType::NUMBER)],
471
            ],
472
            'target value: string float, value: string float with the same value, but extra decimal place (0), type: number, operator: ===' => [
473
                '100.50',
474
                [new Compare('100.5', type: CompareType::NUMBER, operator: '===')],
475
            ],
476
            'target value: string float, value: string float with the same value, but extra decimal place (0), type: original, operator: ==' => [
477
                '100.50', [new Compare('100.5', type: CompareType::ORIGINAL)], ['' => ['Value must be equal to "100.5".']]
478
            ],
479
480
            // Number / original specific, decimal places, values provided via stringable objects
481
482
            'target value: stringable float, value: stringable float with the same value, but extra decimal place (0), type: number, operator: ==' => [
483
                $stringableFloat,
484
                [new Compare($targetStringableFloat, type: CompareType::NUMBER)],
485
            ],
486
            'target value: stringable float, value: stringable float with the same value, but extra decimal place (0), type: number, operator: >=' => [
487
                $stringableFloat,
488
                [new Compare($targetStringableFloat, type: CompareType::NUMBER, operator: '>=')],
489
            ],
490
491
            // String / original specific, character order, directly provided values
492
493
            'target value: uuidv4, value: greater uuidv4, type: string, operator: >' => [
494
                'd62f2b3f-707f-451a-8819-046ff8436a4f',
495
                [new Compare('3b98a689-7d49-48bb-8741-7e27f220b69a', operator: '>')],
496
            ],
497
            'target value: character, value: character located further within alphabet, type: string, operator: ==' => [
498
                'b',
499
                [new Compare('a', operator: '>')],
500
            ],
501
502
            // String / original specific, character order, values provided via stringable objects
503
504
            'target value: stringable uuidv4, value: greater stringable uuidv4, type: string, operator: >' => [
505
                $stringableUuid,
506
                [new Compare($targetStringableUuid, operator: '>')],
507
            ],
508
509
            // Original specific, datetime objects
510
511
            'target value: DateTime object, value: DateTime object with the same value, type: original, operator: ==' => [
512
                new DateTime('2023-02-01 12:57:12'),
513
                [new Compare(new DateTime('2023-02-01 12:57:12'), type: CompareType::ORIGINAL)],
514
            ],
515
            'target value: DateTime object, value: DateTime object with the same value, type: original, operator: !==' => [
516
                new DateTime('2023-02-01 12:57:12'),
517
                [new Compare(new DateTime('2023-02-01 12:57:12'), type: CompareType::ORIGINAL, operator: '!==')],
518
            ],
519
            'target value: DateTime object, value: DateTime object with the same value, type: original, operator: >=' => [
520
                new DateTime('2023-02-01 12:57:12'),
521
                [new Compare(new DateTime('2023-02-01 12:57:12'), type: CompareType::ORIGINAL, operator: '>=')],
522
            ],
523
        ];
524
    }
525
526
    public function dataValidationPassedWithDifferentTypes(): array
527
    {
528
        $customDataSet = new class () implements DataSetInterface {
529
            public function getAttributeValue(string $attribute): mixed
530
            {
531
                return 100;
532
            }
533
534
            public function getData(): ?array
535
            {
536
                return null;
537
            }
538
539
            public function hasAttribute(string $attribute): bool
540
            {
541
                return true;
542
            }
543
        };
544
        $initialData = [
545
            // Basic
546
547
            'target value: integer, value: integer with the same value, type: string, operator: ==' => [
548
                100,
549
                [new Compare(100)],
550
            ],
551
            'target value: integer, value: integer with the same value, type: string, operator: ===' => [
552
                100,
553
                [new Compare(100, operator: '===')],
554
            ],
555
            'target value: integer, value: lower integer, type: string, operator: !=' => [
556
                99,
557
                [new Compare(100, operator: '!=')],
558
            ],
559
            'target value: integer, value: greater integer, type: string, operator: !=' => [
560
                101,
561
                [new Compare(100, operator: '!=')],
562
            ],
563
            'target value: integer, value: lower integer, type: string, operator: !==' => [
564
                101,
565
                [new Compare(100, operator: '!==')],
566
            ],
567
            'target value: integer, value: greater integer, type: string, operator: !==' => [
568
                101,
569
                [new Compare(100, operator: '!==')],
570
            ],
571
            'target value: integer, value: greater integer, type: string, operator: >' => [
572
                101,
573
                [new Compare(100, operator: '>')],
574
            ],
575
            'target value: integer, value: integer with the same value, type: string, operator: >=' => [
576
                100,
577
                [new Compare(100, operator: '>=')],
578
            ],
579
            'target value: integer, value: greater integer, type: string, operator: >=' => [
580
                101,
581
                [new Compare(100, operator: '>=')],
582
            ],
583
            'target value: integer, value: lower integer, type: string, operator: <' => [
584
                99,
585
                [new Compare(100, operator: '<')],
586
            ],
587
            'target value: integer, value: integer with the same value, type: string, operator: <=' => [
588
                100,
589
                [new Compare(100, operator: '<=')],
590
            ],
591
            'target value: integer, value: lower integer, type: string, operator: <=' => [
592
                99,
593
                [new Compare(100, operator: '<=')],
594
            ],
595
596
            // Boolean
597
598
            'target value: boolean (false), value: boolean (true), type: string, operator: >=' => [
599
                true,
600
                [new Compare(false, operator: '>=')],
601
            ],
602
603
            // Different types for non-strict equality
604
605
            'target value: empty string, value: null, type: string, operator: ==' => [
606
                null,
607
                [new Compare('')],
608
            ],
609
            'target value: integer, value: string integer with the same value, type: string, operator: ==' => [
610
                '100',
611
                [new Compare(100)],
612
            ],
613
614
            // Different types for non-strict inequality
615
616
            'target value: integer, value: float, type: string, operator: !=' => [
617
                100.00001,
618
                [new Compare(100, operator: '!=')],
619
            ],
620
            'target value: integer, value: boolean, type: string, operator: !=' => [
621
                false,
622
                [new Compare(100, operator: '!=')],
623
            ],
624
625
            // Different types for strict inequality
626
627
            'target value: integer, value: boolean, type: string, operator: !==' => [
628
                false,
629
                [new Compare(100, operator: '!==')],
630
            ],
631
            'target value: integer, value: string integer with the same value, type: string, operator: !==' => [
632
                '100',
633
                [new Compare(100, operator: '!==')],
634
            ],
635
            'target value: integer, value: float with the same value, but extra decimal place (0), type: string, operator: !==' => [
636
                100.0,
637
                [new Compare(100, operator: '!==')],
638
            ],
639
640
            // Target attribute
641
642
            'target attribute: array key, target attribute value: integer, attribute value: integer with the same value, type: string, operator: ==' => [
643
                ['attribute' => 100, 'number' => 100],
644
                ['number' => new Compare(targetAttribute: 'attribute')],
645
            ],
646
            'target attribute: array key, target attribute value: integer, attribute value: lower integer, type: string, operator: <=' => [
647
                ['attribute' => 100, 'number' => 99],
648
                ['number' => new Compare(targetAttribute: 'attribute', operator: '<=')],
649
            ],
650
            'target attribute: object property, target attribute value: integer, attribute value: integer with the same value, type: string, operator: ==' => [
651
                new class () {
652
                    public int $attribute = 100;
653
                    public int $number = 100;
654
                },
655
                ['number' => new Compare(targetAttribute: 'attribute', operator: '<=')],
656
            ],
657
            'target attribute: custom data set attribute, target attribute value: integer, attribute value: integer with the same value, type: string, operator: ==' => [
658
                $customDataSet,
659
                ['number' => new Compare(targetAttribute: 'attribute', operator: '<=')],
660
            ],
661
        ];
662
663
        return $this->extendDataWithDifferentTypes($initialData);
664
    }
665
666
    /**
667
     * @dataProvider dataValidationPassed
668
     * @dataProvider dataValidationPassedWithDifferentTypes
669
     */
670
    public function testValidationPassed(mixed $data, ?array $rules = null): void
671
    {
672
        parent::testValidationPassed($data, $rules);
673
    }
674
675
    public function dataValidationFailed(): array
676
    {
677
        $incorrectDataSet = new class () implements DataWrapperInterface {
678
            public function getAttributeValue(string $attribute): mixed
679
            {
680
                return new stdClass();
681
            }
682
683
            public function getData(): ?array
684
            {
685
                return null;
686
            }
687
688
            public function getSource(): mixed
689
            {
690
                return false;
691
            }
692
693
            public function hasAttribute(string $attribute): bool
694
            {
695
                return false;
696
            }
697
        };
698
        $targetStringableFloat = new class () implements Stringable {
699
            public function __toString(): string
700
            {
701
                return '100.5';
702
            }
703
        };
704
        $stringableFloat = new class () implements Stringable
705
        {
706
            public function __toString(): string
707
            {
708
                return '100.50';
709
            }
710
        };
711
712
        return [
713
            // Incorrect input
714
715
            'incorrect input' => [
716
                [],
717
                [new Compare(false)],
718
                ['' => ['The allowed types are integer, float, string, boolean and null.']],
719
            ],
720
            'custom incorrect input message' => [
721
                [],
722
                [new Compare(false, incorrectInputMessage: 'Custom incorrect input message.')],
723
                ['' => ['Custom incorrect input message.']],
724
            ],
725
            'custom incorrect input message with parameters' => [
726
                [],
727
                [new Compare(false, incorrectInputMessage: 'Attribute - {attribute}, type - {type}.')],
728
                ['' => ['Attribute - , type - array.']],
729
            ],
730
            'custom incorrect input message with parameters, attribute set' => [
731
                ['data' => []],
732
                ['data' => new Compare(false, incorrectInputMessage: 'Attribute - {attribute}, type - {type}.')],
733
                ['data' => ['Attribute - data, type - array.']],
734
            ],
735
736
            // Incorrect data set input
737
738
            'incorrect data set type' => [
739
                $incorrectDataSet,
740
                [new Compare(targetAttribute: 'test')],
741
                ['' => ['The attribute value returned from a custom data set must have a scalar type or be null.']],
742
            ],
743
            'custom incorrect data set type message' => [
744
                $incorrectDataSet,
745
                [
746
                    new Compare(
747
                        targetAttribute: 'test',
748
                        incorrectDataSetTypeMessage: 'Custom incorrect data set type message.',
749
                    ),
750
                ],
751
                ['' => ['Custom incorrect data set type message.']],
752
            ],
753
            'custom incorrect data set type message with parameters' => [
754
                $incorrectDataSet,
755
                [
756
                    new Compare(
757
                        targetAttribute: 'test',
758
                        incorrectDataSetTypeMessage: 'Type - {type}.',
759
                    ),
760
                ],
761
                ['' => ['Type - stdClass.']],
762
            ],
763
764
            // Custom message
765
766
            'custom message' => [101, [new Compare(100, message: 'Custom message.')], ['' => ['Custom message.']]],
767
            'custom message with parameters, target value set' => [
768
                101,
769
                [
770
                    new Compare(
771
                        100,
772
                        message: 'Attribute - {attribute}, target value - {targetValue}, target attribute - ' .
773
                        '{targetAttribute}, target value or attribute - {targetValueOrAttribute}, value - {value}.',
774
                    ),
775
                ],
776
                [
777
                    '' => [
778
                        'Attribute - , target value - 100, target attribute - , target value or attribute - 100, ' .
779
                        'value - 101.',
780
                    ],
781
                ],
782
            ],
783
            'custom message with parameters, attribute and target attribute set' => [
784
                ['attribute' => 100, 'number' => 101],
785
                [
786
                    'number' => new Compare(
787
                        null,
788
                        'attribute',
789
                        message: 'Attribute - {attribute}, target value - {targetValue}, target attribute - ' .
790
                        '{targetAttribute}, target value or attribute - {targetValueOrAttribute}, value - {value}.',
791
                        operator: '===',
792
                    ),
793
                ],
794
                [
795
                    'number' => [
796
                        'Attribute - number, target value - , target attribute - attribute, target value or ' .
797
                        'attribute - 100, value - 101.',
798
                    ],
799
                ],
800
            ],
801
802
            // String / original specific, falsy values
803
804
            'target value: integer (0), value: null, type: string, operator: ==' => [
805
                null,
806
                [new Compare(0)],
807
                ['' => ['Value must be equal to "0".']],
808
            ],
809
810
            // Number / original specific, decimal places, directly provided values
811
812
            'target value: string float, value: string float with the same value, but extra decimal place (0), type: string, operator: ==' => [
813
                '100.50', [new Compare('100.5')], ['' => ['Value must be equal to "100.5".']]
814
            ],
815
            'target value: string float, value: string float with the same value, but extra decimal place (0), type: string, operator: ===' => [
816
                '100.50', [new Compare('100.5', operator: '===')], ['' => ['Value must be equal to "100.5".']]
817
            ],
818
            'target value: string float, value: string float with the same value, but extra decimal place (0), type: original, operator: ===' => [
819
                '100.50', [new Compare('100.5', type: CompareType::ORIGINAL, operator: '===')], ['' => ['Value must be equal to "100.5".']]
820
            ],
821
822
            // Number / original specific, decimal places, values provided via stringable objects
823
824
            'target value: stringable float, value: stringable float with the same value, but extra decimal place (0), type: string, operator: ==' => [
825
                $stringableFloat,
826
                [new Compare($targetStringableFloat)],
827
                ['' => ['Value must be equal to "100.5".']],
828
            ],
829
            'target value: stringable float, value: stringable float with the same value, but extra decimal place (0), type: string, operator: ===' => [
830
                $stringableFloat,
831
                [new Compare($targetStringableFloat, operator: '===')],
832
                ['' => ['Value must be equal to "100.5".']],
833
            ],
834
            'target value: stringable float, value: stringable float with the same value, but extra decimal place (0), type: original, operator: ==' => [
835
                $stringableFloat,
836
                [new Compare($targetStringableFloat, type: CompareType::ORIGINAL)],
837
                ['' => ['Value must be equal to "100.5".']],
838
            ],
839
            'target value: stringable float, value: stringable float with the same value, but extra decimal place (0), type: original, operator: ===' => [
840
                $stringableFloat,
841
                [new Compare($targetStringableFloat, type: CompareType::ORIGINAL, operator: '===')],
842
                ['' => ['Value must be equal to "100.5".']],
843
            ],
844
        ];
845
    }
846
847
    public function dataValidationFailedWithDifferentTypes(): array
848
    {
849
        $messageEqual = 'Value must be equal to "100".';
850
        $messageNotEqual = 'Value must not be equal to "100".';
851
        $messageGreaterThan = 'Value must be greater than "100".';
852
        $messageGreaterOrEqualThan = 'Value must be greater than or equal to "100".';
853
        $messageLessThan = 'Value must be less than "100".';
854
        $messageLessOrEqualThan = 'Value must be less than or equal to "100".';
855
        $initialData = [
856
            // Basic
857
858
            'target value: integer, value: lower integer, type: string, operator: ==' => [
859
                99,
860
                [new Compare(100)],
861
                ['' => [$messageEqual]],
862
            ],
863
            'target value: integer, value: greater integer, type: string, operator: ==' => [
864
                101,
865
                [new Compare(100)],
866
                ['' => [$messageEqual]],
867
            ],
868
            'target value: integer, value: lower integer, type: string, operator: ===' => [
869
                99,
870
                [new Compare(100, operator: '===')],
871
                ['' => [$messageEqual]],
872
            ],
873
            'target value: integer, value: greater integer, type: string, operator: ===' => [
874
                101,
875
                [new Compare(100, operator: '===')],
876
                ['' => [$messageEqual]],
877
            ],
878
            'target value: integer, value: integer with the same value, type: string, operator: !=' => [
879
                100,
880
                [new Compare(100, operator: '!=')],
881
                ['' => [$messageNotEqual]],
882
            ],
883
            'target value: integer, value: integer with the same value, type: string, operator: !==' => [
884
                100,
885
                [new Compare(100, operator: '!==')],
886
                ['' => [$messageNotEqual]],
887
            ],
888
            'target value: integer, value: integer with the same value, type: string, operator: >' => [
889
                100,
890
                [new Compare(100, operator: '>')],
891
                ['' => [$messageGreaterThan]],
892
            ],
893
            'target value: integer, value: lower integer, type: string, operator: >' => [
894
                99,
895
                [new Compare(100, operator: '>')],
896
                ['' => [$messageGreaterThan]],
897
            ],
898
            'target value: integer, value: lower integer, type: string, operator: >=' => [
899
                99,
900
                [new Compare(100, operator: '>=')],
901
                ['' => [$messageGreaterOrEqualThan]],
902
            ],
903
            'target value: integer, value: integer with the same value, type: string, operator: <' => [
904
                100,
905
                [new Compare(100, operator: '<')],
906
                ['' => [$messageLessThan]],
907
            ],
908
            'target value: integer, value: greater integer, type: string, operator: <' => [
909
                101,
910
                [new Compare(100, operator: '<')],
911
                ['' => [$messageLessThan]],
912
            ],
913
            'target value: integer, value: greater integer, type: string, operator: <=' => [
914
                101,
915
                [new Compare(100, operator: '<=')],
916
                ['' => [$messageLessOrEqualThan]],
917
            ],
918
919
            // Different types for strict equality
920
921
            'target value: empty string, value: null, type: string, operator: ===' => [
922
                null,
923
                [new Compare('', operator: '===')],
924
                ['' => ['Value must be equal to "".']],
925
            ],
926
            'target value: integer, value: string integer with the same value, type: string, operator: ===' => [
927
                '100',
928
                [new Compare(100, operator: '===')],
929
                ['' => [$messageEqual]],
930
            ],
931
            'target value: integer, value: float with the same value, but extra decimal place (0), type: string, operator: ===' => [
932
                100.0,
933
                [new Compare(100, operator: '===')],
934
                ['' => [$messageEqual]],
935
            ],
936
937
            // Different types for non-strict inequality
938
939
            'target value: integer, value: string integer with the same value, type: string, operator: !=' => [
940
                '100',
941
                [new Compare(100, operator: '!=')],
942
                ['' => [$messageNotEqual]],
943
            ],
944
            'target value: integer, value: float with the same value, but extra decimal place (0), type: string, operator: !=' => [
945
                100.0,
946
                [new Compare(100, operator: '!=')],
947
                ['' => [$messageNotEqual]],
948
            ],
949
950
            // Target attribute
951
952
            'target attribute: array key, target attribute value: string integer, attribute value: integer with the same value, type: string, operator: ===' => [
953
                ['attribute' => '100', 'number' => 100],
954
                ['number' => new Compare(null, 'attribute', operator: '===')],
955
                ['number' => [$messageEqual]],
956
            ],
957
            'target attribute: array key, target attribute value: integer, attribute value: greater integer, type: string, operator: <=' => [
958
                ['attribute' => 100, 'number' => 101],
959
                ['number' => new Compare(null, 'attribute', operator: '<=')],
960
                ['number' => [$messageLessOrEqualThan]],
961
            ],
962
        ];
963
964
        return $this->extendDataWithDifferentTypes($initialData);
965
    }
966
967
    /**
968
     * @dataProvider dataValidationFailed
969
     * @dataProvider dataValidationFailedWithDifferentTypes
970
     */
971
    public function testValidationFailed(
972
        mixed $data,
973
        array|RuleInterface|null $rules,
974
        array $errorMessagesIndexedByPath,
975
    ): void
976
    {
0 ignored issues
show
Coding Style introduced by
The closing parenthesis and the opening brace of a multi-line function declaration must be on the same line
Loading history...
977
        parent::testValidationFailed($data, $rules, $errorMessagesIndexedByPath);
978
    }
979
980
    private function extendDataWithDifferentTypes(array $initialData): array
981
    {
982
        $dynamicData = [];
983
        $mainType = CompareType::STRING;
984
        $remainingTypes = [CompareType::ORIGINAL, CompareType::NUMBER];
985
        foreach ($remainingTypes as $type) {
986
            foreach ($initialData as $key => $item) {
987
                $rules = [];
988
                foreach ($item[1] as $attribute => $rule) {
989
                    if (!$rule instanceof Compare) {
990
                        throw new RuntimeException('Wrong format for rule.');
991
                    }
992
993
                    $rules[$attribute] = new Compare(
994
                        targetValue: $rule->getTargetValue(),
995
                        targetAttribute: $rule->getTargetAttribute(),
996
                        type: $type,
997
                        operator: $rule->getOperator(),
998
                    );
999
                }
1000
1001
                if (!is_string($key)) {
1002
                    throw new RuntimeException('Data set must have a string name.');
1003
                }
1004
1005
                $newKey = str_replace(", type: $mainType,", ", type: $type,", $key);
1006
                if ($key === $newKey) {
1007
                    throw new RuntimeException('Wrong format for type.');
1008
                }
1009
1010
                $itemData = [$item[0], $rules];
1011
                if (isset($item[2])) {
1012
                    $itemData[] = $item[2];
1013
                }
1014
1015
                $dynamicData[$newKey] = $itemData;
1016
            }
1017
        }
1018
1019
        return array_merge($initialData, $dynamicData);
1020
    }
1021
1022
    public function testSkipOnError(): void
1023
    {
1024
        $this->testSkipOnErrorInternal(new Compare(), new Compare(skipOnError: true));
1025
    }
1026
1027
    public function testWhen(): void
1028
    {
1029
        $when = static fn (mixed $value): bool => $value !== null;
1030
        $this->testWhenInternal(new Compare(), new Compare(when: $when));
1031
    }
1032
}
1033