6a74a21266b5f5820b064673c20ff09db0af59e8
[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 use TYPO3\CMS\Core\Tests\UnitTestCase;
24 use TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter;
25
26 /**
27 * Test case
28 */
29 class PersistentObjectConverterTest extends UnitTestCase {
30
31 /**
32 * @var \TYPO3\CMS\Extbase\Property\TypeConverterInterface|\PHPUnit_Framework_MockObject_MockObject
33 */
34 protected $converter;
35
36 /**
37 * @var \TYPO3\CMS\Extbase\Reflection\ReflectionService|\PHPUnit_Framework_MockObject_MockObject
38 */
39 protected $mockReflectionService;
40
41 /**
42 * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface|\PHPUnit_Framework_MockObject_MockObject
43 */
44 protected $mockPersistenceManager;
45
46 /**
47 * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface|\PHPUnit_Framework_MockObject_MockObject
48 */
49 protected $mockObjectManager;
50
51 /**
52 * @var \TYPO3\CMS\Extbase\Object\Container\Container|\PHPUnit_Framework_MockObject_MockObject
53 */
54 protected $mockContainer;
55
56 /**
57 * @throws \InvalidArgumentException
58 * @throws \PHPUnit_Framework_Exception
59 * @throws \RuntimeException
60 */
61 protected function setUp() {
62 $this->converter = new PersistentObjectConverter();
63 $this->mockReflectionService = $this->getMock(\TYPO3\CMS\Extbase\Reflection\ReflectionService::class);
64 $this->inject($this->converter, 'reflectionService', $this->mockReflectionService);
65
66 $this->mockPersistenceManager = $this->getMock(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface::class);
67 $this->inject($this->converter, 'persistenceManager', $this->mockPersistenceManager);
68
69 $this->mockObjectManager = $this->getMock(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface::class);
70 $this->mockObjectManager->expects($this->any())
71 ->method('get')
72 ->will($this->returnCallback(function() {
73 $args = func_get_args();
74 $reflectionClass = new \ReflectionClass(array_shift($args));
75 if (empty($args)) {
76 return $reflectionClass->newInstance();
77 } else {
78 return $reflectionClass->newInstanceArgs($args);
79 }
80 }));
81 $this->inject($this->converter, 'objectManager', $this->mockObjectManager);
82
83 $this->mockContainer = $this->getMock(\TYPO3\CMS\Extbase\Object\Container\Container::class);
84 $this->inject($this->converter, 'objectContainer', $this->mockContainer);
85 }
86
87 /**
88 * @test
89 */
90 public function checkMetadata() {
91 $this->assertEquals(array('integer', 'string', 'array'), $this->converter->getSupportedSourceTypes(), 'Source types do not match');
92 $this->assertEquals('object', $this->converter->getSupportedTargetType(), 'Target type does not match');
93 $this->assertEquals(1, $this->converter->getPriority(), 'Priority does not match');
94 }
95
96 /**
97 * @return array
98 */
99 public function dataProviderForCanConvert() {
100 return array(
101 array(TRUE, FALSE, TRUE),
102 // is entity => can convert
103 array(FALSE, TRUE, TRUE),
104 // is valueobject => can convert
105 array(FALSE, FALSE, FALSE),
106 // is no entity and no value object => can not convert
107 );
108 }
109
110 /**
111 * @test
112 * @dataProvider dataProviderForCanConvert
113 */
114 public function canConvertFromReturnsTrueIfClassIsTaggedWithEntityOrValueObject($isEntity, $isValueObject, $expected) {
115 $className = $this->getUniqueId('Test_Class');
116 if ($isEntity) {
117 eval("class {$className} extends \\" . \TYPO3\CMS\Extbase\DomainObject\AbstractEntity::class . " {}");
118 } elseif ($isValueObject) {
119 eval("class {$className} extends \\" . \TYPO3\CMS\Extbase\DomainObject\AbstractValueObject::class . " {}");
120 } else {
121 eval("class {$className} {}");
122 }
123 $this->assertEquals($expected, $this->converter->canConvertFrom('myInputData', $className));
124 }
125
126 /**
127 * @test
128 */
129 public function getSourceChildPropertiesToBeConvertedReturnsAllPropertiesExceptTheIdentityProperty() {
130 $source = array(
131 'k1' => 'v1',
132 '__identity' => 'someIdentity',
133 'k2' => 'v2'
134 );
135 $expected = array(
136 'k1' => 'v1',
137 'k2' => 'v2'
138 );
139 $this->assertEquals($expected, $this->converter->getSourceChildPropertiesToBeConverted($source));
140 }
141
142 /**
143 * @test
144 */
145 public function getTypeOfChildPropertyShouldUseReflectionServiceToDetermineType() {
146 $mockSchema = $this->getMockBuilder(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class)->disableOriginalConstructor()->getMock();
147 $this->mockReflectionService->expects($this->any())->method('getClassSchema')->with('TheTargetType')->will($this->returnValue($mockSchema));
148
149 $this->mockContainer->expects($this->any())->method('getImplementationClassName')->will($this->returnValue('TheTargetType'));
150 $mockSchema->expects($this->any())->method('hasProperty')->with('thePropertyName')->will($this->returnValue(TRUE));
151 $mockSchema->expects($this->any())->method('getProperty')->with('thePropertyName')->will($this->returnValue(array(
152 'type' => 'TheTypeOfSubObject',
153 'elementType' => NULL
154 )));
155 $configuration = $this->buildConfiguration(array());
156 $this->assertEquals('TheTypeOfSubObject', $this->converter->getTypeOfChildProperty('TheTargetType', 'thePropertyName', $configuration));
157 }
158
159 /**
160 * @test
161 */
162 public function getTypeOfChildPropertyShouldUseConfiguredTypeIfItWasSet() {
163 $this->mockReflectionService->expects($this->never())->method('getClassSchema');
164 $this->mockContainer->expects($this->any())->method('getImplementationClassName')->will($this->returnValue('foo'));
165
166 $configuration = $this->buildConfiguration(array());
167 $configuration->forProperty('thePropertyName')->setTypeConverterOption(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, PersistentObjectConverter::CONFIGURATION_TARGET_TYPE, 'Foo\Bar');
168 $this->assertEquals('Foo\Bar', $this->converter->getTypeOfChildProperty('foo', 'thePropertyName', $configuration));
169 }
170
171 /**
172 * @test
173 */
174 public function convertFromShouldFetchObjectFromPersistenceIfUuidStringIsGiven() {
175 $identifier = '17';
176 $object = new \stdClass();
177
178 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
179 $this->assertSame($object, $this->converter->convertFrom($identifier, 'MySpecialType'));
180 }
181
182 /**
183 * @test
184 */
185 public function convertFromShouldFetchObjectFromPersistenceIfuidStringIsGiven() {
186 $identifier = '17';
187 $object = new \stdClass();
188
189 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
190 $this->assertSame($object, $this->converter->convertFrom($identifier, 'MySpecialType'));
191 }
192
193 /**
194 * @test
195 */
196 public function convertFromShouldFetchObjectFromPersistenceIfOnlyIdentityArrayGiven() {
197 $identifier = '12345';
198 $object = new \stdClass();
199
200 $source = array(
201 '__identity' => $identifier
202 );
203 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
204 $this->assertSame($object, $this->converter->convertFrom($source, 'MySpecialType'));
205 }
206
207 /**
208 * @test
209 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
210 */
211 public function convertFromShouldThrowExceptionIfObjectNeedsToBeModifiedButConfigurationIsNotSet() {
212 $identifier = '12345';
213 $object = new \stdClass();
214 $object->someProperty = 'asdf';
215
216 $source = array(
217 '__identity' => $identifier,
218 'foo' => 'bar'
219 );
220 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with($identifier)->will($this->returnValue($object));
221 $this->converter->convertFrom($source, 'MySpecialType');
222 }
223
224 /**
225 * @param array $typeConverterOptions
226 * @return \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration
227 */
228 protected function buildConfiguration($typeConverterOptions) {
229 $configuration = new \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration();
230 $configuration->setTypeConverterOptions(\TYPO3\CMS\Extbase\Property\TypeConverter\PersistentObjectConverter::class, $typeConverterOptions);
231 return $configuration;
232 }
233
234 /**
235 * @param int $numberOfResults
236 * @param Matcher $howOftenIsGetFirstCalled
237 * @return \stdClass
238 */
239 public function setupMockQuery($numberOfResults, $howOftenIsGetFirstCalled) {
240 $mockClassSchema = $this->getMock(\TYPO3\CMS\Extbase\Reflection\ClassSchema::class, array(), array('Dummy'));
241 $mockClassSchema->expects($this->any())->method('getIdentityProperties')->will($this->returnValue(array('key1' => 'someType')));
242 $this->mockReflectionService->expects($this->any())->method('getClassSchema')->with('SomeType')->will($this->returnValue($mockClassSchema));
243
244 $mockConstraint = $this->getMockBuilder(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\Comparison::class)->disableOriginalConstructor()->getMock();
245
246 $mockObject = new \stdClass();
247 $mockQuery = $this->getMock(\TYPO3\CMS\Extbase\Persistence\QueryInterface::class);
248 $mockQueryResult = $this->getMock(\TYPO3\CMS\Extbase\Persistence\QueryResultInterface::class);
249 $mockQueryResult->expects($this->any())->method('count')->will($this->returnValue($numberOfResults));
250 $mockQueryResult->expects($howOftenIsGetFirstCalled)->method('getFirst')->will($this->returnValue($mockObject));
251 $mockQuery->expects($this->any())->method('equals')->with('key1', 'value1')->will($this->returnValue($mockConstraint));
252 $mockQuery->expects($this->any())->method('matching')->with($mockConstraint)->will($this->returnValue($mockQuery));
253 $mockQuery->expects($this->any())->method('execute')->will($this->returnValue($mockQueryResult));
254
255 $this->mockPersistenceManager->expects($this->any())->method('createQueryForType')->with('SomeType')->will($this->returnValue($mockQuery));
256
257 return $mockObject;
258 }
259
260 /**
261 * @test
262 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException
263 */
264 public function convertFromShouldReturnExceptionIfNoMatchingObjectWasFound() {
265 $this->setupMockQuery(0, $this->never());
266 $this->mockReflectionService->expects($this->never())->method('getClassSchema');
267
268 $source = array(
269 '__identity' => 123
270 );
271 $actual = $this->converter->convertFrom($source, 'SomeType');
272 $this->assertNull($actual);
273 }
274
275 /**
276 * @test
277 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\DuplicateObjectException
278 */
279 public function convertFromShouldThrowExceptionIfMoreThanOneObjectWasFound() {
280 $this->setupMockQuery(2, $this->never());
281
282 $source = array(
283 '__identity' => 666
284 );
285 $this->mockPersistenceManager->expects($this->any())->method('getObjectByIdentifier')->with(666)->will($this->throwException(new \TYPO3\CMS\Extbase\Property\Exception\DuplicateObjectException));
286 $this->converter->convertFrom($source, 'SomeType');
287 }
288
289 /**
290 * @test
291 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidPropertyMappingConfigurationException
292 */
293 public function convertFromShouldThrowExceptionIfObjectNeedsToBeCreatedButConfigurationIsNotSet() {
294 $source = array(
295 'foo' => 'bar'
296 );
297 $this->converter->convertFrom($source, 'MySpecialType');
298 }
299
300 /**
301 * @test
302 */
303 public function convertFromShouldCreateObject() {
304 $source = array(
305 'propertyX' => 'bar'
306 );
307 $convertedChildProperties = array(
308 'property1' => 'bar'
309 );
310 $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters();
311 $expectedObject->property1 = 'bar';
312
313 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class, '__construct')->will($this->throwException(new \ReflectionException()));
314 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class)->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class));
315 $configuration = $this->buildConfiguration(array(PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
316 $result = $this->converter->convertFrom($source, \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class, $convertedChildProperties, $configuration);
317 $this->assertEquals($expectedObject, $result);
318 }
319
320 /**
321 * @test
322 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
323 */
324 public function convertFromShouldThrowExceptionIfPropertyOnTargetObjectCouldNotBeSet() {
325 $source = array(
326 'propertyX' => 'bar'
327 );
328 $object = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters();
329 $convertedChildProperties = array(
330 'propertyNotExisting' => 'bar'
331 );
332 $this->mockObjectManager->expects($this->any())->method('get')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class)->will($this->returnValue($object));
333 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class, '__construct')->will($this->returnValue(array()));
334 $configuration = $this->buildConfiguration(array(PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
335 $result = $this->converter->convertFrom($source, \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSetters::class, $convertedChildProperties, $configuration);
336 $this->assertSame($object, $result);
337 }
338
339 /**
340 * @test
341 */
342 public function convertFromShouldCreateObjectWhenThereAreConstructorParameters() {
343 $source = array(
344 'propertyX' => 'bar'
345 );
346 $convertedChildProperties = array(
347 'property1' => 'param1',
348 'property2' => 'bar'
349 );
350 $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('param1');
351 $expectedObject->setProperty2('bar');
352
353 $this->mockReflectionService
354 ->expects($this->any())
355 ->method('getMethodParameters')
356 ->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, '__construct')
357 ->will($this->returnValue(array(
358 'property1' => array('optional' => FALSE)
359 )));
360 $this->mockReflectionService
361 ->expects($this->any())
362 ->method('hasMethod')
363 ->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, '__construct')
364 ->will($this->returnValue(TRUE));
365 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class)->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class));
366 $this->mockContainer->expects($this->any())->method('getImplementationClassName')->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class));
367 $configuration = $this->buildConfiguration(array(PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
368 $result = $this->converter->convertFrom($source, \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, $convertedChildProperties, $configuration);
369 $this->assertEquals($expectedObject, $result);
370 $this->assertEquals('bar', $expectedObject->getProperty2());
371 }
372
373 /**
374 * @test
375 */
376 public function convertFromShouldCreateObjectWhenThereAreOptionalConstructorParameters() {
377 $source = array(
378 'propertyX' => 'bar'
379 );
380 $expectedObject = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('thisIsTheDefaultValue');
381
382 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, '__construct')->will($this->returnValue(array(
383 'property1' => array('optional' => TRUE, 'defaultValue' => 'thisIsTheDefaultValue')
384 )));
385 $this->mockReflectionService
386 ->expects($this->any())
387 ->method('hasMethod')
388 ->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, '__construct')
389 ->will($this->returnValue(TRUE));
390 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class)->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class));
391 $this->mockContainer->expects($this->any())->method('getImplementationClassName')->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class));
392 $configuration = $this->buildConfiguration(array(PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
393 $result = $this->converter->convertFrom($source, \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, array(), $configuration);
394 $this->assertEquals($expectedObject, $result);
395 }
396
397 /**
398 * @test
399 * @expectedException \TYPO3\CMS\Extbase\Property\Exception\InvalidTargetException
400 */
401 public function convertFromShouldThrowExceptionIfRequiredConstructorParameterWasNotFound() {
402 $source = array(
403 'propertyX' => 'bar'
404 );
405 $object = new \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor('param1');
406 $convertedChildProperties = array(
407 'property2' => 'bar'
408 );
409
410 $this->mockReflectionService->expects($this->any())->method('getMethodParameters')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, '__construct')->will($this->returnValue(array(
411 'property1' => array('optional' => FALSE)
412 )));
413 $this->mockReflectionService
414 ->expects($this->any())
415 ->method('hasMethod')
416 ->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, '__construct')
417 ->will($this->returnValue(TRUE));
418 $this->mockObjectManager->expects($this->any())->method('getClassNameByObjectName')->with(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class)->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class));
419 $this->mockContainer->expects($this->any())->method('getImplementationClassName')->will($this->returnValue(\TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class));
420 $configuration = $this->buildConfiguration(array(PersistentObjectConverter::CONFIGURATION_CREATION_ALLOWED => TRUE));
421 $result = $this->converter->convertFrom($source, \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class, $convertedChildProperties, $configuration);
422 $this->assertSame($object, $result);
423 }
424
425 /**
426 * @test
427 */
428 public function convertFromShouldReturnNullForEmptyString() {
429 $source = '';
430 $result = $this->converter->convertFrom($source, \TYPO3\CMS\Extbase\Tests\Fixture\ClassWithSettersAndConstructor::class);
431 $this->assertNull($result);
432 }
433
434 }