Security update for Core, with self-updated composer
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Entity / EntityTypeManagerTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\Core\Entity\EntityTypeManagerTest.
6  */
7
8 namespace Drupal\Tests\Core\Entity;
9
10 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
11 use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
12 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
13 use Drupal\Core\Cache\CacheBackendInterface;
14 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
15 use Drupal\Core\Entity\EntityHandlerBase;
16 use Drupal\Core\Entity\EntityInterface;
17 use Drupal\Core\Entity\EntityManagerInterface;
18 use Drupal\Core\Entity\EntityTypeInterface;
19 use Drupal\Core\Entity\EntityTypeManager;
20 use Drupal\Core\Entity\EntityTypeManagerInterface;
21 use Drupal\Core\Entity\Exception\InvalidLinkTemplateException;
22 use Drupal\Core\Extension\ModuleHandlerInterface;
23 use Drupal\Core\StringTranslation\TranslationInterface;
24 use Drupal\Tests\UnitTestCase;
25 use Prophecy\Argument;
26 use Symfony\Component\DependencyInjection\ContainerInterface;
27
28 /**
29  * @coversDefaultClass \Drupal\Core\Entity\EntityTypeManager
30  * @group Entity
31  */
32 class EntityTypeManagerTest extends UnitTestCase {
33
34   /**
35    * The entity type manager under test.
36    *
37    * @var \Drupal\Core\Entity\EntityTypeManager
38    */
39   protected $entityTypeManager;
40
41   /**
42    * The translation manager.
43    *
44    * @var \Drupal\Core\StringTranslation\TranslationInterface|\Prophecy\Prophecy\ProphecyInterface
45    */
46   protected $translationManager;
47
48   /**
49    * The plugin discovery.
50    *
51    * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\Prophecy\Prophecy\ProphecyInterface
52    */
53   protected $discovery;
54
55   /**
56    * The module handler.
57    *
58    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ProphecyInterface
59    */
60   protected $moduleHandler;
61
62   /**
63    * The cache backend.
64    *
65    * @var \Drupal\Core\Cache\CacheBackendInterface|\Prophecy\Prophecy\ProphecyInterface
66    */
67   protected $cacheBackend;
68
69   /**
70    * {@inheritdoc}
71    */
72   protected function setUp() {
73     parent::setUp();
74
75     $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
76     $this->moduleHandler->getImplementations('entity_type_build')->willReturn([]);
77     $this->moduleHandler->alter('entity_type', Argument::type('array'))->willReturn(NULL);
78
79     $this->cacheBackend = $this->prophesize(CacheBackendInterface::class);
80     $this->translationManager = $this->prophesize(TranslationInterface::class);
81
82     $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub());
83     $this->discovery = $this->prophesize(DiscoveryInterface::class);
84     $this->entityTypeManager->setDiscovery($this->discovery->reveal());
85   }
86
87   /**
88    * Sets up the entity type manager to be tested.
89    *
90    * @param \Drupal\Core\Entity\EntityTypeInterface[]|\Prophecy\Prophecy\ProphecyInterface[] $definitions
91    *   (optional) An array of entity type definitions.
92    */
93   protected function setUpEntityTypeDefinitions($definitions = []) {
94     $class = $this->getMockClass(EntityInterface::class);
95     foreach ($definitions as $key => $entity_type) {
96       // \Drupal\Core\Entity\EntityTypeInterface::getLinkTemplates() is called
97       // by \Drupal\Core\Entity\EntityManager::processDefinition() so it must
98       // always be mocked.
99       $entity_type->getLinkTemplates()->willReturn([]);
100
101       // Give the entity type a legitimate class to return.
102       $entity_type->getClass()->willReturn($class);
103       $entity_type->setClass($class)->willReturn($entity_type->reveal());
104
105       $definitions[$key] = $entity_type->reveal();
106     }
107
108     $this->discovery->getDefinition(Argument::cetera())
109       ->will(function ($args) use ($definitions) {
110         $entity_type_id = $args[0];
111         $exception_on_invalid = $args[1];
112         if (isset($definitions[$entity_type_id])) {
113           return $definitions[$entity_type_id];
114         }
115         elseif (!$exception_on_invalid) {
116           return NULL;
117         }
118         else {
119           throw new PluginNotFoundException($entity_type_id);
120         }
121       });
122     $this->discovery->getDefinitions()->willReturn($definitions);
123
124   }
125
126   /**
127    * Tests the hasHandler() method.
128    *
129    * @covers ::hasHandler
130    *
131    * @dataProvider providerTestHasHandler
132    */
133   public function testHasHandler($entity_type_id, $expected) {
134     $apple = $this->prophesize(EntityTypeInterface::class);
135     $apple->hasHandlerClass('storage')->willReturn(TRUE);
136
137     $banana = $this->prophesize(EntityTypeInterface::class);
138     $banana->hasHandlerClass('storage')->willReturn(FALSE);
139
140     $this->setUpEntityTypeDefinitions([
141       'apple' => $apple,
142       'banana' => $banana,
143     ]);
144
145     $entity_type = $this->entityTypeManager->hasHandler($entity_type_id, 'storage');
146     $this->assertSame($expected, $entity_type);
147   }
148
149   /**
150    * Provides test data for testHasHandler().
151    *
152    * @return array
153    *   Test data.
154    */
155   public function providerTestHasHandler() {
156     return [
157       ['apple', TRUE],
158       ['banana', FALSE],
159       ['pear', FALSE],
160     ];
161   }
162
163   /**
164    * Tests the getStorage() method.
165    *
166    * @covers ::getStorage
167    */
168   public function testGetStorage() {
169     $class = $this->getTestHandlerClass();
170     $entity = $this->prophesize(EntityTypeInterface::class);
171     $entity->getHandlerClass('storage')->willReturn($class);
172     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
173
174     $this->assertInstanceOf($class, $this->entityTypeManager->getStorage('test_entity_type'));
175   }
176
177   /**
178    * Tests the getListBuilder() method.
179    *
180    * @covers ::getListBuilder
181    */
182   public function testGetListBuilder() {
183     $class = $this->getTestHandlerClass();
184     $entity = $this->prophesize(EntityTypeInterface::class);
185     $entity->getHandlerClass('list_builder')->willReturn($class);
186     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
187
188     $this->assertInstanceOf($class, $this->entityTypeManager->getListBuilder('test_entity_type'));
189   }
190
191   /**
192    * Tests the getViewBuilder() method.
193    *
194    * @covers ::getViewBuilder
195    */
196   public function testGetViewBuilder() {
197     $class = $this->getTestHandlerClass();
198     $entity = $this->prophesize(EntityTypeInterface::class);
199     $entity->getHandlerClass('view_builder')->willReturn($class);
200     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
201
202     $this->assertInstanceOf($class, $this->entityTypeManager->getViewBuilder('test_entity_type'));
203   }
204
205   /**
206    * Tests the getAccessControlHandler() method.
207    *
208    * @covers ::getAccessControlHandler
209    */
210   public function testGetAccessControlHandler() {
211     $class = $this->getTestHandlerClass();
212     $entity = $this->prophesize(EntityTypeInterface::class);
213     $entity->getHandlerClass('access')->willReturn($class);
214     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
215
216     $this->assertInstanceOf($class, $this->entityTypeManager->getAccessControlHandler('test_entity_type'));
217   }
218
219   /**
220    * Tests the getFormObject() method.
221    *
222    * @covers ::getFormObject
223    */
224   public function testGetFormObject() {
225     $entity_manager = $this->prophesize(EntityManagerInterface::class);
226     $container = $this->prophesize(ContainerInterface::class);
227     $container->get('entity.manager')->willReturn($entity_manager->reveal());
228     \Drupal::setContainer($container->reveal());
229
230     $apple = $this->prophesize(EntityTypeInterface::class);
231     $apple->getFormClass('default')->willReturn(TestEntityForm::class);
232
233     $banana = $this->prophesize(EntityTypeInterface::class);
234     $banana->getFormClass('default')->willReturn(TestEntityFormInjected::class);
235
236     $this->setUpEntityTypeDefinitions([
237       'apple' => $apple,
238       'banana' => $banana,
239     ]);
240
241     $apple_form = $this->entityTypeManager->getFormObject('apple', 'default');
242     $this->assertInstanceOf(TestEntityForm::class, $apple_form);
243     $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_form);
244     $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_form);
245
246     $banana_form = $this->entityTypeManager->getFormObject('banana', 'default');
247     $this->assertInstanceOf(TestEntityFormInjected::class, $banana_form);
248     $this->assertAttributeEquals('yellow', 'color', $banana_form);
249
250   }
251
252   /**
253    * Tests the getFormObject() method with an invalid operation.
254    *
255    * @covers ::getFormObject
256    */
257   public function testGetFormObjectInvalidOperation() {
258     $entity = $this->prophesize(EntityTypeInterface::class);
259     $entity->getFormClass('edit')->willReturn('');
260     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
261
262     $this->setExpectedException(InvalidPluginDefinitionException::class);
263     $this->entityTypeManager->getFormObject('test_entity_type', 'edit');
264   }
265
266   /**
267    * Tests the getHandler() method.
268    *
269    * @covers ::getHandler
270    */
271   public function testGetHandler() {
272     $class = $this->getTestHandlerClass();
273     $apple = $this->prophesize(EntityTypeInterface::class);
274     $apple->getHandlerClass('storage')->willReturn($class);
275
276     $this->setUpEntityTypeDefinitions([
277       'apple' => $apple,
278     ]);
279
280     $apple_controller = $this->entityTypeManager->getHandler('apple', 'storage');
281     $this->assertInstanceOf($class, $apple_controller);
282     $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_controller);
283     $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_controller);
284   }
285
286   /**
287    * Tests the getHandler() method when no controller is defined.
288    *
289    * @covers ::getHandler
290    */
291   public function testGetHandlerMissingHandler() {
292     $entity = $this->prophesize(EntityTypeInterface::class);
293     $entity->getHandlerClass('storage')->willReturn('');
294     $this->setUpEntityTypeDefinitions(['test_entity_type' => $entity]);
295     $this->setExpectedException(InvalidPluginDefinitionException::class);
296     $this->entityTypeManager->getHandler('test_entity_type', 'storage');
297   }
298
299   /**
300    * @covers ::getRouteProviders
301    */
302   public function testGetRouteProviders() {
303     $apple = $this->prophesize(EntityTypeInterface::class);
304     $apple->getRouteProviderClasses()->willReturn(['default' => TestRouteProvider::class]);
305
306     $this->setUpEntityTypeDefinitions([
307       'apple' => $apple,
308     ]);
309
310     $apple_route_provider = $this->entityTypeManager->getRouteProviders('apple');
311     $this->assertInstanceOf(TestRouteProvider::class, $apple_route_provider['default']);
312     $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_route_provider['default']);
313     $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_route_provider['default']);
314   }
315
316   /**
317    * Tests the processDefinition() method.
318    *
319    * @covers ::processDefinition
320    */
321   public function testProcessDefinition() {
322     $apple = $this->prophesize(EntityTypeInterface::class);
323     $this->setUpEntityTypeDefinitions(['apple' => $apple]);
324
325     $apple->getLinkTemplates()->willReturn(['canonical' => 'path/to/apple']);
326
327     $definition = $apple->reveal();
328     $this->setExpectedException(InvalidLinkTemplateException::class, "Link template 'canonical' for entity type 'apple' must start with a leading slash, the current link template is 'path/to/apple'");
329     $this->entityTypeManager->processDefinition($definition, 'apple');
330   }
331
332   /**
333    * Tests the getDefinition() method.
334    *
335    * @covers ::getDefinition
336    *
337    * @dataProvider providerTestGetDefinition
338    */
339   public function testGetDefinition($entity_type_id, $expected) {
340     $entity = $this->prophesize(EntityTypeInterface::class);
341
342     $this->setUpEntityTypeDefinitions([
343       'apple' => $entity,
344       'banana' => $entity,
345     ]);
346
347     $entity_type = $this->entityTypeManager->getDefinition($entity_type_id, FALSE);
348     if ($expected) {
349       $this->assertInstanceOf(EntityTypeInterface::class, $entity_type);
350     }
351     else {
352       $this->assertNull($entity_type);
353     }
354   }
355
356   /**
357    * Provides test data for testGetDefinition().
358    *
359    * @return array
360    *   Test data.
361    */
362   public function providerTestGetDefinition() {
363     return [
364       ['apple', TRUE],
365       ['banana', TRUE],
366       ['pear', FALSE],
367     ];
368   }
369
370   /**
371    * Tests the getDefinition() method with an invalid definition.
372    *
373    * @covers ::getDefinition
374    */
375   public function testGetDefinitionInvalidException() {
376     $this->setUpEntityTypeDefinitions();
377
378     $this->setExpectedException(PluginNotFoundException::class, 'The "pear" entity type does not exist.');
379     $this->entityTypeManager->getDefinition('pear', TRUE);
380   }
381
382   /**
383    * Gets a mock controller class name.
384    *
385    * @return string
386    *   A mock controller class name.
387    */
388   protected function getTestHandlerClass() {
389     return get_class($this->getMockForAbstractClass(EntityHandlerBase::class));
390   }
391
392 }
393
394 class TestEntityTypeManager extends EntityTypeManager {
395
396   /**
397    * Sets the discovery for the manager.
398    *
399    * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
400    *   The discovery object.
401    */
402   public function setDiscovery(DiscoveryInterface $discovery) {
403     $this->discovery = $discovery;
404   }
405
406 }
407
408 /**
409  * Provides a test entity form.
410  */
411 class TestEntityForm extends EntityHandlerBase {
412
413   /**
414    * The entity manager.
415    *
416    * @var \Drupal\Core\Entity\EntityManagerInterface
417    */
418   protected $entityManager;
419
420   /**
421    * The entity type manager.
422    *
423    * @var \Drupal\Core\Entity\EntityTypeManagerInterface
424    */
425   protected $entityTypeManager;
426
427   /**
428    * {@inheritdoc}
429    */
430   public function getBaseFormId() {
431     return 'the_base_form_id';
432   }
433
434   /**
435    * {@inheritdoc}
436    */
437   public function getFormId() {
438     return 'the_form_id';
439   }
440
441   /**
442    * {@inheritdoc}
443    */
444   public function setEntity(EntityInterface $entity) {
445     return $this;
446   }
447
448   /**
449    * {@inheritdoc}
450    */
451   public function setOperation($operation) {
452     return $this;
453   }
454
455   /**
456    * {@inheritdoc}
457    */
458   public function setEntityManager(EntityManagerInterface $entity_manager) {
459     $this->entityManager = $entity_manager;
460     return $this;
461   }
462
463   /**
464    * {@inheritdoc}
465    */
466   public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
467     $this->entityTypeManager = $entity_type_manager;
468     return $this;
469   }
470
471 }
472
473 /**
474  * Provides a test entity form that uses injection.
475  */
476 class TestEntityFormInjected extends TestEntityForm implements ContainerInjectionInterface {
477
478   /**
479    * The color of the entity type.
480    *
481    * @var string
482    */
483   protected $color;
484
485   /**
486    * Constructs a new TestEntityFormInjected.
487    *
488    * @param string $color
489    *   The color of the entity type.
490    */
491   public function __construct($color) {
492     $this->color = $color;
493   }
494
495   /**
496    * {@inheritdoc}
497    */
498   public static function create(ContainerInterface $container) {
499     return new static('yellow');
500   }
501
502 }
503
504 /**
505  * Provides a test entity route provider.
506  */
507 class TestRouteProvider extends EntityHandlerBase {
508
509 }