[TASK] Make current persistence replaceable
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Persistence / RepositoryTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence\Generic;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010 Bastian Waidelich <bastian@typo3.org>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26 class RepositoryTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
27
28 /**
29 * @var \TYPO3\CMS\Extbase\Persistence\Repository|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
30 */
31 protected $repository;
32
33 /**
34 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
35 */
36 protected $mockObjectManager;
37
38 /**
39 * @var \TYPO3\CMS\Extbase\Persistence\Generic\IdentityMap
40 */
41 protected $mockIdentityMap;
42
43 /**
44 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
45 */
46 protected $mockSession;
47
48 /**
49 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QueryFactory
50 */
51 protected $mockQueryFactory;
52
53 /**
54 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
55 */
56 protected $mockPersistenceManager;
57
58 /**
59 * @var \TYPO3\CMS\Extbase\Persistence\QueryInterface
60 */
61 protected $mockQuery;
62
63 /**
64 * @var \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
65 */
66 protected $mockBackend;
67
68 /**
69 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
70 */
71 protected $mockQuerySettings;
72
73 public function setUp() {
74 $this->mockSession = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Session');
75 $this->mockIdentityMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\IdentityMap');
76 $this->mockQuery = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Query', array('equals', 'matching', 'execute', 'comparison', 'setLimit'));
77 $this->mockQuerySettings = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
78 $this->mockQuery->expects($this->any())->method('getQuerySettings')->will($this->returnValue($this->mockQuerySettings));
79 $this->mockQueryFactory = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QueryFactory');
80 $this->mockQueryFactory->expects($this->any())->method('create')->will($this->returnValue($this->mockQuery));
81
82 $configuration = $this->getMock('\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface');
83 $this->mockBackend = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Backend', array('getIdentifierByObject', 'replaceObject'), array($configuration));
84 $this->mockBackend->injectIdentityMap($this->mockIdentityMap);
85 $this->mockBackend->injectQueryFactory($this->mockQueryFactory);
86 $this->mockBackend->expects($this->any())->method('replaceObject');
87
88 $this->mockPersistenceManager = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\PersistenceManager', array('getIdentifierByObject'));
89 $this->mockPersistenceManager->_set('addedObjects', new \TYPO3\CMS\Extbase\Persistence\ObjectStorage);
90 $this->mockPersistenceManager->_set('removedObjects', new \TYPO3\CMS\Extbase\Persistence\ObjectStorage);
91 $this->mockPersistenceManager->injectQueryFactory($this->mockQueryFactory);
92 $this->mockPersistenceManager->injectBackend($this->mockBackend);
93 $this->mockPersistenceManager->injectSession($this->mockSession);
94 $this->mockPersistenceManager->setDefaultQuerySettings($this->mockQuerySettings);
95
96 $this->mockObjectManager = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManagerInterface');
97 $this->repository = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Repository', array('dummy'), array($this->mockObjectManager));
98 $this->repository->injectPersistenceManager($this->mockPersistenceManager);
99 }
100
101 /**
102 * @test
103 */
104 public function abstractRepositoryImplementsRepositoryInterface() {
105 $this->assertTrue($this->repository instanceof \TYPO3\CMS\Extbase\Persistence\RepositoryInterface);
106 }
107
108 /**
109 * @test
110 */
111 public function addActuallyAddsAnObjectToTheInternalObjectsArray() {
112 $someObject = new \stdClass();
113 $this->repository->_set('entityClassName', get_class($someObject));
114 $this->repository->add($someObject);
115 $this->assertTrue($this->repository->getAddedObjects()->contains($someObject));
116 }
117
118 /**
119 * @test
120 */
121 public function removeActuallyRemovesAnObjectFromTheInternalObjectsArray() {
122 $object1 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
123 $object2 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
124 $object3 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
125 $this->repository->_set('entityClassName', get_class($object1));
126 $this->repository->add($object1);
127 $this->repository->add($object2);
128 $this->repository->add($object3);
129 $this->repository->remove($object2);
130 $this->assertTrue($this->repository->getAddedObjects()->contains($object1));
131 $this->assertFalse($this->repository->getAddedObjects()->contains($object2));
132 $this->assertTrue($this->repository->getAddedObjects()->contains($object3));
133 }
134
135 /**
136 * @test
137 */
138 public function removeRemovesTheRightObjectEvenIfItHasBeenModifiedSinceItsAddition() {
139 $object1 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
140 $object2 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
141 $object3 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
142 $this->repository->_set('entityClassName', get_class($object1));
143 $this->repository->add($object1);
144 $this->repository->add($object2);
145 $this->repository->add($object3);
146 $object2->setPid(1);
147 $object3->setPid(2);
148 $this->repository->remove($object2);
149 $this->assertTrue($this->repository->getAddedObjects()->contains($object1));
150 $this->assertFalse($this->repository->getAddedObjects()->contains($object2));
151 $this->assertTrue($this->repository->getAddedObjects()->contains($object3));
152 }
153
154 /**
155 * Make sure we remember the objects that are not currently add()ed
156 * but might be in persistent storage.
157 *
158 * @test
159 */
160 public function removeRetainsObjectForObjectsNotInCurrentSession() {
161 $object = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
162 // if the object is not currently add()ed, it is not new
163 $object->expects($this->once())->method('_isNew')->will($this->returnValue(FALSE));
164 $this->repository->_set('entityClassName', get_class($object));
165 $this->repository->remove($object);
166 $this->assertTrue($this->repository->getRemovedObjects()->contains($object));
167 }
168
169 /**
170 * dataProvider for createQueryCallsQueryFactoryWithExpectedType
171 *
172 * @return array
173 */
174 public function modelAndRepositoryClassNames() {
175 return array(
176 array('Tx_BlogExample_Domain_Repository_BlogRepository', 'Tx_BlogExample_Domain_Model_Blog'),
177 array('_Domain_Repository_Content_PageRepository', '_Domain_Model_Content_Page'),
178 array('Tx_RepositoryExample_Domain_Repository_SomeModelRepository', 'Tx_RepositoryExample_Domain_Model_SomeModel'),
179 array('Tx_RepositoryExample_Domain_Repository_RepositoryRepository', 'Tx_RepositoryExample_Domain_Model_Repository'),
180 array('Tx_Repository_Domain_Repository_RepositoryRepository', 'Tx_Repository_Domain_Model_Repository')
181 );
182 }
183
184 /**
185 * @test
186 * @dataProvider modelAndRepositoryClassNames
187 * @param string $repositoryClassName
188 * @param string $modelClassName
189 */
190 public function constructSetsObjectTypeFromClassName($repositoryClassName, $modelClassName) {
191 $mockClassName = 'MockRepository' . uniqid();
192 eval('class ' . $repositoryClassName . ' extends TYPO3\\CMS\\Extbase\\Persistence\\Repository {}');
193 $this->repository = new $repositoryClassName($this->mockObjectManager);
194 $this->repository->injectPersistenceManager($this->mockPersistenceManager);
195 $this->assertEquals($modelClassName, $this->repository->getEntityClassName());
196 }
197
198 /**
199 * dataProvider for createQueryCallsQueryFactoryWithExpectedType
200 *
201 * @return array
202 */
203 public function modelAndRepositoryNamespacedClassNames() {
204 return array(
205 array('VENDOR\\EXT\\Domain\\Repository', 'BlogRepository', 'VENDOR\\EXT\\Domain\\Model\\Blog'),
206 array('VENDOR\\EXT\\Domain\\Repository', '_PageRepository', 'VENDOR\\EXT\\Domain\\Model\\_Page'),
207 array('VENDOR\\Repository\\Domain\\Repository', 'SomeModelRepository', 'VENDOR\\Repository\\Domain\\Model\\SomeModel'),
208 array('VENDOR\\EXT\\Domain\\Repository', 'RepositoryRepository', 'VENDOR\\EXT\\Domain\\Model\\Repository'),
209 array('VENDOR\\Repository\\Domain\\Repository', 'RepositoryRepository', 'VENDOR\\Repository\\Domain\\Model\\Repository'),
210 );
211 }
212
213 /**
214 * @test
215 * @dataProvider modelAndRepositoryNamespacedClassNames
216 * @param string $namespace
217 * @param string $repositoryClassName
218 * @param string $modelClassName
219 */
220 public function constructSetsObjectTypeFromNamespacedClassName($namespace, $repositoryClassName, $modelClassName) {
221 $mockClassName = 'MockRepository' . uniqid();
222 eval('namespace ' . $namespace . '; class ' . $repositoryClassName . ' extends \\TYPO3\\CMS\\Extbase\\Persistence\\Repository {}');
223 $namespacedRepositoryClassName = '\\' . $namespace . '\\' . $repositoryClassName;
224 $this->repository = new $namespacedRepositoryClassName($this->mockObjectManager);
225 $this->repository->injectPersistenceManager($this->mockPersistenceManager);
226 $this->assertEquals($modelClassName, $this->repository->getEntityClassName());
227 }
228
229 /**
230 * @test
231 */
232 public function createQueryCallsQueryFactoryWithExpectedClassName() {
233 $this->mockQueryFactory->expects($this->once())->method('create')->with('ExpectedType');
234 $this->repository->_set('entityClassName', 'ExpectedType');
235 $this->repository->createQuery();
236 }
237
238 /**
239 * @test
240 */
241 public function createQueryReturnsQueryWithUnmodifiedDefaultQuerySettings() {
242 $mockQuerySettings = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
243 $mockQuery = new \TYPO3\CMS\Extbase\Persistence\Generic\Query('foo');
244 $mockQuery->setQuerySettings($mockQuerySettings);
245 $this->repository->createQuery();
246 $instanceQuerySettings = $mockQuery->getQuerySettings();
247 $this->assertEquals($this->mockQuerySettings, $instanceQuerySettings);
248 $this->assertNotSame($this->mockQuerySettings, $instanceQuerySettings);
249 }
250
251 /**
252 * @test
253 */
254 public function findAllCreatesQueryAndReturnsResultOfExecuteCall() {
255 $expectedResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
256 $this->mockQuery->expects($this->once())->method('execute')->with()->will($this->returnValue($expectedResult));
257 $this->assertSame($expectedResult, $this->repository->findAll());
258 }
259
260 /**
261 * @test
262 */
263 public function findByUidReturnsResultOfGetObjectByIdentifierCall() {
264 $fakeUid = '123';
265 $object = new \stdClass();
266 $this->repository->_set('entityClassName', 'someObjectType');
267 $this->mockIdentityMap->expects($this->once())->method('hasIdentifier')->with($fakeUid, 'someObjectType')->will($this->returnValue(TRUE));
268 $this->mockIdentityMap->expects($this->once())->method('getObjectByIdentifier')->with($fakeUid)->will($this->returnValue($object));
269 $expectedResult = $object;
270 $actualResult = $this->repository->findByUid($fakeUid);
271 $this->assertSame($expectedResult, $actualResult);
272 }
273
274 /**
275 * Replacing a reconstituted object (which has a uuid) by a new object
276 * will ask the persistence backend to replace them accordingly in the
277 * identity map.
278 *
279 * @test
280 * @return void
281 */
282 public function replaceReplacesReconstitutedEntityByNewObject() {
283 $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
284 $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
285
286 $this->mockPersistenceManager->expects($this->any())->method('getIdentifierByObject')->will($this->returnValue('123'));
287 $this->mockBackend->expects($this->any())->method('getIdentifierByObject')->will($this->returnValue('123'));
288
289 $this->repository->_set('entityClassName', get_class($newObject));
290 $this->repository->replace($existingObject, $newObject);
291 }
292
293 /**
294 * Replacing a reconstituted object which during this session has been
295 * marked for removal (by calling the repository's remove method)
296 * additionally registers the "newObject" for removal and removes the
297 * "existingObject" from the list of removed objects.
298 *
299 * @test
300 * @return void
301 */
302 public function replaceRemovesReconstitutedObjectWhichIsMarkedToBeRemoved() {
303 $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
304 $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
305
306 $removedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
307 $removedObjects->attach($existingObject);
308
309 $this->mockPersistenceManager->expects($this->any())->method('getIdentifierByObject')->will($this->returnValue('123'));
310 $this->mockBackend->expects($this->any())->method('getIdentifierByObject')->will($this->returnValue('123'));
311
312 $this->repository->_set('entityClassName', get_class($newObject));
313 $this->repository->_get('persistenceManager')->_set('removedObjects', $removedObjects);
314 $this->repository->replace($existingObject, $newObject);
315
316 $this->assertFalse($this->repository->getRemovedObjects()->contains($existingObject));
317 $this->assertTrue($this->repository->getRemovedObjects()->contains($newObject));
318 }
319
320 /**
321 * Replacing a new object which has not yet been persisted by another
322 * new object will just replace them in the repository's list of added
323 * objects.
324 *
325 * @test
326 * @return void
327 */
328 public function replaceAddsNewObjectToAddedObjects() {
329 $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
330 $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
331
332 $addedObjects = new \TYPO3\CMS\Extbase\Persistence\ObjectStorage();
333 $addedObjects->attach($existingObject);
334
335 $this->repository->_set('entityClassName', get_class($newObject));
336 $this->repository->_get('persistenceManager')->_set('addedObjects', $addedObjects);
337 $this->repository->replace($existingObject, $newObject);
338
339 $this->assertFalse($addedObjects->contains($existingObject));
340 $this->assertTrue($addedObjects->contains($newObject));
341 }
342
343 /**
344 * @test
345 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
346 */
347 public function replaceChecksObjectType() {
348 $this->repository->_set('entityClassName', 'ExpectedObjectType');
349 $this->repository->replace(new \stdClass(), new \stdClass());
350 }
351
352 /**
353 * @test
354 */
355 public function updateReplacesAnObjectWithTheSameUuidByTheGivenObject() {
356 $existingObject = new \stdClass();
357 $modifiedObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
358 $modifiedObject->expects($this->once())->method('getUid')->will($this->returnValue('123'));
359
360 $mockPersistenceManager = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\PersistenceManager', array('getObjectByIdentifier', 'findByUid', 'replace'));
361 $mockPersistenceManager->_set('addedObjects', new \TYPO3\CMS\Extbase\Persistence\ObjectStorage);
362 $mockPersistenceManager->_set('removedObjects', new \TYPO3\CMS\Extbase\Persistence\ObjectStorage);
363 $mockPersistenceManager->injectBackend($this->mockBackend);
364 $mockPersistenceManager->injectSession($this->mockSession);
365 $mockPersistenceManager->expects($this->once())->method('getObjectByIdentifier')->with('123')->will($this->returnValue($existingObject));
366 $mockPersistenceManager->expects($this->once())->method('replace')->with($existingObject, $modifiedObject);
367
368 $this->repository->injectPersistenceManager($mockPersistenceManager);
369 $this->repository->_set('entityClassName', get_class($modifiedObject));
370 $this->repository->update($modifiedObject);
371 }
372
373 /**
374 * @test
375 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
376 */
377 public function updateRejectsUnknownObjects() {
378 $someObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
379 $someObject->expects($this->once())->method('getUid')->will($this->returnValue(NULL));
380 $this->repository->_set('entityClassName', get_class($someObject));
381 $this->repository->update($someObject);
382 }
383
384 /**
385 * @test
386 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
387 */
388 public function updateRejectsObjectsOfWrongType() {
389 $this->repository->_set('entityClassName', 'Foo');
390 $this->repository->update(new \stdClass());
391 }
392
393 /**
394 * @test
395 */
396 public function magicCallMethodAcceptsFindBySomethingCallsAndExecutesAQueryWithThatCriteria() {
397 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
398 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
399 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
400 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
401 $this->assertSame($mockQueryResult, $this->repository->findByFoo('bar'));
402 }
403
404 /**
405 * @test
406 */
407 public function magicCallMethodAcceptsFindOneBySomethingCallsAndExecutesAQueryWithThatCriteria() {
408 $object = new \stdClass();
409 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
410 $mockQueryResult->expects($this->once())->method('getFirst')->will($this->returnValue($object));
411 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
412 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
413 $this->mockQuery->expects($this->any())->method('setLimit')->with(1)->will($this->returnValue($this->mockQuery));
414 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
415 $this->assertSame($object, $this->repository->findOneByFoo('bar'));
416 }
417
418 /**
419 * @test
420 */
421 public function magicCallMethodAcceptsCountBySomethingCallsAndExecutesAQueryWithThatCriteria() {
422 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
423 $mockQueryResult->expects($this->once())->method('count')->will($this->returnValue(2));
424 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
425 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
426 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
427 $this->assertSame(2, $this->repository->countByFoo('bar'));
428 }
429
430 /**
431 * @test
432 * @expectedException \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedMethodException
433 */
434 public function magicCallMethodTriggersAnErrorIfUnknownMethodsAreCalled() {
435 $this->repository->__call('foo', array());
436 }
437
438 /**
439 * @test
440 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
441 */
442 public function addChecksObjectType() {
443 $this->repository->_set('entityClassName', 'ExpectedObjectType');
444 $this->repository->add(new \stdClass());
445 }
446
447 /**
448 * @test
449 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
450 */
451 public function removeChecksObjectType() {
452 $this->repository->_set('entityClassName', 'ExpectedObjectType');
453 $this->repository->remove(new \stdClass());
454 }
455 }
456
457 ?>