Passed
Pull Request — master (#444)
by Sergei
02:59
created

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