GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#75)
by joseph
14:44
created

AbstractEntityTest   F

Complexity

Total Complexity 67

Size/Duplication

Total Lines 644
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
wmc 67
eloc 281
dl 0
loc 644
ccs 0
cts 308
cp 0
rs 3.04
c 0
b 0
f 0

25 Methods

Rating   Name   Duplication   Size   Complexity  
A testValidateSchema() 0 12 3
A getEntityManager() 0 16 4
A loadEntity() 0 3 1
A getSchemaErrors() 0 9 3
B updateEntityFields() 0 37 8
A validateEntity() 0 3 1
A getGetterNameForField() 0 7 2
A isUniqueField() 0 8 3
A testReloadedEntityHasNoAssociations() 0 7 1
A getTestedEntityFqn() 0 7 2
A testGeneratedCreate() 0 12 1
A removeAllAssociations() 0 18 4
A testGetDefaults() 0 3 1
A testConstructor() 0 7 1
A assertCorrectMappings() 0 26 6
A testUniqueFieldsMustBeUnique() 0 24 5
A setup() 0 17 1
A assertAllAssociationsAreNotEmpty() 0 28 3
A testLoadedEntity() 0 15 1
A assertCorrectMapping() 0 50 2
A getTestedEntityReflectionClass() 0 9 2
A callEntityGettersAndAssertNotNull() 0 32 5
A testGetGetters() 0 6 2
A assertAllAssociationsAreEmpty() 0 21 3
A testSetSetters() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like AbstractEntityTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AbstractEntityTest, and based on these observations, apply Extract Interface, too.

1
<?php declare(strict_types=1);
2
3
namespace EdmondsCommerce\DoctrineStaticMeta\Entity\Testing;
4
5
use Doctrine\Common\Cache\ArrayCache;
6
use Doctrine\Common\Inflector\Inflector;
7
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
8
use Doctrine\ORM\EntityManager;
9
use Doctrine\ORM\Mapping\ClassMetadata;
10
use Doctrine\ORM\Tools\SchemaValidator;
11
use Doctrine\ORM\Utility\PersisterHelper;
12
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\CodeHelper;
13
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\Generator\RelationsGenerator;
14
use EdmondsCommerce\DoctrineStaticMeta\CodeGeneration\NamespaceHelper;
15
use EdmondsCommerce\DoctrineStaticMeta\Config;
16
use EdmondsCommerce\DoctrineStaticMeta\ConfigInterface;
17
use EdmondsCommerce\DoctrineStaticMeta\Entity\Embeddable\Objects\AbstractEmbeddableObject;
18
use EdmondsCommerce\DoctrineStaticMeta\Entity\Interfaces\EntityInterface;
19
use EdmondsCommerce\DoctrineStaticMeta\Entity\Savers\EntitySaver;
20
use EdmondsCommerce\DoctrineStaticMeta\Entity\Savers\EntitySaverFactory;
21
use EdmondsCommerce\DoctrineStaticMeta\Entity\Validation\EntityValidatorFactory;
22
use EdmondsCommerce\DoctrineStaticMeta\EntityManager\EntityManagerFactory;
23
use EdmondsCommerce\DoctrineStaticMeta\Exception\ConfigException;
24
use EdmondsCommerce\DoctrineStaticMeta\SimpleEnv;
25
use PHPUnit\Framework\TestCase;
26
use Symfony\Component\Validator\Mapping\Cache\DoctrineCache;
27
28
/**
29
 * Class AbstractEntityTest
30
 *
31
 * This abstract test is designed to give you a good level of test coverage for your entities without any work required.
32
 *
33
 * You should extend the test with methods that test your specific business logic, your validators and anything else.
34
 *
35
 * You can override the methods, properties and constants as you see fit.
36
 *
37
 * @package EdmondsCommerce\DoctrineStaticMeta\Entity
38
 * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
39
 * @SuppressWarnings(PHPMD.NumberOfChildren)
40
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
41
 */
