Completed
Pull Request — 3.x (#368)
by
unknown
33:14 queued 22:24
created

ModelManagerTest::testCollections()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 23
rs 9.552
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\DoctrineMongoDBAdminBundle\Tests\Model;
15
16
use Doctrine\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Persistence\Mapping\ClassMetadataFactory;
18
use Doctrine\Common\Persistence\ObjectManager;
19
use Doctrine\ODM\MongoDB\DocumentManager;
20
use Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
21
use PHPUnit\Framework\TestCase;
22
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
23
use Sonata\AdminBundle\Datagrid\Datagrid;
24
use Sonata\AdminBundle\Datagrid\DatagridInterface;
25
use Sonata\DoctrineMongoDBAdminBundle\Admin\FieldDescription;
26
use Sonata\DoctrineMongoDBAdminBundle\Model\ModelManager;
27
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\AbstractDocument;
28
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\AssociatedDocument;
29
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\ContainerDocument;
30
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\EmbeddedDocument;
31
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\ProtectedDocument;
32
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\SimpleDocument;
33
use Sonata\DoctrineMongoDBAdminBundle\Tests\Fixtures\Document\SimpleDocumentWithPrivateSetter;
34
use Symfony\Bridge\Doctrine\ManagerRegistry;
35
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
36
37
class ModelManagerTest extends TestCase
38
{
39
    /**
40
     * @dataProvider getWrongDocuments
41
     *
42
     * @param mixed $document
43
     */
44
    public function testNormalizedIdentifierException($document): void
45
    {
46
        $registry = $this->createStub(ManagerRegistry::class);
47
48
        $model = new ModelManager($registry);
49
50
        $this->expectException(\RuntimeException::class);
51
52
        $model->getNormalizedIdentifier($document);
53
    }
54
55
    public function getWrongDocuments(): iterable
56
    {
57
        yield [0];
58
        yield [1];
59
        yield [false];
60
        yield [true];
61
        yield [[]];
62
        yield [''];
63
        yield ['sonata-project'];
64
    }
65
66
    public function testGetNormalizedIdentifierNull(): void
67
    {
68
        $registry = $this->createStub(ManagerRegistry::class);
69
70
        $model = new ModelManager($registry);
71
72
        $this->assertNull($model->getNormalizedIdentifier(null));
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
73
    }
74
75
    public function testSortParameters(): void
76
    {
77
        $registry = $this->createStub(ManagerRegistry::class);
78
79
        $manager = new ModelManager($registry);
80
81
        $datagrid1 = $this->createStub(Datagrid::class);
82
        $datagrid2 = $this->createStub(Datagrid::class);
83
84
        $field1 = new FieldDescription();
85
        $field1->setName('field1');
86
87
        $field2 = new FieldDescription();
88
        $field2->setName('field2');
89
90
        $field3 = new FieldDescription();
91
        $field3->setName('field3');
92
        $field3->setOption('sortable', 'field3sortBy');
93
94
        $datagrid1
95
            ->method('getValues')
96
            ->willReturn([
97
                '_sort_by' => $field1,
98
                '_sort_order' => 'ASC',
99
            ]);
100
101
        $datagrid2
102
            ->method('getValues')
103
            ->willReturn([
104
                '_sort_by' => $field3,
105
                '_sort_order' => 'ASC',
106
            ]);
107
108
        $parameters = $manager->getSortParameters($field1, $datagrid1);
109
110
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
111
        $this->assertSame('field1', $parameters['filter']['_sort_by']);
112
113
        $parameters = $manager->getSortParameters($field2, $datagrid1);
114
115
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
116
        $this->assertSame('field2', $parameters['filter']['_sort_by']);
117
118
        $parameters = $manager->getSortParameters($field3, $datagrid1);
119
120
        $this->assertSame('ASC', $parameters['filter']['_sort_order']);
121
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
122
123
        $parameters = $manager->getSortParameters($field3, $datagrid2);
124
125
        $this->assertSame('DESC', $parameters['filter']['_sort_order']);
126
        $this->assertSame('field3sortBy', $parameters['filter']['_sort_by']);
127
    }
128
129
    public function testGetParentMetadataForProperty(): void
130
    {
131
        $containerDocumentClass = ContainerDocument::class;
132
        $associatedDocumentClass = AssociatedDocument::class;
133
        $embeddedDocumentClass = EmbeddedDocument::class;
134
135
        $dm = $this->createStub(DocumentManager::class);
136
137
        $registry = $this->createStub(ManagerRegistry::class);
138
139
        $modelManager = new ModelManager($registry);
140
141
        $registry
142
            ->method('getManagerForClass')
143
            ->willReturn($dm);
144
145
        $metadataFactory = $this->createStub(ClassMetadataFactory::class);
146
147
        $dm
148
            ->method('getMetadataFactory')
149
            ->willReturn($metadataFactory);
150
151
        $containerDocumentMetadata = $this->getMetadataForContainerDocument();
152
        $associatedDocumentMetadata = $this->getMetadataForAssociatedDocument();
153
        $embeddedDocumentMetadata = $this->getMetadataForEmbeddedDocument();
154
155
        $metadataFactory->method('getMetadataFor')
156
            ->willReturnMap(
157
                [
158
                    [$containerDocumentClass, $containerDocumentMetadata],
159
                    [$embeddedDocumentClass, $embeddedDocumentMetadata],
160
                    [$associatedDocumentClass, $associatedDocumentMetadata],
161
                ]
162
            );
163
164
        /** @var ClassMetadata $metadata */
165
        [$metadata, $lastPropertyName] = $modelManager
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $lastPropertyName does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
166
            ->getParentMetadataForProperty($containerDocumentClass, 'plainField');
167
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'integer');
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
168
169
        [$metadata, $lastPropertyName] = $modelManager
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
170
            ->getParentMetadataForProperty($containerDocumentClass, 'associatedDocument.plainField');
171
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'string');
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
172
173
        [$metadata, $lastPropertyName] = $modelManager
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
174
            ->getParentMetadataForProperty($containerDocumentClass, 'embeddedDocument.plainField');
175
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
176
177
        $this->assertSame($metadata->fieldMappings[$lastPropertyName]['type'], 'boolean');
0 ignored issues
show
Bug introduced by
The variable $metadata does not exist. Did you mean $metadataFactory?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
178
    }
