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