• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

api-platform / core / 17063025732

19 Aug 2025 07:44AM UTC coverage: 22.236% (+0.05%) from 22.188%
17063025732

push

github

soyuka
test: skip mongodb bundle when extension is not loaded

11683 of 52542 relevant lines covered (22.24%)

24.04 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/Symfony/Tests/Doctrine/EventListener/PurgeHttpCacheListenerTest.php
1
<?php
2

3
/*
4
 * This file is part of the API Platform project.
5
 *
6
 * (c) Kévin Dunglas <dunglas@gmail.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11

12
declare(strict_types=1);
13

14
namespace ApiPlatform\Symfony\Tests\Doctrine\EventListener;
15

16
use ApiPlatform\HttpCache\PurgerInterface;
17
use ApiPlatform\Metadata\Exception\InvalidArgumentException;
18
use ApiPlatform\Metadata\Exception\ItemNotFoundException;
19
use ApiPlatform\Metadata\GetCollection;
20
use ApiPlatform\Metadata\IriConverterInterface;
21
use ApiPlatform\Metadata\ResourceClassResolverInterface;
22
use ApiPlatform\Metadata\UrlGeneratorInterface;
23
use ApiPlatform\Symfony\Doctrine\EventListener\PurgeHttpCacheListener;
24
use ApiPlatform\Symfony\Tests\Fixtures\MappedEntity;
25
use ApiPlatform\Symfony\Tests\Fixtures\NotAResource;
26
use ApiPlatform\Symfony\Tests\Fixtures\TestBundle\Entity\ContainNonResource;
27
use ApiPlatform\Symfony\Tests\Fixtures\TestBundle\Entity\Dummy;
28
use ApiPlatform\Symfony\Tests\Fixtures\TestBundle\Entity\DummyNoGetOperation;
29
use ApiPlatform\Symfony\Tests\Fixtures\TestBundle\Entity\RelatedDummy;
30
use Doctrine\ORM\EntityManagerInterface;
31
use Doctrine\ORM\Event\OnFlushEventArgs;
32
use Doctrine\ORM\Event\PreUpdateEventArgs;
33
use Doctrine\ORM\Mapping\ClassMetadata;
34
use Doctrine\ORM\UnitOfWork;
35
use PHPUnit\Framework\TestCase;
36
use Prophecy\Argument;
37
use Prophecy\PhpUnit\ProphecyTrait;
38
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
39

40
/**
41
 * @author Kévin Dunglas <dunglas@gmail.com>
42
 */
