3aa26b27f620fa062f75f11ef3c6c371e49ffc62
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Persistence / Generic / PersistenceManagerTest.php
1 <?php
2
3 namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
19 use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
20 use TYPO3\CMS\Extbase\Persistence\Generic\Backend;
21 use TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface;
22 use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
23 use TYPO3\CMS\Extbase\Persistence\Generic\Session;
24 use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
25 use TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface;
26 use TYPO3\CMS\Extbase\Persistence\RepositoryInterface;
27 use TYPO3\CMS\Extbase\Tests\Unit\Persistence\Fixture\Model\Entity2;
28 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
29
30 /**
31 * Test case
32 */
33 class PersistenceManagerTest extends UnitTestCase
34 {
35 /**
36 * @var ObjectManagerInterface
37 */
38 protected $mockObjectManager;
39
40 /**
41 *
42 */
43 protected function setUp(): void
44 {
45 $this->mockObjectManager = $this->createMock(ObjectManagerInterface::class);
46 }
47
48 /**
49 * @test
50 */
51 public function persistAllPassesAddedObjectsToBackend(): void
52 {
53 $entity2 = new Entity2();
54 $objectStorage = new ObjectStorage();
55 $objectStorage->attach($entity2);
56 $mockBackend = $this->createMock(BackendInterface::class);
57 $mockBackend->expects($this->once())->method('setAggregateRootObjects')->with($objectStorage);
58
59 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $manager */
60 $manager = $this->getAccessibleMock(
61 PersistenceManager::class,
62 ['dummy']
63 );
64 $manager->_set('backend', $mockBackend);
65 $manager->add($entity2);
66
67 $manager->persistAll();
68 }
69
70 /**
71 * @test
72 */
73 public function persistAllPassesRemovedObjectsToBackend(): void
74 {
75 $entity2 = new Entity2();
76 $objectStorage = new ObjectStorage();
77 $objectStorage->attach($entity2);
78 $mockBackend = $this->createMock(BackendInterface::class);
79 $mockBackend->expects($this->once())->method('setDeletedEntities')->with($objectStorage);
80
81 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $manager */
82 $manager = $this->getAccessibleMock(
83 PersistenceManager::class,
84 ['dummy']
85 );
86 $manager->_set('backend', $mockBackend);
87 $manager->remove($entity2);
88
89 $manager->persistAll();
90 }
91
92 /**
93 * @test
94 */
95 public function getIdentifierByObjectReturnsIdentifierFromBackend(): void
96 {
97 $fakeUuid = 'fakeUuid';
98 $object = new \stdClass();
99
100 $mockBackend = $this->createMock(BackendInterface::class);
101 $mockBackend->expects($this->once())->method('getIdentifierByObject')->with($object)->will($this->returnValue($fakeUuid));
102
103 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $manager */
104 $manager = $this->getAccessibleMock(
105 PersistenceManager::class,
106 ['dummy']
107 );
108 $manager->_set('backend', $mockBackend);
109
110 $this->assertEquals($manager->getIdentifierByObject($object), $fakeUuid);
111 }
112
113 /**
114 * @test
115 */
116 public function getObjectByIdentifierReturnsObjectFromSessionIfAvailable(): void
117 {
118 $fakeUuid = 'fakeUuid';
119 $object = new \stdClass();
120
121 $mockSession = $this->createMock(Session::class);
122 $mockSession->expects($this->once())->method('hasIdentifier')->with($fakeUuid)->will($this->returnValue(true));
123 $mockSession->expects($this->once())->method('getObjectByIdentifier')->with($fakeUuid)->will($this->returnValue($object));
124
125 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $manager */
126 $manager = $this->getAccessibleMock(
127 PersistenceManager::class,
128 ['dummy']
129 );
130 $manager->_set('persistenceSession', $mockSession);
131
132 $this->assertEquals($manager->getObjectByIdentifier($fakeUuid), $object);
133 }
134
135 /**
136 * @test
137 */
138 public function getObjectByIdentifierReturnsObjectFromPersistenceIfAvailable(): void
139 {
140 $fakeUuid = '42';
141 $object = new \stdClass();
142 $fakeEntityType = get_class($object);
143
144 $mockSession = $this->createMock(Session::class);
145 $mockSession->expects($this->once())->method('hasIdentifier')->with($fakeUuid)->will($this->returnValue(false));
146
147 $mockBackend = $this->createMock(BackendInterface::class);
148 $mockBackend->expects($this->once())->method('getObjectByIdentifier')->with(
149 $fakeUuid,
150 $fakeEntityType
151 )->will($this->returnValue($object));
152
153 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $manager */
154 $manager = $this->getAccessibleMock(
155 PersistenceManager::class,
156 ['dummy']
157 );
158 $manager->_set('persistenceSession', $mockSession);
159 $manager->_set('backend', $mockBackend);
160
161 $this->assertEquals($manager->getObjectByIdentifier($fakeUuid, $fakeEntityType), $object);
162 }
163
164 /**
165 * @test
166 */
167 public function getObjectByIdentifierReturnsNullForUnknownObject(): void
168 {
169 $fakeUuid = '42';
170 $fakeEntityType = 'foobar';
171
172 $mockSession = $this->createMock(Session::class);
173 $mockSession->expects($this->once())->method('hasIdentifier')->with(
174 $fakeUuid,
175 $fakeEntityType
176 )->will($this->returnValue(false));
177
178 $mockBackend = $this->createMock(BackendInterface::class);
179 $mockBackend->expects($this->once())->method('getObjectByIdentifier')->with(
180 $fakeUuid,
181 $fakeEntityType
182 )->will($this->returnValue(null));
183
184 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $manager */
185 $manager = $this->getAccessibleMock(
186 PersistenceManager::class,
187 ['dummy']
188 );
189 $manager->_set('persistenceSession', $mockSession);
190 $manager->_set('backend', $mockBackend);
191
192 $this->assertNull($manager->getObjectByIdentifier($fakeUuid, $fakeEntityType));
193 }
194
195 /**
196 * @test
197 */
198 public function addActuallyAddsAnObjectToTheInternalObjectsArray(): void
199 {
200 $someObject = new \stdClass();
201 $persistenceManager = new PersistenceManager();
202 $persistenceManager->add($someObject);
203
204 $this->assertAttributeContains($someObject, 'addedObjects', $persistenceManager);
205 }
206
207 /**
208 * @test
209 */
210 public function removeActuallyRemovesAnObjectFromTheInternalObjectsArray(): void
211 {
212 $object1 = new \stdClass();
213 $object2 = new \stdClass();
214 $object3 = new \stdClass();
215
216 $persistenceManager = new PersistenceManager();
217 $persistenceManager->add($object1);
218 $persistenceManager->add($object2);
219 $persistenceManager->add($object3);
220
221 $persistenceManager->remove($object2);
222
223 $this->assertAttributeContains($object1, 'addedObjects', $persistenceManager);
224 $this->assertAttributeNotContains($object2, 'addedObjects', $persistenceManager);
225 $this->assertAttributeContains($object3, 'addedObjects', $persistenceManager);
226 }
227
228 /**
229 * @test
230 */
231 public function removeRemovesTheRightObjectEvenIfItHasBeenModifiedSinceItsAddition(): void
232 {
233 $object1 = new \ArrayObject(['val' => '1']);
234 $object2 = new \ArrayObject(['val' => '2']);
235 $object3 = new \ArrayObject(['val' => '3']);
236
237 $persistenceManager = new PersistenceManager();
238 $persistenceManager->add($object1);
239 $persistenceManager->add($object2);
240 $persistenceManager->add($object3);
241
242 $object2['foo'] = 'bar';
243 $object3['val'] = '2';
244
245 $persistenceManager->remove($object2);
246
247 $this->assertAttributeContains($object1, 'addedObjects', $persistenceManager);
248 $this->assertAttributeNotContains($object2, 'addedObjects', $persistenceManager);
249 $this->assertAttributeContains($object3, 'addedObjects', $persistenceManager);
250 }
251
252 /**
253 * Make sure we remember the objects that are not currently add()ed
254 * but might be in persistent storage.
255 *
256 * @test
257 */
258 public function removeRetainsObjectForObjectsNotInCurrentSession(): void
259 {
260 $object = new \ArrayObject(['val' => '1']);
261 $persistenceManager = new PersistenceManager();
262 $persistenceManager->remove($object);
263
264 $this->assertAttributeContains($object, 'removedObjects', $persistenceManager);
265 }
266
267 /**
268 * @test
269 */
270 public function updateSchedulesAnObjectForPersistence(): void
271 {
272 $className = $this->getUniqueId('BazFixture');
273 eval('
274 namespace ' . __NAMESPACE__ . '\\Domain\\Model;
275 class ' . $className . ' extends \\' . AbstractEntity::class . ' {
276 protected $uid = 42;
277 }
278 ');
279 eval('
280 namespace ' . __NAMESPACE__ . '\\Domain\\Repository;
281 class ' . $className . 'Repository extends \\TYPO3\\CMS\\Extbase\\Persistence\\Repository {}
282 ');
283 $classNameWithNamespace = __NAMESPACE__ . '\\Domain\\Model\\' . $className;
284 $repositorClassNameWithNamespace = __NAMESPACE__ . '\\Domain\\Repository\\' . $className . 'Repository';
285
286 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $persistenceManager */
287 $persistenceManager = $this->getAccessibleMock(
288 PersistenceManager::class,
289 ['dummy']
290 );
291 $session = new Session();
292 $changedEntities = new ObjectStorage();
293 $entity1 = new $classNameWithNamespace();
294 /** @var RepositoryInterface|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $repository */
295 $repository = $this->getAccessibleMock($repositorClassNameWithNamespace, ['dummy'], [$this->mockObjectManager]);
296 $repository->_set('objectType', get_class($entity1));
297 $mockBackend = $this->getMockBuilder($this->buildAccessibleProxy(Backend::class))
298 ->setMethods(['commit', 'setChangedEntities'])
299 ->disableOriginalConstructor()
300 ->getMock();
301 $mockBackend->expects($this->once())
302 ->method('setChangedEntities')
303 ->with($this->equalTo($changedEntities));
304
305 $persistenceManager->_set('backend', $mockBackend);
306 $persistenceManager->_set('persistenceSession', $session);
307
308 $repository->_set('persistenceManager', $persistenceManager);
309 $session->registerObject($entity1, 42);
310 $changedEntities->attach($entity1);
311 $repository->update($entity1);
312 $persistenceManager->persistAll();
313 }
314
315 /**
316 * @test
317 */
318 public function clearStateForgetsAboutNewObjects(): void
319 {
320 /** @var PersistenceManagerInterface|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $mockObject */
321 $mockObject = $this->createMock(PersistenceManagerInterface::class);
322 $mockObject->Persistence_Object_Identifier = 'abcdefg';
323
324 $mockSession = $this->createMock(Session::class);
325 $mockSession->expects($this->any())->method('hasIdentifier')->will($this->returnValue(false));
326 $mockBackend = $this->createMock(BackendInterface::class);
327
328 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $persistenceManager */
329 $persistenceManager = $this->getAccessibleMock(
330 PersistenceManager::class,
331 ['dummy']
332 );
333 $persistenceManager->_set('persistenceSession', $mockSession);
334 $persistenceManager->_set('backend', $mockBackend);
335
336 $persistenceManager->registerNewObject($mockObject);
337 $persistenceManager->clearState();
338
339 $object = $persistenceManager->getObjectByIdentifier('abcdefg');
340 $this->assertNull($object);
341 }
342
343 /**
344 * @test
345 */
346 public function tearDownWithBackendSupportingTearDownDelegatesCallToBackend(): void
347 {
348 $methods = array_merge(
349 get_class_methods(BackendInterface::class),
350 ['tearDown']
351 );
352 $mockBackend = $this->getMockBuilder(BackendInterface::class)
353 ->setMethods($methods)
354 ->getMock();
355 $mockBackend->expects($this->once())->method('tearDown');
356
357 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $persistenceManager */
358 $persistenceManager = $this->getAccessibleMock(
359 PersistenceManager::class,
360 ['dummy']
361 );
362 $persistenceManager->_set('backend', $mockBackend);
363
364 $persistenceManager->tearDown();
365 }
366
367 /**
368 * @test
369 *
370 * This test and the related Fixtures TxDomainModelTestEntity and
371 * TxDomainRepositoryTestEntityRepository can be removed if we do not need to support
372 * underscore class names instead of namespaced class names
373 */
374 public function persistAllAddsReconstitutedObjectFromSessionToBackendsAggregateRootObjects(): void
375 {
376 $className = $this->getUniqueId('BazFixture');
377 eval('
378 class Foo_Bar_Domain_Model_' . $className . ' extends \\' . AbstractEntity::class . ' {}
379 ');
380 eval('
381 class Foo_Bar_Domain_Repository_' . $className . 'Repository {}
382 ');
383 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $persistenceManager */
384 $persistenceManager = $this->getAccessibleMock(
385 PersistenceManager::class,
386 ['dummy']
387 );
388 $aggregateRootObjects = new ObjectStorage();
389 $fullClassName = 'Foo_Bar_Domain_Model_' . $className;
390 $entity1 = new $fullClassName();
391 $aggregateRootObjects->attach($entity1);
392 $mockBackend = $this->getMockBuilder($this->buildAccessibleProxy(Backend::class))
393 ->setMethods(['commit', 'setAggregateRootObjects', 'setDeletedEntities'])
394 ->disableOriginalConstructor()
395 ->getMock();
396 $mockBackend->expects($this->once())
397 ->method('setAggregateRootObjects')
398 ->with($this->equalTo($aggregateRootObjects));
399 $persistenceManager->_set('backend', $mockBackend);
400 $persistenceManager->add($entity1);
401 $persistenceManager->persistAll();
402 }
403
404 /**
405 * @test
406 */
407 public function persistAllAddsNamespacedReconstitutedObjectFromSessionToBackendsAggregateRootObjects(): void
408 {
409 $className = $this->getUniqueId('BazFixture');
410 eval('
411 namespace ' . __NAMESPACE__ . '\\Domain\\Model;
412 class ' . $className . ' extends \\' . AbstractEntity::class . ' {}
413 ');
414 eval('
415 namespace ' . __NAMESPACE__ . '\\Domain\\Repository;
416 class ' . $className . 'Repository {}
417 ');
418 /** @var PersistenceManager|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $persistenceManager */
419 $persistenceManager = $this->getAccessibleMock(
420 PersistenceManager::class,
421 ['dummy']
422 );
423 $aggregateRootObjects = new ObjectStorage();
424 $classNameWithNamespace = __NAMESPACE__ . '\\Domain\\Model\\' . $className;
425 $entity1 = new $classNameWithNamespace();
426 $aggregateRootObjects->attach($entity1);
427 $mockBackend = $this->getMockBuilder($this->buildAccessibleProxy(Backend::class))
428 ->setMethods(['commit', 'setAggregateRootObjects', 'setDeletedEntities'])
429 ->disableOriginalConstructor()
430 ->getMock();
431 $mockBackend->expects($this->once())
432 ->method('setAggregateRootObjects')
433 ->with($this->equalTo($aggregateRootObjects));
434 $persistenceManager->_set('backend', $mockBackend);
435 $persistenceManager->add($entity1);
436 $persistenceManager->persistAll();
437 }
438 }