141668def57562bc1b95f0005e1a3ed46047f62f
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Entity / EntityUnitTest.php
1 <?php
2
3 namespace Drupal\Tests\Core\Entity;
4
5 use Drupal\Core\Access\AccessResult;
6 use Drupal\Core\Cache\Cache;
7 use Drupal\Core\DependencyInjection\ContainerBuilder;
8 use Drupal\Core\Entity\EntityStorageInterface;
9 use Drupal\Core\Entity\EntityTypeManagerInterface;
10 use Drupal\Core\Entity\EntityTypeRepositoryInterface;
11 use Drupal\Core\Language\Language;
12 use Drupal\entity_test\Entity\EntityTestMul;
13 use Drupal\Tests\UnitTestCase;
14
15 /**
16  * @coversDefaultClass \Drupal\Core\Entity\Entity
17  * @group Entity
18  * @group Access
19  */
20 class EntityUnitTest extends UnitTestCase {
21
22   /**
23    * The entity under test.
24    *
25    * @var \Drupal\Core\Entity\Entity|\PHPUnit_Framework_MockObject_MockObject
26    */
27   protected $entity;
28
29   /**
30    * The entity type used for testing.
31    *
32    * @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
33    */
34   protected $entityType;
35
36   /**
37    * The entity type manager used for testing.
38    *
39    * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
40    */
41   protected $entityTypeManager;
42
43   /**
44    * The ID of the type of the entity under test.
45    *
46    * @var string
47    */
48   protected $entityTypeId;
49
50   /**
51    * The route provider used for testing.
52    *
53    * @var \Drupal\Core\Routing\RouteProvider|\PHPUnit_Framework_MockObject_MockObject
54    */
55   protected $routeProvider;
56
57   /**
58    * The UUID generator used for testing.
59    *
60    * @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
61    */
62   protected $uuid;
63
64   /**
65    * The language manager.
66    *
67    * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
68    */
69   protected $languageManager;
70
71   /**
72    * The mocked cache tags invalidator.
73    *
74    * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
75    */
76   protected $cacheTagsInvalidator;
77
78   /**
79    * The entity values.
80    *
81    * @var array
82    */
83   protected $values;
84
85   /**
86    * {@inheritdoc}
87    */
88   protected function setUp() {
89     $this->values = [
90       'id' => 1,
91       'langcode' => 'en',
92       'uuid' => '3bb9ee60-bea5-4622-b89b-a63319d10b3a',
93     ];
94     $this->entityTypeId = $this->randomMachineName();
95
96     $this->entityType = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
97     $this->entityType->expects($this->any())
98       ->method('getListCacheTags')
99       ->willReturn([$this->entityTypeId . '_list']);
100
101     $this->entityTypeManager = $this->getMockForAbstractClass(EntityTypeManagerInterface::class);
102     $this->entityTypeManager->expects($this->any())
103       ->method('getDefinition')
104       ->with($this->entityTypeId)
105       ->will($this->returnValue($this->entityType));
106
107     $this->uuid = $this->getMock('\Drupal\Component\Uuid\UuidInterface');
108
109     $this->languageManager = $this->getMock('\Drupal\Core\Language\LanguageManagerInterface');
110     $this->languageManager->expects($this->any())
111       ->method('getLanguage')
112       ->with('en')
113       ->will($this->returnValue(new Language(['id' => 'en'])));
114
115     $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidator');
116
117     $container = new ContainerBuilder();
118     // Ensure that Entity doesn't use the deprecated entity.manager service.
119     $container->set('entity.manager', NULL);
120     $container->set('entity_type.manager', $this->entityTypeManager);
121     $container->set('uuid', $this->uuid);
122     $container->set('language_manager', $this->languageManager);
123     $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
124     \Drupal::setContainer($container);
125
126     $this->entity = $this->getMockForAbstractClass('\Drupal\Core\Entity\Entity', [$this->values, $this->entityTypeId]);
127   }
128
129   /**
130    * @covers ::id
131    */
132   public function testId() {
133     $this->assertSame($this->values['id'], $this->entity->id());
134   }
135
136   /**
137    * @covers ::uuid
138    */
139   public function testUuid() {
140     $this->assertSame($this->values['uuid'], $this->entity->uuid());
141   }
142
143   /**
144    * @covers ::isNew
145    * @covers ::enforceIsNew
146    */
147   public function testIsNew() {
148     // We provided an ID, so the entity is not new.
149     $this->assertFalse($this->entity->isNew());
150     // Force it to be new.
151     $this->assertSame($this->entity, $this->entity->enforceIsNew());
152     $this->assertTrue($this->entity->isNew());
153   }
154
155   /**
156    * @covers ::getEntityType
157    */
158   public function testGetEntityType() {
159     $this->assertSame($this->entityType, $this->entity->getEntityType());
160   }
161
162   /**
163    * @covers ::bundle
164    */
165   public function testBundle() {
166     $this->assertSame($this->entityTypeId, $this->entity->bundle());
167   }
168
169   /**
170    * @covers ::label
171    * @group legacy
172    */
173   public function testLabel() {
174     // Make a mock with one method that we use as the entity's uri_callback. We
175     // check that it is called, and that the entity's label is the callback's
176     // return value.
177     $callback_label = $this->randomMachineName();
178     $property_label = $this->randomMachineName();
179     $callback_container = $this->getMock(get_class());
180     $callback_container->expects($this->once())
181       ->method(__FUNCTION__)
182       ->will($this->returnValue($callback_label));
183     $this->entityType->expects($this->at(0))
184       ->method('getLabelCallback')
185       ->will($this->returnValue([$callback_container, __FUNCTION__]));
186     $this->entityType->expects($this->at(1))
187       ->method('getLabelCallback')
188       ->will($this->returnValue(NULL));
189     $this->entityType->expects($this->at(2))
190       ->method('getKey')
191       ->with('label')
192       ->will($this->returnValue('label'));
193
194     // Set a dummy property on the entity under test to test that the label can
195     // be returned form a property if there is no callback.
196     $this->entityTypeManager->expects($this->at(1))
197       ->method('getDefinition')
198       ->with($this->entityTypeId)
199       ->will($this->returnValue([
200         'entity_keys' => [
201           'label' => 'label',
202         ],
203       ]));
204     $this->entity->label = $property_label;
205
206     $this->assertSame($callback_label, $this->entity->label());
207     $this->assertSame($property_label, $this->entity->label());
208   }
209
210   /**
211    * @covers ::access
212    */
213   public function testAccess() {
214     $access = $this->getMock('\Drupal\Core\Entity\EntityAccessControlHandlerInterface');
215     $operation = $this->randomMachineName();
216     $access->expects($this->at(0))
217       ->method('access')
218       ->with($this->entity, $operation)
219       ->will($this->returnValue(AccessResult::allowed()));
220     $access->expects($this->at(1))
221       ->method('createAccess')
222       ->will($this->returnValue(AccessResult::allowed()));
223     $this->entityTypeManager->expects($this->exactly(2))
224       ->method('getAccessControlHandler')
225       ->will($this->returnValue($access));
226
227     $this->assertEquals(AccessResult::allowed(), $this->entity->access($operation));
228     $this->assertEquals(AccessResult::allowed(), $this->entity->access('create'));
229   }
230
231   /**
232    * @covers ::language
233    */
234   public function testLanguage() {
235     $this->entityType->expects($this->any())
236       ->method('getKey')
237       ->will($this->returnValueMap([
238         ['langcode', 'langcode'],
239       ]));
240     $this->assertSame('en', $this->entity->language()->getId());
241   }
242
243   /**
244    * Setup for the tests of the ::load() method.
245    */
246   public function setupTestLoad() {
247     // Base our mocked entity on a real entity class so we can test if calling
248     // Entity::load() on the base class will bubble up to an actual entity.
249     $this->entityTypeId = 'entity_test_mul';
250     $methods = get_class_methods(EntityTestMul::class);
251     unset($methods[array_search('load', $methods)]);
252     unset($methods[array_search('loadMultiple', $methods)]);
253     unset($methods[array_search('create', $methods)]);
254     $this->entity = $this->getMockBuilder(EntityTestMul::class)
255       ->disableOriginalConstructor()
256       ->setMethods($methods)
257       ->getMock();
258
259   }
260
261   /**
262    * @covers ::load
263    *
264    * Tests Entity::load() when called statically on a subclass of Entity.
265    */
266   public function testLoad() {
267     $this->setupTestLoad();
268
269     $class_name = get_class($this->entity);
270
271     $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class);
272     $entity_type_repository->expects($this->once())
273       ->method('getEntityTypeFromClass')
274       ->with($class_name)
275       ->willReturn($this->entityTypeId);
276
277     $storage = $this->getMock(EntityStorageInterface::class);
278     $storage->expects($this->once())
279       ->method('load')
280       ->with(1)
281       ->will($this->returnValue($this->entity));
282
283     $this->entityTypeManager->expects($this->once())
284       ->method('getStorage')
285       ->with($this->entityTypeId)
286       ->will($this->returnValue($storage));
287
288     \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository);
289
290     // Call Entity::load statically and check that it returns the mock entity.
291     $this->assertSame($this->entity, $class_name::load(1));
292   }
293
294   /**
295    * @covers ::loadMultiple
296    *
297    * Tests Entity::loadMultiple() when called statically on a subclass of
298    * Entity.
299    */
300   public function testLoadMultiple() {
301     $this->setupTestLoad();
302
303     $class_name = get_class($this->entity);
304
305     $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class);
306     $entity_type_repository->expects($this->once())
307       ->method('getEntityTypeFromClass')
308       ->with($class_name)
309       ->willReturn($this->entityTypeId);
310
311     $storage = $this->getMock(EntityStorageInterface::class);
312     $storage->expects($this->once())
313       ->method('loadMultiple')
314       ->with([1])
315       ->will($this->returnValue([1 => $this->entity]));
316
317     $this->entityTypeManager->expects($this->once())
318       ->method('getStorage')
319       ->with($this->entityTypeId)
320       ->will($this->returnValue($storage));
321
322     \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository);
323
324     // Call Entity::loadMultiple statically and check that it returns the mock
325     // entity.
326     $this->assertSame([1 => $this->entity], $class_name::loadMultiple([1]));
327   }
328
329   /**
330    * @covers ::create
331    */
332   public function testCreate() {
333     $this->setupTestLoad();
334
335     $class_name = get_class($this->entity);
336
337     $entity_type_repository = $this->getMockForAbstractClass(EntityTypeRepositoryInterface::class);
338     $entity_type_repository->expects($this->once())
339       ->method('getEntityTypeFromClass')
340       ->with($class_name)
341       ->willReturn($this->entityTypeId);
342
343     $storage = $this->getMock(EntityStorageInterface::class);
344     $storage->expects($this->once())
345       ->method('create')
346       ->with([])
347       ->will($this->returnValue($this->entity));
348
349     $this->entityTypeManager->expects($this->once())
350       ->method('getStorage')
351       ->with($this->entityTypeId)
352       ->will($this->returnValue($storage));
353
354     \Drupal::getContainer()->set('entity_type.repository', $entity_type_repository);
355
356     // Call Entity::create() statically and check that it returns the mock
357     // entity.
358     $this->assertSame($this->entity, $class_name::create([]));
359   }
360
361   /**
362    * @covers ::save
363    */
364   public function testSave() {
365     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
366     $storage->expects($this->once())
367       ->method('save')
368       ->with($this->entity);
369
370     $this->entityTypeManager->expects($this->once())
371       ->method('getStorage')
372       ->with($this->entityTypeId)
373       ->will($this->returnValue($storage));
374
375     $this->entity->save();
376   }
377
378   /**
379    * @covers ::delete
380    */
381   public function testDelete() {
382     $this->entity->id = $this->randomMachineName();
383     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
384     // Testing the argument of the delete() method consumes too much memory.
385     $storage->expects($this->once())
386       ->method('delete');
387
388     $this->entityTypeManager->expects($this->once())
389       ->method('getStorage')
390       ->with($this->entityTypeId)
391       ->will($this->returnValue($storage));
392
393     $this->entity->delete();
394   }
395
396   /**
397    * @covers ::getEntityTypeId
398    */
399   public function testGetEntityTypeId() {
400     $this->assertSame($this->entityTypeId, $this->entity->getEntityTypeId());
401   }
402
403   /**
404    * @covers ::preSave
405    */
406   public function testPreSave() {
407     // This method is internal, so check for errors on calling it only.
408     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
409     // Our mocked entity->preSave() returns NULL, so assert that.
410     $this->assertNull($this->entity->preSave($storage));
411   }
412
413   /**
414    * @covers ::postSave
415    */
416   public function testPostSave() {
417     $this->cacheTagsInvalidator->expects($this->at(0))
418       ->method('invalidateTags')
419       ->with([
420         // List cache tag.
421         $this->entityTypeId . '_list',
422       ]);
423     $this->cacheTagsInvalidator->expects($this->at(1))
424       ->method('invalidateTags')
425       ->with([
426         // Own cache tag.
427         $this->entityTypeId . ':' . $this->values['id'],
428         // List cache tag.
429         $this->entityTypeId . '_list',
430       ]);
431
432     // This method is internal, so check for errors on calling it only.
433     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
434
435     // A creation should trigger the invalidation of the "list" cache tag.
436     $this->entity->postSave($storage, FALSE);
437     // An update should trigger the invalidation of both the "list" and the
438     // "own" cache tags.
439     $this->entity->postSave($storage, TRUE);
440   }
441
442   /**
443    * @covers ::preCreate
444    */
445   public function testPreCreate() {
446     // This method is internal, so check for errors on calling it only.
447     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
448     $values = [];
449     // Our mocked entity->preCreate() returns NULL, so assert that.
450     $this->assertNull($this->entity->preCreate($storage, $values));
451   }
452
453   /**
454    * @covers ::postCreate
455    */
456   public function testPostCreate() {
457     // This method is internal, so check for errors on calling it only.
458     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
459     // Our mocked entity->postCreate() returns NULL, so assert that.
460     $this->assertNull($this->entity->postCreate($storage));
461   }
462
463   /**
464    * @covers ::preDelete
465    */
466   public function testPreDelete() {
467     // This method is internal, so check for errors on calling it only.
468     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
469     // Our mocked entity->preDelete() returns NULL, so assert that.
470     $this->assertNull($this->entity->preDelete($storage, [$this->entity]));
471   }
472
473   /**
474    * @covers ::postDelete
475    */
476   public function testPostDelete() {
477     $this->cacheTagsInvalidator->expects($this->once())
478       ->method('invalidateTags')
479       ->with([
480         $this->entityTypeId . ':' . $this->values['id'],
481         $this->entityTypeId . '_list',
482       ]);
483     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
484     $storage->expects($this->once())
485       ->method('getEntityType')
486       ->willReturn($this->entityType);
487
488     $entities = [$this->values['id'] => $this->entity];
489     $this->entity->postDelete($storage, $entities);
490   }
491
492   /**
493    * @covers ::postLoad
494    */
495   public function testPostLoad() {
496     // This method is internal, so check for errors on calling it only.
497     $storage = $this->getMock('\Drupal\Core\Entity\EntityStorageInterface');
498     $entities = [$this->entity];
499     // Our mocked entity->postLoad() returns NULL, so assert that.
500     $this->assertNull($this->entity->postLoad($storage, $entities));
501   }
502
503   /**
504    * @covers ::referencedEntities
505    */
506   public function testReferencedEntities() {
507     $this->assertSame([], $this->entity->referencedEntities());
508   }
509
510   /**
511    * @covers ::getCacheTags
512    * @covers ::getCacheTagsToInvalidate
513    * @covers ::addCacheTags
514    */
515   public function testCacheTags() {
516     // Ensure that both methods return the same by default.
517     $this->assertEquals([$this->entityTypeId . ':' . 1], $this->entity->getCacheTags());
518     $this->assertEquals([$this->entityTypeId . ':' . 1], $this->entity->getCacheTagsToInvalidate());
519
520     // Add an additional cache tag and make sure only getCacheTags() returns
521     // that.
522     $this->entity->addCacheTags(['additional_cache_tag']);
523
524     // EntityTypeId is random so it can shift order. We need to duplicate the
525     // sort from \Drupal\Core\Cache\Cache::mergeTags().
526     $tags = ['additional_cache_tag', $this->entityTypeId . ':' . 1];
527     sort($tags);
528     $this->assertEquals($tags, $this->entity->getCacheTags());
529     $this->assertEquals([$this->entityTypeId . ':' . 1], $this->entity->getCacheTagsToInvalidate());
530   }
531
532   /**
533    * @covers ::getCacheContexts
534    * @covers ::addCacheContexts
535    */
536   public function testCacheContexts() {
537     $cache_contexts_manager = $this->getMockBuilder('Drupal\Core\Cache\Context\CacheContextsManager')
538       ->disableOriginalConstructor()
539       ->getMock();
540     $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE);
541
542     $container = new ContainerBuilder();
543     $container->set('cache_contexts_manager', $cache_contexts_manager);
544     \Drupal::setContainer($container);
545
546     // There are no cache contexts by default.
547     $this->assertEquals([], $this->entity->getCacheContexts());
548
549     // Add an additional cache context.
550     $this->entity->addCacheContexts(['user']);
551     $this->assertEquals(['user'], $this->entity->getCacheContexts());
552   }
553
554   /**
555    * @covers ::getCacheMaxAge
556    * @covers ::mergeCacheMaxAge
557    */
558   public function testCacheMaxAge() {
559     // Cache max age is permanent by default.
560     $this->assertEquals(Cache::PERMANENT, $this->entity->getCacheMaxAge());
561
562     // Set two cache max ages, the lower value is the one that needs to be
563     // returned.
564     $this->entity->mergeCacheMaxAge(600);
565     $this->entity->mergeCacheMaxAge(1800);
566     $this->assertEquals(600, $this->entity->getCacheMaxAge());
567   }
568
569 }