1<?php
2
3namespace Drupal\Tests\serialization\Unit\Normalizer;
4
5use Drupal\Core\Entity\EntityFieldManagerInterface;
6use Drupal\Core\Entity\EntityTypeManagerInterface;
7use Drupal\Core\Entity\EntityTypeRepositoryInterface;
8use Drupal\Core\Entity\FieldableEntityInterface;
9use Drupal\Core\Field\FieldItemListInterface;
10use Drupal\serialization\Normalizer\EntityNormalizer;
11use Drupal\Tests\UnitTestCase;
12use Symfony\Component\Serializer\Exception\UnexpectedValueException;
13
14/**
15 * @coversDefaultClass \Drupal\serialization\Normalizer\EntityNormalizer
16 * @group serialization
17 */
18class EntityNormalizerTest extends UnitTestCase {
19
20  /**
21   * The mock entity field manager.
22   *
23   * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit\Framework\MockObject\MockObject
24   */
25  protected $entityFieldManager;
26
27  /**
28   * The mock entity type manager.
29   *
30   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
31   */
32  protected $entityTypeManager;
33
34  /**
35   * The mock entity type repository.
36   *
37   * @var \Drupal\Core\Entity\EntityTypeRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
38   */
39  protected $entityTypeRepository;
40
41  /**
42   * The mock serializer.
43   *
44   * @var \Symfony\Component\Serializer\SerializerInterface|\PHPUnit\Framework\MockObject\MockObject
45   */
46  protected $serializer;
47
48  /**
49   * The entity normalizer.
50   *
51   * @var \Drupal\serialization\Normalizer\EntityNormalizer
52   */
53  protected $entityNormalizer;
54
55  /**
56   * {@inheritdoc}
57   */
58  protected function setUp(): void {
59    $this->entityFieldManager = $this->createMock(EntityFieldManagerInterface::class);
60    $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
61    $this->entityTypeRepository = $this->createMock(EntityTypeRepositoryInterface::class);
62
63    $this->entityNormalizer = new EntityNormalizer(
64      $this->entityTypeManager,
65      $this->entityTypeRepository,
66      $this->entityFieldManager
67    );
68  }
69
70  /**
71   * Tests the normalize() method.
72   *
73   * @covers ::normalize
74   */
75  public function testNormalize() {
76    $list_item_1 = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
77    $list_item_2 = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
78
79    $definitions = [
80      'field_1' => $list_item_1,
81      'field_2' => $list_item_2,
82    ];
83
84    $content_entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
85      ->disableOriginalConstructor()
86      ->onlyMethods(['getFields'])
87      ->getMockForAbstractClass();
88    $content_entity->expects($this->once())
89      ->method('getFields')
90      ->will($this->returnValue($definitions));
91
92    $serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
93      ->disableOriginalConstructor()
94      ->onlyMethods(['normalize'])
95      ->getMock();
96    $serializer->expects($this->exactly(2))
97      ->method('normalize')
98      ->withConsecutive(
99        [$list_item_1, 'test_format'],
100        [$list_item_2, 'test_format'],
101      );
102
103    $this->entityNormalizer->setSerializer($serializer);
104
105    $this->entityNormalizer->normalize($content_entity, 'test_format');
106  }
107
108  /**
109   * Tests the denormalize() method with no entity type provided in context.
110   *
111   * @covers ::denormalize
112   */
113  public function testDenormalizeWithNoEntityType() {
114    $this->expectException(UnexpectedValueException::class);
115    $this->entityNormalizer->denormalize([], 'Drupal\Core\Entity\ContentEntityBase');
116  }
117
118  /**
119   * Tests the denormalize method with a bundle property.
120   *
121   * @covers ::denormalize
122   */
123  public function testDenormalizeWithValidBundle() {
124    $test_data = [
125      'key_1' => 'value_1',
126      'key_2' => 'value_2',
127      'test_type' => [
128        ['name' => 'test_bundle'],
129      ],
130    ];
131
132    $entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
133
134    $entity_type->expects($this->once())
135      ->method('id')
136      ->willReturn('test');
137    $entity_type->expects($this->once())
138      ->method('hasKey')
139      ->with('bundle')
140      ->will($this->returnValue(TRUE));
141    $entity_type->expects($this->once())
142      ->method('getKey')
143      ->with('bundle')
144      ->will($this->returnValue('test_type'));
145    $entity_type->expects($this->once())
146      ->method('entityClassImplements')
147      ->with(FieldableEntityInterface::class)
148      ->willReturn(TRUE);
149
150    $entity_type->expects($this->once())
151      ->method('getBundleEntityType')
152      ->will($this->returnValue('test_bundle'));
153
154    $entity_type_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
155    $entity_type_storage_definition->expects($this->once())
156      ->method('getMainPropertyName')
157      ->will($this->returnValue('name'));
158
159    $entity_type_definition = $this->createMock('Drupal\Core\Field\FieldDefinitionInterface');
160    $entity_type_definition->expects($this->once())
161      ->method('getFieldStorageDefinition')
162      ->will($this->returnValue($entity_type_storage_definition));
163
164    $base_definitions = [
165      'test_type' => $entity_type_definition,
166    ];
167
168    $this->entityTypeManager->expects($this->once())
169      ->method('getDefinition')
170      ->with('test')
171      ->will($this->returnValue($entity_type));
172    $this->entityFieldManager->expects($this->once())
173      ->method('getBaseFieldDefinitions')
174      ->with('test')
175      ->will($this->returnValue($base_definitions));
176
177    $entity_query_mock = $this->createMock('Drupal\Core\Entity\Query\QueryInterface');
178    $entity_query_mock->expects($this->once())
179      ->method('execute')
180      ->will($this->returnValue(['test_bundle' => 'test_bundle']));
181
182    $entity_type_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
183    $entity_type_storage->expects($this->once())
184      ->method('getQuery')
185      ->will($this->returnValue($entity_query_mock));
186
187    $key_1 = $this->createMock(FieldItemListInterface::class);
188    $key_2 = $this->createMock(FieldItemListInterface::class);
189
190    $entity = $this->createMock(FieldableEntityInterface::class);
191    $entity->expects($this->exactly(2))
192      ->method('get')
193      ->willReturnMap([
194        ['key_1', $key_1],
195        ['key_2', $key_2],
196      ]);
197
198    $storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
199    // Create should only be called with the bundle property at first.
200    $expected_test_data = [
201      'test_type' => 'test_bundle',
202    ];
203
204    $storage->expects($this->once())
205      ->method('create')
206      ->with($expected_test_data)
207      ->will($this->returnValue($entity));
208
209    $this->entityTypeManager->expects($this->exactly(2))
210      ->method('getStorage')
211      ->willReturnMap([
212        ['test_bundle', $entity_type_storage],
213        ['test', $storage],
214      ]);
215
216    // Setup expectations for the serializer. This will be called for each field
217    // item.
218    $serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
219      ->disableOriginalConstructor()
220      ->onlyMethods(['denormalize'])
221      ->getMock();
222    $serializer->expects($this->exactly(2))
223      ->method('denormalize')
224      ->withConsecutive(
225        ['value_1', get_class($key_1), NULL, ['target_instance' => $key_1, 'entity_type' => 'test']],
226        ['value_2', get_class($key_2), NULL, ['target_instance' => $key_2, 'entity_type' => 'test']],
227      );
228
229    $this->entityNormalizer->setSerializer($serializer);
230
231    $this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
232  }
233
234  /**
235   * Tests the denormalize method with a bundle property.
236   *
237   * @covers ::denormalize
238   */
239  public function testDenormalizeWithInvalidBundle() {
240    $test_data = [
241      'key_1' => 'value_1',
242      'key_2' => 'value_2',
243      'test_type' => [
244        ['name' => 'test_bundle'],
245      ],
246    ];
247
248    $entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
249
250    $entity_type->expects($this->once())
251      ->method('id')
252      ->willReturn('test');
253    $entity_type->expects($this->once())
254      ->method('hasKey')
255      ->with('bundle')
256      ->will($this->returnValue(TRUE));
257    $entity_type->expects($this->once())
258      ->method('getKey')
259      ->with('bundle')
260      ->will($this->returnValue('test_type'));
261    $entity_type->expects($this->once())
262      ->method('entityClassImplements')
263      ->with(FieldableEntityInterface::class)
264      ->willReturn(TRUE);
265
266    $entity_type->expects($this->once())
267      ->method('getBundleEntityType')
268      ->will($this->returnValue('test_bundle'));
269
270    $entity_type_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
271    $entity_type_storage_definition->expects($this->once())
272      ->method('getMainPropertyName')
273      ->will($this->returnValue('name'));
274
275    $entity_type_definition = $this->createMock('Drupal\Core\Field\FieldDefinitionInterface');
276    $entity_type_definition->expects($this->once())
277      ->method('getFieldStorageDefinition')
278      ->will($this->returnValue($entity_type_storage_definition));
279
280    $base_definitions = [
281      'test_type' => $entity_type_definition,
282    ];
283
284    $this->entityTypeManager->expects($this->once())
285      ->method('getDefinition')
286      ->with('test')
287      ->will($this->returnValue($entity_type));
288    $this->entityFieldManager->expects($this->once())
289      ->method('getBaseFieldDefinitions')
290      ->with('test')
291      ->will($this->returnValue($base_definitions));
292
293    $entity_query_mock = $this->createMock('Drupal\Core\Entity\Query\QueryInterface');
294    $entity_query_mock->expects($this->once())
295      ->method('execute')
296      ->will($this->returnValue(['test_bundle_other' => 'test_bundle_other']));
297
298    $entity_type_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
299    $entity_type_storage->expects($this->once())
300      ->method('getQuery')
301      ->will($this->returnValue($entity_query_mock));
302
303    $this->entityTypeManager->expects($this->once())
304      ->method('getStorage')
305      ->with('test_bundle')
306      ->will($this->returnValue($entity_type_storage));
307
308    $this->expectException(UnexpectedValueException::class);
309    $this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']);
310  }
311
312  /**
313   * Tests the denormalize method with no bundle defined.
314   *
315   * @covers ::denormalize
316   */
317  public function testDenormalizeWithNoBundle() {
318    $test_data = [
319      'key_1' => 'value_1',
320      'key_2' => 'value_2',
321    ];
322
323    $entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
324    $entity_type->expects($this->once())
325      ->method('entityClassImplements')
326      ->with(FieldableEntityInterface::class)
327      ->willReturn(TRUE);
328    $entity_type->expects($this->once())
329      ->method('hasKey')
330      ->with('bundle')
331      ->will($this->returnValue(FALSE));
332    $entity_type->expects($this->never())
333      ->method('getKey');
334
335    $this->entityTypeManager->expects($this->once())
336      ->method('getDefinition')
337      ->with('test')
338      ->will($this->returnValue($entity_type));
339
340    $key_1 = $this->createMock(FieldItemListInterface::class);
341    $key_2 = $this->createMock(FieldItemListInterface::class);
342
343    $entity = $this->createMock(FieldableEntityInterface::class);
344    $entity->expects($this->exactly(2))
345      ->method('get')
346      ->willReturnMap([
347        ['key_1', $key_1],
348        ['key_2', $key_2],
349      ]);
350
351    $storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
352    $storage->expects($this->once())
353      ->method('create')
354      ->with([])
355      ->will($this->returnValue($entity));
356
357    $this->entityTypeManager->expects($this->once())
358      ->method('getStorage')
359      ->with('test')
360      ->will($this->returnValue($storage));
361
362    $this->entityFieldManager->expects($this->never())
363      ->method('getBaseFieldDefinitions');
364
365    // Setup expectations for the serializer. This will be called for each field
366    // item.
367    $serializer = $this->getMockBuilder('Symfony\Component\Serializer\Serializer')
368      ->disableOriginalConstructor()
369      ->onlyMethods(['denormalize'])
370      ->getMock();
371    $serializer->expects($this->exactly(2))
372      ->method('denormalize')
373      ->withConsecutive(
374        ['value_1', get_class($key_1), NULL, ['target_instance' => $key_1, 'entity_type' => 'test']],
375        ['value_2', get_class($key_2), NULL, ['target_instance' => $key_2, 'entity_type' => 'test']],
376      );
377
378    $this->entityNormalizer->setSerializer($serializer);
379
380    $this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
381  }
382
383  /**
384   * Tests the denormalize method with no bundle defined.
385   *
386   * @covers ::denormalize
387   */
388  public function testDenormalizeWithNoFieldableEntityType() {
389    $test_data = [
390      'key_1' => 'value_1',
391      'key_2' => 'value_2',
392    ];
393
394    $entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
395    $entity_type->expects($this->once())
396      ->method('entityClassImplements')
397      ->with(FieldableEntityInterface::class)
398      ->willReturn(FALSE);
399
400    $entity_type->expects($this->never())
401      ->method('getKey');
402
403    $this->entityTypeManager->expects($this->once())
404      ->method('getDefinition')
405      ->with('test')
406      ->will($this->returnValue($entity_type));
407
408    $storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
409    $storage->expects($this->once())
410      ->method('create')
411      ->with($test_data)
412      ->will($this->returnValue($this->createMock('Drupal\Core\Entity\EntityInterface')));
413
414    $this->entityTypeManager->expects($this->once())
415      ->method('getStorage')
416      ->with('test')
417      ->will($this->returnValue($storage));
418
419    $this->entityFieldManager->expects($this->never())
420      ->method('getBaseFieldDefinitions');
421
422    $this->assertNotNull($this->entityNormalizer->denormalize($test_data, 'Drupal\Core\Entity\ContentEntityBase', NULL, ['entity_type' => 'test']));
423  }
424
425}
426