42
abstract class AbstractEntityTest extends TestCase implements EntityTestInterface
43
{
44
    /**
45
     * The fully qualified name of the Entity being tested, as calculated by the test class name
46
     *
47
     * @var string
48
     */
49
    protected $testedEntityFqn;
50
51
    /**
52
     * Reflection of the tested entity
53
     *
54
     * @var \ts\Reflection\ReflectionClass
55
     */
56
    protected $testedEntityReflectionClass;
57
58
59
    /**
60
     * @var EntityValidatorFactory
61
     */
62
    protected $entityValidatorFactory;
63
64
    /**
65
     * @var EntityManager
66
     */
67
    protected $entityManager;
68
69
    /**
70
     * @var array
71
     */
72
    protected $schemaErrors = [];
73
74
75
    /**
76
     * @var TestEntityGenerator
77
     */
78
    protected $testEntityGenerator;
79
80
    /**
81
     * @var EntitySaverFactory
82
     */
83
    protected $entitySaverFactory;
84
85
    /**
86
     * @var CodeHelper
87
     */
88
    protected $codeHelper;
89
90
    /**
91
     * @throws ConfigException
92
     * @throws \Exception
93
     * @SuppressWarnings(PHPMD.StaticAccess)
94
     */
95
    protected function setup()
96
    {
97
        $this->getEntityManager(true);
98
        $this->entityValidatorFactory = new EntityValidatorFactory(new DoctrineCache(new ArrayCache()));
99
        $this->entitySaverFactory     = new EntitySaverFactory(
100
            $this->entityManager,
101
            new EntitySaver($this->entityManager),
102
            new NamespaceHelper()
103
        );
104
        $this->testEntityGenerator    = new TestEntityGenerator(
105
            static::SEED,
106
            static::FAKER_DATA_PROVIDERS,
107
            $this->getTestedEntityReflectionClass(),
108
            $this->entitySaverFactory,
109
            $this->entityValidatorFactory
110
        );
111
        $this->codeHelper             = new CodeHelper(new NamespaceHelper());
112
    }
113
114
    /**
115
     * Use Doctrine's standard schema validation to get errors for the whole schema
116
     *
117
     * @param bool $update
118
     *
119
     * @return array
120
     * @throws \Exception
121
     * @SuppressWarnings(PHPMD.BooleanArgumentFlag)
122
     */
123
    protected function getSchemaErrors(bool $update = false): array
124
    {
125
        if (empty($this->schemaErrors) || true === $update) {
126
            $entityManager      = $this->getEntityManager();
127
            $validator          = new SchemaValidator($entityManager);
128
            $this->schemaErrors = $validator->validateMapping();
129
        }
130
131
        return $this->schemaErrors;
132
    }
133
134
    /**
135
     * If a global function dsmGetEntityManagerFactory is defined, we use this
136
     *
137
     * Otherwise, we use the standard DevEntityManagerFactory,
138
     * we define a DB name which is the main DB from env but with `_test` suffixed
139
     *
140
     * @param bool $new
141
     *
142
     * @return EntityManager
143
     * @throws ConfigException
144
     * @throws \Exception
145
     * @SuppressWarnings(PHPMD)
146
     */
147
    protected function getEntityManager(bool $new = false): EntityManager
148
    {
149
        if (null === $this->entityManager || true === $new) {
150
            if (\function_exists(self::GET_ENTITY_MANAGER_FUNCTION_NAME)) {
151
                $this->entityManager = \call_user_func(self::GET_ENTITY_MANAGER_FUNCTION_NAME);
152
            } else {
153
                SimpleEnv::setEnv(Config::getProjectRootDirectory().'/.env');
154
                $testConfig                                 = $_SERVER;
155
                $testConfig[ConfigInterface::PARAM_DB_NAME] = $_SERVER[ConfigInterface::PARAM_DB_NAME].'_test';
156
                $config                                     = new Config($testConfig);
157
                $this->entityManager                        = (new EntityManagerFactory(new ArrayCache()))
158
                    ->getEntityManager($config);
159
            }
160
        }
161
162
        return $this->entityManager;
163
    }
164
165
166
    /**
167
     * Use Doctrine's built in schema validation tool to catch issues
168
     */
169
    public function testValidateSchema()
170
    {
171
        $errors  = $this->getSchemaErrors();
172
        $class   = $this->getTestedEntityFqn();
173
        $message = '';
174
        if (isset($errors[$class])) {
175
            $message = "Failed ORM Validate Schema:\n";
176
            foreach ($errors[$class] as $err) {
177
                $message .= "\n * $err \n";
178
            }
179
        }
180
        self::assertEmpty($message);
181
    }
182
183
184
    /**
185
     * @param string        $class
186
     * @param int|string    $id
187
     * @param EntityManager $entityManager
188
     *
189
     * @return EntityInterface|null
190
     */
191
    protected function loadEntity(string $class, $id, EntityManager $entityManager): ?EntityInterface
192
    {
193
        return $entityManager->getRepository($class)->find($id);
194
    }
195
196
    public function testConstructor(): EntityInterface
197
    {
198
        $class  = $this->getTestedEntityFqn();
199
        $entity = new $class($this->entityValidatorFactory);
200
        self::assertInstanceOf($class, $entity);
201
202
        return $entity;
203
    }
204
205
    /**
206
     * @param EntityInterface $entity
207
     *
208
     * @throws ConfigException
209
     * @throws \Doctrine\ORM\Query\QueryException
210
     * @throws \ReflectionException
211
     * @depends testConstructor
212
     */
213
    public function testGetDefaults(EntityInterface $entity)
214
    {
215
        $this->callEntityGettersAndAssertNotNull($entity);
216
    }
217
218
    /**
219
     * Loop through Entity fields, call the getter and where possible assert there is a value returned
220
     *
221
     * @param EntityInterface $entity
222
     *
223
     * @throws ConfigException
224
     * @throws \Doctrine\ORM\Query\QueryException
225
     * @throws \ReflectionException
226
     * @SuppressWarnings(PHPMD.StaticAccess)
227
     */
228
    protected function callEntityGettersAndAssertNotNull(EntityInterface $entity): void
229
    {
230
        $class         = $this->getTestedEntityFqn();
231
        $entityManager = $this->getEntityManager();
232
        $meta          = $entityManager->getClassMetadata($class);
233
        foreach ($meta->getFieldNames() as $fieldName) {
234
            $type   = PersisterHelper::getTypeOfField($fieldName, $meta, $entityManager)[0];
235
            $method = $this->getGetterNameForField($fieldName, $type);
236
            if (\ts\stringContains($method, '.')) {
237
                list($getEmbeddableMethod,) = explode('.', $method);
238
                $embeddable = $entity->$getEmbeddableMethod();
239
                self::assertInstanceOf(AbstractEmbeddableObject::class, $embeddable);
240
                continue;
241
            }
242
            $reflectionMethod = new \ReflectionMethod($entity, $method);
243
            if ($reflectionMethod->hasReturnType()) {
244
                $returnType = $reflectionMethod->getReturnType();
245
                $allowsNull = $returnType->allowsNull();
246
                if ($allowsNull) {
247
                    // As we can't assert anything here so simply call
248
                    // the method and allow the type hint to raise any
249
                    // errors.
250
                    $entity->$method();
251
                    continue;
252
                }
253
                self::assertNotNull($entity->$method(), "$fieldName getter returned null");
254
                continue;
255
            }
256
            // If there is no return type then we can't assert anything,
257
            // but again we can just call the getter to check for errors
258
            $entity->$method();
259
            self::assertTrue(true);
260
        }
261
    }
262
263
    /**
264
     * Generate a new entity and then update our Entity with the values from the generated one
265
     *
266
     * @param EntityInterface $entity
267
     *
268
     * @throws ConfigException
269
     * @throws \Doctrine\ORM\Mapping\MappingException
270
     * @throws \Doctrine\ORM\Query\QueryException
271
     * @throws \ReflectionException
272
     * @SuppressWarnings(PHPMD.StaticAccess)
273
     */
274
    protected function updateEntityFields(EntityInterface $entity): void
275
    {
276
        $class         = $this->getTestedEntityFqn();
277
        $entityManager = $this->getEntityManager();
278
        $meta          = $entityManager->getClassMetadata($class);
279
        $entityManager = $this->getEntityManager();
280
        $class         = $this->getTestedEntityFqn();
281
        $generated     = $this->testEntityGenerator->generateEntity($entityManager, $class, 10);
282
        $identifiers   = \array_flip($meta->getIdentifier());
283
        foreach ($meta->getFieldNames() as $fieldName) {
284
            if (isset($identifiers[$fieldName])) {
285
                continue;
286
            }
287
            if (true === $this->isUniqueField($meta, $fieldName)) {
288
                continue;
289
            }
290
            $setter = 'set'.$fieldName;
291
            if (!\method_exists($entity, $setter)) {
292
                continue;
293
            }
294
            $type   = PersisterHelper::getTypeOfField($fieldName, $meta, $entityManager)[0];
295
            $getter = $this->getGetterNameForField($fieldName, $type);
296
            if (\ts\stringContains($getter, '.')) {
297
                list($getEmbeddableMethod, $fieldInEmbeddable) = explode('.', $getter);
298
                $getterInEmbeddable  = 'get'.$fieldInEmbeddable;
299
                $setterInEmbeddable  = 'set'.$fieldInEmbeddable;
300
                $generatedEmbeddable = $generated->$getEmbeddableMethod();
301
                $embeddable          = $entity->$getEmbeddableMethod();
302
                if (
303
                    \method_exists($embeddable, $setterInEmbeddable)
304
                    && \method_exists($embeddable, $getterInEmbeddable)
305
                ) {
306
                    $embeddable->$setterInEmbeddable($generatedEmbeddable->$getterInEmbeddable());
307
                }
308
                continue;
309
            }
310
            $entity->$setter($generated->$getter());
311
        }
312
    }
313
314
    protected function isUniqueField(ClassMetadata $meta, string $fieldName): bool
315
    {
316
        $fieldMapping = $meta->getFieldMapping($fieldName);
317
        if (array_key_exists('unique', $fieldMapping) && true === $fieldMapping['unique']) {
318
            return true;
319
        }
320
321
        return false;
322
    }
323
324
    /**
325
     * Test that we have correctly generated an instance of our test entity
326
     *
327
     * @throws ConfigException
328
     * @throws \Doctrine\ORM\Query\QueryException
329
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
330
     * @throws \Exception
331
     * @throws \ReflectionException
332
     * @SuppressWarnings(PHPMD.StaticAccess)
333
     */
334
    public function testGeneratedCreate(): EntityInterface
335
    {
336
        $entityManager = $this->getEntityManager();
337
        $class         = $this->getTestedEntityFqn();
338
        $generated     = $this->testEntityGenerator->generateEntity($entityManager, $class);
339
        self::assertInstanceOf($class, $generated);
340
        $this->testEntityGenerator->addAssociationEntities($entityManager, $generated);
341
        $this->validateEntity($generated);
342
        $this->callEntityGettersAndAssertNotNull($generated);
343
        $this->entitySaverFactory->getSaverForEntity($generated)->save($generated);
344
345
        return $generated;
346
    }
347
348
    /**
349
     * Test that we can load the entity and then get and set
350
     *
351
     * @param EntityInterface $entity
352
     *
353
     * @return EntityInterface|null
354
     * @throws ConfigException
355
     * @throws \Doctrine\ORM\Mapping\MappingException
356
     * @throws \Doctrine\ORM\Query\QueryException
357
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
358
     * @throws \ReflectionException
359
     * @depends testGeneratedCreate
360
     */
361
    public function testLoadedEntity(EntityInterface $entity): EntityInterface
362
    {
363
        $class         = $this->getTestedEntityFqn();
364
        $entityManager = $this->getEntityManager();
365
        $loaded        = $this->loadEntity($class, $entity->getId(), $entityManager);
366
        self::assertSame($entity->getId(), $loaded->getId());
367
        self::assertInstanceOf($class, $loaded);
368
        $this->updateEntityFields($loaded);
369
        $this->assertAllAssociationsAreNotEmpty($loaded);
370
        $this->validateEntity($loaded);
371
        $this->removeAllAssociations($loaded);
372
        $this->assertAllAssociationsAreEmpty($loaded);
373
        $this->entitySaverFactory->getSaverForEntity($loaded)->save($loaded);
374
375
        return $loaded;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $loaded could return the type null which is incompatible with the type-hinted return EdmondsCommerce\Doctrine...erfaces\EntityInterface. Consider adding an additional type-check to rule them out.
Loading history...
376
    }
377
378
    /**
379
     * @param EntityInterface $entity
380
     *
381
     * @throws ConfigException
382
     * @depends testLoadedEntity
383
     */
384
    public function testReloadedEntityHasNoAssociations(EntityInterface $entity): void
385
    {
386
        $class         = $this->getTestedEntityFqn();
387
        $entityManager = $this->getEntityManager();
388
        $reLoaded      = $this->loadEntity($class, $entity->getId(), $entityManager);
389
        self::assertEquals($entity->__toString(), $reLoaded->__toString());
390
        $this->assertAllAssociationsAreEmpty($reLoaded);
391
    }
392
393
    protected function assertAllAssociationsAreNotEmpty(EntityInterface $entity)
394
    {
395
        $entityManager = $this->getEntityManager();
396
        $class         = $this->getTestedEntityFqn();
397
        $meta          = $entityManager->getClassMetadata($class);
398
        foreach ($meta->getAssociationMappings() as $mapping) {
399
            $getter = 'get'.$mapping['fieldName'];
400
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
401
                $collection = $entity->$getter()->toArray();
402
                self::assertNotEmpty(
403
                    $collection,
404
                    'Failed to load the collection of the associated entity ['.$mapping['fieldName']
405
                    .'] from the generated '.$class
406
                    .', make sure you have reciprocal adding of the association'
407
                );
408
                self::assertCorrectMappings($class, $mapping, $entityManager);
0 ignored issues
show
Bug Best Practice introduced by
The method EdmondsCommerce\Doctrine...assertCorrectMappings() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

408
                self::/** @scrutinizer ignore-call */ 
409
                      assertCorrectMappings($class, $mapping, $entityManager);
Loading history...
409
                continue;
410
            }
411
            $association = $entity->$getter();
412
            self::assertNotEmpty(
413
                $association,
414
                'Failed to load the associated entity: ['.$mapping['fieldName']
415
                .'] from the generated '.$class
416
            );
417
            self::assertNotEmpty(
418
                $association->getId(),
419
                'Failed to get the ID of the associated entity: ['.$mapping['fieldName']
420
                .'] from the generated '.$class
421
            );
422
        }
423
    }
