EntityRepositoryTest::testEntityDelete()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 18
dl 0
loc 27
rs 9.6666
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\DoctrineEntityRepository;
6
7
use Arp\DoctrineEntityRepository\Constant\QueryServiceOption;
8
use Arp\DoctrineEntityRepository\EntityRepository;
9
use Arp\DoctrineEntityRepository\EntityRepositoryInterface;
10
use Arp\DoctrineEntityRepository\Exception\EntityRepositoryException;
11
use Arp\DoctrineEntityRepository\Persistence\Exception\PersistenceException;
12
use Arp\DoctrineEntityRepository\Persistence\PersistServiceInterface;
13
use Arp\DoctrineEntityRepository\Query\Exception\QueryServiceException;
14
use Arp\DoctrineEntityRepository\Query\QueryServiceInterface;
15
use Arp\Entity\EntityInterface;
16
use Doctrine\Persistence\ObjectRepository;
17
use PHPUnit\Framework\MockObject\MockObject;
18
use PHPUnit\Framework\TestCase;
19
use Psr\Log\LoggerInterface;
20
21
/**
22
 * @covers \Arp\DoctrineEntityRepository\AbstractEntityRepository
23
 * @covers \Arp\DoctrineEntityRepository\EntityRepository
24
 *
25
 * @author  Alex Patterson <[email protected]>
26
 * @package ArpTest\DoctrineEntityRepository
27
 */
