1<?php
2
3namespace Drupal\Tests\Core\Config\Entity;
4
5use Drupal\Component\Uuid\UuidInterface;
6use Drupal\Core\Cache\Cache;
7use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
8use Drupal\Core\Cache\MemoryCache\MemoryCache;
9use Drupal\Core\Config\Config;
10use Drupal\Core\Config\ConfigDuplicateUUIDException;
11use Drupal\Core\Config\ConfigFactoryInterface;
12use Drupal\Core\Config\ConfigManagerInterface;
13use Drupal\Core\Config\Entity\ConfigEntityBase;
14use Drupal\Core\Config\Entity\ConfigEntityInterface;
15use Drupal\Core\Config\Entity\ConfigEntityStorage;
16use Drupal\Core\Config\Entity\ConfigEntityType;
17use Drupal\Core\Config\ImmutableConfig;
18use Drupal\Core\Config\TypedConfigManagerInterface;
19use Drupal\Core\Entity\EntityInterface;
20use Drupal\Core\Entity\EntityMalformedException;
21use Drupal\Core\Entity\EntityStorageException;
22use Drupal\Core\Entity\EntityTypeManagerInterface;
23use Drupal\Core\Entity\Query\QueryFactoryInterface;
24use Drupal\Core\Entity\Query\QueryInterface;
25use Drupal\Core\Extension\ModuleHandlerInterface;
26use Drupal\Core\Language\Language;
27use Drupal\Core\Language\LanguageManagerInterface;
28use Drupal\Tests\UnitTestCase;
29use Prophecy\Argument;
30use Symfony\Component\DependencyInjection\ContainerBuilder;
31
32/**
33 * @coversDefaultClass \Drupal\Core\Config\Entity\ConfigEntityStorage
34 * @group Config
35 */
36class ConfigEntityStorageTest extends UnitTestCase {
37
38  /**
39   * The type ID of the entity under test.
40   *
41   * @var string
42   */
43  protected $entityTypeId;
44
45  /**
46   * The module handler.
47   *
48   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ProphecyInterface
49   */
50  protected $moduleHandler;
51
52  /**
53   * The UUID service.
54   *
55   * @var \Drupal\Component\Uuid\UuidInterface|\Prophecy\Prophecy\ProphecyInterface
56   */
57  protected $uuidService;
58
59  /**
60   * The language manager.
61   *
62   * @var \Drupal\Core\Language\LanguageManagerInterface|\Prophecy\Prophecy\ProphecyInterface
63   */
64  protected $languageManager;
65
66  /**
67   * The config storage.
68   *
69   * @var \Drupal\Core\Config\Entity\ConfigEntityStorage
70   */
71  protected $entityStorage;
72
73  /**
74   * The config factory service.
75   *
76   * @var \Drupal\Core\Config\ConfigFactoryInterface|\Prophecy\Prophecy\ProphecyInterface
77   */
78  protected $configFactory;
79
80  /**
81   * The entity query.
82   *
83   * @var \Drupal\Core\Entity\Query\QueryInterface|\Prophecy\Prophecy\ProphecyInterface
84   */
85  protected $entityQuery;
86
87  /**
88   * The mocked cache backend.
89   *
90   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\Prophecy\Prophecy\ProphecyInterface
91   */
92  protected $cacheTagsInvalidator;
93
94  /**
95   * The configuration manager.
96   *
97   * @var \Drupal\Core\Config\ConfigManagerInterface|\Prophecy\Prophecy\ProphecyInterface
98   */
99  protected $configManager;
100
101  /**
102   * {@inheritdoc}
103   *
104   * @covers ::__construct
105   */
106  protected function setUp(): void {
107    parent::setUp();
108
109    $this->entityTypeId = 'test_entity_type';
110
111    $entity_type = new ConfigEntityType([
112      'id' => $this->entityTypeId,
113      'class' => get_class($this->getMockEntity()),
114      'provider' => 'the_provider',
115      'config_prefix' => 'the_config_prefix',
116      'entity_keys' => [
117        'id' => 'id',
118        'uuid' => 'uuid',
119        'langcode' => 'langcode',
120      ],
121      'config_export' => [
122        'id',
123      ],
124      'list_cache_tags' => [$this->entityTypeId . '_list'],
125    ]);
126
127    $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
128
129    $this->uuidService = $this->prophesize(UuidInterface::class);
130
131    $this->languageManager = $this->prophesize(LanguageManagerInterface::class);
132    $this->languageManager->getCurrentLanguage()->willReturn(new Language(['id' => 'hu']));
133
134    $this->configFactory = $this->prophesize(ConfigFactoryInterface::class);
135
136    $this->entityQuery = $this->prophesize(QueryInterface::class);
137    $entity_query_factory = $this->prophesize(QueryFactoryInterface::class);
138    $entity_query_factory->get($entity_type, 'AND')->willReturn($this->entityQuery->reveal());
139
140    $this->entityStorage = new ConfigEntityStorage($entity_type, $this->configFactory->reveal(), $this->uuidService->reveal(), $this->languageManager->reveal(), new MemoryCache());
141    $this->entityStorage->setModuleHandler($this->moduleHandler->reveal());
142
143    $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
144    $entity_type_manager->getDefinition('test_entity_type')->willReturn($entity_type);
145
146    $this->cacheTagsInvalidator = $this->prophesize(CacheTagsInvalidatorInterface::class);
147
148    $typed_config_manager = $this->prophesize(TypedConfigManagerInterface::class);
149    $typed_config_manager
150      ->getDefinition(Argument::containingString('the_provider.the_config_prefix.'))
151      ->willReturn(['mapping' => ['id' => '', 'uuid' => '', 'dependencies' => '']]);
152
153    $this->configManager = $this->prophesize(ConfigManagerInterface::class);
154
155    $container = new ContainerBuilder();
156    $container->set('entity_type.manager', $entity_type_manager->reveal());
157    $container->set('entity.query.config', $entity_query_factory->reveal());
158    $container->set('config.typed', $typed_config_manager->reveal());
159    $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator->reveal());
160    $container->set('config.manager', $this->configManager->reveal());
161    $container->set('language_manager', $this->languageManager->reveal());
162    \Drupal::setContainer($container);
163
164  }
165
166  /**
167   * @covers ::create
168   * @covers ::doCreate
169   */
170  public function testCreateWithPredefinedUuid() {
171    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
172
173    $entity = $this->getMockEntity();
174    $entity->set('id', 'foo');
175    $entity->set('langcode', 'hu');
176    $entity->set('uuid', 'baz');
177    $entity->setOriginalId('foo');
178    $entity->enforceIsNew();
179
180    $this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
181      ->shouldBeCalled();
182    $this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
183      ->shouldBeCalled();
184
185    $this->uuidService->generate()->shouldNotBeCalled();
186
187    $entity = $this->entityStorage->create(['id' => 'foo', 'uuid' => 'baz']);
188    $this->assertInstanceOf(EntityInterface::class, $entity);
189    $this->assertSame('foo', $entity->id());
190    $this->assertSame('baz', $entity->uuid());
191  }
192
193  /**
194   * @covers ::create
195   * @covers ::doCreate
196   *
197   * @return \Drupal\Core\Entity\EntityInterface
198   */
199  public function testCreate() {
200    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
201
202    $entity = $this->getMockEntity();
203    $entity->set('id', 'foo');
204    $entity->set('langcode', 'hu');
205    $entity->set('uuid', 'bar');
206    $entity->setOriginalId('foo');
207    $entity->enforceIsNew();
208
209    $this->moduleHandler->invokeAll('test_entity_type_create', [$entity])
210      ->shouldBeCalled();
211    $this->moduleHandler->invokeAll('entity_create', [$entity, 'test_entity_type'])
212      ->shouldBeCalled();
213
214    $this->uuidService->generate()->willReturn('bar');
215
216    $entity = $this->entityStorage->create(['id' => 'foo']);
217    $this->assertInstanceOf(EntityInterface::class, $entity);
218    $this->assertSame('foo', $entity->id());
219    $this->assertSame('bar', $entity->uuid());
220    return $entity;
221  }
222
223  /**
224   * @covers ::create
225   * @covers ::doCreate
226   */
227  public function testCreateWithCurrentLanguage() {
228    $this->languageManager->getLanguage('hu')->willReturn(new Language(['id' => 'hu']));
229
230    $entity = $this->entityStorage->create(['id' => 'foo']);
231    $this->assertSame('hu', $entity->language()->getId());
232  }
233
234  /**
235   * @covers ::create
236   * @covers ::doCreate
237   */
238  public function testCreateWithExplicitLanguage() {
239    $this->languageManager->getLanguage('en')->willReturn(new Language(['id' => 'en']));
240
241    $entity = $this->entityStorage->create(['id' => 'foo', 'langcode' => 'en']);
242    $this->assertSame('en', $entity->language()->getId());
243  }
244
245  /**
246   * @covers ::save
247   * @covers ::doSave
248   *
249   * @param \Drupal\Core\Entity\EntityInterface $entity
250   *
251   * @return \Drupal\Core\Entity\EntityInterface
252   *
253   * @depends testCreate
254   */
255  public function testSaveInsert(EntityInterface $entity) {
256    $immutable_config_object = $this->prophesize(ImmutableConfig::class);
257    $immutable_config_object->isNew()->willReturn(TRUE);
258
259    $config_object = $this->prophesize(Config::class);
260    $config_object
261      ->setData([
262        'id' => 'foo',
263        'uuid' => 'bar',
264        'dependencies' => [],
265        'langcode' => 'hu',
266        'status' => TRUE,
267      ])
268      ->shouldBeCalled();
269    $config_object->save(FALSE)->shouldBeCalled();
270    $config_object->get()->willReturn([]);
271
272    $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
273      ->shouldBeCalled();
274
275    $this->configFactory->get('the_provider.the_config_prefix.foo')
276      ->willReturn($immutable_config_object->reveal());
277    $this->configFactory->getEditable('the_provider.the_config_prefix.foo')
278      ->willReturn($config_object->reveal());
279
280    $this->moduleHandler->invokeAll('test_entity_type_presave', [$entity])
281      ->shouldBeCalled();
282    $this->moduleHandler->invokeAll('entity_presave', [$entity, 'test_entity_type'])
283      ->shouldBeCalled();
284    $this->moduleHandler->invokeAll('test_entity_type_insert', [$entity])
285      ->shouldBeCalled();
286    $this->moduleHandler->invokeAll('entity_insert', [$entity, 'test_entity_type'])
287      ->shouldBeCalled();
288
289    $this->entityQuery->condition('uuid', 'bar')->willReturn($this->entityQuery);
290    $this->entityQuery->execute()->willReturn([]);
291
292    $return = $this->entityStorage->save($entity);
293    $this->assertSame(SAVED_NEW, $return);
294    return $entity;
295  }
296
297  /**
298   * @covers ::save
299   * @covers ::doSave
300   *
301   * @param \Drupal\Core\Entity\EntityInterface $entity
302   *
303   * @return \Drupal\Core\Entity\EntityInterface
304   *
305   * @depends testSaveInsert
306   */
307  public function testSaveUpdate(EntityInterface $entity) {
308    $immutable_config_object = $this->prophesize(ImmutableConfig::class);
309    $immutable_config_object->isNew()->willReturn(FALSE);
310
311    $config_object = $this->prophesize(Config::class);
312    $config_object
313      ->setData([
314        'id' => 'foo',
315        'uuid' => 'bar',
316        'dependencies' => [],
317        'langcode' => 'hu',
318        'status' => TRUE,
319      ])
320      ->shouldBeCalled();
321    $config_object->save(FALSE)->shouldBeCalled();
322    $config_object->get()->willReturn([]);
323
324    $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
325      ->shouldBeCalled();
326
327    $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
328      ->willReturn([])
329      ->shouldBeCalledTimes(2);
330    $this->configFactory
331      ->get('the_provider.the_config_prefix.foo')
332      ->willReturn($immutable_config_object->reveal())
333      ->shouldBeCalledTimes(1);
334    $this->configFactory
335      ->getEditable('the_provider.the_config_prefix.foo')
336      ->willReturn($config_object->reveal())
337      ->shouldBeCalledTimes(1);
338
339    $this->moduleHandler->invokeAll('test_entity_type_presave', [$entity])
340      ->shouldBeCalled();
341    $this->moduleHandler->invokeAll('entity_presave', [$entity, 'test_entity_type'])
342      ->shouldBeCalled();
343    $this->moduleHandler->invokeAll('test_entity_type_update', [$entity])
344      ->shouldBeCalled();
345    $this->moduleHandler->invokeAll('entity_update', [$entity, 'test_entity_type'])
346      ->shouldBeCalled();
347
348    $this->entityQuery->condition('uuid', 'bar')->willReturn($this->entityQuery);
349    $this->entityQuery->execute()->willReturn([$entity->id()]);
350
351    $return = $this->entityStorage->save($entity);
352    $this->assertSame(SAVED_UPDATED, $return);
353    return $entity;
354  }
355
356  /**
357   * @covers ::save
358   * @covers ::doSave
359   *
360   * @depends testSaveInsert
361   */
362  public function testSaveRename(ConfigEntityInterface $entity) {
363    $immutable_config_object = $this->prophesize(ImmutableConfig::class);
364    $immutable_config_object->isNew()->willReturn(FALSE);
365
366    $config_object = $this->prophesize(Config::class);
367    $config_object
368      ->setData([
369        'id' => 'bar',
370        'uuid' => 'bar',
371        'dependencies' => [],
372        'langcode' => 'hu',
373        'status' => TRUE,
374      ])
375      ->shouldBeCalled();
376    $config_object->save(FALSE)
377      ->shouldBeCalled();
378    $config_object->get()->willReturn([]);
379
380    $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
381      ->shouldBeCalled();
382
383    $this->configFactory->get('the_provider.the_config_prefix.foo')
384      ->willReturn($immutable_config_object->reveal());
385    $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
386      ->willReturn([]);
387    $this->configFactory->rename('the_provider.the_config_prefix.foo', 'the_provider.the_config_prefix.bar')
388      ->shouldBeCalled();
389    $this->configFactory->getEditable('the_provider.the_config_prefix.bar')
390      ->willReturn($config_object->reveal());
391
392    // Performing a rename does not change the original ID until saving.
393    $this->assertSame('foo', $entity->getOriginalId());
394    $entity->set('id', 'bar');
395    $this->assertSame('foo', $entity->getOriginalId());
396
397    $this->entityQuery->condition('uuid', 'bar')->willReturn($this->entityQuery);
398    $this->entityQuery->execute()->willReturn([$entity->id()]);
399
400    $return = $this->entityStorage->save($entity);
401    $this->assertSame(SAVED_UPDATED, $return);
402    $this->assertSame('bar', $entity->getOriginalId());
403  }
404
405  /**
406   * @covers ::save
407   */
408  public function testSaveInvalid() {
409    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
410      ->shouldNotBeCalled();
411
412    $entity = $this->getMockEntity();
413    $this->expectException(EntityMalformedException::class);
414    $this->expectExceptionMessage('The entity does not have an ID.');
415    $this->entityStorage->save($entity);
416  }
417
418  /**
419   * @covers ::save
420   * @covers ::doSave
421   */
422  public function testSaveDuplicate() {
423    $config_object = $this->prophesize(ImmutableConfig::class);
424    $config_object->isNew()->willReturn(FALSE);
425
426    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
427      ->shouldNotBeCalled();
428
429    $this->configFactory->get('the_provider.the_config_prefix.foo')
430      ->willReturn($config_object->reveal());
431
432    $entity = $this->getMockEntity(['id' => 'foo']);
433    $entity->enforceIsNew();
434
435    $this->expectException(EntityStorageException::class);
436    $this->entityStorage->save($entity);
437  }
438
439  /**
440   * @covers ::save
441   * @covers ::doSave
442   */
443  public function testSaveMismatch() {
444    $config_object = $this->prophesize(ImmutableConfig::class);
445    $config_object->isNew()->willReturn(TRUE);
446
447    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
448      ->shouldNotBeCalled();
449
450    $this->configFactory->get('the_provider.the_config_prefix.foo')
451      ->willReturn($config_object->reveal());
452
453    $this->entityQuery->condition('uuid', NULL)->willReturn($this->entityQuery);
454    $this->entityQuery->execute()->willReturn(['baz']);
455
456    $entity = $this->getMockEntity(['id' => 'foo']);
457    $this->expectException(ConfigDuplicateUUIDException::class);
458    $this->expectExceptionMessage('when this UUID is already used for');
459    $this->entityStorage->save($entity);
460  }
461
462  /**
463   * @covers ::save
464   * @covers ::doSave
465   */
466  public function testSaveNoMismatch() {
467    $immutable_config_object = $this->prophesize(ImmutableConfig::class);
468    $immutable_config_object->isNew()->willReturn(TRUE);
469
470    $config_object = $this->prophesize(Config::class);
471    $config_object->get()->willReturn([]);
472    $config_object
473      ->setData([
474        'id' => 'foo',
475        'uuid' => NULL,
476        'dependencies' => [],
477        'langcode' => 'en',
478        'status' => TRUE,
479      ])
480      ->shouldBeCalled();
481    $config_object->save(FALSE)->shouldBeCalled();
482
483    $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
484      ->shouldBeCalled();
485
486    $this->configFactory->get('the_provider.the_config_prefix.baz')
487      ->willReturn($immutable_config_object->reveal())
488      ->shouldBeCalled();
489    $this->configFactory->rename('the_provider.the_config_prefix.baz', 'the_provider.the_config_prefix.foo')
490      ->shouldBeCalled();
491    $this->configFactory->getEditable('the_provider.the_config_prefix.foo')
492      ->willReturn($config_object->reveal())
493      ->shouldBeCalled();
494
495    $this->entityQuery->condition('uuid', NULL)->willReturn($this->entityQuery);
496    $this->entityQuery->execute()->willReturn(['baz']);
497
498    $entity = $this->getMockEntity(['id' => 'foo']);
499    $entity->setOriginalId('baz');
500    $entity->enforceIsNew();
501    $this->entityStorage->save($entity);
502  }
503
504  /**
505   * @covers ::save
506   * @covers ::doSave
507   */
508  public function testSaveChangedUuid() {
509    $config_object = $this->prophesize(ImmutableConfig::class);
510    $config_object->get()->willReturn(['id' => 'foo']);
511    $config_object->get('id')->willReturn('foo');
512    $config_object->isNew()->willReturn(FALSE);
513    $config_object->getName()->willReturn('foo');
514    $config_object->getCacheContexts()->willReturn([]);
515    $config_object->getCacheTags()->willReturn(['config:foo']);
516    $config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
517
518    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
519      ->shouldNotBeCalled();
520
521    $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
522      ->willReturn([$config_object->reveal()]);
523    $this->configFactory->get('the_provider.the_config_prefix.foo')
524      ->willReturn($config_object->reveal());
525    $this->configFactory->rename(Argument::cetera())->shouldNotBeCalled();
526
527    $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
528    $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
529
530    $this->entityQuery->condition('uuid', 'baz')->willReturn($this->entityQuery);
531    $this->entityQuery->execute()->willReturn(['foo']);
532
533    $entity = $this->getMockEntity(['id' => 'foo']);
534
535    $entity->set('uuid', 'baz');
536    $this->expectException(ConfigDuplicateUUIDException::class);
537    $this->expectExceptionMessage('when this entity already exists with UUID');
538    $this->entityStorage->save($entity);
539  }
540
541  /**
542   * @covers ::load
543   * @covers ::postLoad
544   * @covers ::mapFromStorageRecords
545   * @covers ::doLoadMultiple
546   */
547  public function testLoad() {
548    $config_object = $this->prophesize(ImmutableConfig::class);
549    $config_object->get()->willReturn(['id' => 'foo']);
550    $config_object->get('id')->willReturn('foo');
551    $config_object->getCacheContexts()->willReturn([]);
552    $config_object->getCacheTags()->willReturn(['config:foo']);
553    $config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
554    $config_object->getName()->willReturn('foo');
555
556    $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
557      ->willReturn([$config_object->reveal()]);
558
559    $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
560    $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
561
562    $entity = $this->entityStorage->load('foo');
563    $this->assertInstanceOf(EntityInterface::class, $entity);
564    $this->assertSame('foo', $entity->id());
565
566    $this->expectException(\AssertionError::class);
567    $this->expectExceptionMessage(sprintf('Cannot load the "%s" entity with NULL ID.', $this->entityTypeId));
568    $this->entityStorage->load(NULL);
569  }
570
571  /**
572   * @covers ::loadMultiple
573   * @covers ::postLoad
574   * @covers ::mapFromStorageRecords
575   * @covers ::doLoadMultiple
576   */
577  public function testLoadMultipleAll() {
578    $foo_config_object = $this->prophesize(ImmutableConfig::class);
579    $foo_config_object->get()->willReturn(['id' => 'foo']);
580    $foo_config_object->get('id')->willReturn('foo');
581    $foo_config_object->getCacheContexts()->willReturn([]);
582    $foo_config_object->getCacheTags()->willReturn(['config:foo']);
583    $foo_config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
584    $foo_config_object->getName()->willReturn('foo');
585
586    $bar_config_object = $this->prophesize(ImmutableConfig::class);
587    $bar_config_object->get()->willReturn(['id' => 'bar']);
588    $bar_config_object->get('id')->willReturn('bar');
589    $bar_config_object->getCacheContexts()->willReturn([]);
590    $bar_config_object->getCacheTags()->willReturn(['config:bar']);
591    $bar_config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
592    $bar_config_object->getName()->willReturn('foo');
593
594    $this->configFactory->listAll('the_provider.the_config_prefix.')
595      ->willReturn(['the_provider.the_config_prefix.foo', 'the_provider.the_config_prefix.bar']);
596    $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo', 'the_provider.the_config_prefix.bar'])
597      ->willReturn([$foo_config_object->reveal(), $bar_config_object->reveal()]);
598
599    $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
600    $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
601
602    $entities = $this->entityStorage->loadMultiple();
603    $expected['foo'] = 'foo';
604    $expected['bar'] = 'bar';
605    $this->assertContainsOnlyInstancesOf(EntityInterface::class, $entities);
606    foreach ($entities as $id => $entity) {
607      $this->assertSame($id, $entity->id());
608      $this->assertSame($expected[$id], $entity->id());
609    }
610  }
611
612  /**
613   * @covers ::loadMultiple
614   * @covers ::postLoad
615   * @covers ::mapFromStorageRecords
616   * @covers ::doLoadMultiple
617   */
618  public function testLoadMultipleIds() {
619    $config_object = $this->prophesize(ImmutableConfig::class);
620    $config_object->get()->willReturn(['id' => 'foo']);
621    $config_object->get('id')->willReturn('foo');
622    $config_object->getCacheContexts()->willReturn([]);
623    $config_object->getCacheTags()->willReturn(['config:foo']);
624    $config_object->getCacheMaxAge()->willReturn(Cache::PERMANENT);
625    $config_object->getName()->willReturn('foo');
626
627    $this->configFactory->loadMultiple(['the_provider.the_config_prefix.foo'])
628      ->willReturn([$config_object->reveal()]);
629
630    $this->moduleHandler->getImplementations('entity_load')->willReturn([]);
631    $this->moduleHandler->getImplementations('test_entity_type_load')->willReturn([]);
632
633    $entities = $this->entityStorage->loadMultiple(['foo']);
634    $this->assertContainsOnlyInstancesOf(EntityInterface::class, $entities);
635    foreach ($entities as $id => $entity) {
636      $this->assertSame($id, $entity->id());
637    }
638  }
639
640  /**
641   * @covers ::loadRevision
642   */
643  public function testLoadRevision() {
644    $this->assertSame(NULL, $this->entityStorage->loadRevision(1));
645  }
646
647  /**
648   * @covers ::deleteRevision
649   */
650  public function testDeleteRevision() {
651    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())
652      ->shouldNotBeCalled();
653
654    $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
655  }
656
657  /**
658   * @covers ::delete
659   * @covers ::doDelete
660   */
661  public function testDelete() {
662    // Dependencies are tested in
663    // \Drupal\Tests\config\Kernel\ConfigDependencyTest.
664    $this->configManager
665      ->getConfigEntitiesToChangeOnDependencyRemoval('config', ['the_provider.the_config_prefix.foo'], FALSE)
666      ->willReturn(['update' => [], 'delete' => [], 'unchanged' => []]);
667    $this->configManager
668      ->getConfigEntitiesToChangeOnDependencyRemoval('config', ['the_provider.the_config_prefix.bar'], FALSE)
669      ->willReturn(['update' => [], 'delete' => [], 'unchanged' => []]);
670
671    $entities = [];
672    foreach (['foo', 'bar'] as $id) {
673      $entity = $this->getMockEntity(['id' => $id]);
674      $entities[] = $entity;
675
676      $config_object = $this->prophesize(Config::class);
677      $config_object->delete()->shouldBeCalled();
678
679      $this->configFactory->getEditable("the_provider.the_config_prefix.$id")
680        ->willReturn($config_object->reveal());
681
682      $this->moduleHandler->invokeAll('test_entity_type_predelete', [$entity])
683        ->shouldBeCalled();
684      $this->moduleHandler->invokeAll('entity_predelete', [$entity, 'test_entity_type'])
685        ->shouldBeCalled();
686
687      $this->moduleHandler->invokeAll('test_entity_type_delete', [$entity])
688        ->shouldBeCalled();
689      $this->moduleHandler->invokeAll('entity_delete', [$entity, 'test_entity_type'])
690        ->shouldBeCalled();
691    }
692
693    $this->cacheTagsInvalidator->invalidateTags([$this->entityTypeId . '_list'])
694      ->shouldBeCalled();
695
696    $this->entityStorage->delete($entities);
697  }
698
699  /**
700   * @covers ::delete
701   * @covers ::doDelete
702   */
703  public function testDeleteNothing() {
704    $this->moduleHandler->getImplementations(Argument::cetera())->shouldNotBeCalled();
705    $this->moduleHandler->invokeAll(Argument::cetera())->shouldNotBeCalled();
706
707    $this->configFactory->get(Argument::cetera())->shouldNotBeCalled();
708    $this->configFactory->getEditable(Argument::cetera())->shouldNotBeCalled();
709
710    $this->cacheTagsInvalidator->invalidateTags(Argument::cetera())->shouldNotBeCalled();
711
712    $this->entityStorage->delete([]);
713  }
714
715  /**
716   * Creates an entity with specific methods mocked.
717   *
718   * @param array $values
719   *   (optional) Values to pass to the constructor.
720   * @param array $methods
721   *   (optional) The methods to mock.
722   *
723   * @return \Drupal\Core\Entity\EntityInterface|\PHPUnit\Framework\MockObject\MockObject
724   */
725  public function getMockEntity(array $values = [], $methods = []) {
726    return $this->getMockForAbstractClass(ConfigEntityBase::class, [$values, 'test_entity_type'], '', TRUE, TRUE, TRUE, $methods);
727  }
728
729}
730
731namespace Drupal\Core\Config\Entity;
732
733if (!defined('SAVED_NEW')) {
734  define('SAVED_NEW', 1);
735}
736if (!defined('SAVED_UPDATED')) {
737  define('SAVED_UPDATED', 2);
738}
739