179
180
    public function testModelReverseTransformWithSetter(): void
181
    {
182
        $class = SimpleDocument::class;
183
184
        $manager = $this->createModelManager($class);
185
        $object = $manager->modelReverseTransform(
186
            $class,
187
            [
188
                'schmeckles' => 42,
189
                'multi_word_property' => 'hello',
190
                'schwifty' => true,
191
            ]
192
        );
193
        $this->assertInstanceOf($class, $object);
194
        $this->assertSame(42, $object->getSchmeckles());
195
        $this->assertSame('hello', $object->getMultiWordProperty());
196
        $this->assertTrue($object->schwifty);
197
    }
198
199
    public function testModelReverseTransformFailsWithPrivateSetter(): void
200
    {
201
        $class = SimpleDocumentWithPrivateSetter::class;
202
        $manager = $this->createModelManager($class);
203
204
        $this->expectException(NoSuchPropertyException::class);
205
206
        $manager->modelReverseTransform($class, ['schmeckles' => 42]);
207
    }
208
209
    public function testModelReverseTransformFailsWithPrivateProperties(): void
210
    {
211
        $class = SimpleDocument::class;
212
        $manager = $this->createModelManager($class);
213
214
        $this->expectException(NoSuchPropertyException::class);
215
216
        $manager->modelReverseTransform($class, ['plumbus' => 42]);
217
    }
218
219
    public function testModelReverseTransformFailsWithPrivateProperties2(): void
220
    {
221
        $class = SimpleDocument::class;
222
        $manager = $this->createModelManager($class);
223
224
        $this->expectException(NoSuchPropertyException::class);
225
226
        $manager->modelReverseTransform($class, ['plumbus' => 42]);
227
    }
228
229
    public function testCollections(): void
230
    {
231
        $registry = $this->createStub(ManagerRegistry::class);
232
        $model = new ModelManager($registry);
233
234
        $collection = $model->getModelCollectionInstance('whyDoWeEvenHaveThisParameter');
235
        $this->assertInstanceOf(ArrayCollection::class, $collection);
236
237
        $item1 = new \stdClass();
238
        $item2 = new \stdClass();
239
        $model->collectionAddElement($collection, $item1);
0 ignored issues
show
Bug introduced by
It seems like $collection defined by $model->getModelCollecti...EvenHaveThisParameter') on line 234 can also be of type object<ArrayAccess>; however, Sonata\DoctrineMongoDBAd...:collectionAddElement() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
240
        $model->collectionAddElement($collection, $item2);
241
242
        $this->assertTrue($model->collectionHasElement($collection, $item1));
243
244
        $model->collectionRemoveElement($collection, $item1);
245
246
        $this->assertFalse($model->collectionHasElement($collection, $item1));
247
248
        $model->collectionClear($collection);
249
250
        $this->assertTrue($collection->isEmpty());
0 ignored issues
show
Bug introduced by
The method isEmpty cannot be called on $collection (of type array).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
251
    }
252
253
    public function testModelTransform(): void
254
    {
255
        $registry = $this->createStub(ManagerRegistry::class);
256
        $model = new ModelManager($registry);
257
258
        $instance = new \stdClass();
259
        $result = $model->modelTransform('thisIsNotUsed', $instance);
260
261
        $this->assertSame($instance, $result);
262
    }
263
264
    public function testGetPaginationParameters(): void
265
    {
266
        $datagrid = $this->createMock(DatagridInterface::class);
267
        $fieldDescription = $this->createMock(FieldDescriptionInterface::class);
268
        $registry = $this->createStub(ManagerRegistry::class);
269
270
        $datagrid->expects($this->once())
271
            ->method('getValues')
272
            ->willReturn(['_sort_by' => $fieldDescription]);
273
274
        $fieldDescription->expects($this->once())
275
            ->method('getName')
276
            ->willReturn($name = 'test');
277
278
        $model = new ModelManager($registry);
279
280
        $result = $model->getPaginationParameters($datagrid, $page = 5);
281
282
        $this->assertSame($page, $result['filter']['_page']);
283
        $this->assertSame($name, $result['filter']['_sort_by']);
284
    }
