3 namespace Drupal\Tests\Core\Entity\KeyValueStore;
5 use Drupal\Core\Config\Entity\ConfigEntityInterface;
6 use Drupal\Core\DependencyInjection\ContainerBuilder;
7 use Drupal\Core\Entity\EntityFieldManagerInterface;
8 use Drupal\Core\Entity\EntityInterface;
9 use Drupal\Core\Entity\EntityMalformedException;
10 use Drupal\Core\Entity\EntityManager;
11 use Drupal\Core\Entity\EntityStorageException;
12 use Drupal\Core\Entity\EntityTypeManagerInterface;
13 use Drupal\Core\Language\Language;
14 use Drupal\Tests\UnitTestCase;
15 use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage;
18 * @coversDefaultClass \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
21 class KeyValueEntityStorageTest extends UnitTestCase {
26 * @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
28 protected $entityType;
31 * The key value store.
33 * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface|\PHPUnit_Framework_MockObject_MockObject
35 protected $keyValueStore;
40 * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
42 protected $moduleHandler;
47 * @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
49 protected $uuidService;
52 * The language manager.
54 * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
56 protected $languageManager;
59 * @var \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
61 protected $entityStorage;
66 * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
68 protected $entityManager;
71 * The mocked entity type manager.
73 * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
75 protected $entityTypeManager;
78 * The mocked entity field manager.
80 * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject
82 protected $entityFieldManager;
85 * The mocked cache tags invalidator.
87 * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
89 protected $cacheTagsInvalidator;
94 protected function setUp() {
96 $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
100 * Prepares the key value entity storage.
102 * @covers ::__construct
104 * @param string $uuid_key
105 * (optional) The entity key used for the UUID. Defaults to 'uuid'.
107 protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') {
108 $this->entityType->expects($this->atLeastOnce())
110 ->will($this->returnValueMap([
113 ['langcode', 'langcode'],
115 $this->entityType->expects($this->atLeastOnce())
117 ->will($this->returnValue('test_entity_type'));
118 $this->entityType->expects($this->any())
119 ->method('getListCacheTags')
120 ->willReturn(['test_entity_type_list']);
122 $this->entityManager = new EntityManager();
124 $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
125 $this->entityTypeManager->expects($this->any())
126 ->method('getDefinition')
127 ->with('test_entity_type')
128 ->will($this->returnValue($this->entityType));
130 $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class);
132 $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
134 $this->keyValueStore = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
135 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
136 $this->uuidService = $this->getMock('Drupal\Component\Uuid\UuidInterface');
137 $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
138 $language = new Language(['langcode' => 'en']);
139 $this->languageManager->expects($this->any())
140 ->method('getDefaultLanguage')
141 ->will($this->returnValue($language));
142 $this->languageManager->expects($this->any())
143 ->method('getCurrentLanguage')
144 ->will($this->returnValue($language));
146 $this->entityStorage = new KeyValueEntityStorage($this->entityType, $this->keyValueStore, $this->uuidService, $this->languageManager);
147 $this->entityStorage->setModuleHandler($this->moduleHandler);
149 $container = new ContainerBuilder();
150 $container->set('entity.manager', $this->entityManager);
151 $container->set('entity_field.manager', $this->entityFieldManager);
152 $container->set('entity_type.manager', $this->entityTypeManager);
153 $container->set('language_manager', $this->languageManager);
154 $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
155 // Inject the container into entity.manager so it can defer to
156 // entity_type.manager and other services.
157 $this->entityManager->setContainer($container);
158 \Drupal::setContainer($container);
165 public function testCreateWithPredefinedUuid() {
166 $this->entityType->expects($this->once())
168 ->will($this->returnValue(get_class($this->getMockEntity())));
169 $this->setUpKeyValueEntityStorage();
171 $this->moduleHandler->expects($this->at(0))
172 ->method('invokeAll')
173 ->with('test_entity_type_create');
174 $this->moduleHandler->expects($this->at(1))
175 ->method('invokeAll')
176 ->with('entity_create');
177 $this->uuidService->expects($this->never())
178 ->method('generate');
180 $entity = $this->entityStorage->create(['id' => 'foo', 'uuid' => 'baz']);
181 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
182 $this->assertSame('foo', $entity->id());
183 $this->assertSame('baz', $entity->uuid());
190 public function testCreateWithoutUuidKey() {
191 // Set up the entity storage to expect no UUID key.
192 $this->entityType->expects($this->once())
194 ->will($this->returnValue(get_class($this->getMockEntity())));
195 $this->setUpKeyValueEntityStorage(NULL);
197 $this->moduleHandler->expects($this->at(0))
198 ->method('invokeAll')
199 ->with('test_entity_type_create');
200 $this->moduleHandler->expects($this->at(1))
201 ->method('invokeAll')
202 ->with('entity_create');
203 $this->uuidService->expects($this->never())
204 ->method('generate');
206 $entity = $this->entityStorage->create(['id' => 'foo', 'uuid' => 'baz']);
207 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
208 $this->assertSame('foo', $entity->id());
209 $this->assertSame('baz', $entity->uuid());
216 * @return \Drupal\Core\Entity\EntityInterface
218 public function testCreate() {
219 $entity = $this->getMockEntity('Drupal\Core\Entity\Entity', [], ['toArray']);
220 $this->entityType->expects($this->once())
222 ->will($this->returnValue(get_class($entity)));
223 $this->setUpKeyValueEntityStorage();
225 $this->moduleHandler->expects($this->at(0))
226 ->method('invokeAll')
227 ->with('test_entity_type_create');
228 $this->moduleHandler->expects($this->at(1))
229 ->method('invokeAll')
230 ->with('entity_create');
231 $this->uuidService->expects($this->once())
233 ->will($this->returnValue('bar'));
235 $entity = $this->entityStorage->create(['id' => 'foo']);
236 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
237 $this->assertSame('foo', $entity->id());
238 $this->assertSame('bar', $entity->uuid());
246 * @param \Drupal\Core\Entity\EntityInterface $entity
248 * @return \Drupal\Core\Entity\EntityInterface
250 * @depends testCreate
252 public function testSaveInsert(EntityInterface $entity) {
253 $this->entityType->expects($this->once())
255 ->will($this->returnValue(get_class($entity)));
256 $this->setUpKeyValueEntityStorage();
258 $expected = ['id' => 'foo'];
259 $this->keyValueStore->expects($this->exactly(2))
262 ->will($this->returnValue(FALSE));
263 $this->keyValueStore->expects($this->never())
264 ->method('getMultiple');
265 $this->keyValueStore->expects($this->never())
268 $entity->expects($this->atLeastOnce())
270 ->will($this->returnValue($expected));
272 $this->moduleHandler->expects($this->at(0))
273 ->method('invokeAll')
274 ->with('test_entity_type_presave');
275 $this->moduleHandler->expects($this->at(1))
276 ->method('invokeAll')
277 ->with('entity_presave');
278 $this->moduleHandler->expects($this->at(2))
279 ->method('invokeAll')
280 ->with('test_entity_type_insert');
281 $this->moduleHandler->expects($this->at(3))
282 ->method('invokeAll')
283 ->with('entity_insert');
284 $this->keyValueStore->expects($this->once())
286 ->with('foo', $expected);
287 $return = $this->entityStorage->save($entity);
288 $this->assertSame(SAVED_NEW, $return);
296 * @param \Drupal\Core\Entity\EntityInterface $entity
298 * @return \Drupal\Core\Entity\EntityInterface
300 * @depends testSaveInsert
302 public function testSaveUpdate(EntityInterface $entity) {
303 $this->entityType->expects($this->once())
305 ->will($this->returnValue(get_class($entity)));
306 $this->setUpKeyValueEntityStorage();
308 $expected = ['id' => 'foo'];
309 $this->keyValueStore->expects($this->exactly(2))
312 ->will($this->returnValue(TRUE));
313 $this->keyValueStore->expects($this->once())
314 ->method('getMultiple')
316 ->will($this->returnValue([['id' => 'foo']]));
317 $this->keyValueStore->expects($this->never())
320 $this->moduleHandler->expects($this->at(0))
321 ->method('getImplementations')
322 ->with('entity_load')
323 ->will($this->returnValue([]));
324 $this->moduleHandler->expects($this->at(1))
325 ->method('getImplementations')
326 ->with('test_entity_type_load')
327 ->will($this->returnValue([]));
328 $this->moduleHandler->expects($this->at(2))
329 ->method('invokeAll')
330 ->with('test_entity_type_presave');
331 $this->moduleHandler->expects($this->at(3))
332 ->method('invokeAll')
333 ->with('entity_presave');
334 $this->moduleHandler->expects($this->at(4))
335 ->method('invokeAll')
336 ->with('test_entity_type_update');
337 $this->moduleHandler->expects($this->at(5))
338 ->method('invokeAll')
339 ->with('entity_update');
340 $this->keyValueStore->expects($this->once())
342 ->with('foo', $expected);
343 $return = $this->entityStorage->save($entity);
344 $this->assertSame(SAVED_UPDATED, $return);
352 public function testSaveConfigEntity() {
353 $this->setUpKeyValueEntityStorage();
355 $entity = $this->getMockEntity('Drupal\Core\Config\Entity\ConfigEntityBase', [['id' => 'foo']], [
359 $entity->enforceIsNew();
360 // When creating a new entity, the ID is tracked as the original ID.
361 $this->assertSame('foo', $entity->getOriginalId());
363 $expected = ['id' => 'foo'];
364 $entity->expects($this->atLeastOnce())
366 ->will($this->returnValue($expected));
368 $this->keyValueStore->expects($this->exactly(2))
371 ->will($this->returnValue(FALSE));
372 $this->keyValueStore->expects($this->once())
374 ->with('foo', $expected);
375 $this->keyValueStore->expects($this->never())
378 $return = $this->entityStorage->save($entity);
379 $this->assertSame(SAVED_NEW, $return);
387 * @depends testSaveConfigEntity
389 public function testSaveRenameConfigEntity(ConfigEntityInterface $entity) {
390 $this->entityType->expects($this->once())
392 ->will($this->returnValue(get_class($entity)));
393 $this->setUpKeyValueEntityStorage();
395 $this->moduleHandler->expects($this->at(0))
396 ->method('getImplementations')
397 ->with('entity_load')
398 ->will($this->returnValue([]));
399 $this->moduleHandler->expects($this->at(1))
400 ->method('getImplementations')
401 ->with('test_entity_type_load')
402 ->will($this->returnValue([]));
403 $expected = ['id' => 'foo'];
404 $entity->expects($this->once())
406 ->will($this->returnValue($expected));
407 $this->keyValueStore->expects($this->exactly(2))
410 ->will($this->returnValue(TRUE));
411 $this->keyValueStore->expects($this->once())
412 ->method('getMultiple')
414 ->will($this->returnValue([['id' => 'foo']]));
415 $this->keyValueStore->expects($this->once())
418 $this->keyValueStore->expects($this->once())
420 ->with('bar', $expected);
422 // Performing a rename does not change the original ID until saving.
423 $this->assertSame('foo', $entity->getOriginalId());
424 $entity->set('id', 'bar');
425 $this->assertSame('foo', $entity->getOriginalId());
427 $return = $this->entityStorage->save($entity);
428 $this->assertSame(SAVED_UPDATED, $return);
429 $this->assertSame('bar', $entity->getOriginalId());
436 public function testSaveContentEntity() {
437 $this->entityType->expects($this->any())
439 ->will($this->returnValue([
442 $this->setUpKeyValueEntityStorage();
444 $expected = ['id' => 'foo'];
445 $this->keyValueStore->expects($this->exactly(2))
448 ->will($this->returnValue(FALSE));
449 $this->keyValueStore->expects($this->once())
451 ->with('foo', $expected);
452 $this->keyValueStore->expects($this->never())
454 $entity = $this->getMockEntity('Drupal\Core\Entity\ContentEntityBase', [], [
458 $entity->expects($this->atLeastOnce())
460 ->will($this->returnValue('foo'));
461 $entity->expects($this->once())
463 ->will($this->returnValue($expected));
464 $this->entityStorage->save($entity);
471 public function testSaveInvalid() {
472 $this->setUpKeyValueEntityStorage();
474 $entity = $this->getMockEntity('Drupal\Core\Config\Entity\ConfigEntityBase');
475 $this->keyValueStore->expects($this->never())
477 $this->keyValueStore->expects($this->never())
479 $this->keyValueStore->expects($this->never())
481 $this->setExpectedException(EntityMalformedException::class, 'The entity does not have an ID.');
482 $this->entityStorage->save($entity);
489 public function testSaveDuplicate() {
490 $this->setUpKeyValueEntityStorage();
492 $entity = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
493 $entity->enforceIsNew();
494 $this->keyValueStore->expects($this->once())
496 ->will($this->returnValue(TRUE));
497 $this->keyValueStore->expects($this->never())
499 $this->keyValueStore->expects($this->never())
501 $this->setExpectedException(EntityStorageException::class, "'test_entity_type' entity with ID 'foo' already exists");
502 $this->entityStorage->save($entity);
509 public function testLoad() {
510 $entity = $this->getMockEntity();
511 $this->entityType->expects($this->once())
513 ->will($this->returnValue(get_class($entity)));
514 $this->setUpKeyValueEntityStorage();
516 $this->keyValueStore->expects($this->once())
517 ->method('getMultiple')
519 ->will($this->returnValue([['id' => 'foo']]));
520 $this->moduleHandler->expects($this->at(0))
521 ->method('getImplementations')
522 ->with('entity_load')
523 ->will($this->returnValue([]));
524 $this->moduleHandler->expects($this->at(1))
525 ->method('getImplementations')
526 ->with('test_entity_type_load')
527 ->will($this->returnValue([]));
528 $entity = $this->entityStorage->load('foo');
529 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
530 $this->assertSame('foo', $entity->id());
536 public function testLoadMissingEntity() {
537 $this->entityType->expects($this->once())
538 ->method('getClass');
539 $this->setUpKeyValueEntityStorage();
541 $this->keyValueStore->expects($this->once())
542 ->method('getMultiple')
544 ->will($this->returnValue([]));
545 $this->moduleHandler->expects($this->never())
546 ->method('getImplementations');
547 $entity = $this->entityStorage->load('foo');
548 $this->assertNull($entity);
552 * @covers ::loadMultiple
554 * @covers ::mapFromStorageRecords
555 * @covers ::doLoadMultiple
557 public function testLoadMultipleAll() {
558 $expected['foo'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
559 $expected['bar'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'bar']]);
560 $this->entityType->expects($this->once())
562 ->will($this->returnValue(get_class(reset($expected))));
563 $this->setUpKeyValueEntityStorage();
565 $this->keyValueStore->expects($this->once())
567 ->will($this->returnValue([['id' => 'foo'], ['id' => 'bar']]));
568 $this->moduleHandler->expects($this->at(0))
569 ->method('getImplementations')
570 ->with('entity_load')
571 ->will($this->returnValue([]));
572 $this->moduleHandler->expects($this->at(1))
573 ->method('getImplementations')
574 ->with('test_entity_type_load')
575 ->will($this->returnValue([]));
576 $entities = $this->entityStorage->loadMultiple();
577 foreach ($entities as $id => $entity) {
578 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
579 $this->assertSame($id, $entity->id());
580 $this->assertSame($id, $expected[$id]->id());
585 * @covers ::loadMultiple
587 * @covers ::mapFromStorageRecords
588 * @covers ::doLoadMultiple
590 public function testLoadMultipleIds() {
591 $entity = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
592 $this->entityType->expects($this->once())
594 ->will($this->returnValue(get_class($entity)));
595 $this->setUpKeyValueEntityStorage();
597 $expected[] = $entity;
598 $this->keyValueStore->expects($this->once())
599 ->method('getMultiple')
601 ->will($this->returnValue([['id' => 'foo']]));
602 $this->moduleHandler->expects($this->at(0))
603 ->method('getImplementations')
604 ->with('entity_load')
605 ->will($this->returnValue([]));
606 $this->moduleHandler->expects($this->at(1))
607 ->method('getImplementations')
608 ->with('test_entity_type_load')
609 ->will($this->returnValue([]));
610 $entities = $this->entityStorage->loadMultiple(['foo']);
611 foreach ($entities as $id => $entity) {
612 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
613 $this->assertSame($id, $entity->id());
618 * @covers ::loadRevision
620 public function testLoadRevision() {
621 $this->setUpKeyValueEntityStorage();
623 $this->assertSame(NULL, $this->entityStorage->loadRevision(1));
627 * @covers ::deleteRevision
629 public function testDeleteRevision() {
630 $this->setUpKeyValueEntityStorage();
632 $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
639 public function testDelete() {
640 $entities['foo'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
641 $entities['bar'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'bar']]);
642 $this->entityType->expects($this->once())
644 ->will($this->returnValue(get_class(reset($entities))));
645 $this->setUpKeyValueEntityStorage();
647 $this->moduleHandler->expects($this->at(0))
648 ->method('invokeAll')
649 ->with('test_entity_type_predelete');
650 $this->moduleHandler->expects($this->at(1))
651 ->method('invokeAll')
652 ->with('entity_predelete');
653 $this->moduleHandler->expects($this->at(2))
654 ->method('invokeAll')
655 ->with('test_entity_type_predelete');
656 $this->moduleHandler->expects($this->at(3))
657 ->method('invokeAll')
658 ->with('entity_predelete');
659 $this->moduleHandler->expects($this->at(4))
660 ->method('invokeAll')
661 ->with('test_entity_type_delete');
662 $this->moduleHandler->expects($this->at(5))
663 ->method('invokeAll')
664 ->with('entity_delete');
665 $this->moduleHandler->expects($this->at(6))
666 ->method('invokeAll')
667 ->with('test_entity_type_delete');
668 $this->moduleHandler->expects($this->at(7))
669 ->method('invokeAll')
670 ->with('entity_delete');
672 $this->keyValueStore->expects($this->once())
673 ->method('deleteMultiple')
674 ->with(['foo', 'bar']);
675 $this->entityStorage->delete($entities);
682 public function testDeleteNothing() {
683 $this->setUpKeyValueEntityStorage();
685 $this->moduleHandler->expects($this->never())
686 ->method($this->anything());
687 $this->keyValueStore->expects($this->never())
689 $this->keyValueStore->expects($this->never())
690 ->method('deleteMultiple');
692 $this->entityStorage->delete([]);
696 * Creates an entity with specific methods mocked.
698 * @param string $class
699 * (optional) The concrete entity class to mock. Defaults to
700 * 'Drupal\Core\Entity\Entity'.
701 * @param array $arguments
702 * (optional) Arguments to pass to the constructor. An empty set of values
703 * and an entity type ID will be provided.
704 * @param array $methods
705 * (optional) The methods to mock.
707 * @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
709 public function getMockEntity($class = 'Drupal\Core\Entity\Entity', array $arguments = [], $methods = []) {
710 // Ensure the entity is passed at least an array of values and an entity
712 if (!isset($arguments[0])) {
715 if (!isset($arguments[1])) {
716 $arguments[1] = 'test_entity_type';
718 return $this->getMockForAbstractClass($class, $arguments, '', TRUE, TRUE, TRUE, $methods);
723 namespace Drupal\Core\Entity\KeyValueStore;
725 if (!defined('SAVED_NEW')) {
726 define('SAVED_NEW', 1);
728 if (!defined('SAVED_UPDATED')) {
729 define('SAVED_UPDATED', 2);