Passed
Pull Request — master (#412)
by
unknown
17:58 queued 15:20
created

objectWithDataSetAndRulesProviderDataProvider()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 0
dl 0
loc 9
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Validator\Tests\DataSet;
6
7
use InvalidArgumentException;
8
use PHPUnit\Framework\TestCase;
9
use ReflectionProperty;
10
use stdClass;
11
use Traversable;
12
use Yiisoft\Validator\DataSet\ObjectDataSet;
13
use Yiisoft\Validator\Rule\Callback;
14
use Yiisoft\Validator\Rule\Equal;
15
use Yiisoft\Validator\Rule\HasLength;
16
use Yiisoft\Validator\Rule\Required;
17
use Yiisoft\Validator\RuleInterface;
18
use Yiisoft\Validator\Tests\Support\Data\ObjectWithCallbackMethod\ObjectWithCallbackMethod;
19
use Yiisoft\Validator\Tests\Support\Data\ObjectWithCallbackMethod\ObjectWithNonExistingCallbackMethod;
20
use Yiisoft\Validator\Tests\Support\Data\ObjectWithDataSet;
21
use Yiisoft\Validator\Tests\Support\Data\ObjectWithDataSetAndRulesProvider;
22
use Yiisoft\Validator\Tests\Support\Data\ObjectWithDifferentPropertyVisibility;
23
use Yiisoft\Validator\Tests\Support\Data\ObjectWithDynamicDataSet;
24
use Yiisoft\Validator\Tests\Support\Data\ObjectWithRulesProvider;
25
use Yiisoft\Validator\Tests\Support\Data\Post;
26
use Yiisoft\Validator\Tests\Support\Rule\RuleWithCallsCount;
27
use Yiisoft\Validator\Tests\Support\Data\TitleTrait;
28
use Yiisoft\Validator\Tests\Support\Rule\NotRuleAttribute;
29
use Yiisoft\Validator\Tests\Support\ValidatorFactory;
30
31
final class ObjectDataSetTest extends TestCase
32
{
33
    public function propertyVisibilityDataProvider(): array
34
    {
35
        return [
36
            [
37
                new ObjectDataSet(new ObjectWithDifferentPropertyVisibility()),
38
                ['name' => '', 'age' => 17, 'number' => 42],
39
                ['name' => '', 'age' => 17, 'number' => 42, 'non-exist' => null],
40
                ['name', 'age', 'number'],
41
            ],
42
            [
43
                new ObjectDataSet(new ObjectWithDifferentPropertyVisibility(), ReflectionProperty::IS_PRIVATE),
44
                ['number' => 42],
45
                ['name' => null, 'age' => null, 'number' => 42, 'non-exist' => null],
46
                ['number'],
47
            ],
48
            [
49
                new ObjectDataSet(new ObjectWithDifferentPropertyVisibility(), ReflectionProperty::IS_PROTECTED),
50
                ['age' => 17],
51
                ['name' => null, 'age' => 17, 'number' => null, 'non-exist' => null],
52
                ['age'],
53
            ],
54
            [
55
                new ObjectDataSet(new ObjectWithDifferentPropertyVisibility(), ReflectionProperty::IS_PUBLIC),
56
                ['name' => ''],
57
                ['name' => '', 'age' => null, 'number' => null, 'non-exist' => null],
58
                ['name'],
59
            ],
60
            [
61
                new ObjectDataSet(
62
                    new ObjectWithDifferentPropertyVisibility(),
63
                    ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED
64
                ),
65
                ['name' => '', 'age' => 17],
66
                ['name' => '', 'age' => 17, 'number' => null, 'non-exist' => null],
67
                ['name', 'age'],
68
            ],
69
        ];
70
    }
71
72
    /**
73
     * @dataProvider propertyVisibilityDataProvider
74
     */
75
    public function testPropertyVisibility(
76
        ObjectDataSet $initialDataSet,
77
        array $expectedData,
78
        array $expectedAttributeValuesMap,
79
        array $expectedRulesKeys
80
    ): void {
81
        $dataSets = [
82
            $initialDataSet,
83
            $initialDataSet, // Not a duplicate. Used to test caching.
84
        ];
85
        foreach ($dataSets as $dataSet) {
86
            /** @var ObjectDataSet $dataSet */
87
88
            $this->assertSame($expectedData, $dataSet->getData());
89
90
            foreach ($expectedAttributeValuesMap as $attribute => $value) {
91
                $this->assertSame($value, $dataSet->getAttributeValue($attribute));
92
            }
93
94
            $this->assertSame($expectedRulesKeys, array_keys($dataSet->getRules()));
95
        }
96
    }
97
98
    public function objectWithDataSetDataProvider(): array
99
    {
100
        $dataSet = new ObjectDataSet(new ObjectWithDataSet());
101
102
        return [
103
            [new ObjectDataSet(new ObjectWithDataSet())],
104
            [new ObjectDataSet(new ObjectWithDataSet())], // Not a duplicate. Used to test caching.
105
            [$dataSet],
106
            [$dataSet], // Not a duplicate. Used to test caching.
107
        ];
108
    }
109
110
    /**
111
     * @dataProvider objectWithDataSetDataProvider
112
     */
113
    public function testObjectWithDataSet(ObjectDataSet $dataSet): void
114
    {
115
        $this->assertSame(['key1' => 7, 'key2' => 42], $dataSet->getData());
116
        $this->assertSame(7, $dataSet->getAttributeValue('key1'));
117
        $this->assertSame(42, $dataSet->getAttributeValue('key2'));
118
119
        $this->assertNull($dataSet->getAttributeValue('name'));
120
        $this->assertNull($dataSet->getAttributeValue('age'));
121
        $this->assertNull($dataSet->getAttributeValue('number'));
122
        $this->assertNull($dataSet->getAttributeValue('non-exist'));
123
124
        $this->assertSame([], $dataSet->getRules());
125
    }
126
127
    public function objectWithRulesProvider(): array
128
    {
129
        $dataSet = new ObjectDataSet(new ObjectWithRulesProvider());
130
131
        return [
132
            [new ObjectDataSet(new ObjectWithRulesProvider())],
133
            [new ObjectDataSet(new ObjectWithRulesProvider())], // Not a duplicate. Used to test caching.
134
            [$dataSet],
135
            [$dataSet], // Not a duplicate. Used to test caching.
136
        ];
137
    }
138
139
    /**
140
     * @dataProvider objectWithRulesProvider
141
     */
142
    public function testObjectWithRulesProvider(ObjectDataSet $dataSet): void
143
    {
144
        $rules = $dataSet->getRules();
145
146
        $this->assertSame(['name' => '', 'age' => 17, 'number' => 42], $dataSet->getData());
147
148
        $this->assertSame('', $dataSet->getAttributeValue('name'));
149
        $this->assertSame(17, $dataSet->getAttributeValue('age'));
150
        $this->assertSame(42, $dataSet->getAttributeValue('number'));
151
        $this->assertNull($dataSet->getAttributeValue('non-exist'));
152
153
        $this->assertSame(['age'], array_keys($rules));
154
        $this->assertCount(2, $rules['age']);
155
        $this->assertInstanceOf(Required::class, $rules['age'][0]);
156
        $this->assertInstanceOf(Equal::class, $rules['age'][1]);
157
    }
158
159
    public function objectWithDataSetAndRulesProviderDataProvider(): array
160
    {
161
        $dataSet = new ObjectDataSet(new ObjectWithDataSetAndRulesProvider());
162
163
        return [
164
            [new ObjectDataSet(new ObjectWithDataSetAndRulesProvider())],
165
            [new ObjectDataSet(new ObjectWithDataSetAndRulesProvider())], // Not a duplicate. Used to test caching.
166
            [$dataSet],
167
            [$dataSet], // Not a duplicate. Used to test caching.
168
        ];
169
    }
170
171
    /**
172
     * @dataProvider objectWithDataSetAndRulesProviderDataProvider
173
     */
174
    public function testObjectWithDataSetAndRulesProvider(ObjectDataSet $dataSet): void
175
    {
176
        $rules = $dataSet->getRules();
177
178
        $this->assertSame(['key1' => 7, 'key2' => 42], $dataSet->getData());
179
        $this->assertSame(7, $dataSet->getAttributeValue('key1'));
180
        $this->assertSame(42, $dataSet->getAttributeValue('key2'));
181
182
        $this->assertNull($dataSet->getAttributeValue('name'));
183
        $this->assertNull($dataSet->getAttributeValue('age'));
184
        $this->assertNull($dataSet->getAttributeValue('number'));
185
        $this->assertNull($dataSet->getAttributeValue('non-exist'));
186
187
        $this->assertSame(['key1', 'key2'], array_keys($rules));
188
        $this->assertCount(1, $rules['key1']);
189
        $this->assertInstanceOf(Required::class, $rules['key1'][0]);
190
        $this->assertCount(2, $rules['key2']);
191
        $this->assertInstanceOf(Required::class, $rules['key2'][0]);
192
        $this->assertInstanceOf(Equal::class, $rules['key2'][1]);
193
    }
194
195
    /**
196
     * @dataProvider dataCollectRules
197
     *
198
     * @param RuleInterface[]|RuleInterface[][]|RuleInterface[][][] $expectedRules
199
     */
200
    public function testCollectRules(object $object, array $expectedRules): void
201
    {
202
        $dataSet = new ObjectDataSet($object);
203
204
        $actualRules = [];
205
        foreach ($dataSet->getRules() as $attribute => $rules) {
206
            $actualRules[$attribute] = $rules instanceof Traversable ? iterator_to_array($rules) : (array) $rules;
207
        }
208
209
        $this->assertEquals($expectedRules, $actualRules);
210
    }
211
212
    public function dataCollectRules(): array
213
    {
214
        return [
215
            [
216
                new class () {
217
                },
218
                [],
219
            ],
220
            [
221
                new class () {
222
                    private $property1;
223
                },
224
                [],
225
            ],
226
            [
227
                new class () {
228
                    #[NotRuleAttribute]
229
                    private $property1;
230
                },
231
                [],
232
            ],
233
            [
234
                new class () {
235
                    #[Required()]
236
                    private $property1;
237
                },
238
                [
239
                    'property1' => [
240
                        new Required(),
241
                    ],
242
                ],
243
            ],
244
            [
245
                new class () {
246
                    use TitleTrait;
247
                },
248
                [
249
                    'title' => [
250
                        new HasLength(max: 255),
251
                    ],
252
                ],
253
            ],
254
            [
255
                new class () {
256
                    #[Required()]
257
                    #[HasLength(max: 255, skipOnEmpty: true)]
258
                    private $property1;
259
                    #[Required()]
260
                    #[HasLength(max: 255, skipOnEmpty: true)]
261
                    private $property2;
262
                },
263
                [
264
                    'property1' => [
265
                        new Required(),
266
                        new HasLength(max: 255, skipOnEmpty: true),
267
                    ],
268
                    'property2' => [
269
                        new Required(),
270
                        new HasLength(max: 255, skipOnEmpty: true),
271
                    ],
272
                ],
273
            ],
274
            [
275
                new class () {
276
                    #[HasLength(max: 255, skipOnEmpty: true)]
277
                    #[HasLength(max: 255, skipOnEmpty: false)]
278
                    private $property1;
279
                },
280
                [
281
                    'property1' => [
282
                        new HasLength(max: 255, skipOnEmpty: true),
283
                        new HasLength(max: 255, skipOnEmpty: false),
284
                    ],
285
                ],
286
            ],
287
        ];
288
    }
289
290
    /**
291
     * @link https://github.com/yiisoft/validator/issues/198
292
     */
293
    public function testGetRulesViaTraits(): void
294
    {
295
        $dataSet = new ObjectDataSet(new Post());
296
        $expectedRules = ['title' => [new HasLength(max: 255)]];
297
298
        $this->assertEquals($expectedRules, $dataSet->getRules());
299
    }
300
301
    /**
302
     * @link https://github.com/yiisoft/validator/issues/223
303
     */
304
    public function testValidateWithCallbackMethod(): void
305
    {
306
        $dataSet = new ObjectDataSet(new ObjectWithCallbackMethod());
307
        $validator = ValidatorFactory::make();
308
309
        /** @var array $rules */
310
        $rules = $dataSet->getRules();
311
        $this->assertSame(['name'], array_keys($rules));
312
        $this->assertCount(1, $rules['name']);
313
        $this->assertInstanceOf(Callback::class, $rules['name'][0]);
314
315
        $result = $validator->validate(['name' => 'bar'], $rules);
316
        $this->assertSame(['name' => ['Value must be "foo"!']], $result->getErrorMessagesIndexedByPath());
317
    }
318
319
    public function testValidateWithWrongCallbackMethod(): void
320
    {
321
        $object = new ObjectWithNonExistingCallbackMethod();
322
        $dataSet = new ObjectDataSet($object);
323
324
        $this->expectException(InvalidArgumentException::class);
325
        $this->expectExceptionMessage(
326
            sprintf(
327
                'Method "%s" does not exist in class "%s".',
328
                'validateName',
329
                $object::class,
330
            )
331
        );
332
        $dataSet->getRules();
333
    }
334
335
    public function objectWithDynamicDataSetProvider(): array
336
    {
337
        return [
338
            [
339
                new ObjectDataSet(new ObjectWithDynamicDataSet('A')),
340
                ['name' => 'A'],
341
            ],
342
            [
343
                new ObjectDataSet(new ObjectWithDynamicDataSet('B')),
344
                ['name' => 'B'],
345
            ],
346
        ];
347
    }
348
349
    /**
350
     * @dataProvider objectWithDynamicDataSetProvider
351
     */
352
    public function testObjectWithDynamicDataSet(ObjectDataSet $dataSet, array $expectedData): void
353
    {
354
        $this->assertSame($expectedData, $dataSet->getData());
355
    }
356
357
    public function testCaching(): void
358
    {
359
        $object = new class () {
360
            #[RuleWithCallsCount]
361
            public int $x = 7;
362
        };
363
364
        (new ObjectDataSet($object))->getRules();
365
        (new ObjectDataSet($object))->getRules();
366
367
        $this->assertSame(1, RuleWithCallsCount::$afterInitAttributeCallsCount);
368
    }
369
370
    public function testWithoutCache(): void
371
    {
372
        $object1 = new stdClass();
373
        $object1->a = 4;
374
        $dataSet1 = new ObjectDataSet($object1, useCache: false);
375
376
        $object2 = new stdClass();
377
        $object2->b = 2;
378
        $dataSet2 = new ObjectDataSet($object2, useCache: false);
379
380
        $this->assertSame(['a' => 4], $dataSet1->getData());
381
        $this->assertSame(['b' => 2], $dataSet2->getData());
382
    }
383
384
    public function testHasAttributeWithDataSetProvided(): void
385
    {
386
        $objectDataSet = new ObjectDataSet(new ObjectWithDataSet());
387
        $this->assertTrue($objectDataSet->hasAttribute('key1'));
388
        $this->assertFalse($objectDataSet->hasAttribute('non-existing-key'));
389
    }
390
}
391