43
class PurgeHttpCacheListenerTest extends TestCase
44
{
45
    use ProphecyTrait;
46

47
    public function testOnFlush(): void
48
    {
49
        $toInsert1 = new Dummy();
×
50
        $toInsert2 = new Dummy();
×
51

52
        $toUpdate1 = new Dummy();
×
53
        $toUpdate1->setId(1);
×
54
        $toUpdate2 = new Dummy();
×
55
        $toUpdate2->setId(2);
×
56

57
        $toDelete1 = new Dummy();
×
58
        $toDelete1->setId(3);
×
59
        $toDelete2 = new Dummy();
×
60
        $toDelete2->setId(4);
×
61

62
        $toDeleteNoPurge = new DummyNoGetOperation();
×
63
        $toDeleteNoPurge->setId(5);
×
64

65
        $purgerProphecy = $this->prophesize(PurgerInterface::class);
×
66
        $purgerProphecy->purge(['/dummies', '/dummies/1', '/dummies/2', '/dummies/3', '/dummies/4'])->shouldBeCalled();
×
67

68
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
69
        $iriConverterProphecy->getIriFromResource(Argument::type(Dummy::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willReturn('/dummies')->shouldBeCalled();
×
70
        $iriConverterProphecy->getIriFromResource($toUpdate1)->willReturn('/dummies/1')->shouldBeCalled();
×
71
        $iriConverterProphecy->getIriFromResource($toUpdate2)->willReturn('/dummies/2')->shouldBeCalled();
×
72
        $iriConverterProphecy->getIriFromResource($toDelete1)->willReturn('/dummies/3')->shouldBeCalled();
×
73
        $iriConverterProphecy->getIriFromResource($toDelete2)->willReturn('/dummies/4')->shouldBeCalled();
×
74
        $iriConverterProphecy->getIriFromResource(Argument::type(DummyNoGetOperation::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willThrow(new InvalidArgumentException())->shouldBeCalled();
×
75
        $iriConverterProphecy->getIriFromResource(Argument::any())->willThrow(new ItemNotFoundException());
×
76

77
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
78
        $resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(true)->shouldBeCalled();
×
79

80
        $uowProphecy = $this->prophesize(UnitOfWork::class);
×
81
        $uowProphecy->getScheduledEntityInsertions()->willReturn([$toInsert1, $toInsert2])->shouldBeCalled();
×
82
        $uowProphecy->getScheduledEntityUpdates()->willReturn([$toUpdate1, $toUpdate2])->shouldBeCalled();
×
83
        $uowProphecy->getScheduledEntityDeletions()->willReturn([$toDelete1, $toDelete2, $toDeleteNoPurge])->shouldBeCalled();
×
84

85
        $emProphecy = $this->prophesize(EntityManagerInterface::class);
×
86
        $emProphecy->getUnitOfWork()->willReturn($uowProphecy->reveal())->shouldBeCalled();
×
87
        $dummyClassMetadata = new ClassMetadata(Dummy::class);
×
88
        // @phpstan-ignore-next-line
89
        $dummyClassMetadata->associationMappings = [
×
90
            'relatedDummy' => [],
×
91
            'relatedOwningDummy' => [],
×
92
        ];
×
93
        $emProphecy->getClassMetadata(Dummy::class)->willReturn($dummyClassMetadata)->shouldBeCalled();
×
94
        $emProphecy->getClassMetadata(DummyNoGetOperation::class)->willReturn(new ClassMetadata(DummyNoGetOperation::class))->shouldBeCalled();
×
95
        $eventArgs = new OnFlushEventArgs($emProphecy->reveal());
×
96

97
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
98
        $propertyAccessorProphecy->isReadable(Argument::type(Dummy::class), 'relatedDummy')->willReturn(true);
×
99
        $propertyAccessorProphecy->isReadable(Argument::type(Dummy::class), 'relatedOwningDummy')->willReturn(false);
×
100
        $propertyAccessorProphecy->getValue(Argument::type(Dummy::class), 'relatedDummy')->willReturn(null)->shouldBeCalled();
×
101
        $propertyAccessorProphecy->getValue(Argument::type(Dummy::class), 'relatedOwningDummy')->willReturn(null)->shouldNotBeCalled();
×
102

103
        $listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal());
×
104
        $listener->onFlush($eventArgs);
×
105
        $listener->postFlush();
×
106

107
        $iriConverterProphecy->getIriFromResource(Argument::type(Dummy::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->shouldHaveBeenCalled();
×
108
        $iriConverterProphecy->getIriFromResource(Argument::type(DummyNoGetOperation::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->shouldHaveBeenCalled();
×
109
    }
110

111
    public function testPreUpdate(): void
112
    {
113
        $oldRelatedDummy = new RelatedDummy();
×
114
        $oldRelatedDummy->setId(1);
×
115

116
        $newRelatedDummy = new RelatedDummy();
×
117
        $newRelatedDummy->setId(2);
×
118

119
        $dummy = new Dummy();
×
120
        $dummy->setId(1);
×
121

122
        $purgerProphecy = $this->prophesize(PurgerInterface::class);
×
123
        $purgerProphecy->purge(['/dummies', '/dummies/1', '/related_dummies/old', '/related_dummies/new'])->shouldBeCalled();
×
124

125
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
126
        $iriConverterProphecy->getIriFromResource(Argument::type(Dummy::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willReturn('/dummies')->shouldBeCalled();
×
127
        $iriConverterProphecy->getIriFromResource($dummy)->willReturn('/dummies/1')->shouldBeCalled();
×
128
        $iriConverterProphecy->getIriFromResource($oldRelatedDummy)->willReturn('/related_dummies/old')->shouldBeCalled();
×
129
        $iriConverterProphecy->getIriFromResource($newRelatedDummy)->willReturn('/related_dummies/new')->shouldBeCalled();
×
130

131
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
132
        $resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(true)->shouldBeCalled();
×
133

134
        $emProphecy = $this->prophesize(EntityManagerInterface::class);
×
135

136
        $classMetadata = new ClassMetadata(Dummy::class);
×
137
        $classMetadata->mapManyToOne(['fieldName' => 'relatedDummy', 'targetEntity' => RelatedDummy::class]);
×
138
        $emProphecy->getClassMetadata(Dummy::class)->willReturn($classMetadata)->shouldBeCalled();
×
139

140
        $changeSet = ['relatedDummy' => [$oldRelatedDummy, $newRelatedDummy]];
×
141
        $eventArgs = new PreUpdateEventArgs($dummy, $emProphecy->reveal(), $changeSet);
×
142

143
        $listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal());
×
144
        $listener->preUpdate($eventArgs);
×
145
        $listener->postFlush();
×
146
    }
147

148
    public function testNothingToPurge(): void
149
    {
150
        $dummyNoGetOperation = new DummyNoGetOperation();
×
151
        $dummyNoGetOperation->setId(1);
×
152

153
        $purgerProphecy = $this->prophesize(PurgerInterface::class);
×
154
        $purgerProphecy->purge([])->shouldNotBeCalled();
×
155

156
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
157
        $iriConverterProphecy->getIriFromResource(Argument::type(DummyNoGetOperation::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willThrow(new InvalidArgumentException())->shouldBeCalled();
×
158
        $iriConverterProphecy->getIriFromResource($dummyNoGetOperation)->shouldNotBeCalled();
×
159

160
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
161
        $resourceClassResolverProphecy->isResourceClass(DummyNoGetOperation::class)->willReturn(true)->shouldBeCalled();
×
162
        $resourceClassResolverProphecy->getResourceClass(Argument::type(DummyNoGetOperation::class))->willReturn(DummyNoGetOperation::class)->shouldNotBeCalled();
×
163

164
        $emProphecy = $this->prophesize(EntityManagerInterface::class);
×
165

166
        $classMetadata = new ClassMetadata(DummyNoGetOperation::class);
×
167
        $emProphecy->getClassMetadata(DummyNoGetOperation::class)->willReturn($classMetadata)->shouldBeCalled();
×
168

169
        $changeSet = ['lorem' => ['ipsum1', 'ipsum2']];
×
170
        $eventArgs = new PreUpdateEventArgs($dummyNoGetOperation, $emProphecy->reveal(), $changeSet);
×
171

172
        $listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal());
×
173
        $listener->preUpdate($eventArgs);
×
174
        $listener->postFlush();
×
175
    }
176

177
    public function testNotAResourceClass(): void
178
    {
179
        $containNonResource = new ContainNonResource();
×
180
        $nonResource1 = new NotAResource('foo', 'bar');
×
181
        $nonResource2 = new NotAResource('baz', 'qux');
×
182
        $collectionOfNotAResource = [$nonResource1, $nonResource2];
×
183

184
        $purgerProphecy = $this->prophesize(PurgerInterface::class);
×
185
        $purgerProphecy->purge(['/dummies'])->shouldBeCalled();
×
186

187
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
188
        $iriConverterProphecy->getIriFromResource(Argument::type(ContainNonResource::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willReturn('/dummies')->shouldBeCalled();
×
189
        $iriConverterProphecy->getIriFromResource($nonResource1)->willThrow(new InvalidArgumentException())->shouldBeCalled();
×
190
        $iriConverterProphecy->getIriFromResource($nonResource2)->willThrow(new InvalidArgumentException())->shouldBeCalled();
×
191

192
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
193
        $resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(true)->shouldBeCalled();
×
194

195
        $uowProphecy = $this->prophesize(UnitOfWork::class);
×
196
        $uowProphecy->getScheduledEntityInsertions()->willReturn([$containNonResource])->shouldBeCalled();
×
197
        $uowProphecy->getScheduledEntityUpdates()->willReturn([])->shouldBeCalled();
×
198
        $uowProphecy->getScheduledEntityDeletions()->willReturn([])->shouldBeCalled();
×
199

200
        $emProphecy = $this->prophesize(EntityManagerInterface::class);
×
201
        $emProphecy->getUnitOfWork()->willReturn($uowProphecy->reveal())->shouldBeCalled();
×
202

203
        $dummyClassMetadata = new ClassMetadata(ContainNonResource::class);
×
204
        // @phpstan-ignore-next-line
205
        $dummyClassMetadata->associationMappings = [
×
206
            'notAResource' => [],
×
207
            'collectionOfNotAResource' => ['targetEntity' => NotAResource::class],
×
208
        ];
×
209
        $emProphecy->getClassMetadata(ContainNonResource::class)->willReturn($dummyClassMetadata);
×
210
        $eventArgs = new OnFlushEventArgs($emProphecy->reveal());
×
211

212
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
213
        $propertyAccessorProphecy->isReadable(Argument::type(ContainNonResource::class), 'notAResource')->willReturn(true);
×
214
        $propertyAccessorProphecy->isReadable(Argument::type(ContainNonResource::class), 'collectionOfNotAResource')->willReturn(true);
×
215
        $propertyAccessorProphecy->getValue(Argument::type(ContainNonResource::class), 'notAResource')->shouldBeCalled()->willReturn($nonResource1);
×
216
        $propertyAccessorProphecy->getValue(Argument::type(ContainNonResource::class), 'collectionOfNotAResource')->shouldBeCalled()->willReturn($collectionOfNotAResource);
×
217

218
        $listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal());
×
219
        $listener->onFlush($eventArgs);
×
220
        $listener->postFlush();
×
221
    }
222

223
    public function testAddTagsForCollection(): void
224
    {
225
        $dummy1 = new Dummy();
×
226
        $dummy1->setId(1);
×
227
        $dummy2 = new Dummy();
×
228
        $dummy2->setId(2);
×
229
        $collection = [$dummy1, $dummy2];
×
230

231
        $purgerProphecy = $this->prophesize(PurgerInterface::class);
×
232
        $purgerProphecy->purge(['/dummies', '/dummies/1', '/dummies/2'])->shouldBeCalled();
×
233

234
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
235
        $iriConverterProphecy->getIriFromResource(Argument::type(Dummy::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willReturn('/dummies')->shouldBeCalled();
×
236
        $iriConverterProphecy->getIriFromResource($dummy1)->willReturn('/dummies/1')->shouldBeCalled();
×
237
        $iriConverterProphecy->getIriFromResource($dummy2)->willReturn('/dummies/2')->shouldBeCalled();
×
238

239
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
240
        $resourceClassResolverProphecy->isResourceClass(Argument::type('string'))->willReturn(true)->shouldBeCalled();
×
241

242
        $dummyWithCollection = new Dummy();
×
243
        $dummyWithCollection->setId(3);
×
244

245
        $uowProphecy = $this->prophesize(UnitOfWork::class);
×
246
        $uowProphecy->getScheduledEntityInsertions()->willReturn([$dummyWithCollection])->shouldBeCalled();
×
247
        $uowProphecy->getScheduledEntityUpdates()->willReturn([])->shouldBeCalled();
×
248
        $uowProphecy->getScheduledEntityDeletions()->willReturn([])->shouldBeCalled();
×
249

250
        $emProphecy = $this->prophesize(EntityManagerInterface::class);
×
251
        $emProphecy->getUnitOfWork()->willReturn($uowProphecy->reveal())->shouldBeCalled();
×
252

253
        $dummyClassMetadata = new ClassMetadata(Dummy::class);
×
254
        // @phpstan-ignore-next-line
255
        $dummyClassMetadata->associationMappings = [
×
256
            'relatedDummies' => ['targetEntity' => Dummy::class],
×
257
        ];
×
258
        $emProphecy->getClassMetadata(Dummy::class)->willReturn($dummyClassMetadata)->shouldBeCalled();
×
259
        $eventArgs = new OnFlushEventArgs($emProphecy->reveal());
×
260

261
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
262
        $propertyAccessorProphecy->isReadable(Argument::type(Dummy::class), 'relatedDummies')->willReturn(true);
×
263
        $propertyAccessorProphecy->getValue(Argument::type(Dummy::class), 'relatedDummies')->willReturn($collection)->shouldBeCalled();
×
264

265
        $listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal());
×
266
        $listener->onFlush($eventArgs);
×
267
        $listener->postFlush();
×
268
    }
269

270
    public function testMappedResources(): void
271
    {
272
        $mappedEntity = new MappedEntity();
×
273

274
        $purgerProphecy = $this->prophesize(PurgerInterface::class);
×
275
        $purgerProphecy->purge(['/mapped_ressources'])->shouldBeCalled();
×
276

277
        $iriConverterProphecy = $this->prophesize(IriConverterInterface::class);
×
278
        $iriConverterProphecy->getIriFromResource(Argument::type(MappedEntity::class), UrlGeneratorInterface::ABS_PATH, new GetCollection())->willReturn('/mapped_ressources')->shouldBeCalled();
×
279

280
        $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class);
×
281
        $resourceClassResolverProphecy->isResourceClass(MappedEntity::class)->willReturn(true)->shouldBeCalled();
×
282

283
        $uowProphecy = $this->prophesize(UnitOfWork::class);
×
284
        $uowProphecy->getScheduledEntityInsertions()->willReturn([$mappedEntity])->shouldBeCalled();
×
285
        $uowProphecy->getScheduledEntityUpdates()->willReturn([])->shouldBeCalled();
×
286
        $uowProphecy->getScheduledEntityDeletions()->willReturn([])->shouldBeCalled();
×
287

288
        $emProphecy = $this->prophesize(EntityManagerInterface::class);
×
289
        $emProphecy->getUnitOfWork()->willReturn($uowProphecy->reveal())->shouldBeCalled();
×
290
        $classMetadata = new ClassMetadata(MappedEntity::class);
×
291
        $classMetadata->associationMappings = [];
×
292
        $emProphecy->getClassMetadata(MappedEntity::class)->willReturn($classMetadata)->shouldBeCalled();
×
293
        $eventArgs = new OnFlushEventArgs($emProphecy->reveal());
×
294

295
        $propertyAccessorProphecy = $this->prophesize(PropertyAccessorInterface::class);
×
296

297
        $listener = new PurgeHttpCacheListener($purgerProphecy->reveal(), $iriConverterProphecy->reveal(), $resourceClassResolverProphecy->reveal(), $propertyAccessorProphecy->reveal());
×
298
        $listener->onFlush($eventArgs);
×
299
        $listener->postFlush();
×
300
    }
301
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc