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 (#100)
by joseph
18:26
created

AbstractEntityTest::testGeneratedCreate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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

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

410
        foreach ($meta->/** @scrutinizer ignore-call */ getAssociationMappings() as $mapping) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
411
            $getter = 'get' . $mapping['fieldName'];
412
            if ($meta->isCollectionValuedAssociation($mapping['fieldName'])) {
413
                $collection = $entity->$getter()->toArray();
414
                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

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

605
                /** @scrutinizer ignore-call */ 
606
                $pass = self::assertCorrectMapping($mapping, $associationMapping, $classFqn);
Loading history...
606
                break;
607
            }
608
        }
609
        self::assertTrue(
610
            $pass,
611
            'Failed finding association mapping to test for ' . "\n" . $mapping['targetEntity']
612
        );
613
    }
614
615
    /**
616
     * @param array  $mapping
617
     * @param array  $associationMapping
618
     * @param string $classFqn
619
     *
620
     * @return bool
621
     */
622
    protected function assertCorrectMapping(array $mapping, array $associationMapping, string $classFqn): bool
623
    {
624
        if (empty($mapping['joinTable'])) {
625
            self::assertArrayNotHasKey(
626
                'joinTable',
627
                $associationMapping,
628
                $classFqn . ' join table is empty,
629
                        but association ' . $mapping['targetEntity'] . ' join table is not empty'
630
            );
631
632
            return true;
633
        }
634
        self::assertNotEmpty(
635
            $associationMapping['joinTable'],
636
            "$classFqn joinTable is set to " . $mapping['joinTable']['name']
637
            . " \n association " . $mapping['targetEntity'] . ' join table is empty'
638
        );
639
        self::assertSame(
640
            $mapping['joinTable']['name'],
641
            $associationMapping['joinTable']['name'],
642
            "join tables not the same: \n * $classFqn = " . $mapping['joinTable']['name']
643
            . " \n * association " . $mapping['targetEntity']
644
            . ' = ' . $associationMapping['joinTable']['name']
645
        );
646
        self::assertArrayHasKey(
647
            'inverseJoinColumns',
648
            $associationMapping['joinTable'],
649
            "join table join columns not the same: \n * $classFqn joinColumn = "
650
            . $mapping['joinTable']['joinColumns'][0]['name']
651
            . " \n * association " . $mapping['targetEntity']
652
            . ' inverseJoinColumn is not set'
653
        );
654
        self::assertSame(
655
            $mapping['joinTable']['joinColumns'][0]['name'],
656
            $associationMapping['joinTable']['inverseJoinColumns'][0]['name'],
657
            "join table join columns not the same: \n * $classFqn joinColumn = "
658
            . $mapping['joinTable']['joinColumns'][0]['name']
659
            . " \n * association " . $mapping['targetEntity']
660
            . ' inverseJoinColumn = ' . $associationMapping['joinTable']['inverseJoinColumns'][0]['name']
661
        );
662
        self::assertSame(
663
            $mapping['joinTable']['inverseJoinColumns'][0]['name'],
664
            $associationMapping['joinTable']['joinColumns'][0]['name'],
665
            "join table join columns  not the same: \n * $classFqn inverseJoinColumn = "
666
            . $mapping['joinTable']['inverseJoinColumns'][0]['name']
667
            . " \n * association " . $mapping['targetEntity'] . ' joinColumn = '
668
            . $associationMapping['joinTable']['joinColumns'][0]['name']
669
        );
670
671
        return true;
672
    }
673
674
675
    protected function validateEntity(EntityInterface $entity): void
676
    {
677
        $entity->validate();
678
    }
679
680
681
    /**
682
     * Get the fully qualified name of the Entity we are testing,
683
     * assumes EntityNameTest as the entity class short name
684
     *
685
     * @return string
686
     */
687
    protected function getTestedEntityFqn(): string
688
    {
689
        if (null === $this->testedEntityFqn) {
690
            $this->testedEntityFqn = \substr(static::class, 0, -4);
691
        }
692
693
        return $this->testedEntityFqn;
694
    }
695
696
697
    /**
698
     * Get a \ReflectionClass for the currently tested Entity
699
     *
700
     * @return \ts\Reflection\ReflectionClass
701
     * @throws \ReflectionException
702
     */
703
    protected function getTestedEntityReflectionClass(): \ts\Reflection\ReflectionClass
704
    {
705
        if (null === $this->testedEntityReflectionClass) {
706
            $this->testedEntityReflectionClass = new \ts\Reflection\ReflectionClass(
707
                $this->getTestedEntityFqn()
708
            );
709
        }
710
711
        return $this->testedEntityReflectionClass;
712
    }
713
}
714