424
425
    /**
426
     * @param EntityInterface $entity
427
     *
428
     * @throws ConfigException
429
     * @SuppressWarnings(PHPMD.StaticAccess)
430
     */
431
    protected function removeAllAssociations(EntityInterface $entity)
432
    {
433
        $entityManager = $this->getEntityManager();
434
        $class         = $this->getTestedEntityFqn();
435
        $meta          = $entityManager->getClassMetadata($class);
436
        foreach ($meta->getAssociationMappings() as $mapping) {
437
            $remover = 'remove'.Inflector::singularize($mapping['fieldName']);
438
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
439
                $getter    = 'get'.$mapping['fieldName'];
440
                $relations = $entity->$getter();
441
                foreach ($relations as $relation) {
442
                    $entity->$remover($relation);
443
                }
444
                continue;
445
            }
446
            $entity->$remover();
447
        }
448
        $this->assertAllAssociationsAreEmpty($entity);
449
    }
450
451
    protected function assertAllAssociationsAreEmpty(EntityInterface $entity)
452
    {
453
        $entityManager = $this->getEntityManager();
454
        $class         = $this->getTestedEntityFqn();
455
        $meta          = $entityManager->getClassMetadata($class);
456
        foreach ($meta->getAssociationMappings() as $mapping) {
457
            $getter = 'get'.$mapping['fieldName'];
458
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
459
                $collection = $entity->$getter()->toArray();
460
                self::assertEmpty(
461
                    $collection,
462
                    'Collection of the associated entity ['.$mapping['fieldName']
463
                    .'] is not empty after calling remove'
464
                );
465
                continue;
466
            }
467
            $association = $entity->$getter();
468
            self::assertEmpty(
469
                $association,
470
                'Failed to remove associated entity: ['.$mapping['fieldName']
471
                .'] from the generated '.$class
472
            );
473
        }