28
final class EntityRepositoryTest extends TestCase
29
{
30
    /**
31
     * @var string
32
     */
33
    private string $entityName;
34
35
    /**
36
     * @var QueryServiceInterface&MockObject
37
     */
38
    private $queryService;
39
40
    /**
41
     * @var PersistServiceInterface&MockObject
42
     */
43
    private $persistService;
44
45
    /**
46
     * @var LoggerInterface&MockObject
47
     */
48
    private $logger;
49
50
    /**
51
     * Set up the test case dependencies.
52
     */
53
    public function setUp(): void
54
    {
55
        $this->entityName = EntityInterface::class;
56
57
        $this->queryService = $this->getMockForAbstractClass(QueryServiceInterface::class);
58
59
        $this->persistService = $this->getMockForAbstractClass(PersistServiceInterface::class);
60
61
        $this->logger = $this->getMockForAbstractClass(LoggerInterface::class);
62
    }
63
64
    /**
65
     * Assert the EntityRepository implements the EntityRepositoryInterface
66
     */
67
    public function testImplementsEntityRepositoryInterface(): void
68
    {
69
        $repository = new EntityRepository(
70
            $this->entityName,
71
            $this->queryService,
72
            $this->persistService,
73
            $this->logger
74
        );
75
76
        $this->assertInstanceOf(EntityRepositoryInterface::class, $repository);
77
    }
78
79
    /**
80
     * Assert the EntityRepository implements the ObjectRepository
81
     */
82
    public function testImplementsObjectRepository(): void
83
    {
84
        $repository = new EntityRepository(
85
            $this->entityName,
86
            $this->queryService,
87
            $this->persistService,
88
            $this->logger
89
        );
90
91
        $this->assertInstanceOf(ObjectRepository::class, $repository);
92
    }
93
94
    /**
95
     * Assert that method getClassName() will return the FQCN of the managed entity
96
     */
97
    public function testGetClassNameWillReturnTheFullyQualifiedEntityClassName(): void
98
    {
99
        $repository = new EntityRepository(
100
            $this->entityName,
101
            $this->queryService,
102
            $this->persistService,
103
            $this->logger
104
        );
105
106
        $this->assertSame($this->entityName, $repository->getClassName());
107
    }
108
109
    /**
110
     * Assert that if find() cannot load/fails then any thrown exception/throwable will be caught and rethrown as
111
     * a EntityRepositoryException
112
     *
113
     * @throws EntityRepositoryException
114
     */
115
    public function testFindCatchAndRethrowExceptionsAsEntityRepositoryExceptionIfTheFindQueryFails(): void
116
    {
117
        $repository = new EntityRepository(
118
            $this->entityName,
119
            $this->queryService,
120
            $this->persistService,
121
            $this->logger
122
        );
123
124
        $entityId = 'FOO123';
125
126
        $exceptionMessage = 'This is a test exception message for '  . __FUNCTION__;
127
        $exceptionCode = 123;
128
        $exception = new QueryServiceException($exceptionMessage, $exceptionCode);
129
130
        $this->queryService->expects($this->once())
131
            ->method('findOneById')
132
            ->with($entityId)
133
            ->willThrowException($exception);
134
135
        $errorMessage = sprintf(
136
            'Unable to find entity of type \'%s\': %s',
137
            $this->entityName,
138
            $exceptionMessage
139
        );
140
141
        $this->logger->expects($this->once())
142
            ->method('error')
143
            ->with($errorMessage, ['exception' => $exception, 'id' => $entityId]);
144
145
        $this->expectException(EntityRepositoryException::class);
146
        $this->expectExceptionCode($exceptionCode);
147
        $this->expectExceptionMessage($errorMessage);
148
149
        $repository->find($entityId);
150
    }
151
152
    /**
153
     * Assert that find() will return NULL if the entity cannot be found by it's $id
154
     *
155
     * @throws EntityRepositoryException
156
     */
157
    public function testFindWillReturnNullIfNotEntityCanBeFound(): void
158
    {
159
        $repository = new EntityRepository(
160
            $this->entityName,
161
            $this->queryService,
162
            $this->persistService,
163
            $this->logger
164
        );
165
166
        $entityId = 'FOO123';
167
168
        $this->queryService->expects($this->once())
169
            ->method('findOneById')
170
            ->with($entityId)
171
            ->willReturn(null);
172
173
        $this->assertNull($repository->find($entityId));
174
    }
175
176
    /**
177
     * Assert that find() will the found entity by it's $id
178
     *
179
     * @throws EntityRepositoryException
180
     */
181
    public function testFindWillReturnTheMatchedEntity(): void
182
    {
183
        $repository = new EntityRepository(
184
            $this->entityName,
185
            $this->queryService,
186
            $this->persistService,
187
            $this->logger
188
        );
189
190
        /** @var EntityInterface&MockObject $entity */
191
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
192
        $entityId = 'FOO123';
193
194
        $this->queryService->expects($this->once())
195
            ->method('findOneById')
196
            ->with($entityId)
197
            ->willReturn($entity);
198
199
        $this->assertSame($entity, $repository->find($entityId));
200
    }
201
202
    /**
203
     * Assert that if the findOneBy() will catch and rethrow exception messages as EntityRepositoryException
204
     *
205
     * @throws EntityRepositoryException
206
     */
207
    public function testFindOneByWillThrowEntityRepositoryExceptionIfTheQueryCannotBePerformed(): void
208
    {
209
        $repository = new EntityRepository(
210
            $this->entityName,
211
            $this->queryService,
212
            $this->persistService,
213
            $this->logger
214
        );
215
216
        $criteria = [
217
            'name'  => 'Test',
218
            'hello' => 'World',
219
            'test'  => 123,
220
        ];
221
222
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
223
        $exceptionCode = 456;
224
225
        $exception = new QueryServiceException($exceptionMessage, $exceptionCode);
226
227
        $this->queryService->expects($this->once())
228
            ->method('findOne')
229
            ->with($criteria)
230
            ->willThrowException($exception);
231
232
        $errorMessage = sprintf('Unable to find entity of type \'%s\': %s', $this->entityName, $exceptionMessage);
233
234
        $this->logger->error($errorMessage, compact('exception', 'criteria'));
235
236
        $this->expectException(EntityRepositoryException::class);
237
        $this->expectExceptionMessage($errorMessage);
238
        $this->expectExceptionCode($exceptionCode);
239
240
        $repository->findOneBy($criteria);
241
    }
242
243
    /**
244
     * Assert that findOneBy() will return NULL if the provided criteria doesn't match any existing entities
245
     *
246
     * @throws EntityRepositoryException
247
     */
248
    public function testFindOneByWillReturnNullIfAMatchingEntityCannotBeFound(): void
249
    {
250
        $repository = new EntityRepository(
251
            $this->entityName,
252
            $this->queryService,
253
            $this->persistService,
254
            $this->logger
255
        );
256
257
        $criteria = [
258
            'foo'  => 'bar',
259
            'test' => 789,
260
        ];
261
262
        $this->queryService->expects($this->once())
263
            ->method('findOne')
264
            ->with($criteria)
265
            ->willReturn(null);
266
267
        $this->assertNull($repository->findOneBy($criteria));
268
    }
269
270
    /**
271
     * Assert findOneBy() will return a single matched entity instance
272
     *
273
     * @throws EntityRepositoryException
274
     */
275
    public function testFindOneByWillReturnASingleMatchEntity(): void
276
    {
277
        $repository = new EntityRepository(
278
            $this->entityName,
279
            $this->queryService,
280
            $this->persistService,
281
            $this->logger
282
        );
283
284
        $criteria = [
285
            'fred' => 'test',
286
            'bob'  => 123,
287
        ];
288
289
        /** @var EntityInterface&MockObject $entity */
290
        $entity = $this->createMock(EntityInterface::class);
291
292
        $this->queryService->expects($this->once())
293
            ->method('findOne')
294
            ->with($criteria)
295
            ->willReturn($entity);
296
297
        $this->assertSame($entity, $repository->findOneBy($criteria));
298
    }
299
300
    /**
301
     * Assert that calls to findAll() will proxy to findBy() with an empty array.
302
     *
303
     * @covers \Arp\DoctrineEntityRepository\EntityRepository::findAll
304
     *
305
     * @throws EntityRepositoryException
306
     */
307
    public function testFindAllWillProxyAnEmptyArrayToFindBy(): void
308
    {
309
        /** @var EntityRepository&MockObject $repository */
310
        $repository = $this->getMockBuilder(EntityRepository::class)
311
            ->setConstructorArgs(
312
                [
313
                    $this->entityName,
314
                    $this->queryService,
315
                    $this->persistService,
316
                    $this->logger,
317
                ]
318
            )
319
            ->onlyMethods(['findBy'])
320
            ->getMock();
321
322
        /** @var EntityInterface[]&MockObject[] $entities */
323
        $entities = [
324
            $this->createMock(EntityInterface::class),
325
            $this->createMock(EntityInterface::class),
326
            $this->createMock(EntityInterface::class),
327
        ];
328
329
        $repository->expects($this->once())
330
            ->method('findBy')
331
            ->with([])
332
            ->willReturn($entities);
333
334
        $this->assertSame($entities, $repository->findAll());
335
    }
336
337
    /**
338
     * Assert that the findBy() method will catch \Throwable exceptions, log the exception data and rethrow as a
339
     * EntityRepositoryException
340
     *
341
     * @throws EntityRepositoryException
342
     */
343
    public function testFindByWillCatchAndRethrowEntityRepositoryExceptionOnFailure(): void
344
    {
345
        /** @var EntityRepository&MockObject $repository */
346
        $repository = new EntityRepository(
347
            $this->entityName,
348
            $this->queryService,
349
            $this->persistService,
350
            $this->logger
351
        );
352
353
        $criteria = [];
354
        $options = [];
355
356
        $exceptionMessage = 'This is a foo test exception for ' . __FUNCTION__;
357
        $exceptionCode = 456;
358
        $exception = new QueryServiceException($exceptionMessage, $exceptionCode);
359
360
        $this->queryService->expects($this->once())
361
            ->method('findMany')
362
            ->with($criteria, $options)
363
            ->willThrowException($exception);
364
365
        $errorMessage = sprintf(
366
            'Unable to return a collection of type \'%s\': %s',
367
            $this->entityName,
368
            $exceptionMessage
369
        );
370
371
        $this->logger->expects($this->once())
372
            ->method('error')
373
            ->with($errorMessage, compact('exception', 'criteria', 'options'));
374
375
        $this->expectException(EntityRepositoryException::class);
376
        $this->expectExceptionMessage($errorMessage);
377
378
        $repository->findBy([]);
379
    }
380
381
    /**
382
     * Assert the required search arguments are passed to findMany().
383
     *
384
     * @param array<mixed> $data
385
     *
386
     * @covers       \Arp\DoctrineEntityRepository\EntityRepository::findBy
387
     *
388
     * @dataProvider getFindByWithSearchArgumentsData
389
     *
390
     * @throws EntityRepositoryException
391
     */
392
    public function testFindByWillPassValidSearchArguments(array $data): void
393
    {
394
        /** @var EntityRepository&MockObject $repository */
395
        $repository = new EntityRepository(
396
            $this->entityName,
397
            $this->queryService,
398
            $this->persistService,
399
            $this->logger
400
        );
401
402
        $criteria = $data['criteria'] ?? [];
403
        $options = [];
404
405
        if (isset($data['order_by'])) {
406
            $options[QueryServiceOption::ORDER_BY] = $data['order_by'];
407
        }
408
409
        if (isset($data['limit'])) {
410
            $options[QueryServiceOption::MAX_RESULTS] = $data['limit'];
411
        }
412
413
        if (isset($data['offset'])) {
414
            $options[QueryServiceOption::FIRST_RESULT] = $data['offset'];
415
        }
416
417
        /** @var EntityInterface[]&MockObject[] $collection */
418
        $collection = [
419
            $this->createMock(EntityInterface::class),
420
            $this->createMock(EntityInterface::class),
421
            $this->createMock(EntityInterface::class),
422
        ];
423
424
        $this->queryService->expects($this->once())
425
            ->method('findMany')
426
            ->with($criteria, $options)
427
            ->willReturn($collection);
428
429
        $result = $repository->findBy(
430
            $criteria,
431
            $data['order_by'] ?? null,
432
            $data['limit'] ?? null,
433
            $data['offset'] ?? null
434
        );
435
436
        $this->assertSame($collection, $result);
437
    }
438
439
    /**
440
     * @return array<mixed>
441
     */
442
    public function getFindByWithSearchArgumentsData(): array
443
    {
444
        return [
445
            [
446
                // Empty Data
447
                [
448
449
                ],
450
            ],
451
452
            [
453
                [
454
                    'criteria' => [
455
                        'name'  => 'test',
456
                        'hello' => 'foo',
457
                    ],
458
                ],
459
            ],
460
461
            [
462
                [
463
                    'limit' => 100,
464
                ],
465
            ],
466
467
            [
468
                [
469
                    'offset' => 10,
470
                ],
471
            ],
472
473
            [
474
                [
475
                    'order_by' => [
476
                        'name' => 'desc',
477
                        'foo'  => 'asc',
478
                    ],
479
                ],
480
            ],
481
482
            [
483
                [
484
                    'criteria' => [
485
                        'name'  => 'test',
486
                        'hello' => 'foo',
487
                        'foo'   => 123,
488
                    ],
489
                    'offset'   => 7,
490
                    'limit'    => 1000,
491
                    'order_by' => [
492
                        'hello' => 'asc',
493
                    ],
494
                ],
495
            ],
496
        ];
497
    }
498
499
    /**
500
     * Assert that errors during save that throw a \Throwable exception are caught, logged and rethrown as a
501
     * EntityRepositoryException
502
     *
503
     * @throws EntityRepositoryException
504
     */
505
    public function testSaveWillCatchThrowableAndRethrowEntityRepositoryException(): void
506
    {
507
        /** @var EntityRepository&MockObject $repository */
508
        $repository = new EntityRepository(
509
            $this->entityName,
510
            $this->queryService,
511
            $this->persistService,
512
            $this->logger
513
        );
514
515
        /** @var EntityInterface&MockObject $entity */
516
        $entity = $this->createMock(EntityInterface::class);
517
518
        $exceptionMessage = 'This is a test exception message for save()';
519
        $exceptionCode = 999;
520
        $exception = new PersistenceException($exceptionMessage, $exceptionCode);
521
522
        $errorMessage = sprintf('Unable to save entity of type \'%s\': %s', $this->entityName, $exceptionMessage);
523
524
        $this->persistService->expects($this->once())
525
            ->method('save')
526
            ->with($entity)
527
            ->willThrowException($exception);
528
529
        $this->expectException(EntityRepositoryException::class);
530
        $this->expectExceptionMessage($errorMessage);
531
        $this->expectExceptionCode($exceptionCode);
532
533
        $this->assertSame($entity, $repository->save($entity));
534
    }
535
536
    /**
537
     * Assert that calls to save() will result in the entity being passed to the persist service
538
     *
539
     * @throws EntityRepositoryException
540
     */
541
    public function testSaveAndReturnEntityWithProvidedOptions(): void
542
    {
543
        $options = [
544
            'foo' => 'bar',
545
        ];
546
547
        $repository = new EntityRepository(
548
            $this->entityName,
549
            $this->queryService,
550
            $this->persistService,
551
            $this->logger
552
        );
553
554
        /** @var EntityInterface&MockObject $entity */
555
        $entity = $this->createMock(EntityInterface::class);
556
557
        $this->persistService->expects($this->once())
558
            ->method('save')
559
            ->with($entity, $options)
560
            ->willReturn($entity);
561
562
        $this->assertSame($entity, $repository->save($entity, $options));
563
    }
564
565
    /**
566
     * Assert PersistenceException errors are caught an rethrown as EntityRepositoryException
567
     * when calling saveCollection()
568
     *
569
     * @throws EntityRepositoryException
570
     */
571
    public function testSaveCollectionExceptionWillBeCaughtAndRethrownAsEntityRepositoryException(): void
572
    {
573
        $repository = new EntityRepository(
574
            $this->entityName,
575
            $this->queryService,
576
            $this->persistService,
577
            $this->logger
578
        );
579
580
        $options = [
581
            'testing' => 456,
582
            123 => 'Hello',
583
            'nice' => true,
584
        ];
585
586
        /** @var array<EntityInterface> $collection */
587
        $collection = [
588
            $this->createMock(EntityInterface::class),
589
            $this->createMock(EntityInterface::class),
590
        ];
591
592
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
593
        $exceptionCode = 123;
594
        $exception = new PersistenceException($exceptionMessage, $exceptionCode);
595
596
        $this->persistService->expects($this->once())
597
            ->method('saveCollection')
598
            ->with($collection, $options)
599
            ->willThrowException($exception);
600
601
        $errorMessage = sprintf('Unable to save entity of type \'%s\': %s', $this->entityName, $exceptionMessage);
602
603
        $this->logger->expects($this->once())
604
            ->method('error')
605
            ->with($errorMessage, ['exception' => $exception, 'entity_name' => $this->entityName]);
606
607
        $this->expectException(EntityRepositoryException::class);
608
        $this->expectExceptionCode($exceptionCode);
609
        $this->expectExceptionMessage($errorMessage);
610
611
        $repository->saveCollection($collection, $options);
612
    }
613
614
    /**
615
     * Assert that an entity collection can be successfully saved using saveCollection()
616
     *
617
     * @throws EntityRepositoryException
618
     */
619
    public function testSaveCollection(): void
620
    {
621
        $repository = new EntityRepository(
622
            $this->entityName,
623
            $this->queryService,
624
            $this->persistService,
625
            $this->logger
626
        );
627
628
        $options = [
629
            'foo' => 123,
630
            'bar' => 'Test',
631
        ];
632
633
        /** @var array<EntityInterface> $collection */
634
        $collection = [
635
            $this->createMock(EntityInterface::class),
636
            $this->createMock(EntityInterface::class),
637
        ];
638
639
        $this->persistService->expects($this->once())
640
            ->method('saveCollection')
641
            ->with($collection, $options)
642
            ->willReturn($collection);
643
644
        $this->assertSame($collection, $repository->saveCollection($collection, $options));
645
    }
646
647
    /**
648
     * Assert that if we provide an incorrect data type for $entity to delete() an EntityRepositoryException
649
     * will be thrown
650
     *
651
     * @param mixed $entity
652
     *
653
     * @dataProvider getDeleteWillThrowEntityRepositoryExceptionIfProvidedEntityIsInvalidData
654
     *
655
     * @throws EntityRepositoryException
656
     */
657
    public function testDeleteWillThrowEntityRepositoryExceptionIfProvidedEntityIsInvalid($entity): void
658
    {
659
        $repository = new EntityRepository(
660
            $this->entityName,
661
            $this->queryService,
662
            $this->persistService,
663
            $this->logger
664
        );
665
666
        $errorMessage = sprintf(
667
            'The \'entity\' argument must be a \'string\' or an object of type \'%s\'; '
668
            . '\'%s\' provided in \'%s::delete\'',
669
            EntityInterface::class,
670
            (is_object($entity) ? get_class($entity) : gettype($entity)),
671
            EntityRepository::class
672
        );
673
674
        $this->logger->expects($this->once())
675
            ->method('error')
676
            ->with($errorMessage);
677
678
        $this->expectException(EntityRepositoryException::class);
679
        $this->expectExceptionMessage($errorMessage);
680
681
        $repository->delete($entity);
682
    }
683
684
    /**
685
     * @return array<mixed>
686
     */
687
    public function getDeleteWillThrowEntityRepositoryExceptionIfProvidedEntityIsInvalidData(): array
688
    {
689
        return [
690
            [true],
691
            [new \stdClass()],
692
            [45.67],
693
        ];
694
    }
695
696
    /**
697
     * Assert that a EntityRepositoryException will be thrown from delete if providing an entity id that does not
698
     * exist
699
     *
700
     * @throws EntityRepositoryException
701
     */
702
    public function testDeleteWillThrowEntityNotFoundExceptionIfEntityCannotBeFoundWithStringId(): void
703
    {
704
        $repository = new EntityRepository(
705
            $this->entityName,
706
            $this->queryService,
707
            $this->persistService,
708
            $this->logger
709
        );
710
711
        $id = 'FOO123';
712
713
        $errorMessage = sprintf(
714
            'Unable to delete entity \'%s::%s\': The entity could not be found',
715
            $this->entityName,
716
            $id
717
        );
718
719
        $this->logger->expects($this->once())
720
            ->method('error')
721
            ->with($errorMessage);
722
723
        $this->expectException(EntityRepositoryException::class);
724
        $this->expectExceptionMessage($errorMessage);
725
726
        $repository->delete($id);
727
    }
728
729
    /**
730
     * Assert that a EntityRepositoryException will be thrown if the call to delete() fails.
731
     *
732
     * @throws EntityRepositoryException
733
     */
734
    public function testDeleteWillThrowEntityRepositoryExceptionIfEntityCannotDeleted(): void
735
    {
736
        $repository = new EntityRepository(
737
            $this->entityName,
738
            $this->queryService,
739
            $this->persistService,
740
            $this->logger
741
        );
742
743
        /** @var EntityInterface&MockObject $entity */
744
        $entity = $this->createMock(EntityInterface::class);
745
746
        $options = [
747
            'foo' => 123,
748
            'bar' => 'Test',
749
        ];
750
751
        $exceptionMessage = 'This is a test exception message';
752
        $exceptionCode = 789;
753
        $exception = new PersistenceException($exceptionMessage, $exceptionCode);
754
755
        $this->persistService->expects($this->once())
756
            ->method('delete')
757
            ->with($entity, $options)
758
            ->willThrowException($exception);
759
760
        $errorMessage = sprintf(
761
            'Unable to delete entity of type \'%s\': %s',
762
            $this->entityName,
763
            $exceptionMessage
764
        );
765
766
        $this->logger->expects($this->once())
767
            ->method('error')
768
            ->with($errorMessage, ['exception' => $exception, 'entity_name' => $this->entityName]);
769
770
        $this->expectException(EntityRepositoryException::class);
771
        $this->expectExceptionMessage($errorMessage);
772
773
        $repository->delete($entity, $options);
774
    }
775
776
    /**
777
     * Assert that we are able to delete an entity by it's id or instance
778
     *
779
     * @param EntityInterface|string $entity
780
     * @param array<mixed>           $options
781
     *
782
     * @dataProvider getEntityDeleteData
783
     * @throws EntityRepositoryException
784
     */
785
    public function testEntityDelete($entity, array $options = []): void
786
    {
787
        $repository = new EntityRepository(
788
            $this->entityName,
789
            $this->queryService,
790
            $this->persistService,
791
            $this->logger
792
        );
793
794
        if (is_string($entity)) {
795
            /** @var EntityInterface&MockObject $entityObject */
796
            $entityObject = $this->createMock(EntityInterface::class);
797
798
            $this->queryService->expects($this->once())
799
                ->method('findOneById')
800
                ->with($entity)
801
                ->willReturn($entityObject);
802
        } else {
803
            $entityObject = $entity;
804
        }
805
806
        $this->persistService->expects($this->once())
807
            ->method('delete')
808
            ->with($entityObject, $options)
809
            ->willReturn(true);
810
811
        $this->assertTrue($repository->delete($entity, $options));
812
    }
813
814
    /**
815
     * @return array<mixed>
816
     */
817
    public function getEntityDeleteData(): array
818
    {
819
        return [
820
            [
821
                'Foo123',
822
                [
823
                    'foo'  => 'bar',
824
                    'test' => 'Hello',
825
                ],
826
            ],
827
            [
828
                $this->createMock(EntityInterface::class),
829
                [
830
                    'foo'  => 'bar',
831
                    'test' => 'Hello',
832
                ],
833
            ],
834
        ];
835
    }
836
837
    /**
838
     * Assert PersistenceException errors are caught an rethrown as EntityRepositoryException
839
     * when calling deleteCollection()
840
     *
841
     * @throws EntityRepositoryException
842
     */
843
    public function testDeleteCollectionExceptionWillBeCaughtAndRethrownAsEntityRepositoryException(): void
844
    {
845
        $repository = new EntityRepository(
846
            $this->entityName,
847
            $this->queryService,
848
            $this->persistService,
849
            $this->logger
850
        );
851
852
        $options = [
853
            'testing' => 456,
854
            123 => 'Hello',
855
            'nice' => true,
856
        ];
857
858
        /** @var array<EntityInterface> $collection */
859
        $collection = [
860
            $this->createMock(EntityInterface::class),
861
            $this->createMock(EntityInterface::class),
862
            $this->createMock(EntityInterface::class),
863
            $this->createMock(EntityInterface::class),
864
        ];
865
866
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
867
        $exceptionCode = 123;
868
        $exception = new PersistenceException($exceptionMessage, $exceptionCode);
869
870
        $this->persistService->expects($this->once())
871
            ->method('deleteCollection')
872
            ->with($collection, $options)
873
            ->willThrowException($exception);
874
875
        $errorMessage = sprintf(
876
            'Unable to delete entity collection of type \'%s\': %s',
877
            $this->entityName,
878
            $exceptionMessage
879
        );
880
881
        $this->logger->expects($this->once())
882
            ->method('error')
883
            ->with($errorMessage, ['exception' => $exception, 'entity_name' => $this->entityName]);
884
885
        $this->expectException(EntityRepositoryException::class);
886
        $this->expectExceptionCode($exceptionCode);
887
        $this->expectExceptionMessage($errorMessage);
888
889
        $repository->deleteCollection($collection, $options);
890
    }
891
892
    /**
893
     * Assert that an entity collection can be successfully saved using deleteCollection()
894
     *
895
     * @throws EntityRepositoryException
896
     */
897
    public function testSDeleteCollection(): void
898
    {
899
        $repository = new EntityRepository(
900
            $this->entityName,
901
            $this->queryService,
902
            $this->persistService,
903
            $this->logger
904
        );
905
906
        $options = [
907
            'foo' => 456,
908
            'bar123' => 'Testing Delete',
909
        ];
910
911
        /** @var array<EntityInterface> $collection */
912
        $collection = [
913
            $this->createMock(EntityInterface::class),
914
            $this->createMock(EntityInterface::class),
915
        ];
916
917
        $this->persistService->expects($this->once())
918
            ->method('deleteCollection')
919
            ->with($collection, $options)
920
            ->willReturn(123);
921
922
        $this->assertSame(123, $repository->deleteCollection($collection, $options));
923
    }
924
925
    /**
926
     * Assert that calls to clear that fail will be caught and rethrown as EntityRepositoryException
927
     *
928
     * @throws EntityRepositoryException
929
     */
930
    public function testClearWillThrowEntityRepositoryExceptionOnFailure(): void
931
    {
932
        $repository = new EntityRepository(
933
            $this->entityName,
934
            $this->queryService,
935
            $this->persistService,
936
            $this->logger
937
        );
938
939
        $exceptionCode = 123;
940
        $exceptionMessage = 'This is a test exception message';
941
        $exception = new PersistenceException($exceptionMessage, $exceptionCode);
942
943
        $this->persistService->expects($this->once())
944
            ->method('clear')
945
            ->willThrowException($exception);
946
947
        $errorMessage = sprintf(
948
            'Unable to clear entity of type \'%s\': %s',
949
            $this->entityName,
950
            $exceptionMessage
951
        );
952
953
        $this->logger->expects($this->once())
954
            ->method('error')
955
            ->with($errorMessage, ['exception' => $exception, 'entity_name' => $this->entityName]);
956
957
        $this->expectException(EntityRepositoryException::class);
958
        $this->expectExceptionMessage($errorMessage);
959
        $this->expectExceptionCode($exceptionCode);
960
961
        $repository->clear();
962
    }
963
964
    /**
965
     * Assert that calls to clear() will proxy to PersistService::clear()
966
     *
967
     * @throws EntityRepositoryException
968
     */
969
    public function testClearWillProxyToPersistServiceClear(): void
970
    {
971
        $repository = new EntityRepository(
972
            $this->entityName,
973
            $this->queryService,
974
            $this->persistService,
975
            $this->logger
976
        );
977
978
        $this->persistService->expects($this->once())->method('clear');
979
980
        $repository->clear();
981
    }
982
983
    /**
984
     * Assert that calls to refresh that fail will be caught and rethrown as EntityRepositoryException
985
     *
986
     * @throws EntityRepositoryException
987
     */
988
    public function testRefreshWillThrowEntityRepositoryExceptionOnFailure(): void
989
    {
990
        $repository = new EntityRepository(
991
            $this->entityName,
992
            $this->queryService,
993
            $this->persistService,
994
            $this->logger
995
        );
996
997
        /** @var EntityInterface&MockObject $entity */
998
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
999
1000
        $exceptionCode = 456;
1001
        $exceptionMessage = 'This is a test exception message';
1002
        $exception = new PersistenceException($exceptionMessage, $exceptionCode);
1003
1004
        $this->persistService->expects($this->once())
1005
            ->method('refresh')
1006
            ->willThrowException($exception);
1007
1008
        $errorMessage = sprintf(
1009
            'Unable to refresh entity of type \'%s\': %s',
1010
            $this->entityName,
1011
            $exceptionMessage
1012
        );
1013
1014
        $id = 'HELLO123';
1015
        $entity->expects($this->once())
1016
            ->method('getId')
1017
            ->willReturn($id);
1018
1019
        $this->logger->expects($this->once())
1020
            ->method('error')
1021
            ->with(
1022
                $errorMessage,
1023
                ['exception' => $exception, 'entity_name' => $this->entityName, 'id' => $id]
1024
            );
1025
1026
        $this->expectException(EntityRepositoryException::class);
1027
        $this->expectExceptionMessage($errorMessage);
1028
        $this->expectExceptionCode($exceptionCode);
1029
1030
        $repository->refresh($entity);
1031
    }
1032
1033
    /**
1034
     * Assert that calls to refresh() will proxy to PersistService::refresh()
1035
     *
1036
     * @throws EntityRepositoryException
1037
     */
1038
    public function testRefreshWillProxyToPersistServiceClear(): void
1039
    {
1040
        $repository = new EntityRepository(
1041
            $this->entityName,
1042
            $this->queryService,
1043
            $this->persistService,
1044
            $this->logger
1045
        );
1046
1047
        /** @var EntityInterface&MockObject $entity */
1048
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
1049
1050
        $this->persistService->expects($this->once())
1051
            ->method('refresh')
1052
            ->with($entity);
1053
1054
        $repository->refresh($entity);
1055
    }
1056
}
1057