[FEATURE] Backport Object Type Converter from Flow
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Tests / Unit / Property / TypeConverter / PersistentObjectConverterTest.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Tests\Unit\Property\TypeConverter;
3
4 /* *
5 * This script belongs to the Extbase framework. *
6 * *
7 * It is free software; you can redistribute it and/or modify it under *
8 * the terms of the GNU Lesser General Public License as published by the *
9 * Free Software Foundation, either version 3 of the License, or (at your *
10 * option) any later version. *
11 * *
12 * This script is distributed in the hope that it will be useful, but *
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHAN- *
14 * TABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser *
15 * General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with the script. *
19 * If not, see http://www.gnu.org/licenses/lgpl.html *
20 * *
21 * The TYPO3 project - inspiring people to share! *
22 * */
23
24 /**
25 * Testcase for the PersistentObjectConverter
26 *
27 * @covers \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter<extended>
28 */
29 class PersistentObjectConverterTest extends \TYPO3\CMS\Extbase\Tests\Unit\BaseTestCase {
30
31 /**
32 * @var \TYPO3\CMS\Extbase\Property\TypeConverterInterface
33 */
34 protected $converter;
35
36 protected $mockReflectionService;
37 protected $mockPersistenceManager;
38 protected $mockObjectManager;
39
40 public function setUp() {
41 $this->converter = new \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter();
42 $this->mockReflectionService = $this->getMock('TYPO3\CMS\Extbase\Reflection\ReflectionService');
43 $this->inject($this->converter, 'reflectionService', $this->mockReflectionService);
44
45 $this->mockPersistenceManager = $this->getMock('TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface');
46 $this->inject($this->converter, 'persistenceManager', $this->mockPersistenceManager);
47
48 $this->mockObjectManager = $this->getMock('TYPO3\CMS\Extbase\Object\ObjectManagerInterface');
49 $this->mockObjectManager->expects($this->any())
50 ->method('get')
51 ->will($this->returnCallback(function() {
52 $args = func_get_args();
53 $reflectionClass = new \ReflectionClass(array_shift($args));
54 if (empty($args)) {
55 return $reflectionClass->newInstance();
56 } else {
57 return $reflectionClass->newInstanceArgs($args);
58 }
59 }));
60 $this->inject($this->converter, 'objectManager', $this->mockObjectManager);
61 }
62
63 /**
64 * @test
65 */
66 public function checkMetadata() {
67 $this->assertEquals(array('string', 'array'), $this->converter->getSupportedSourceTypes(), 'Source types do not match');
68 $this->assertEquals('object', $this->converter->getSupportedTargetType(), 'Target type does not match');
69 $this->assertEquals(1, $this->converter->getPriority(), 'Priority does not match');
70 }
71
72 public function dataProviderForCanConvert() {
73 return array(
74 array(TRUE, FALSE, TRUE),
75 // is entity => can convert
76 array(FALSE, TRUE, TRUE),
77 // is valueobject => can convert
78 array(FALSE, FALSE, FALSE),
79 // is no entity and no value object => can not convert
80 );
81 }
82
83 /**
84 * @test
85 * @dataProvider dataProviderForCanConvert
86 */
87 public function canConvertFromReturnsTrueIfClassIsTaggedWithEntityOrValueObject($isEntity, $isValueObject, $expected) {
88 $className = uniqid('Test_Class');
89 if ($isEntity) {
90 eval("class {$className} extends Tx_Extbase_DomainObject_AbstractEntity {}");
91 } elseif ($isValueObject) {
92 eval("class {$className} extends Tx_Extbase_DomainObject_AbstractValueObject {}");
93 } else {
94 eval("class {$className} {}");
95 }
96 $this->assertEquals($expected, $this->converter->canConvertFrom('myInputData', $className));
97 }
98
99 /**
100 * @test
101 */
102 public function getSourceChildPropertiesToBeConvertedReturnsAllPropertiesExceptTheIdentityProperty() {
103 $source = array(
104 'k1' => 'v1',
105 '__identity' => 'someIdentity',
106 'k2' => 'v2'
107 );
108 $expected = array(
109 'k1' => 'v1',
110 'k2' => 'v2'
111 );
112 $this->assertEquals($expected, $this->converter->getSourceChildPropertiesToBeConverted($source));
113 }
114
115 /**
116 * @test
117 */
118 public function getTypeOfChildPropertyShouldUseReflectionServiceToDetermineType() {
119 $mockSchema = $this->getMockBuilder('TYPO3\\CMS\\Extbase\\Reflection\\ClassSchema')->disableOriginalConstructor()->getMock();
120 $this->mockReflectionService->expects($this->any())->method('getClassSchema')->with('TheTargetType')->will($this->returnValue($mockSchema));
121
122 $mockSchema->expects($this->any())->method('hasProperty')->with('thePropertyName')->will($this->returnValue(TRUE));
123 $mockSchema->expects($this->any())->method('getProperty')->with('thePropertyName')->will($this->returnValue(array(
124 'type' => 'TheTypeOfSubObject',
125 'elementType' => NULL
126 )));
127 $configuration = $this->buildConfiguration(array());
128 $this->assertEquals('TheTypeOfSubObject', $this->converter->getTypeOfChildProperty('TheTargetType', 'thePropertyName', $configuration));
129 }
130
131 /**
132 * @test
133 */
134 public function getTypeOfChildPropertyShouldUseConfiguredTypeIfItWasSet() {
135 $this->mockReflectionService->expects($this->never())->method('getClassSchema');
136
137 $configuration = $this->buildConfiguration(array());
138 $configuration->forProperty('thePropertyName')->setTypeConverterOption('TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter', \TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_TARGET_TYPE, 'Foo\Bar');
139 $this->assertEquals('Foo\Bar', $this->converter->getTypeOfChildProperty('foo', 'thePropertyName', $configuration));
140 }
141
142 /**
143 * @test
144 */
145 public function convertFromShouldFetchObjectFromPersistenceIfUuidStringIsGiven() {
146 $identifier = '17';
147 $object = new \stdClass();
148
149 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
150 $this->assertSame($object, $this->converter->convertFrom($identifier, 'MySpecialType'));
151 }
152
153 /**
154 * @test
155 */
156 public function convertFromShouldFetchObjectFromPersistenceIfuidStringIsGiven() {
157 $identifier = '17';
158 $object = new \stdClass();
159
160 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
161 $this->assertSame($object, $this->converter->convertFrom($identifier, 'MySpecialType'));
162 }
163
164 /**
165 * @test
166 */
167 public function convertFromShouldFetchObjectFromPersistenceIfOnlyIdentityArrayGiven() {
168 $identifier = '12345';
169 $object = new \stdClass();
170
171 $source = array(
172 '__identity' => $identifier
173 );
174 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
175 $this->assertSame($object, $this->converter->convertFrom($source, 'MySpecialType'));
176 }
177
178 /**
179 * @test
180 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
181 */
182 public function convertFromShouldThrowExceptionIfObjectNeedsToBeModifiedButConfigurationIsNotSet() {
183 $identifier = '12345';
184 $object = new \stdClass();
185 $object->someProperty = 'asdf';
186
187 $source = array(
188 '__identity' => $identifier,
189 'foo' => 'bar'
190 );
191 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
192 $this->converter->convertFrom($source, 'MySpecialType');
193 }
194
195 /**
196 * @param array $typeConverterOptions
197 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
198 */
199 protected function buildConfiguration($typeConverterOptions) {
200 $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
201 $configuration->setTypeConverterOptions('TYPO3\\CMS\\Extbase\\Property\\TypeConverter\\PersistentObjectConverter', $typeConverterOptions);
202 return $configuration;
203 }
204
205 /**
206 * @param integer $numberOfResults
207 * @param Matcher $howOftenIsGetFirstCalled
208 * @return \stdClass
209 */
210 public function setupMockQuery($numberOfResults, $howOftenIsGetFirstCalled) {
211 $mockClassSchema = $this->getMock('TYPO3\\CMS\\Extbase\\Reflection\\ClassSchema', array(), array('Dummy'));
212 $mockClassSchema->expects($this->any())->method('getIdentityProperties')->will($this->returnValue(array('key1' => 'someType')));
213 $this->mockReflectionService->expects($this->any())->method('getClassSchema')->with('SomeType')->will($this->returnValue($mockClassSchema));
214
215 $mockConstraint = $this->getMockBuilder('TYPO3\CMS\Extbase\Persistence\Generic\Qom\Comparison')->disableOriginalConstructor()->getMock();
216
217 $mockObject = new \stdClass();
218 $mockQuery = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryInterface');
219 $mockQueryResult = $this->getMock('TYPO3\\CMS\\Extbase\\Persistence\\QueryResultInterface');
220 $mockQueryResult->expects($this->any())->method('count')->will($this->returnValue($numberOfResults));
221 $mockQueryResult->expects($howOftenIsGetFirstCalled)->method('getFirst')->will($this->returnValue($mockObject));
222 $mockQuery->expects($this->any())->method('equals')->with('key1', 'value1')->will($this->returnValue($mockConstraint));
223 $mockQuery->expects($this->any())->method('matching')->with($mockConstraint)->will($this->returnValue($mockQuery));
224 $mockQuery->expects($this->any())->method('execute')->will($this->returnValue($mockQueryResult));
225
226 $this->mockPersistenceManager->expects($this->any())->method('createQueryForType')->with('SomeType')->will($this->returnValue($mockQuery));
227
228 return $mockObject;
229 }
230
231 /**
232 * @test
233 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException
234 */
235 public function convertFromShouldReturnExceptionIfNoMatchingObjectWasFound() {
236 $this->setupMockQuery(0, $this->never());
237 $this->mockReflectionService->expects($this->never())->method('getClassSchema');
238
239 $source = array(
240 '__identity' => 123
241 );
242 $actual = $this->converter->convertFrom($source, 'SomeType');
243 $this->assertNull($actual);
244 }
245
246 /**
247 * @test
248 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\DuplicateObjectException
249 */
250 public function convertFromShouldThrowExceptionIfMoreThanOneObjectWasFound() {
251 $this->setupMockQuery(2, $this->never());
252
253 $source = array(
254 '__identity' => 666
255 );
256 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with(666)->will($this->throwException(new \TYPO3\CMS\Extbase\Property\Exception\DuplicateObjectException));
257 $this->converter->convertFrom($source, 'SomeType');
258 }
259
260 /**
261 * @test
262 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
263 */
264 public function convertFromShouldThrowExceptionIfObjectNeedsToBeCreatedButConfigurationIsNotSet() {
265 $source = array(
266 'foo' => 'bar'
267 );
268 $this->converter->convertFrom($source, 'MySpecialType');
269 }
270
271 /**
272 * @test
273 */
274 public function convertFromShouldCreateObject() {
275 $source = array(
276 'propertyX' => 'bar'
277 );
278 $convertedChildProperties = array(
279 'property1' => 'bar'
280 );
281 $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters();
282 $expectedObject->property1 = 'bar';
283
284 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters', '__construct')->will($this->throwException(new \ReflectionException()));
285 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters'));
286 $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
287 $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters', $convertedChildProperties, $configuration);
288 $this->assertEquals($expectedObject, $result);
289 }
290
291 /**
292 * @test
293 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
294 */
295 public function convertFromShouldThrowExceptionIfPropertyOnTargetObjectCouldNotBeSet() {
296 $source = array(
297 'propertyX' => 'bar'
298 );
299 $object = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters();
300 $convertedChildProperties = array(
301 'propertyNotExisting' => 'bar'
302 );
303 $this->mockObjectManager->expects($this->any())->method('get')->with('TYPO3\\CMS\\Extbase\\Tests\\Fixture\\ClassWithSetters')->will($this->returnValue($object));
304 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\\CMS\\Extbase\\Tests\\Fixture\\ClassWithSetters', '__construct')->will($this->returnValue(array()));
305 $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
306 $result = $this->converter->convertFrom($source, 'TYPO3\\CMS\\Extbase\\Tests\\Fixture\\ClassWithSetters', $convertedChildProperties, $configuration);
307 $this->assertSame($object, $result);
308 }
309
310 /**
311 * @test
312 */
313 public function convertFromShouldCreateObjectWhenThereAreConstructorParameters() {
314 $source = array(
315 'propertyX' => 'bar'
316 );
317 $convertedChildProperties = array(
318 'property1' => 'param1',
319 'property2' => 'bar'
320 );
321 $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('param1');
322 $expectedObject->setProperty2('bar');
323
324 $this->mockReflectionService
325 ->expects($this->any())
326 ->method('getMethodParameters')
327 ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
328 ->will($this->returnValue(array(
329 'property1' => array('optional' => FALSE)
330 )));
331 $this->mockReflectionService
332 ->expects($this->any())
333 ->method('hasMethod')
334 ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
335 ->will($this->returnValue(TRUE));
336 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor'));
337 $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
338 $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', $convertedChildProperties, $configuration);
339 $this->assertEquals($expectedObject, $result);
340 $this->assertEquals('bar', $expectedObject->getProperty2());
341 }
342
343 /**
344 * @test
345 */
346 public function convertFromShouldCreateObjectWhenThereAreOptionalConstructorParameters() {
347 $source = array(
348 'propertyX' => 'bar'
349 );
350 $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('thisIsTheDefaultValue');
351
352 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')->will($this->returnValue(array(
353 'property1' => array('optional' => TRUE, 'defaultValue' => 'thisIsTheDefaultValue')
354 )));
355 $this->mockReflectionService
356 ->expects($this->any())
357 ->method('hasMethod')
358 ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
359 ->will($this->returnValue(TRUE));
360 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor'));
361 $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
362 $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', array(), $configuration);
363 $this->assertEquals($expectedObject, $result);
364 }
365
366 /**
367 * @test
368 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
369 */
370 public function convertFromShouldThrowExceptionIfRequiredConstructorParameterWasNotFound() {
371 $source = array(
372 'propertyX' => 'bar'
373 );
374 $object = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('param1');
375 $convertedChildProperties = array(
376 'property2' => 'bar'
377 );
378
379 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')->will($this->returnValue(array(
380 'property1' => array('optional' => FALSE)
381 )));
382 $this->mockReflectionService
383 ->expects($this->any())
384 ->method('hasMethod')
385 ->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', '__construct')
386 ->will($this->returnValue(TRUE));
387 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor')->will($this->returnValue('TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor'));
388 $configuration = $this->buildConfiguration(array(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
389 $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor', $convertedChildProperties, $configuration);
390 $this->assertSame($object, $result);
391 }
392
393 /**
394 * @test
395 */
396 public function convertFromShouldReturnNullForEmptyString() {
397 $source = '';
398 $result = $this->converter->convertFrom($source, 'TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor');
399 $this->assertNull($result);
400 }
401
402 }
403 ?>