474
    }
475
476
477
    /**
478
     * @depends testConstructor
479
     *
480
     * @param EntityInterface $entity
481
     */
482
    public function testGetGetters(EntityInterface $entity)
483
    {
484
        $getters = $entity->getGetters();
485
        self::assertNotEmpty($getters);
486
        foreach ($getters as $getter) {
487
            self::assertRegExp('%^(get|is|has).+%', $getter);
488
        }
489
    }
490
491
    /**
492
     * @depends testConstructor
493
     *
494
     * @param EntityInterface $entity
495
     */
496
    public function testSetSetters(EntityInterface $entity)
497
    {
498
        $setters = $entity->getSetters();
499
        self::assertNotEmpty($setters);
500
        foreach ($setters as $setter) {
501
            self::assertRegExp('%^(set|add).+%', $setter);
502
        }
503
    }
504
505
506
    protected function getGetterNameForField(string $fieldName, string $type): string
507
    {
508
        if ($type === 'boolean') {
509
            return $this->codeHelper->getGetterMethodNameForBoolean($fieldName);
510
        }
511
512
        return 'get'.$fieldName;
513
    }
514
515
    /**
516
     * Loop through entity fields and find unique ones
517
     *
518
     * Then ensure that the unique rule is being enforced as expected
519
     *
520
     * @throws ConfigException
521
     * @throws \Doctrine\ORM\Mapping\MappingException
522
     * @throws \EdmondsCommerce\DoctrineStaticMeta\Exception\DoctrineStaticMetaException
523
     * @throws \ReflectionException
524
     * @throws \Doctrine\ORM\Mapping\MappingException
525
     */
