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\EntityInterface;
8 use Drupal\Core\Entity\EntityMalformedException;
9 use Drupal\Core\Entity\EntityStorageException;
10 use Drupal\Core\Language\Language;
11 use Drupal\Tests\UnitTestCase;
12 use Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage;
15 * @coversDefaultClass \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
18 class KeyValueEntityStorageTest extends UnitTestCase {
23 * @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
25 protected $entityType;
28 * The key value store.
30 * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface|\PHPUnit_Framework_MockObject_MockObject
32 protected $keyValueStore;
37 * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
39 protected $moduleHandler;
44 * @var \Drupal\Component\Uuid\UuidInterface|\PHPUnit_Framework_MockObject_MockObject
46 protected $uuidService;
49 * The language manager.
51 * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
53 protected $languageManager;
56 * @var \Drupal\Core\Entity\KeyValueStore\KeyValueEntityStorage
58 protected $entityStorage;
61 * The mocked entity manager.
63 * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
65 protected $entityManager;
68 * The mocked cache tags invalidator.
70 * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
72 protected $cacheTagsInvalidator;
77 protected function setUp() {
79 $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
83 * Prepares the key value entity storage.
85 * @covers ::__construct
87 * @param string $uuid_key
88 * (optional) The entity key used for the UUID. Defaults to 'uuid'.
90 protected function setUpKeyValueEntityStorage($uuid_key = 'uuid') {
91 $this->entityType->expects($this->atLeastOnce())
93 ->will($this->returnValueMap([
96 ['langcode', 'langcode'],
98 $this->entityType->expects($this->atLeastOnce())
100 ->will($this->returnValue('test_entity_type'));
101 $this->entityType->expects($this->any())
102 ->method('getListCacheTags')
103 ->willReturn(['test_entity_type_list']);
105 $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
106 $this->entityManager->expects($this->any())
107 ->method('getDefinition')
108 ->with('test_entity_type')
109 ->will($this->returnValue($this->entityType));
111 $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
113 $this->keyValueStore = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
114 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
115 $this->uuidService = $this->getMock('Drupal\Component\Uuid\UuidInterface');
116 $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
117 $language = new Language(['langcode' => 'en']);
118 $this->languageManager->expects($this->any())
119 ->method('getDefaultLanguage')
120 ->will($this->returnValue($language));
121 $this->languageManager->expects($this->any())
122 ->method('getCurrentLanguage')
123 ->will($this->returnValue($language));
125 $this->entityStorage = new KeyValueEntityStorage($this->entityType, $this->keyValueStore, $this->uuidService, $this->languageManager);
126 $this->entityStorage->setModuleHandler($this->moduleHandler);
128 $container = new ContainerBuilder();
129 $container->set('entity.manager', $this->entityManager);
130 $container->set('language_manager', $this->languageManager);
131 $container->set('cache_tags.invalidator', $this->cacheTagsInvalidator);
132 \Drupal::setContainer($container);
139 public function testCreateWithPredefinedUuid() {
140 $this->entityType->expects($this->once())
142 ->will($this->returnValue(get_class($this->getMockEntity())));
143 $this->setUpKeyValueEntityStorage();
145 $this->moduleHandler->expects($this->at(0))
146 ->method('invokeAll')
147 ->with('test_entity_type_create');
148 $this->moduleHandler->expects($this->at(1))
149 ->method('invokeAll')
150 ->with('entity_create');
151 $this->uuidService->expects($this->never())
152 ->method('generate');
154 $entity = $this->entityStorage->create(['id' => 'foo', 'uuid' => 'baz']);
155 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
156 $this->assertSame('foo', $entity->id());
157 $this->assertSame('baz', $entity->uuid());
164 public function testCreateWithoutUuidKey() {
165 // Set up the entity storage to expect no UUID key.
166 $this->entityType->expects($this->once())
168 ->will($this->returnValue(get_class($this->getMockEntity())));
169 $this->setUpKeyValueEntityStorage(NULL);
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 * @return \Drupal\Core\Entity\EntityInterface
192 public function testCreate() {
193 $entity = $this->getMockEntity('Drupal\Core\Entity\Entity', [], ['toArray']);
194 $this->entityType->expects($this->once())
196 ->will($this->returnValue(get_class($entity)));
197 $this->setUpKeyValueEntityStorage();
199 $this->moduleHandler->expects($this->at(0))
200 ->method('invokeAll')
201 ->with('test_entity_type_create');
202 $this->moduleHandler->expects($this->at(1))
203 ->method('invokeAll')
204 ->with('entity_create');
205 $this->uuidService->expects($this->once())
207 ->will($this->returnValue('bar'));
209 $entity = $this->entityStorage->create(['id' => 'foo']);
210 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
211 $this->assertSame('foo', $entity->id());
212 $this->assertSame('bar', $entity->uuid());
220 * @param \Drupal\Core\Entity\EntityInterface $entity
222 * @return \Drupal\Core\Entity\EntityInterface
224 * @depends testCreate
226 public function testSaveInsert(EntityInterface $entity) {
227 $this->entityType->expects($this->once())
229 ->will($this->returnValue(get_class($entity)));
230 $this->setUpKeyValueEntityStorage();
232 $expected = ['id' => 'foo'];
233 $this->keyValueStore->expects($this->exactly(2))
236 ->will($this->returnValue(FALSE));
237 $this->keyValueStore->expects($this->never())
238 ->method('getMultiple');
239 $this->keyValueStore->expects($this->never())
242 $entity->expects($this->atLeastOnce())
244 ->will($this->returnValue($expected));
246 $this->moduleHandler->expects($this->at(0))
247 ->method('invokeAll')
248 ->with('test_entity_type_presave');
249 $this->moduleHandler->expects($this->at(1))
250 ->method('invokeAll')
251 ->with('entity_presave');
252 $this->moduleHandler->expects($this->at(2))
253 ->method('invokeAll')
254 ->with('test_entity_type_insert');
255 $this->moduleHandler->expects($this->at(3))
256 ->method('invokeAll')
257 ->with('entity_insert');
258 $this->keyValueStore->expects($this->once())
260 ->with('foo', $expected);
261 $return = $this->entityStorage->save($entity);
262 $this->assertSame(SAVED_NEW, $return);
270 * @param \Drupal\Core\Entity\EntityInterface $entity
272 * @return \Drupal\Core\Entity\EntityInterface
274 * @depends testSaveInsert
276 public function testSaveUpdate(EntityInterface $entity) {
277 $this->entityType->expects($this->once())
279 ->will($this->returnValue(get_class($entity)));
280 $this->setUpKeyValueEntityStorage();
282 $expected = ['id' => 'foo'];
283 $this->keyValueStore->expects($this->exactly(2))
286 ->will($this->returnValue(TRUE));
287 $this->keyValueStore->expects($this->once())
288 ->method('getMultiple')
290 ->will($this->returnValue([['id' => 'foo']]));
291 $this->keyValueStore->expects($this->never())
294 $this->moduleHandler->expects($this->at(0))
295 ->method('getImplementations')
296 ->with('entity_load')
297 ->will($this->returnValue([]));
298 $this->moduleHandler->expects($this->at(1))
299 ->method('getImplementations')
300 ->with('test_entity_type_load')
301 ->will($this->returnValue([]));
302 $this->moduleHandler->expects($this->at(2))
303 ->method('invokeAll')
304 ->with('test_entity_type_presave');
305 $this->moduleHandler->expects($this->at(3))
306 ->method('invokeAll')
307 ->with('entity_presave');
308 $this->moduleHandler->expects($this->at(4))
309 ->method('invokeAll')
310 ->with('test_entity_type_update');
311 $this->moduleHandler->expects($this->at(5))
312 ->method('invokeAll')
313 ->with('entity_update');
314 $this->keyValueStore->expects($this->once())
316 ->with('foo', $expected);
317 $return = $this->entityStorage->save($entity);
318 $this->assertSame(SAVED_UPDATED, $return);
326 public function testSaveConfigEntity() {
327 $this->setUpKeyValueEntityStorage();
329 $entity = $this->getMockEntity('Drupal\Core\Config\Entity\ConfigEntityBase', [['id' => 'foo']], [
333 $entity->enforceIsNew();
334 // When creating a new entity, the ID is tracked as the original ID.
335 $this->assertSame('foo', $entity->getOriginalId());
337 $expected = ['id' => 'foo'];
338 $entity->expects($this->once())
340 ->will($this->returnValue($expected));
342 $this->keyValueStore->expects($this->exactly(2))
345 ->will($this->returnValue(FALSE));
346 $this->keyValueStore->expects($this->once())
348 ->with('foo', $expected);
349 $this->keyValueStore->expects($this->never())
352 $return = $this->entityStorage->save($entity);
353 $this->assertSame(SAVED_NEW, $return);
361 * @depends testSaveConfigEntity
363 public function testSaveRenameConfigEntity(ConfigEntityInterface $entity) {
364 $this->entityType->expects($this->once())
366 ->will($this->returnValue(get_class($entity)));
367 $this->setUpKeyValueEntityStorage();
369 $this->moduleHandler->expects($this->at(0))
370 ->method('getImplementations')
371 ->with('entity_load')
372 ->will($this->returnValue([]));
373 $this->moduleHandler->expects($this->at(1))
374 ->method('getImplementations')
375 ->with('test_entity_type_load')
376 ->will($this->returnValue([]));
377 $expected = ['id' => 'foo'];
378 $entity->expects($this->once())
380 ->will($this->returnValue($expected));
381 $this->keyValueStore->expects($this->exactly(2))
384 ->will($this->returnValue(TRUE));
385 $this->keyValueStore->expects($this->once())
386 ->method('getMultiple')
388 ->will($this->returnValue([['id' => 'foo']]));
389 $this->keyValueStore->expects($this->once())
392 $this->keyValueStore->expects($this->once())
394 ->with('bar', $expected);
396 // Performing a rename does not change the original ID until saving.
397 $this->assertSame('foo', $entity->getOriginalId());
398 $entity->set('id', 'bar');
399 $this->assertSame('foo', $entity->getOriginalId());
401 $return = $this->entityStorage->save($entity);
402 $this->assertSame(SAVED_UPDATED, $return);
403 $this->assertSame('bar', $entity->getOriginalId());
410 public function testSaveContentEntity() {
411 $this->entityType->expects($this->any())
413 ->will($this->returnValue([
416 $this->setUpKeyValueEntityStorage();
418 $expected = ['id' => 'foo'];
419 $this->keyValueStore->expects($this->exactly(2))
422 ->will($this->returnValue(FALSE));
423 $this->keyValueStore->expects($this->once())
425 ->with('foo', $expected);
426 $this->keyValueStore->expects($this->never())
428 $entity = $this->getMockEntity('Drupal\Core\Entity\ContentEntityBase', [], [
432 $entity->expects($this->atLeastOnce())
434 ->will($this->returnValue('foo'));
435 $entity->expects($this->once())
437 ->will($this->returnValue($expected));
438 $this->entityStorage->save($entity);
445 public function testSaveInvalid() {
446 $this->setUpKeyValueEntityStorage();
448 $entity = $this->getMockEntity('Drupal\Core\Config\Entity\ConfigEntityBase');
449 $this->keyValueStore->expects($this->never())
451 $this->keyValueStore->expects($this->never())
453 $this->keyValueStore->expects($this->never())
455 $this->setExpectedException(EntityMalformedException::class, 'The entity does not have an ID.');
456 $this->entityStorage->save($entity);
463 public function testSaveDuplicate() {
464 $this->setUpKeyValueEntityStorage();
466 $entity = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
467 $entity->enforceIsNew();
468 $this->keyValueStore->expects($this->once())
470 ->will($this->returnValue(TRUE));
471 $this->keyValueStore->expects($this->never())
473 $this->keyValueStore->expects($this->never())
475 $this->setExpectedException(EntityStorageException::class, "'test_entity_type' entity with ID 'foo' already exists");
476 $this->entityStorage->save($entity);
483 public function testLoad() {
484 $entity = $this->getMockEntity();
485 $this->entityType->expects($this->once())
487 ->will($this->returnValue(get_class($entity)));
488 $this->setUpKeyValueEntityStorage();
490 $this->keyValueStore->expects($this->once())
491 ->method('getMultiple')
493 ->will($this->returnValue([['id' => 'foo']]));
494 $this->moduleHandler->expects($this->at(0))
495 ->method('getImplementations')
496 ->with('entity_load')
497 ->will($this->returnValue([]));
498 $this->moduleHandler->expects($this->at(1))
499 ->method('getImplementations')
500 ->with('test_entity_type_load')
501 ->will($this->returnValue([]));
502 $entity = $this->entityStorage->load('foo');
503 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
504 $this->assertSame('foo', $entity->id());
510 public function testLoadMissingEntity() {
511 $this->entityType->expects($this->once())
512 ->method('getClass');
513 $this->setUpKeyValueEntityStorage();
515 $this->keyValueStore->expects($this->once())
516 ->method('getMultiple')
518 ->will($this->returnValue([]));
519 $this->moduleHandler->expects($this->never())
520 ->method('getImplementations');
521 $entity = $this->entityStorage->load('foo');
522 $this->assertNull($entity);
526 * @covers ::loadMultiple
528 * @covers ::mapFromStorageRecords
529 * @covers ::doLoadMultiple
531 public function testLoadMultipleAll() {
532 $expected['foo'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
533 $expected['bar'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'bar']]);
534 $this->entityType->expects($this->once())
536 ->will($this->returnValue(get_class(reset($expected))));
537 $this->setUpKeyValueEntityStorage();
539 $this->keyValueStore->expects($this->once())
541 ->will($this->returnValue([['id' => 'foo'], ['id' => 'bar']]));
542 $this->moduleHandler->expects($this->at(0))
543 ->method('getImplementations')
544 ->with('entity_load')
545 ->will($this->returnValue([]));
546 $this->moduleHandler->expects($this->at(1))
547 ->method('getImplementations')
548 ->with('test_entity_type_load')
549 ->will($this->returnValue([]));
550 $entities = $this->entityStorage->loadMultiple();
551 foreach ($entities as $id => $entity) {
552 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
553 $this->assertSame($id, $entity->id());
554 $this->assertSame($id, $expected[$id]->id());
559 * @covers ::loadMultiple
561 * @covers ::mapFromStorageRecords
562 * @covers ::doLoadMultiple
564 public function testLoadMultipleIds() {
565 $entity = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
566 $this->entityType->expects($this->once())
568 ->will($this->returnValue(get_class($entity)));
569 $this->setUpKeyValueEntityStorage();
571 $expected[] = $entity;
572 $this->keyValueStore->expects($this->once())
573 ->method('getMultiple')
575 ->will($this->returnValue([['id' => 'foo']]));
576 $this->moduleHandler->expects($this->at(0))
577 ->method('getImplementations')
578 ->with('entity_load')
579 ->will($this->returnValue([]));
580 $this->moduleHandler->expects($this->at(1))
581 ->method('getImplementations')
582 ->with('test_entity_type_load')
583 ->will($this->returnValue([]));
584 $entities = $this->entityStorage->loadMultiple(['foo']);
585 foreach ($entities as $id => $entity) {
586 $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
587 $this->assertSame($id, $entity->id());
592 * @covers ::loadRevision
594 public function testLoadRevision() {
595 $this->setUpKeyValueEntityStorage();
597 $this->assertSame(NULL, $this->entityStorage->loadRevision(1));
601 * @covers ::deleteRevision
603 public function testDeleteRevision() {
604 $this->setUpKeyValueEntityStorage();
606 $this->assertSame(NULL, $this->entityStorage->deleteRevision(1));
613 public function testDelete() {
614 $entities['foo'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'foo']]);
615 $entities['bar'] = $this->getMockEntity('Drupal\Core\Entity\Entity', [['id' => 'bar']]);
616 $this->entityType->expects($this->once())
618 ->will($this->returnValue(get_class(reset($entities))));
619 $this->setUpKeyValueEntityStorage();
621 $this->moduleHandler->expects($this->at(0))
622 ->method('invokeAll')
623 ->with('test_entity_type_predelete');
624 $this->moduleHandler->expects($this->at(1))
625 ->method('invokeAll')
626 ->with('entity_predelete');
627 $this->moduleHandler->expects($this->at(2))
628 ->method('invokeAll')
629 ->with('test_entity_type_predelete');
630 $this->moduleHandler->expects($this->at(3))
631 ->method('invokeAll')
632 ->with('entity_predelete');
633 $this->moduleHandler->expects($this->at(4))
634 ->method('invokeAll')
635 ->with('test_entity_type_delete');
636 $this->moduleHandler->expects($this->at(5))
637 ->method('invokeAll')
638 ->with('entity_delete');
639 $this->moduleHandler->expects($this->at(6))
640 ->method('invokeAll')
641 ->with('test_entity_type_delete');
642 $this->moduleHandler->expects($this->at(7))
643 ->method('invokeAll')
644 ->with('entity_delete');
646 $this->keyValueStore->expects($this->once())
647 ->method('deleteMultiple')
648 ->with(['foo', 'bar']);
649 $this->entityStorage->delete($entities);
656 public function testDeleteNothing() {
657 $this->setUpKeyValueEntityStorage();
659 $this->moduleHandler->expects($this->never())
660 ->method($this->anything());
661 $this->keyValueStore->expects($this->never())
663 $this->keyValueStore->expects($this->never())
664 ->method('deleteMultiple');
666 $this->entityStorage->delete([]);
670 * Creates an entity with specific methods mocked.
672 * @param string $class
673 * (optional) The concrete entity class to mock. Defaults to
674 * 'Drupal\Core\Entity\Entity'.
675 * @param array $arguments
676 * (optional) Arguments to pass to the constructor. An empty set of values
677 * and an entity type ID will be provided.
678 * @param array $methods
679 * (optional) The methods to mock.
681 * @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
683 public function getMockEntity($class = 'Drupal\Core\Entity\Entity', array $arguments = [], $methods = []) {
684 // Ensure the entity is passed at least an array of values and an entity
686 if (!isset($arguments[0])) {
689 if (!isset($arguments[1])) {
690 $arguments[1] = 'test_entity_type';
692 return $this->getMockForAbstractClass($class, $arguments, '', TRUE, TRUE, TRUE, $methods);
697 namespace Drupal\Core\Entity\KeyValueStore;
699 if (!defined('SAVED_NEW')) {
700 define('SAVED_NEW', 1);
702 if (!defined('SAVED_UPDATED')) {
703 define('SAVED_UPDATED', 2);