285
286
    public function testGetModelInstanceException(): void
287
    {
288
        $registry = $this->createStub(ManagerRegistry::class);
289
290
        $model = new ModelManager($registry);
291
292
        $this->expectException(\InvalidArgumentException::class);
293
294
        $model->getModelInstance(AbstractDocument::class);
295
    }
296
297
    public function testGetModelInstanceForProtectedDocument(): void
298
    {
299
        $registry = $this->createStub(ManagerRegistry::class);
300
301
        $model = new ModelManager($registry);
302
303
        $this->assertInstanceOf(ProtectedDocument::class, $model->getModelInstance(ProtectedDocument::class));
304
    }
305
306
    public function testFindBadId(): void
307
    {
308
        $registry = $this->createStub(ManagerRegistry::class);
309
310
        $model = new ModelManager($registry);
311
312
        $this->assertNull($model->find('notImportant', null));
313
    }
314
315
    public function testGetUrlSafeIdentifierException(): void
316
    {
317
        $registry = $this->createStub(ManagerRegistry::class);
318
319
        $model = new ModelManager($registry);
320
321
        $this->expectException(\RuntimeException::class);
322
323
        $model->getNormalizedIdentifier(new \stdClass());
324
    }
325
326
    public function testGetUrlSafeIdentifierNull(): void
327
    {
328
        $registry = $this->createStub(ManagerRegistry::class);
329
330
        $model = new ModelManager($registry);
331
332
        $this->assertNull($model->getNormalizedIdentifier(null));
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
333
    }
334
335
    private function createModelManager(string $class): ModelManager
336
    {
337
        $metadataFactory = $this->createMock(ClassMetadataFactory::class);
338
        $modelManager = $this->createMock(ObjectManager::class);
339
        $registry = $this->createMock(ManagerRegistry::class);
340
341
        $classMetadata = new ClassMetadata($class);
342
        $classMetadata->reflClass = new \ReflectionClass($class);
343
344
        $modelManager->expects($this->once())
345
            ->method('getMetadataFactory')
346
            ->willReturn($metadataFactory);
347
        $metadataFactory->expects($this->once())
348
            ->method('getMetadataFor')
349
            ->with($class)
350
            ->willReturn($classMetadata);
351
        $registry->expects($this->once())
352
            ->method('getManagerForClass')
353
            ->with($class)
354
            ->willReturn($modelManager);
355
356
        return new ModelManager($registry);
357
    }
358
359
    private function getMetadataForEmbeddedDocument(): ClassMetadata
360
    {
361
        $metadata = new ClassMetadata(EmbeddedDocument::class);
362
363
        $metadata->fieldMappings = [
364
            'plainField' => [
365
                'fieldName' => 'plainField',
366
                'columnName' => 'plainField',
367
                'type' => 'boolean',
368
            ],
369
        ];
370
371
        return $metadata;
372
    }
373
374
    private function getMetadataForAssociatedDocument(): ClassMetadata
375
    {
376
        $embeddedDocumentClass = EmbeddedDocument::class;
377
378
        $metadata = new ClassMetadata(AssociatedDocument::class);
379
380
        $metadata->fieldMappings = [
381
            'plainField' => [
382
                'fieldName' => 'plainField',
383
                'name' => 'plainField',
384
                'columnName' => 'plainField',
385
                'type' => 'string',
386
            ],
387
        ];
388
389
        $metadata->mapOneEmbedded([
390
            'fieldName' => 'embeddedDocument',
391
            'name' => 'embeddedDocument',
392
            'targetDocument' => $embeddedDocumentClass,
393
        ]);
394
395
        return $metadata;
396
    }
397
398
    private function getMetadataForContainerDocument(): ClassMetadata
399
    {
400
        $containerDocumentClass = ContainerDocument::class;
401
        $associatedDocumentClass = AssociatedDocument::class;
402
        $embeddedDocumentClass = EmbeddedDocument::class;
403
404
        $metadata = new ClassMetadata($containerDocumentClass);
405
406
        $metadata->fieldMappings = [
407
            'plainField' => [
408
                'fieldName' => 'plainField',
409
                'name' => 'plainField',
410
                'columnName' => 'plainField',
411
                'type' => 'integer',
412
            ],
413
        ];
414
415
        $metadata->associationMappings['associatedDocument'] = [
416
            'fieldName' => 'associatedDocument',
417
            'name' => 'associatedDocument',
418
            'targetDocument' => $associatedDocumentClass,
419
            'sourceDocument' => $containerDocumentClass,
420
        ];
421
422
        $metadata->mapOneEmbedded([
423
            'fieldName' => 'embeddedDocument',
424
            'name' => 'embeddedDocument',
425
            'targetDocument' => $embeddedDocumentClass,
426
        ]);
427
428
        return $metadata;
429
    }
430
}
431