526
    public function testUniqueFieldsMustBeUnique(): void
527
    {
528
        $class         = $this->getTestedEntityFqn();
529
        $entityManager = $this->getEntityManager();
530
        $meta          = $entityManager->getClassMetadata($class);
531
        $uniqueFields  = [];
532
        foreach ($meta->getFieldNames() as $fieldName) {
533
            if (true === $this->isUniqueField($meta, $fieldName)) {
534
                $uniqueFields[] = $fieldName;
535
            }
536
        }
537
        if ([] === $uniqueFields) {
538
            return;
539
        }
540
        foreach ($uniqueFields as $fieldName) {
541
            $primary      = $this->testEntityGenerator->generateEntity($entityManager, $class);
542
            $secondary    = $this->testEntityGenerator->generateEntity($entityManager, $class);
543
            $getter       = 'get'.$fieldName;
544
            $setter       = 'set'.$fieldName;
545
            $primaryValue = $primary->$getter();
546
            $secondary->$setter($primaryValue);
547
            $saver = $this->entitySaverFactory->getSaverForEntity($primary);
548
            $this->expectException(UniqueConstraintViolationException::class);
549
            $saver->saveAll([$primary, $secondary]);
550
        }
551
    }
552
553
    /**
554
     * Check the mapping of our class and the associated entity to make sure it's configured properly on both sides.
555
     * Very easy to get wrong. This is in addition to the standard Schema Validation
556
     *
557
     * @param string        $classFqn
558
     * @param array         $mapping
559
     * @param EntityManager $entityManager
560
     */
