Test Failed
Pull Request — master (#8)
by Alex
02:49
created

PersistServiceTest::testCommitTransaction()

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 7
c 1
b 0
f 0
dl 0
loc 12
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArpTest\DoctrineEntityRepository\Persistence;
6
7
use Arp\DoctrineEntityRepository\Constant\EntityEventName;
8
use Arp\DoctrineEntityRepository\Constant\PersistServiceOption;
9
use Arp\DoctrineEntityRepository\Persistence\Event\EntityErrorEvent;
10
use Arp\DoctrineEntityRepository\Persistence\Event\EntityEvent;
11
use Arp\DoctrineEntityRepository\Persistence\Exception\PersistenceException;
12
use Arp\DoctrineEntityRepository\Persistence\PersistService;
13
use Arp\DoctrineEntityRepository\Persistence\PersistServiceInterface;
14
use Arp\Entity\EntityInterface;
15
use Arp\Entity\EntityTrait;
16
use Arp\EventDispatcher\Listener\Exception\EventListenerException;
17
use Doctrine\ORM\EntityManagerInterface;
18
use PHPUnit\Framework\MockObject\MockObject;
19
use PHPUnit\Framework\TestCase;
20
use Psr\EventDispatcher\EventDispatcherInterface;
21
use Psr\Log\LoggerInterface;
22
23
/**
24
 * @covers  \Arp\DoctrineEntityRepository\Persistence\PersistService
25
 *
26
 * @author  Alex Patterson <[email protected]>
27
 * @package ArpTest\DoctrineEntityRepository\Persistence
28
 */
29
final class PersistServiceTest extends TestCase
30
{
31
    /**
32
     * @var string
33
     */
34
    private string $entityName;
35
36
    /**
37
     * @var EntityManagerInterface&MockObject
38
     */
39
    private $entityManager;
40
41
    /**
42
     * @var EventDispatcherInterface&MockObject
43
     */
44
    private $eventDispatcher;
45
46
    /**
47
     * @var LoggerInterface&MockObject
48
     */
49
    private $logger;
50
51
    /**
52
     * Set up the test case dependencies.
53
     */
54
    public function setUp(): void
55
    {
56
        $this->entityName = EntityInterface::class;
57
58
        $this->entityManager = $this->getMockForAbstractClass(EntityManagerInterface::class);
59
60
        $this->eventDispatcher = $this->getMockForAbstractClass(EventDispatcherInterface::class);
61
62
        $this->logger = $this->getMockForAbstractClass(LoggerInterface::class);
63
    }
64
65
    /**
66
     * Assert that the class implements PersistServiceInterface.
67
     *
68
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::__construct
69
     */
70
    public function testImplementsPersistServiceInterface(): void
71
    {
72
        $persistService = new PersistService(
73
            $this->entityName,
74
            $this->entityManager,
75
            $this->eventDispatcher,
76
            $this->logger
77
        );
78
79
        $this->assertInstanceOf(PersistServiceInterface::class, $persistService);
80
    }
81
82
    /**
83
     * Assert that getEntityName() will return the fully qualified class name of the managed entity.
84
     *
85
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::getEntityName
86
     */
87
    public function testGetEntityNameWillReturnFQCNOfEntity(): void
88
    {
89
        $persistService = new PersistService(
90
            $this->entityName,
91
            $this->entityManager,
92
            $this->eventDispatcher,
93
            $this->logger
94
        );
95
96
        $this->assertSame($this->entityName, $persistService->getEntityName());
97
    }
98
99
    /**
100
     * If an exception is raised when calling save(), and our entity has an ID value, then the
101
     * EntityEventName::UPDATE_ERROR event should be triggered and a new exception thrown.
102
     *
103
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::save
104
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::update
105
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::createEventException
106
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::createErrorEvent
107
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::dispatchEvent
108
     *
109
     * @throws PersistenceException
110
     */
111
    public function testSaveExceptionWillBeCaughtLoggedAndTheDispatchErrorEventTriggeredWhenEntityIdIsNotNull(): void
112
    {
113
        $persistService = new PersistService(
114
            $this->entityName,
115
            $this->entityManager,
116
            $this->eventDispatcher,
117
            $this->logger
118
        );
119
120
        /** @var EntityInterface&MockObject $entity */
121
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
122
123
        $entity->expects($this->once())
124
            ->method('hasId')
125
            ->willReturn(true);
126
127
        $exceptionMessage = 'Test exception message for save() and update()';
128
        $exceptionCode = 123;
129
        $exception = new \Error($exceptionMessage, $exceptionCode);
130
131
        $this->eventDispatcher->expects($this->exactly(2))
132
            ->method('dispatch')
133
            ->withConsecutive(
134
                [$this->isInstanceOf(EntityEvent::class)],
135
                [$this->isInstanceOf(EntityErrorEvent::class)],
136
            )->willReturnOnConsecutiveCalls(
137
                $this->throwException($exception),
138
                $this->returnArgument(0)
139
            );
140
141
        $errorMessage = sprintf(
142
            'The persistence operation for entity \'%s\' failed: %s',
143
            $this->entityName,
144
            $exceptionMessage
145
        );
146
147
        $this->expectException(PersistenceException::class);
148
        $this->expectExceptionMessage($errorMessage);
149
        $this->expectExceptionCode($exceptionCode);
150
151
        $this->logger->expects($this->once())
152
            ->method('error')
153
            ->with($errorMessage, $this->arrayHasKey('exception'));
154
155
        $persistService->save($entity);
156
    }
157
158
    /**
159
     * Assert that if we pass an entity with an id to save() that the entity will be updated and returned.
160
     *
161
     * @param array<mixed> $options Optional save options that should be passed to the updated method.
162
     *
163
     * @dataProvider getSaveWillUpdateAndReturnEntityWithIdData
164
     *
165
     * @covers       \Arp\DoctrineEntityRepository\Persistence\PersistService::save
166
     * @covers       \Arp\DoctrineEntityRepository\Persistence\PersistService::update
167
     * @covers       \Arp\DoctrineEntityRepository\Persistence\PersistService::dispatchEvent
168
     *
169
     * @throws PersistenceException
170
     */
171
    public function testSaveWillUpdateAndReturnEntityWithId(array $options = []): void
172
    {
173
        /** @var PersistService&MockObject $persistService */
174
        $persistService = $this->getMockBuilder(PersistService::class)
175
            ->setConstructorArgs(
176
                [
177
                    $this->entityName,
178
                    $this->entityManager,
179
                    $this->eventDispatcher,
180
                    $this->logger,
181
                ]
182
            )->onlyMethods(['createEvent'])
183
            ->getMock();
184
185
        /** @var EntityInterface&MockObject $entity */
186
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
187
188
        $entity->expects($this->once())
189
            ->method('hasId')
190
            ->willReturn(true);
191
192
        /** @var EntityEvent&MockObject $event */
193
        $event = $this->createMock(EntityEvent::class);
194
195
        $persistService->expects($this->once())
196
            ->method('createEvent')
197
            ->with(EntityEventName::UPDATE, $entity, $options)
198
            ->willReturn($event);
199
200
        $this->eventDispatcher->expects($this->once())
201
            ->method('dispatch')
202
            ->with($event)
203
            ->willReturn($event);
204
205
        $event->expects($this->once())
206
            ->method('getEntity')
207
            ->willReturn($entity);
208
209
        $this->assertSame($entity, $persistService->save($entity, $options));
210
    }
211
212
    /**
213
     * @return array<mixed>
214
     */
215
    public function getSaveWillUpdateAndReturnEntityWithIdData(): array
216
    {
217
        return [
218
            [
219
                [
220
                    // empty options
221
                ],
222
                [
223
                    PersistServiceOption::FLUSH => true,
224
                ],
225
                [
226
                    PersistServiceOption::FLUSH => false,
227
                ],
228
            ],
229
        ];
230
    }
231
232
    /**
233
     * If an exception is raised when calling save(), and our entity does NOT have an ID value, then the
234
     * EntityEventName::CREATE_ERROR event should be triggered and a new exception thrown.
235
     *
236
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::save
237
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::insert
238
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::createEventException
239
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::createErrorEvent
240
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::dispatchEvent
241
     *
242
     * @throws PersistenceException
243
     */
244
    public function testSaveExceptionWillBeCaughtLoggedAndTheDispatchErrorEventTriggeredWhenEntityIdIsNull(): void
245
    {
246
        $persistService = new PersistService(
247
            $this->entityName,
248
            $this->entityManager,
249
            $this->eventDispatcher,
250
            $this->logger
251
        );
252
253
        /** @var EntityInterface&MockObject $entity */
254
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
255
256
        $entity->expects($this->once())
257
            ->method('hasId')
258
            ->willReturn(false);
259
260
        $exceptionMessage = 'Test exception message for save() and insert()';
261
        $exceptionCode = 456;
262
        $exception = new \Error($exceptionMessage, $exceptionCode);
263
264
        $this->eventDispatcher->expects($this->exactly(2))
265
            ->method('dispatch')
266
            ->withConsecutive(
267
                [$this->isInstanceOf(EntityEvent::class)],
268
                [$this->isInstanceOf(EntityErrorEvent::class)],
269
            )->willReturnOnConsecutiveCalls(
270
                $this->throwException($exception),
271
                $this->returnArgument(0)
272
            );
273
274
        $errorMessage = sprintf(
275
            'The persistence operation for entity \'%s\' failed: %s',
276
            $this->entityName,
277
            $exceptionMessage
278
        );
279
280
        $this->expectException(PersistenceException::class);
281
        $this->expectExceptionMessage($errorMessage);
282
        $this->expectExceptionCode($exceptionCode);
283
284
        $this->logger->expects($this->once())
285
            ->method('error')
286
            ->with($errorMessage, $this->arrayHasKey('exception'));
287
288
        $persistService->save($entity);
289
    }
290
291
    /**
292
     * Assert that an entity provided to save() that does not have an identity will be proxies to insert().
293
     *
294
     * @param array<mixed> $options
295
     *
296
     * @covers       \Arp\DoctrineEntityRepository\Persistence\PersistService::save
297
     * @covers       \Arp\DoctrineEntityRepository\Persistence\PersistService::insert
298
     * @covers       \Arp\DoctrineEntityRepository\Persistence\PersistService::dispatchEvent
299
     *
300
     * @dataProvider getSaveWillInsertAndReturnEntityWithNoIdData
301
     *
302
     * @throws PersistenceException
303
     */
304
    public function testSaveWillInsertAndReturnEntityWithNoId(array $options = []): void
305
    {
306
        /** @var PersistService&MockObject $persistService */
307
        $persistService = $this->getMockBuilder(PersistService::class)
308
            ->setConstructorArgs(
309
                [
310
                    $this->entityName,
311
                    $this->entityManager,
312
                    $this->eventDispatcher,
313
                    $this->logger,
314
                ]
315
            )->onlyMethods(['createEvent'])
316
            ->getMock();
317
318
        /** @var EntityInterface&MockObject $entity */
319
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
320
321
        $entity->expects($this->once())
322
            ->method('hasId')
323
            ->willReturn(false);
324
325
        /** @var EntityEvent&MockObject $event */
326
        $event = $this->createMock(EntityEvent::class);
327
328
        $persistService->expects($this->once())
329
            ->method('createEvent')
330
            ->with(EntityEventName::CREATE, $entity, $options)
331
            ->willReturn($event);
332
333
        $this->eventDispatcher->expects($this->once())
334
            ->method('dispatch')
335
            ->with($event)
336
            ->willReturn($event);
337
338
        $event->expects($this->once())
339
            ->method('getEntity')
340
            ->willReturn($entity);
341
342
        $this->assertSame($entity, $persistService->save($entity, $options));
343
    }
344
345
    /**
346
     * @return array<mixed>
347
     */
348
    public function getSaveWillInsertAndReturnEntityWithNoIdData(): array
349
    {
350
        return [
351
            [
352
353
            ],
354
        ];
355
    }
356
357
    /**
358
     * Assert that if an exception occurs during the dispatch of the delete event, the delete_error event will be
359
     * dispatched with the entity error event containing the caught exception
360
     *
361
     * @throws PersistenceException
362
     */
363
    public function testDeleteWillTriggerDeleteErrorEventOnError(): void
364
    {
365
        $entityName = EntityInterface::class;
366
367
        /** @var PersistService&MockObject $persistService */
368
        $persistService = $this->getMockBuilder(PersistService::class)
369
            ->setConstructorArgs([
370
                $entityName,
371
                $this->entityManager,
372
                $this->eventDispatcher,
373
                $this->logger,
374
            ])->onlyMethods(['createEvent', 'createErrorEvent'])
375
            ->getMock();
376
377
        /** @var EntityInterface&MockObject $entity */
378
        $entity = $this->createMock(EntityInterface::class);
379
        $options = [];
380
381
        /** @var EntityEvent&MockObject $event */
382
        $event = $this->createMock(EntityEvent::class);
383
384
        $persistService->expects($this->once())
385
            ->method('createEvent')
386
            ->with(EntityEventName::DELETE, $entity, $options)
387
            ->willReturn($event);
388
389
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
390
        $exceptionCode = 123;
391
        $exception = new EventListenerException($exceptionMessage, $exceptionCode);
392
393
        /** @var EntityErrorEvent&MockObject $errorEvent */
394
        $errorEvent = $this->createMock(EntityErrorEvent::class);
395
396
        $this->eventDispatcher->expects($this->exactly(2))
397
            ->method('dispatch')
398
            ->withConsecutive([$event], [$errorEvent])
399
            ->willReturnOnConsecutiveCalls(
400
                $this->throwException($exception),
401
                $errorEvent
402
            );
403
404
        $persistService->expects($this->once())
405
            ->method('createErrorEvent')
406
            ->with(EntityEventName::DELETE_ERROR, $exception)
407
            ->willReturn($errorEvent);
408
409
        $errorEvent->expects($this->once())
410
            ->method('getException')
411
            ->willReturn($exception);
412
413
        $errorMessage = sprintf(
414
            'The persistence operation for entity \'%s\' failed: %s',
415
            $entityName,
416
            $exceptionMessage
417
        );
418
419
        $this->logger->expects($this->once())
420
            ->method('error')
421
            ->with($errorMessage, $this->arrayHasKey('exception'));
422
423
        $this->expectException(PersistenceException::class);
424
        $this->expectExceptionMessage(
425
            sprintf(
426
                'The persistence operation for entity \'%s\' failed: %s',
427
                $this->entityName,
428
                $exceptionMessage
429
            )
430
        );
431
432
        $persistService->delete($entity, $options);
433
    }
434
435
    /**
436
     * Assert that the delete event is dispatched when
437
     *
438
     * @throws PersistenceException
439
     */
440
    public function testDeleteWillDispatchDeleteEventAndReturnTrue(): void
441
    {
442
        $entityName = EntityInterface::class;
443
444
        /** @var PersistService&MockObject $persistService */
445
        $persistService = $this->getMockBuilder(PersistService::class)
446
            ->setConstructorArgs([
447
                $entityName,
448
                $this->entityManager,
449
                $this->eventDispatcher,
450
                $this->logger,
451
            ])->onlyMethods(['createEvent'])
452
            ->getMock();
453
454
        /** @var EntityInterface&MockObject $entity */
455
        $entity = $this->createMock(EntityInterface::class);
456
        $options = [];
457
458
        /** @var EntityEvent&MockObject $event */
459
        $event = $this->createMock(EntityEvent::class);
460
461
        $persistService->expects($this->once())
462
            ->method('createEvent')
463
            ->with(EntityEventName::DELETE, $entity, $options)
464
            ->willReturn($event);
465
466
        $this->eventDispatcher->expects($this->once())
467
            ->method('dispatch')
468
            ->with($event)
469
            ->willReturn($event);
470
471
        $this->assertTrue($persistService->delete($entity, $options));
472
    }
473
474
    /**
475
     * Assert that a PersistenceException will be thrown by persist() if the provided entity instance is not an
476
     * instance of the mapped entity class.
477
     *
478
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::persist
479
     */
480
    public function testPersistWillThrowPersistenceExceptionIfProvidedEntityIsAnInvalidType(): void
481
    {
482
        $entityName = \stdClass::class;
483
484
        $persistService = new PersistService(
485
            $entityName, // Invalid entity class name...
486
            $this->entityManager,
487
            $this->eventDispatcher,
488
            $this->logger
489
        );
490
491
        /** @var EntityInterface&MockObject $entity */
492
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
493
494
        $errorMessage = sprintf(
495
            'The \'entity\' argument must be an object of type \'%s\'; \'%s\' provided in \'%s::%s\'',
496
            $entityName,
497
            get_class($entity),
498
            PersistService::class,
499
            'persist'
500
        );
501
502
        $this->logger->expects($this->once())
503
            ->method('error')
504
            ->with($errorMessage);
505
506
        $this->expectException(PersistenceException::class);
507
        $this->expectExceptionMessage($errorMessage);
508
509
        $persistService->persist($entity);
510
    }
511
512
    /**
513
     * Assert that if the $entity provided to persist() cannot be persisted any raised exception is caught, logged
514
     * and then rethrown as a PersistenceException.
515
     *
516
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::persist
517
     */
518
    public function testPersistWillThrowPersistenceExceptionIfTheEntityCannotBePersisted(): void
519
    {
520
        $persistService = new PersistService(
521
            $this->entityName,
522
            $this->entityManager,
523
            $this->eventDispatcher,
524
            $this->logger
525
        );
526
527
        /** @var EntityInterface&MockObject $entity */
528
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
529
530
        $exceptionMessage = 'This is a test error message for persist()';
531
        $exceptionCode = 456;
532
        $exception = new \Error($exceptionMessage, $exceptionCode);
533
534
        $this->entityManager->expects($this->once())
535
            ->method('persist')
536
            ->with($entity)
537
            ->willThrowException($exception);
538
539
        $errorMessage = sprintf(
540
            'The persist operation failed for entity \'%s\': %s',
541
            $this->entityName,
542
            $exceptionMessage
543
        );
544
545
        $this->logger->expects($this->once())
546
            ->method('error')
547
            ->with($errorMessage, ['exception' => $exception]);
548
549
        $this->expectException(PersistenceException::class);
550
        $this->expectExceptionMessage($errorMessage);
551
        $this->expectExceptionCode($exceptionCode);
552
553
        $persistService->persist($entity);
554
    }
555
556
    /**
557
     * Assert that persist() will successfully proxy the provided $entity to the entity manager persist().
558
     *
559
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::persist
560
     *
561
     * @throws PersistenceException
562
     */
563
    public function testPersist(): void
564
    {
565
        $persistService = new PersistService(
566
            $this->entityName,
567
            $this->entityManager,
568
            $this->eventDispatcher,
569
            $this->logger
570
        );
571
572
        /** @var EntityInterface&MockObject $entity */
573
        $entity = $this->getMockForAbstractClass(EntityInterface::class);
574
575
        $this->entityManager->expects($this->once())
576
            ->method('persist')
577
            ->with($entity);
578
579
        $persistService->persist($entity);
580
    }
581
582
    /**
583
     * Assert that exception raised from flush() are logged and rethrown as PersistenceException.
584
     *
585
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::flush
586
     *
587
     * @throws PersistenceException
588
     */
589
    public function testFlushExceptionsAreLoggedAndRethrownAsPersistenceException(): void
590
    {
591
        $persistService = new PersistService(
592
            $this->entityName,
593
            $this->entityManager,
594
            $this->eventDispatcher,
595
            $this->logger
596
        );
597
598
        $exceptionMessage = 'This is a test error message for flush()';
599
        $exceptionCode = 999;
600
        $exception = new \Error($exceptionMessage, $exceptionCode);
601
602
        $this->entityManager->expects($this->once())
603
            ->method('flush')
604
            ->willThrowException($exception);
605
606
        $errorMessage = sprintf(
607
            'The flush operation failed for entity \'%s\': %s',
608
            $this->entityName,
609
            $exceptionMessage
610
        );
611
612
        $this->logger->expects($this->once())
613
            ->method('error')
614
            ->with($errorMessage, ['exception' => $exception, 'entity_name' => $this->entityName]);
615
616
        $this->expectException(PersistenceException::class);
617
        $this->expectExceptionMessage($errorMessage);
618
        $this->expectExceptionCode($exceptionCode);
619
620
        $persistService->flush();
621
    }
622
623
    /**
624
     * Assert that flush() will call the internal entity manager flush().
625
     *
626
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::flush
627
     *
628
     * @throws PersistenceException
629
     */
630
    public function testFlush(): void
631
    {
632
        $persistService = new PersistService(
633
            $this->entityName,
634
            $this->entityManager,
635
            $this->eventDispatcher,
636
            $this->logger
637
        );
638
639
        $this->entityManager->expects($this->once())->method('flush');
640
641
        $persistService->flush();
642
    }
643
644
    /**
645
     * Assert that exceptions raised in clear() are logged and rethrown as PersistenceException.
646
     *
647
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::clear
648
     *
649
     * @throw  PersistenceException
650
     */
651
    public function testClearExceptionsAreLoggedAndRethrownAsPersistenceException(): void
652
    {
653
        $persistService = new PersistService(
654
            $this->entityName,
655
            $this->entityManager,
656
            $this->eventDispatcher,
657
            $this->logger
658
        );
659
660
        $exceptionMessage = 'This is a test exception message for clear()';
661
        $exceptionCode = 888;
662
        $exception = new \Error($exceptionMessage, $exceptionCode);
663
664
        $this->entityManager->expects($this->once())
665
            ->method('clear')
666
            ->willThrowException($exception);
667
668
        $errorMessage = sprintf(
669
            'The clear operation failed for entity \'%s\': %s',
670
            $this->entityName,
671
            $exceptionMessage
672
        );
673
674
        $this->logger->expects($this->once())
675
            ->method('error')
676
            ->with($errorMessage, ['exception' => $exception, 'entity_name' => $this->entityName]);
677
678
        $this->expectException(PersistenceException::class);
679
        $this->expectExceptionMessage($errorMessage);
680
        $this->expectExceptionCode($exceptionCode);
681
682
        $persistService->clear();
683
    }
684
685
    /**
686
     * Assert that calls to clear() will proxy to the internal entity manager clear().
687
     *
688
     * @covers \Arp\DoctrineEntityRepository\Persistence\PersistService::clear
689
     *
690
     * @throws PersistenceException
691
     */
692
    public function testClear(): void
693
    {
694
        $persistService = new PersistService(
695
            $this->entityName,
696
            $this->entityManager,
697
            $this->eventDispatcher,
698
            $this->logger
699
        );
700
701
        $this->entityManager->expects($this->once())->method('clear');
702
703
        $persistService->clear();
704
    }
705
706
    /**
707
     * Assert a PersistenceException is thrown from refresh() if the provided Entity is not managed by this
708
     * repository class
709
     *
710
     * @throws PersistenceException
711
     */
712
    public function testRefreshWillThrowPersistenceExceptionForInvalidEntity(): void
713
    {
714
        $entity = new class() implements EntityInterface {
715
            use EntityTrait;
716
        };
717
718
        $entity2 = new class() implements EntityInterface {
719
            use EntityTrait;
720
        };
721
722
        $this->entityName = get_class($entity2);
723
724
        $persistService = new PersistService(
725
            $this->entityName,
726
            $this->entityManager,
727
            $this->eventDispatcher,
728
            $this->logger
729
        );
730
731
        $this->entityManager->expects($this->never())
732
            ->method('refresh')
733
            ->with($entity);
734
735
        $this->expectException(PersistenceException::class);
736
        $this->expectExceptionMessage(
737
            sprintf(
738
                'The \'entity\' argument must be an object of type \'%s\'; \'%s\' provided in \'%s\'',
739
                $this->entityName,
740
                get_class($entity),
741
                PersistService::class . '::refresh'
742
            )
743
        );
744
745
        $persistService->refresh($entity);
746
    }
747
748
    /**
749
     * Assert a PersistenceException is thrown from refresh() if the refresh() action fails
750
     *
751
     * @throws PersistenceException
752
     */
753
    public function testRefreshError(): void
754
    {
755
        $persistService = new PersistService(
756
            $this->entityName,
757
            $this->entityManager,
758
            $this->eventDispatcher,
759
            $this->logger
760
        );
761
762
        /** @var EntityInterface&MockObject $entity */
763
        $entity = $this->createMock(EntityInterface::class);
764
765
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
766
        $exceptionCode = 987;
767
        $exception = new \RuntimeException($exceptionMessage, $exceptionCode);
768
769
        $this->entityManager->expects($this->once())
770
            ->method('refresh')
771
            ->with($entity)
772
            ->willThrowException($exception);
773
774
        $this->expectException(PersistenceException::class);
775
        $this->expectExceptionCode($exceptionCode);
776
        $this->expectExceptionMessage(
777
            sprintf(
778
                'The refresh operation failed for entity \'%s\' : %s',
779
                $this->entityName,
780
                $exceptionMessage
781
            )
782
        );
783
784
        $persistService->refresh($entity);
785
    }
786
787
    /**
788
     * Assert calls to refresh() will result in the $entity being passed to EntityManager::refresh()
789
     *
790
     * @throws PersistenceException
791
     */
792
    public function testRefresh(): void
793
    {
794
        $persistService = new PersistService(
795
            $this->entityName,
796
            $this->entityManager,
797
            $this->eventDispatcher,
798
            $this->logger
799
        );
800
801
        /** @var EntityInterface&MockObject $entity */
802
        $entity = $this->createMock(EntityInterface::class);
803
804
        $this->entityManager->expects($this->once())
805
            ->method('refresh')
806
            ->with($entity);
807
808
        $persistService->refresh($entity);
809
    }
810
811
    /**
812
     * Assert calls to beginTransaction() will proxy to the managed EntityManager instance
813
     *
814
     * @throws PersistenceException
815
     */
816
    public function testBeginTransaction(): void
817
    {
818
        $persistService = new PersistService(
819
            $this->entityName,
820
            $this->entityManager,
821
            $this->eventDispatcher,
822
            $this->logger
823
        );
824
825
        $this->entityManager->expects($this->once())->method('beginTransaction');
826
827
        $persistService->beginTransaction();
828
    }
829
830
    /**
831
     * Assert calls to beginTransaction() will throw a PersistenceException on error
832
     *
833
     * @throws PersistenceException
834
     */
835
    public function testBeginTransactionError(): void
836
    {
837
        $persistService = new PersistService(
838
            $this->entityName,
839
            $this->entityManager,
840
            $this->eventDispatcher,
841
            $this->logger
842
        );
843
844
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
845
        $exceptionCode = 123;
846
        $exception = new \RuntimeException($exceptionMessage, $exceptionCode);
847
848
        $this->entityManager->expects($this->once())
849
            ->method('beginTransaction')
850
            ->willThrowException($exception);
851
852
        $this->expectException(PersistenceException::class);
853
        $this->expectExceptionMessage(sprintf('Failed to start transaction : %s', $exceptionMessage));
854
        $this->expectExceptionCode($exceptionCode);
855
856
        $persistService->beginTransaction();
857
    }
858
859
    /**
860
     * Assert calls to commitTransaction() will proxy to the managed EntityManager instance
861
     *
862
     * @throws PersistenceException
863
     */
864
    public function testCommitTransaction(): void
865
    {
866
        $persistService = new PersistService(
867
            $this->entityName,
868
            $this->entityManager,
869
            $this->eventDispatcher,
870
            $this->logger
871
        );
872
873
        $this->entityManager->expects($this->once())->method('commit');
874
875
        $persistService->commitTransaction();
876
    }
877
878
    /**
879
     * Assert calls to commitTransaction() will throw a PersistenceException on error
880
     *
881
     * @throws PersistenceException
882
     */
883
    public function testCommitTransactionError(): void
884
    {
885
        $persistService = new PersistService(
886
            $this->entityName,
887
            $this->entityManager,
888
            $this->eventDispatcher,
889
            $this->logger
890
        );
891
892
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
893
        $exceptionCode = 123;
894
        $exception = new \RuntimeException($exceptionMessage, $exceptionCode);
895
896
        $this->entityManager->expects($this->once())
897
            ->method('commit')
898
            ->willThrowException($exception);
899
900
        $this->expectException(PersistenceException::class);
901
        $this->expectExceptionMessage(sprintf('Failed to commit transaction : %s', $exceptionMessage));
902
        $this->expectExceptionCode($exceptionCode);
903
904
        $persistService->commitTransaction();
905
    }
906
907
    /**
908
     * Assert calls to rollbackTransaction() will proxy to the managed EntityManager instance
909
     *
910
     * @throws PersistenceException
911
     */
912
    public function testRollbackTransaction(): void
913
    {
914
        $persistService = new PersistService(
915
            $this->entityName,
916
            $this->entityManager,
917
            $this->eventDispatcher,
918
            $this->logger
919
        );
920
921
        $this->entityManager->expects($this->once())->method('rollback');
922
923
        $persistService->rollbackTransaction();
924
    }
925
926
    /**
927
     * Assert calls to rollbackTransaction() will throw a PersistenceException on error
928
     *
929
     * @throws PersistenceException
930
     */
931
    public function testRollbackTransactionError(): void
932
    {
933
        $persistService = new PersistService(
934
            $this->entityName,
935
            $this->entityManager,
936
            $this->eventDispatcher,
937
            $this->logger
938
        );
939
940
        $exceptionMessage = 'This is a test exception message for ' . __FUNCTION__;
941
        $exceptionCode = 123;
942
        $exception = new \RuntimeException($exceptionMessage, $exceptionCode);
943
944
        $this->entityManager->expects($this->once())
945
            ->method('rollback')
946
            ->willThrowException($exception);
947
948
        $this->expectException(PersistenceException::class);
949
        $this->expectExceptionMessage(sprintf('Failed to rollback transaction : %s', $exceptionMessage));
950
        $this->expectExceptionCode($exceptionCode);
951
952
        $persistService->rollbackTransaction();
953
    }
954
}
955