Passed
Pull Request — master (#550)
by Alexander
05:08 queued 02:32
created

anonymous//tests/Rule/CompositeTest.php$4   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 5
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 5
rs 10
c 1
b 0
f 0
wmc 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Tests\Rule;
6
7
use InvalidArgumentException;
8
use Yiisoft\Validator\Result;
9
use Yiisoft\Validator\Rule\Composite;
10
use Yiisoft\Validator\Rule\CompositeHandler;
11
use Yiisoft\Validator\Rule\Equal;
12
use Yiisoft\Validator\Rule\Length;
13
use Yiisoft\Validator\Rule\Number;
14
use Yiisoft\Validator\Rule\Required;
15
use Yiisoft\Validator\Tests\Rule\Base\DifferentRuleInHandlerTestTrait;
16
use Yiisoft\Validator\Tests\Rule\Base\RuleTestCase;
17
use Yiisoft\Validator\Tests\Rule\Base\RuleWithOptionsTestTrait;
18
use Yiisoft\Validator\Tests\Rule\Base\SkipOnErrorTestTrait;
19
use Yiisoft\Validator\Tests\Rule\Base\WhenTestTrait;
20
use Yiisoft\Validator\Tests\Support\Rule\CoordinatesRuleSet;
21
use Yiisoft\Validator\Tests\Support\Rule\RuleWithoutOptions;
22
23
final class CompositeTest extends RuleTestCase
24
{
25
    use DifferentRuleInHandlerTestTrait;
26
    use RuleWithOptionsTestTrait;
27
    use SkipOnErrorTestTrait;
28
    use WhenTestTrait;
29
30
    public function testGetName(): void
31
    {
32
        $rule = new Composite([]);
33
        $this->assertSame('composite', $rule->getName());
34
    }
35
36
    public function dataOptions(): array
37
    {
38
        return [
39
            [
40
                new Composite([
41
                    new Number(max: 13, pattern: '/1/'),
42
                    new Number(max: 14, pattern: '/2/'),
43
                ]),
44
                [
45
                    'skipOnEmpty' => false,
46
                    'skipOnError' => false,
47
                    'rules' => [
48
                        [
49
                            'number',
50
                            'min' => null,
51
                            'max' => 13,
52
                            'incorrectInputMessage' => [
53
                                'template' => 'The allowed types are integer, float and string.',
54
                                'parameters' => [],
55
                            ],
56
                            'notNumberMessage' => [
57
                                'template' => 'Value must be a number.',
58
                                'parameters' => [],
59
                            ],
60
                            'lessThanMinMessage' => [
61
                                'template' => 'Value must be no less than {min}.',
62
                                'parameters' => ['min' => null],
63
                            ],
64
                            'greaterThanMaxMessage' => [
65
                                'template' => 'Value must be no greater than {max}.',
66
                                'parameters' => ['max' => 13],
67
                            ],
68
                            'skipOnEmpty' => false,
69
                            'skipOnError' => false,
70
                            'pattern' => '/1/',
71
                        ],
72
                        [
73
                            'number',
74
                            'min' => null,
75
                            'max' => 14,
76
                            'incorrectInputMessage' => [
77
                                'template' => 'The allowed types are integer, float and string.',
78
                                'parameters' => [],
79
                            ],
80
                            'notNumberMessage' => [
81
                                'template' => 'Value must be a number.',
82
                                'parameters' => [],
83
                            ],
84
                            'lessThanMinMessage' => [
85
                                'template' => 'Value must be no less than {min}.',
86
                                'parameters' => ['min' => null],
87
                            ],
88
                            'greaterThanMaxMessage' => [
89
                                'template' => 'Value must be no greater than {max}.',
90
                                'parameters' => ['max' => 14],
91
                            ],
92
                            'skipOnEmpty' => false,
93
                            'skipOnError' => false,
94
                            'pattern' => '/2/',
95
                        ],
96
                    ],
97
                ],
98
            ],
99
            'rule without options' => [
100
                new Composite([
101
                    new Number(max: 13, pattern: '/1/'),
102
                    new RuleWithoutOptions(),
103
                ]),
104
                [
105
                    'skipOnEmpty' => false,
106
                    'skipOnError' => false,
107
                    'rules' => [
108
                        [
109
                            'number',
110
                            'min' => null,
111
                            'max' => 13,
112
                            'incorrectInputMessage' => [
113
                                'template' => 'The allowed types are integer, float and string.',
114
                                'parameters' => [],
115
                            ],
116
                            'notNumberMessage' => [
117
                                'template' => 'Value must be a number.',
118
                                'parameters' => [],
119
                            ],
120
                            'lessThanMinMessage' => [
121
                                'template' => 'Value must be no less than {min}.',
122
                                'parameters' => [
123
                                    'min' => null,
124
                                ],
125
                            ],
126
                            'greaterThanMaxMessage' => [
127
                                'template' => 'Value must be no greater than {max}.',
128
                                'parameters' => [
129
                                    'max' => 13,
130
                                ],
131
                            ],
132
                            'skipOnEmpty' => false,
133
                            'skipOnError' => false,
134
                            'pattern' => '/1/',
135
                        ],
136
                        [
137
                            'test',
138
                        ],
139
                    ],
140
                ],
141
            ],
142
            'callable' => [
143
                new Composite([
144
                    static fn () => (new Result())->addError('Bad value.'),
145
                ]),
146
                [
147
                    'skipOnEmpty' => false,
148
                    'skipOnError' => false,
149
                    'rules' => [
150
                        [
151
                            'callback',
152
                            'method' => null,
153
                            'skipOnEmpty' => false,
154
                            'skipOnError' => false,
155
                        ],
156
                    ],
157
                ],
158
            ],
159
            'inheritance' => [
160
                new class () extends Composite {
161
                    public function getRules(): iterable
162
                    {
163
                        return [
164
                            new Required(),
165
                        ];
166
                    }
167
168
                    public function getOptions(): array
169
                    {
170
                        return [
171
                            'specific-key' => 42,
172
                            'rules' => $this->dumpRulesAsArray(),
173
                        ];
174
                    }
175
                },
176
                [
177
                    'specific-key' => 42,
178
                    'rules' => [
179
                        [
180
                            'required',
181
                            'message' => [
182
                                'template' => 'Value cannot be blank.',
183
                                'parameters' => [],
184
                            ],
185
                            'notPassedMessage' => [
186
                                'template' => 'Value not passed.',
187
                                'parameters' => [],
188
                            ],
189
                            'skipOnError' => false,
190
                        ],
191
                    ],
192
                ],
193
            ],
194
        ];
195
    }
196
197
    public function testOptionsWithNotRule(): void
198
    {
199
        $rule = new Composite([
200
            new Number(max: 13, pattern: '/1/'),
201
            new class () {
202
            },
203
        ]);
204
205
        $this->expectException(InvalidArgumentException::class);
206
        $message = 'Rule must be either an instance of Yiisoft\Validator\RuleInterface or a callable, ' .
207
            'class@anonymous given.';
208
        $this->expectExceptionMessage($message);
209
        $rule->getOptions();
210
    }
211
212
    public function dataValidationPassed(): array
213
    {
214
        return [
215
            [
216
                20,
217
                [
218
                    new Composite(
219
                        [
220
                            new Number(max: 13),
221
                            new Number(max: 14),
222
                        ],
223
                        when: fn () => false,
224
                    ),
225
                ],
226
            ],
227
            'override constructor' => [
228
                20,
229
                [
230
                    new class () extends Composite {
231
                        public function __construct()
232
                        {
233
                        }
234
                    },
235
                ],
236
            ],
237
            [
238
                null,
239
                [
240
                    new Composite(
241
                        [
242
                            new Number(max: 13),
243
                            new Number(max: 14),
244
                        ],
245
                        skipOnEmpty: true,
246
                    ),
247
                ],
248
            ],
249
            'multiple attributes' => [
250
                ['latitude' => -90, 'longitude' => 180],
251
                [new CoordinatesRuleSet()],
252
            ],
253
        ];
254
    }
255
256
    public function dataValidationFailed(): array
257
    {
258
        return [
259
            'callable' => [
260
                20,
261
                [
262
                    new Composite([
263
                        static fn () => (new Result())->addError('Bad value.'),
264
                        static fn () => (new Result())->addError('Very bad value.'),
265
                    ]),
266
                ],
267
                [
268
                    '' => [
269
                        'Bad value.',
270
                        'Very bad value.',
271
                    ],
272
                ],
273
            ],
274
            'when true' => [
275
                20,
276
                [
277
                    new Composite(
278
                        [new Number(max: 13), new Number(min: 21)],
279
                        when: fn () => true,
280
                    ),
281
                ],
282
                [
283
                    '' => [
284
                        'Value must be no greater than 13.',
285
                        'Value must be no less than 21.',
286
                    ],
287
                ],
288
            ],
289
            'skip on error with previous error' => [
290
                20,
291
                [
292
                    new Equal(19),
293
                    new Composite(
294
                        [new Number(max: 13)],
295
                        skipOnError: true,
296
                    ),
297
                ],
298
                [
299
                    '' => ['Value must be equal to "19".'],
300
                ],
301
            ],
302
            'skip on error without previous error' => [
303
                20,
304
                [
305
                    new Composite(
306
                        [new Number(max: 13)],
307
                        skipOnError: true,
308
                    ),
309
                ],
310
                [
311
                    '' => ['Value must be no greater than 13.'],
312
                ],
313
            ],
314
            'custom error' => [
315
                20,
316
                [
317
                    new Composite(
318
                        [new Number(max: 13, greaterThanMaxMessage: 'Custom error')],
319
                        when: fn () => true,
320
                    ),
321
                ],
322
                ['' => ['Custom error']],
323
            ],
324
            'override constructor' => [
325
                null,
326
                [
327
                    new class () extends Composite {
328
                        public function __construct()
329
                        {
330
                            $this->rules = [new Required()];
331
                        }
332
                    },
333
                ],
334
                ['' => ['Value cannot be blank.']],
335
            ],
336
            'multiple attributes' => [
337
                ['latitude' => -91, 'longitude' => 181],
338
                [new CoordinatesRuleSet()],
339
                [
340
                    'latitude' => ['Value must be no less than -90.'],
341
                    'longitude' => ['Value must be no greater than 180.'],
342
                ],
343
            ],
344
            'attribute with multiple rules' => [
345
                [],
346
                new class () extends Composite {
347
                    public function getRules(): array
348
                    {
349
                        return [
350
                            'name' => [new Required(), new Length(min: 1)],
351
                        ];
352
                    }
353
                },
354
                [
355
                    'name' => [
356
                        'Value not passed.',
357
                        'The value must be a string.',
358
                    ],
359
                ],
360
            ],
361
        ];
362
    }
363
364
    public function testSkipOnError(): void
365
    {
366
        $this->testSkipOnErrorInternal(new Composite([]), new Composite([], skipOnError: true));
367
    }
368
369
    public function testWhen(): void
370
    {
371
        $when = static fn (mixed $value): bool => $value !== null;
372
        $this->testWhenInternal(new Composite([]), new Composite([], when: $when));
373
    }
374
375
    protected function getDifferentRuleInHandlerItems(): array
376
    {
377
        return [Composite::class, CompositeHandler::class];
378
    }
379
}
380