561
    protected function assertCorrectMappings(string $classFqn, array $mapping, EntityManager $entityManager)
562
    {
563
        $pass                                 = false;
564
        $associationFqn                       = $mapping['targetEntity'];
565
        $associationMeta                      = $entityManager->getClassMetadata($associationFqn);
566
        $classTraits                          = $entityManager->getClassMetadata($classFqn)
567
                                                              ->getReflectionClass()
568
                                                              ->getTraits();
569
        $unidirectionalTraitShortNamePrefixes = [
570
            'Has'.$associationFqn::getSingular().RelationsGenerator::PREFIX_UNIDIRECTIONAL,
571
            'Has'.$associationFqn::getPlural().RelationsGenerator::PREFIX_UNIDIRECTIONAL,
572
        ];
573
        foreach ($classTraits as $trait) {
574
            foreach ($unidirectionalTraitShortNamePrefixes as $namePrefix) {
575
                if (0 === \stripos($trait->getShortName(), $namePrefix)) {
576
                    return;
577
                }
578
            }
579
        }
580
        foreach ($associationMeta->getAssociationMappings() as $associationMapping) {
581
            if ($classFqn === $associationMapping['targetEntity']) {
582
                $pass = self::assertCorrectMapping($mapping, $associationMapping, $classFqn);
0 ignored issues
show
Bug Best Practice introduced by
The method EdmondsCommerce\Doctrine...:assertCorrectMapping() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

582
                /** @scrutinizer ignore-call */ 
583
                $pass = self::assertCorrectMapping($mapping, $associationMapping, $classFqn);
Loading history...
583
                break;
584
            }
585
        }
586
        self::assertTrue($pass, 'Failed finding association mapping to test for '."\n".$mapping['targetEntity']);
587
    }
