Completed
Push — master ( f9d483...c44aff )
by Alex
15s queued 12s
created

testRefreshWillThrowEntityRepositoryExceptionOnFailure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 35
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

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