[BUGFIX] Added handling for array in findOneByXXX result
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Persistence / RepositoryTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Persistence;
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\QueryFactory
45 */
46 protected $mockQueryFactory;
47
48 /**
49 * @var \TYPO3\CMS\Extbase\Persistence\Generic\BackendInterface
50 */
51 protected $mockBackend;
52
53 /**
54 * @var \TYPO3\CMS\Extbase\Persistence\Generic\Session
55 */
56 protected $mockSession;
57
58 /**
59 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface
60 */
61 protected $mockPersistenceManager;
62
63 /**
64 * @var \TYPO3\CMS\Extbase\Persistence\QueryInterface
65 */
66 protected $mockQuery;
67
68 /**
69 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
70 */
71 protected $querySettings;
72
73 /**
74 * @var \TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface
75 */
76 protected $mockQuerySettings;
77
78 public function setUp() {
79 $this->mockIdentityMap = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\IdentityMap');
80 $this->mockQueryFactory = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QueryFactory');
81 $this->mockQuery = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryInterface');
82 $this->mockQuerySettings = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
83 $this->mockQuery->expects($this->any())->method('getQuerySettings')->will($this->returnValue($this->mockQuerySettings));
84 $this->mockQueryFactory->expects($this->any())->method('create')->will($this->returnValue($this->mockQuery));
85 $this->mockBackend = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\BackendInterface');
86 $this->mockSession = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\Session');
87 $this->mockPersistenceManager = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\PersistenceManagerInterface');
88 $this->mockObjectManager = $this->getMock('TYPO3\\CMS\\Extbase\\Object\\ObjectManagerInterface');
89 $this->repository = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Repository', array('dummy'), array($this->mockObjectManager));
90 $this->repository->injectIdentityMap($this->mockIdentityMap);
91 $this->repository->injectQueryFactory($this->mockQueryFactory);
92 $this->repository->injectPersistenceManager($this->mockPersistenceManager);
93 $this->repository->injectBackend($this->mockBackend);
94 $this->repository->injectSession($this->mockSession);
95 }
96
97 /**
98 * @test
99 */
100 public function abstractRepositoryImplementsRepositoryInterface() {
101 $this->assertTrue($this->repository instanceof \TYPO3\CMS\Extbase\Persistence\RepositoryInterface);
102 }
103
104 /**
105 * @test
106 */
107 public function addActuallyAddsAnObjectToTheInternalObjectsArray() {
108 $someObject = new \stdClass();
109 $this->repository->_set('objectType', get_class($someObject));
110 $this->repository->add($someObject);
111 $this->assertTrue($this->repository->getAddedObjects()->contains($someObject));
112 }
113
114 /**
115 * @test
116 */
117 public function removeActuallyRemovesAnObjectFromTheInternalObjectsArray() {
118 $object1 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
119 $object2 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
120 $object3 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
121 $this->repository->_set('objectType', get_class($object1));
122 $this->repository->add($object1);
123 $this->repository->add($object2);
124 $this->repository->add($object3);
125 $this->repository->remove($object2);
126 $this->assertTrue($this->repository->getAddedObjects()->contains($object1));
127 $this->assertFalse($this->repository->getAddedObjects()->contains($object2));
128 $this->assertTrue($this->repository->getAddedObjects()->contains($object3));
129 }
130
131 /**
132 * @test
133 */
134 public function removeRemovesTheRightObjectEvenIfItHasBeenModifiedSinceItsAddition() {
135 $object1 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
136 $object2 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
137 $object3 = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
138 $this->repository->_set('objectType', get_class($object1));
139 $this->repository->add($object1);
140 $this->repository->add($object2);
141 $this->repository->add($object3);
142 $object2->setPid(1);
143 $object3->setPid(2);
144 $this->repository->remove($object2);
145 $this->assertTrue($this->repository->getAddedObjects()->contains($object1));
146 $this->assertFalse($this->repository->getAddedObjects()->contains($object2));
147 $this->assertTrue($this->repository->getAddedObjects()->contains($object3));
148 }
149
150 /**
151 * Make sure we remember the objects that are not currently add()ed
152 * but might be in persistent storage.
153 *
154 * @test
155 */
156 public function removeRetainsObjectForObjectsNotInCurrentSession() {
157 $object = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\AbstractDomainObject');
158 // if the object is not currently add()ed, it is not new
159 $object->expects($this->once())->method('_isNew')->will($this->returnValue(FALSE));
160 $this->repository->_set('objectType', get_class($object));
161 $this->repository->remove($object);
162 $this->assertTrue($this->repository->getRemovedObjects()->contains($object));
163 }
164
165 /**
166 * dataProvider for createQueryCallsQueryFactoryWithExpectedType
167 *
168 * @return array
169 */
170 public function modelAndRepositoryClassNames() {
171 return array(
172 array('Tx_BlogExample_Domain_Repository_BlogRepository', 'Tx_BlogExample_Domain_Model_Blog'),
173 array('_Domain_Repository_Content_PageRepository', '_Domain_Model_Content_Page'),
174 array('Tx_RepositoryExample_Domain_Repository_SomeModelRepository', 'Tx_RepositoryExample_Domain_Model_SomeModel'),
175 array('Tx_RepositoryExample_Domain_Repository_RepositoryRepository', 'Tx_RepositoryExample_Domain_Model_Repository'),
176 array('Tx_Repository_Domain_Repository_RepositoryRepository', 'Tx_Repository_Domain_Model_Repository')
177 );
178 }
179
180 /**
181 * @test
182 * @dataProvider modelAndRepositoryClassNames
183 * @param string $repositoryClassName
184 * @param string $modelClassName
185 */
186 public function constructSetsObjectTypeFromClassName($repositoryClassName, $modelClassName) {
187 $mockClassName = 'MockRepository' . uniqid();
188 eval('class ' . $mockClassName . ' extends TYPO3\\CMS\\Extbase\\Persistence\\Repository {
189 protected function getRepositoryClassName() {
190 return \'' . $repositoryClassName . '\';
191 }
192 public function _getObjectType() {
193 return $this->objectType;
194 }
195 }');
196 $this->repository = new $mockClassName($this->mockObjectManager);
197 $this->assertEquals($modelClassName, $this->repository->_getObjectType());
198 }
199
200 /**
201 * @test
202 */
203 public function createQueryCallsQueryFactoryWithExpectedClassName() {
204 $this->mockQueryFactory->expects($this->once())->method('create')->with('ExpectedType');
205 $this->repository->_set('objectType', 'ExpectedType');
206 $this->repository->createQuery();
207 }
208
209 /**
210 * @test
211 */
212 public function createQueryReturnsQueryWithUnmodifiedDefaultQuerySettings() {
213 $mockQueryFactory = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QueryFactory');
214 $mockQuery = new \TYPO3\CMS\Extbase\Persistence\Generic\Query('foo');
215 $mockDefaultQuerySettings = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\Generic\\QuerySettingsInterface');
216 $this->repository->injectQueryFactory($mockQueryFactory);
217 $this->repository->setDefaultQuerySettings($mockDefaultQuerySettings);
218 $mockQueryFactory->expects($this->once())->method('create')->will($this->returnValue($mockQuery));
219 $this->repository->createQuery();
220 $instanceQuerySettings = $mockQuery->getQuerySettings();
221 $this->assertEquals($mockDefaultQuerySettings, $instanceQuerySettings);
222 $this->assertNotSame($mockDefaultQuerySettings, $instanceQuerySettings);
223 }
224
225 /**
226 * @test
227 */
228 public function findAllCreatesQueryAndReturnsResultOfExecuteCall() {
229 $expectedResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
230 $this->mockQuery->expects($this->once())->method('execute')->with()->will($this->returnValue($expectedResult));
231 $this->assertSame($expectedResult, $this->repository->findAll());
232 }
233
234 /**
235 * @test
236 */
237 public function findByUidReturnsResultOfGetObjectByIdentifierCall() {
238 $fakeUid = '123';
239 $object = new \stdClass();
240 $this->repository->_set('objectType', 'someObjectType');
241 $this->mockIdentityMap->expects($this->once())->method('hasIdentifier')->with($fakeUid, 'someObjectType')->will($this->returnValue(TRUE));
242 $this->mockIdentityMap->expects($this->once())->method('getObjectByIdentifier')->with($fakeUid)->will($this->returnValue($object));
243 $expectedResult = $object;
244 $actualResult = $this->repository->findByUid($fakeUid);
245 $this->assertSame($expectedResult, $actualResult);
246 }
247
248 /**
249 * Replacing a reconstituted object (which has a uuid) by a new object
250 * will ask the persistence backend to replace them accordingly in the
251 * identity map.
252 *
253 * @test
254 * @return void
255 */
256 public function replaceReplacesReconstitutedEntityByNewObject() {
257 $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
258 $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
259 $this->mockPersistenceManager->expects($this->once())->method('getIdentifierByObject')->with($existingObject)->will($this->returnValue('123'));
260 $this->mockBackend->expects($this->once())->method('replaceObject')->with($existingObject, $newObject);
261 $this->repository->_set('objectType', get_class($newObject));
262 $this->repository->replace($existingObject, $newObject);
263 }
264
265 /**
266 * Replacing a reconstituted object which during this session has been
267 * marked for removal (by calling the repository's remove method)
268 * additionally registers the "newObject" for removal and removes the
269 * "existingObject" from the list of removed objects.
270 *
271 * @test
272 * @return void
273 */
274 public function replaceRemovesReconstitutedObjectWhichIsMarkedToBeRemoved() {
275 $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
276 $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
277 $removedObjects = new \SplObjectStorage();
278 $removedObjects->attach($existingObject);
279 $this->mockPersistenceManager->expects($this->once())->method('getIdentifierByObject')->with($existingObject)->will($this->returnValue('123'));
280 $this->mockBackend->expects($this->once())->method('replaceObject')->with($existingObject, $newObject);
281 $this->repository->_set('objectType', get_class($newObject));
282 $this->repository->_set('removedObjects', $removedObjects);
283 $this->repository->replace($existingObject, $newObject);
284 $this->assertFalse($removedObjects->contains($existingObject));
285 $this->assertTrue($removedObjects->contains($newObject));
286 }
287
288 /**
289 * Replacing a new object which has not yet been persisted by another
290 * new object will just replace them in the repository's list of added
291 * objects.
292 *
293 * @test
294 * @return void
295 */
296 public function replaceAddsNewObjectToAddedObjects() {
297 $existingObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
298 $newObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
299 $addedObjects = new \SplObjectStorage();
300 $addedObjects->attach($existingObject);
301 $this->mockPersistenceManager->expects($this->once())->method('getIdentifierByObject')->with($existingObject)->will($this->returnValue(NULL));
302 $this->mockBackend->expects($this->never())->method('replaceObject');
303 $this->repository->_set('objectType', get_class($newObject));
304 $this->repository->_set('addedObjects', $addedObjects);
305 $this->repository->replace($existingObject, $newObject);
306 $this->assertFalse($addedObjects->contains($existingObject));
307 $this->assertTrue($addedObjects->contains($newObject));
308 }
309
310 /**
311 * @test
312 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
313 */
314 public function replaceChecksObjectType() {
315 $this->repository->_set('objectType', 'ExpectedObjectType');
316 $this->repository->replace(new \stdClass(), new \stdClass());
317 }
318
319 /**
320 * @test
321 */
322 public function updateReplacesAnObjectWithTheSameUuidByTheGivenObject() {
323 $existingObject = new \stdClass();
324 $modifiedObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
325 $modifiedObject->expects($this->once())->method('getUid')->will($this->returnValue('123'));
326 /** @var \TYPO3\CMS\Extbase\Persistence\Repository|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface */
327 $repository = $this->getAccessibleMock('TYPO3\\CMS\\Extbase\\Persistence\\Repository', array('findByUid', 'replace'), array($this->mockObjectManager));
328 $repository->expects($this->once())->method('findByUid')->with('123')->will($this->returnValue($existingObject));
329 $repository->expects($this->once())->method('replace')->with($existingObject, $modifiedObject);
330 $repository->_set('objectType', get_class($modifiedObject));
331 $repository->update($modifiedObject);
332 }
333
334 /**
335 * @test
336 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\UnknownObjectException
337 */
338 public function updateRejectsUnknownObjects() {
339 $someObject = $this->getMock('TYPO3\\CMS\\Extbase\\DomainObject\\DomainObjectInterface');
340 $someObject->expects($this->once())->method('getUid')->will($this->returnValue(NULL));
341 $this->repository->_set('objectType', get_class($someObject));
342 $this->repository->update($someObject);
343 }
344
345 /**
346 * @test
347 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
348 */
349 public function updateRejectsObjectsOfWrongType() {
350 $this->repository->_set('objectType', 'Foo');
351 $this->repository->update(new \stdClass());
352 }
353
354 /**
355 * @test
356 */
357 public function magicCallMethodAcceptsFindBySomethingCallsAndExecutesAQueryWithThatCriteria() {
358 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
359 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
360 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
361 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
362 $this->assertSame($mockQueryResult, $this->repository->findByFoo('bar'));
363 }
364
365 /**
366 * @test
367 */
368 public function magicCallMethodAcceptsFindOneBySomethingCallsAndExecutesAQueryWithThatCriteria() {
369 $object = new \stdClass();
370 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
371 $mockQueryResult->expects($this->once())->method('getFirst')->will($this->returnValue($object));
372 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
373 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
374 $this->mockQuery->expects($this->once())->method('setLimit')->with(1)->will($this->returnValue($this->mockQuery));
375 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
376 $this->assertSame($object, $this->repository->findOneByFoo('bar'));
377 }
378
379 /**
380 * @test
381 */
382 public function magicCallMethodReturnsFirstArrayKeyInFindOneBySomethingIfQueryReturnsRawResult() {
383 $queryResultArray = array(
384 0 => array(
385 'foo' => 'bar',
386 ),
387 );
388 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
389 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
390 $this->mockQuery->expects($this->once())->method('setLimit')->with(1)->will($this->returnValue($this->mockQuery));
391 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($queryResultArray));
392 $this->assertSame(array('foo' => 'bar'), $this->repository->findOneByFoo('bar'));
393 }
394
395 /**
396 * @test
397 */
398 public function magicCallMethodReturnsNullInFindOneBySomethingIfQueryReturnsEmptyRawResult() {
399 $queryResultArray = array();
400 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
401 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
402 $this->mockQuery->expects($this->once())->method('setLimit')->with(1)->will($this->returnValue($this->mockQuery));
403 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($queryResultArray));
404 $this->assertNull($this->repository->findOneByFoo('bar'));
405 }
406
407 /**
408 * @test
409 */
410 public function magicCallMethodAcceptsCountBySomethingCallsAndExecutesAQueryWithThatCriteria() {
411 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
412 $mockQueryResult->expects($this->once())->method('count')->will($this->returnValue(2));
413 $this->mockQuery->expects($this->once())->method('equals')->with('foo', 'bar')->will($this->returnValue('matchCriteria'));
414 $this->mockQuery->expects($this->once())->method('matching')->with('matchCriteria')->will($this->returnValue($this->mockQuery));
415 $this->mockQuery->expects($this->once())->method('execute')->will($this->returnValue($mockQueryResult));
416 $this->assertSame(2, $this->repository->countByFoo('bar'));
417 }
418
419 /**
420 * @test
421 * @expectedException \TYPO3\CMS\Extbase\Persistence\Generic\Exception\UnsupportedMethodException
422 */
423 public function magicCallMethodTriggersAnErrorIfUnknownMethodsAreCalled() {
424 $this->repository->__call('foo', array());
425 }
426
427 /**
428 * @test
429 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
430 */
431 public function addChecksObjectType() {
432 $this->repository->_set('objectType', 'ExpectedObjectType');
433 $this->repository->add(new \stdClass());
434 }
435
436 /**
437 * @test
438 * @expectedException \TYPO3\CMS\Extbase\Persistence\Exception\IllegalObjectTypeException
439 */
440 public function removeChecksObjectType() {
441 $this->repository->_set('objectType', 'ExpectedObjectType');
442 $this->repository->remove(new \stdClass());
443 }
444 }
445
446 ?>