588
589
    /**
590
     * @param array  $mapping
591
     * @param array  $associationMapping
592
     * @param string $classFqn
593
     *
594
     * @return bool
595
     */
596
    protected function assertCorrectMapping(array $mapping, array $associationMapping, string $classFqn): bool
597
    {
598
        if (empty($mapping['joinTable'])) {
599
            self::assertArrayNotHasKey(
600
                'joinTable',
601
                $associationMapping,
602
                $classFqn.' join table is empty,
603
                        but association '.$mapping['targetEntity'].' join table is not empty'
604
            );
605
606
            return true;
607
        }
608
        self::assertNotEmpty(
609
            $associationMapping['joinTable'],
610
            "$classFqn joinTable is set to ".$mapping['joinTable']['name']
611
            ." \n association ".$mapping['targetEntity'].' join table is empty'
612
        );
613
        self::assertSame(
614
            $mapping['joinTable']['name'],
615
            $associationMapping['joinTable']['name'],
616
            "join tables not the same: \n * $classFqn = ".$mapping['joinTable']['name']
617
            ." \n * association ".$mapping['targetEntity']
618
            .' = '.$associationMapping['joinTable']['name']
619
        );
620
        self::assertArrayHasKey(
621
            'inverseJoinColumns',
622
            $associationMapping['joinTable'],
623
            "join table join columns not the same: \n * $classFqn joinColumn = "
624
            .$mapping['joinTable']['joinColumns'][0]['name']
625
            ." \n * association ".$mapping['targetEntity']
626
            .' inverseJoinColumn is not set'
627
        );
628
        self::assertSame(
629
            $mapping['joinTable']['joinColumns'][0]['name'],
630
            $associationMapping['joinTable']['inverseJoinColumns'][0]['name'],
631
            "join table join columns not the same: \n * $classFqn joinColumn = "
632
            .$mapping['joinTable']['joinColumns'][0]['name']
633
            ." \n * association ".$mapping['targetEntity']
634
            .' inverseJoinColumn = '.$associationMapping['joinTable']['inverseJoinColumns'][0]['name']
635
        );
636
        self::assertSame(
637
            $mapping['joinTable']['inverseJoinColumns'][0]['name'],
638
            $associationMapping['joinTable']['joinColumns'][0]['name'],
639
            "join table join columns  not the same: \n * $classFqn inverseJoinColumn = "
640
            .$mapping['joinTable']['inverseJoinColumns'][0]['name']
641
            ." \n * association ".$mapping['targetEntity'].' joinColumn = '
642
            .$associationMapping['joinTable']['joinColumns'][0]['name']
643
        );
644
645
        return true;
646
    }
647
648
649
    protected function validateEntity(EntityInterface $entity): void
650
    {
651
        $entity->validate();
652
    }
653
654
655
    /**
656
     * Get the fully qualified name of the Entity we are testing,
657
     * assumes EntityNameTest as the entity class short name
658
     *
659
     * @return string
660
     */
661
    protected function getTestedEntityFqn(): string
662
    {
663
        if (null === $this->testedEntityFqn) {
664
            $this->testedEntityFqn = \substr(static::class, 0, -4);
665
        }
666
667
        return $this->testedEntityFqn;
668
    }
669
670
671
    /**
672
     * Get a \ReflectionClass for the currently tested Entity
673
     *
674
     * @return \ts\Reflection\ReflectionClass
675
     * @throws \ReflectionException
676
     */
677
    protected function getTestedEntityReflectionClass(): \ts\Reflection\ReflectionClass
678
    {
679
        if (null === $this->testedEntityReflectionClass) {
680
            $this->testedEntityReflectionClass = new \ts\Reflection\ReflectionClass(
681
                $this->getTestedEntityFqn()
682
            );
683
        }
684
685
        return $this->testedEntityReflectionClass;
686
    }
687
}
688