SqlContentEntityStorageTest.php

Same filename in other branches
  1. 9 core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
  2. 10 core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php
  3. 11.x core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php

Contains \Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTest.

Namespace

Drupal\Tests\Core\Entity\Sql

File

core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php

View source
<?php


/**
 * @file
 * Contains \Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTest.
 */
namespace Drupal\Tests\Core\Entity\Sql;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\MemoryCache\MemoryCache;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Entity\EntityFieldManager;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\Query\QueryFactoryInterface;
use Drupal\Core\Entity\Sql\DefaultTableMapping;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Language\Language;
use Drupal\Tests\UnitTestCase;

/**
 * @coversDefaultClass \Drupal\Core\Entity\Sql\SqlContentEntityStorage
 * @group Entity
 */
class SqlContentEntityStorageTest extends UnitTestCase {
    
    /**
     * The content entity database storage used in this test.
     *
     * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $entityStorage;
    
    /**
     * The mocked entity type used in this test.
     *
     * @var \Drupal\Core\Entity\ContentEntityTypeInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $entityType;
    
    /**
     * An array of field definitions used for this test, keyed by field name.
     *
     * @var \Drupal\Core\Field\BaseFieldDefinition[]|\PHPUnit\Framework\MockObject\MockObject[]
     */
    protected $fieldDefinitions = [];
    
    /**
     * The mocked entity type manager used in this test.
     *
     * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $entityTypeManager;
    
    /**
     * The mocked entity type bundle info used in this test.
     *
     * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $entityTypeBundleInfo;
    
    /**
     * The mocked entity field manager used in this test.
     *
     * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $entityFieldManager;
    
    /**
     * The entity type ID.
     *
     * @var string
     */
    protected $entityTypeId = 'entity_test';
    
    /**
     * The dependency injection container.
     *
     * @var \Symfony\Component\DependencyInjection\ContainerBuilder
     */
    protected $container;
    
    /**
     * The module handler.
     *
     * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $moduleHandler;
    
    /**
     * The cache backend to use.
     *
     * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $cache;
    
    /**
     * The language manager.
     *
     * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $languageManager;
    
    /**
     * The database connection to use.
     *
     * @var \Drupal\Core\Database\Connection|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $connection;
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() {
        $this->entityType = $this->createMock('Drupal\\Core\\Entity\\ContentEntityTypeInterface');
        $this->entityType
            ->expects($this->any())
            ->method('id')
            ->will($this->returnValue($this->entityTypeId));
        $this->container = new ContainerBuilder();
        \Drupal::setContainer($this->container);
        $this->entityTypeManager = $this->createMock(EntityTypeManager::class);
        $this->entityTypeBundleInfo = $this->createMock(EntityTypeBundleInfoInterface::class);
        $this->entityFieldManager = $this->createMock(EntityFieldManager::class);
        $this->moduleHandler = $this->createMock('Drupal\\Core\\Extension\\ModuleHandlerInterface');
        $this->cache = $this->createMock('Drupal\\Core\\Cache\\CacheBackendInterface');
        $this->languageManager = $this->createMock('Drupal\\Core\\Language\\LanguageManagerInterface');
        $this->languageManager
            ->expects($this->any())
            ->method('getDefaultLanguage')
            ->will($this->returnValue(new Language([
            'langcode' => 'en',
        ])));
        $this->connection = $this->getMockBuilder('Drupal\\Core\\Database\\Connection')
            ->disableOriginalConstructor()
            ->getMock();
        $this->container
            ->set('entity_type.manager', $this->entityTypeManager);
        $this->container
            ->set('entity_field.manager', $this->entityFieldManager);
    }
    
    /**
     * Tests SqlContentEntityStorage::getBaseTable().
     *
     * @param string $base_table
     *   The base table to be returned by the mocked entity type.
     * @param string $expected
     *   The expected return value of
     *   SqlContentEntityStorage::getBaseTable().
     *
     * @covers ::__construct
     * @covers ::getBaseTable
     *
     * @dataProvider providerTestGetBaseTable
     */
    public function testGetBaseTable($base_table, $expected) {
        $this->entityType
            ->expects($this->once())
            ->method('getBaseTable')
            ->willReturn($base_table);
        $this->setUpEntityStorage();
        $this->assertSame($expected, $this->entityStorage
            ->getBaseTable());
    }
    
    /**
     * Provides test data for testGetBaseTable().
     *
     * @return array[]
     *   An nested array where each inner array has the base table to be returned
     *   by the mocked entity type as the first value and the expected return
     *   value of SqlContentEntityStorage::getBaseTable() as the second
     *   value.
     */
    public function providerTestGetBaseTable() {
        return [
            // Test that the entity type's base table is used, if provided.
[
                'entity_test',
                'entity_test',
            ],
            // Test that the storage falls back to the entity type ID.
[
                NULL,
                'entity_test',
            ],
        ];
    }
    
    /**
     * Tests SqlContentEntityStorage::getRevisionTable().
     *
     * @param string $revision_table
     *   The revision table to be returned by the mocked entity type.
     * @param string $expected
     *   The expected return value of
     *   SqlContentEntityStorage::getRevisionTable().
     *
     * @covers ::__construct
     * @covers ::getRevisionTable
     *
     * @dataProvider providerTestGetRevisionTable
     */
    public function testGetRevisionTable($revision_table, $expected) {
        $this->entityType
            ->expects($this->any())
            ->method('isRevisionable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->once())
            ->method('getRevisionTable')
            ->will($this->returnValue($revision_table));
        $this->entityType
            ->expects($this->any())
            ->method('getRevisionMetadataKeys')
            ->willReturn([]);
        $this->setUpEntityStorage();
        $this->assertSame($expected, $this->entityStorage
            ->getRevisionTable());
    }
    
    /**
     * Provides test data for testGetRevisionTable().
     *
     * @return array[]
     *   An nested array where each inner array has the revision table to be
     *   returned by the mocked entity type as the first value and the expected
     *   return value of SqlContentEntityStorage::getRevisionTable() as the
     *   second value.
     */
    public function providerTestGetRevisionTable() {
        return [
            // Test that the entity type's revision table is used, if provided.
[
                'entity_test_revision',
                'entity_test_revision',
            ],
            // Test that the storage falls back to the entity type ID with a
            // '_revision' suffix.
[
                NULL,
                'entity_test_revision',
            ],
        ];
    }
    
    /**
     * Tests SqlContentEntityStorage::getDataTable().
     *
     * @covers ::__construct
     * @covers ::getDataTable
     */
    public function testGetDataTable() {
        $this->entityType
            ->expects($this->any())
            ->method('isTranslatable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->exactly(1))
            ->method('getDataTable')
            ->will($this->returnValue('entity_test_field_data'));
        $this->entityType
            ->expects($this->any())
            ->method('getRevisionMetadataKeys')
            ->willReturn([]);
        $this->setUpEntityStorage();
        $this->assertSame('entity_test_field_data', $this->entityStorage
            ->getDataTable());
    }
    
    /**
     * Tests SqlContentEntityStorage::getRevisionDataTable().
     *
     * @param string $revision_data_table
     *   The revision data table to be returned by the mocked entity type.
     * @param string $expected
     *   The expected return value of
     *   SqlContentEntityStorage::getRevisionDataTable().
     *
     * @covers ::__construct
     * @covers ::getRevisionDataTable
     *
     * @dataProvider providerTestGetRevisionDataTable
     */
    public function testGetRevisionDataTable($revision_data_table, $expected) {
        $this->entityType
            ->expects($this->any())
            ->method('isRevisionable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->any())
            ->method('isTranslatable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->exactly(1))
            ->method('getDataTable')
            ->will($this->returnValue('entity_test_field_data'));
        $this->entityType
            ->expects($this->once())
            ->method('getRevisionDataTable')
            ->will($this->returnValue($revision_data_table));
        $this->entityType
            ->expects($this->any())
            ->method('getRevisionMetadataKeys')
            ->willReturn([]);
        $this->setUpEntityStorage();
        $actual = $this->entityStorage
            ->getRevisionDataTable();
        $this->assertSame($expected, $actual);
    }
    
    /**
     * Provides test data for testGetRevisionDataTable().
     *
     * @return array[]
     *   An nested array where each inner array has the revision data table to be
     *   returned by the mocked entity type as the first value and the expected
     *   return value of SqlContentEntityStorage::getRevisionDataTable() as
     *   the second value.
     */
    public function providerTestGetRevisionDataTable() {
        return [
            // Test that the entity type's revision data table is used, if provided.
[
                'entity_test_field_revision',
                'entity_test_field_revision',
            ],
            // Test that the storage falls back to the entity type ID with a
            // '_field_revision' suffix.
[
                NULL,
                'entity_test_field_revision',
            ],
        ];
    }
    
    /**
     * Tests that setting a new table mapping also updates the table names.
     *
     * @covers ::setTableMapping
     */
    public function testSetTableMapping() {
        $this->entityType
            ->expects($this->any())
            ->method('isRevisionable')
            ->will($this->returnValue(FALSE));
        $this->entityType
            ->expects($this->any())
            ->method('isTranslatable')
            ->will($this->returnValue(FALSE));
        $this->entityType
            ->expects($this->any())
            ->method('getRevisionMetadataKeys')
            ->willReturn([]);
        $this->setUpEntityStorage();
        $this->assertSame('entity_test', $this->entityStorage
            ->getBaseTable());
        $this->assertNull($this->entityStorage
            ->getRevisionTable());
        $this->assertNull($this->entityStorage
            ->getDataTable());
        $this->assertNull($this->entityStorage
            ->getRevisionDataTable());
        // Change the entity type definition and instantiate a new table mapping
        // with it.
        $updated_entity_type = $this->createMock('Drupal\\Core\\Entity\\ContentEntityTypeInterface');
        $updated_entity_type->expects($this->any())
            ->method('id')
            ->will($this->returnValue($this->entityTypeId));
        $updated_entity_type->expects($this->any())
            ->method('isRevisionable')
            ->will($this->returnValue(TRUE));
        $updated_entity_type->expects($this->any())
            ->method('isTranslatable')
            ->will($this->returnValue(TRUE));
        $table_mapping = new DefaultTableMapping($updated_entity_type, []);
        $this->entityStorage
            ->setTableMapping($table_mapping);
        $this->assertSame('entity_test', $this->entityStorage
            ->getBaseTable());
        $this->assertSame('entity_test_revision', $this->entityStorage
            ->getRevisionTable());
        $this->assertSame('entity_test_field_data', $this->entityStorage
            ->getDataTable());
        $this->assertSame('entity_test_field_revision', $this->entityStorage
            ->getRevisionDataTable());
    }
    
    /**
     * Tests ContentEntityDatabaseStorage::onEntityTypeCreate().
     *
     * @covers ::__construct
     * @covers ::onEntityTypeCreate
     * @covers ::getTableMapping
     */
    public function testOnEntityTypeCreate() {
        $columns = [
            'value' => [
                'type' => 'int',
            ],
        ];
        $this->fieldDefinitions = $this->mockFieldDefinitions([
            'id',
        ]);
        $this->fieldDefinitions['id']
            ->expects($this->any())
            ->method('getColumns')
            ->will($this->returnValue($columns));
        $this->fieldDefinitions['id']
            ->expects($this->once())
            ->method('getSchema')
            ->will($this->returnValue([
            'columns' => $columns,
        ]));
        $this->entityType
            ->expects($this->once())
            ->method('getKeys')
            ->will($this->returnValue([
            'id' => 'id',
        ]));
        $this->entityType
            ->expects($this->any())
            ->method('hasKey')
            ->will($this->returnValueMap([
            // SqlContentEntityStorageSchema::initializeBaseTable()
[
                'revision',
                FALSE,
            ],
            // SqlContentEntityStorageSchema::processBaseTable()
[
                'id',
                TRUE,
            ],
        ]));
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            // EntityStorageBase::__construct()
[
                'id',
                'id',
            ],
            // ContentEntityStorageBase::__construct()
[
                'uuid',
                NULL,
            ],
            [
                'bundle',
                NULL,
            ],
            // SqlContentEntityStorageSchema::initializeBaseTable()
[
                'id' => 'id',
            ],
            // SqlContentEntityStorageSchema::processBaseTable()
[
                'id' => 'id',
            ],
        ]));
        $this->setUpEntityStorage();
        $expected = [
            'description' => 'The base table for entity_test entities.',
            'fields' => [
                'id' => [
                    'type' => 'serial',
                    'not null' => TRUE,
                ],
            ],
            'primary key' => [
                'id',
            ],
            'unique keys' => [],
            'indexes' => [],
            'foreign keys' => [],
        ];
        $schema_handler = $this->getMockBuilder('Drupal\\Core\\Database\\Schema')
            ->disableOriginalConstructor()
            ->getMock();
        $schema_handler->expects($this->any())
            ->method('createTable')
            ->with($this->equalTo('entity_test'), $this->equalTo($expected));
        $this->connection
            ->expects($this->once())
            ->method('schema')
            ->will($this->returnValue($schema_handler));
        $storage = $this->getMockBuilder('Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage')
            ->setConstructorArgs([
            $this->entityType,
            $this->connection,
            $this->entityFieldManager,
            $this->cache,
            $this->languageManager,
            new MemoryCache(),
            $this->entityTypeBundleInfo,
            $this->entityTypeManager,
        ])
            ->setMethods([
            'getStorageSchema',
        ])
            ->getMock();
        $key_value = $this->createMock('Drupal\\Core\\KeyValueStore\\KeyValueStoreInterface');
        $schema_handler = $this->getMockBuilder('Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorageSchema')
            ->setConstructorArgs([
            $this->entityTypeManager,
            $this->entityType,
            $storage,
            $this->connection,
            $this->entityFieldManager,
        ])
            ->setMethods([
            'installedStorageSchema',
            'createSharedTableSchema',
        ])
            ->getMock();
        $schema_handler->expects($this->any())
            ->method('installedStorageSchema')
            ->will($this->returnValue($key_value));
        $storage->expects($this->any())
            ->method('getStorageSchema')
            ->will($this->returnValue($schema_handler));
        $storage->onEntityTypeCreate($this->entityType);
    }
    
    /**
     * Tests getTableMapping() with an empty entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     */
    public function testGetTableMappingEmpty() {
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $this->assertSame([
            'entity_test',
        ], $mapping->getTableNames());
        $this->assertSame([], $mapping->getFieldNames('entity_test'));
        $this->assertSame([], $mapping->getExtraColumns('entity_test'));
    }
    
    /**
     * Tests getTableMapping() with a simple entity type.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingSimple(array $entity_keys) {
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            [
                'id',
                $entity_keys['id'],
            ],
            [
                'uuid',
                $entity_keys['uuid'],
            ],
            [
                'bundle',
                $entity_keys['bundle'],
            ],
        ]));
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $this->assertEquals([
            'entity_test',
        ], $mapping->getTableNames());
        $expected = array_values(array_filter($entity_keys));
        $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
        $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
    }
    
    /**
     * Tests getTableMapping() with a simple entity type with some base fields.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingSimpleWithFields(array $entity_keys) {
        $base_field_names = [
            'title',
            'description',
            'owner',
        ];
        $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
        $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $this->assertEquals([
            'entity_test',
        ], $mapping->getTableNames());
        $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
        $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
    }
    
    /**
     * Provides test data for testGetTableMappingSimple().
     *
     * @return array[]
     *   A nested array, where each inner array has a single value being a  map of
     *   entity keys to use for the mocked entity type.
     */
    public function providerTestGetTableMappingSimple() {
        return [
            [
                [
                    'id' => 'test_id',
                    'bundle' => NULL,
                    'uuid' => NULL,
                ],
            ],
            [
                [
                    'id' => 'test_id',
                    'bundle' => 'test_bundle',
                    'uuid' => NULL,
                ],
            ],
            [
                [
                    'id' => 'test_id',
                    'bundle' => NULL,
                    'uuid' => 'test_uuid',
                ],
            ],
            [
                [
                    'id' => 'test_id',
                    'bundle' => 'test_bundle',
                    'uuid' => 'test_uuid',
                ],
            ],
        ];
    }
    
    /**
     * Tests getTableMapping() with a base field that requires a dedicated table.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     */
    public function testGetTableMappingSimpleWithDedicatedStorageFields() {
        $base_field_names = [
            'multi_valued_base_field',
        ];
        // Set up one entity key in order to have a base table.
        $this->fieldDefinitions = $this->mockFieldDefinitions([
            'test_id',
        ]);
        // Set up the multi-valued base field.
        $this->fieldDefinitions += $this->mockFieldDefinitions($base_field_names, [
            'hasCustomStorage' => FALSE,
            'isMultiple' => TRUE,
            'getTargetEntityTypeId' => 'entity_test',
        ]);
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $this->assertEquals([
            'entity_test',
            'entity_test__multi_valued_base_field',
        ], $mapping->getTableNames());
        $this->assertEquals($base_field_names, $mapping->getFieldNames('entity_test__multi_valued_base_field'));
        $extra_columns = [
            'bundle',
            'deleted',
            'entity_id',
            'revision_id',
            'langcode',
            'delta',
        ];
        $this->assertEquals($extra_columns, $mapping->getExtraColumns('entity_test__multi_valued_base_field'));
    }
    
    /**
     * Tests getTableMapping() with a revisionable, non-translatable entity type.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingRevisionable(array $entity_keys) {
        // This allows to re-use the data provider.
        $entity_keys = [
            'id' => $entity_keys['id'],
            'revision' => 'test_revision',
            'bundle' => $entity_keys['bundle'],
            'uuid' => $entity_keys['uuid'],
        ];
        $this->entityType
            ->expects($this->exactly(4))
            ->method('isRevisionable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            [
                'id',
                $entity_keys['id'],
            ],
            [
                'uuid',
                $entity_keys['uuid'],
            ],
            [
                'bundle',
                $entity_keys['bundle'],
            ],
            [
                'revision',
                $entity_keys['revision'],
            ],
        ]));
        $this->entityType
            ->expects($this->any())
            ->method('getRevisionMetadataKeys')
            ->will($this->returnValue([]));
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $expected = [
            'entity_test',
            'entity_test_revision',
        ];
        $this->assertEquals($expected, $mapping->getTableNames());
        $expected = array_values(array_filter($entity_keys));
        $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
        $expected = [
            $entity_keys['id'],
            $entity_keys['revision'],
        ];
        $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
        $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
        $this->assertEquals([], $mapping->getExtraColumns('entity_test_revision'));
    }
    
    /**
     * Tests getTableMapping() with a revisionable entity type with fields.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
        // This allows to re-use the data provider.
        $entity_keys = [
            'id' => $entity_keys['id'],
            'revision' => 'test_revision',
            'bundle' => $entity_keys['bundle'],
            'uuid' => $entity_keys['uuid'],
        ];
        // PHPUnit does not allow for multiple data providers.
        $test_cases = [
            [],
            [
                'revision_created' => 'revision_timestamp',
            ],
            [
                'revision_user' => 'revision_uid',
            ],
            [
                'revision_log_message' => 'revision_log',
            ],
            [
                'revision_created' => 'revision_timestamp',
                'revision_user' => 'revision_uid',
            ],
            [
                'revision_created' => 'revision_timestamp',
                'revision_log_message' => 'revision_log',
            ],
            [
                'revision_user' => 'revision_uid',
                'revision_log_message' => 'revision_log',
            ],
            [
                'revision_created' => 'revision_timestamp',
                'revision_user' => 'revision_uid',
                'revision_log_message' => 'revision_log',
            ],
        ];
        foreach ($test_cases as $revision_metadata_field_names) {
            $this->setUp();
            $base_field_names = [
                'title',
            ];
            $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
            $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
            $revisionable_field_names = [
                'description',
                'owner',
            ];
            $field_names = array_merge($field_names, $revisionable_field_names);
            $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), [
                'isRevisionable' => TRUE,
            ]);
            $this->entityType
                ->expects($this->exactly(4))
                ->method('isRevisionable')
                ->will($this->returnValue(TRUE));
            $this->entityType
                ->expects($this->any())
                ->method('getKey')
                ->will($this->returnValueMap([
                [
                    'id',
                    $entity_keys['id'],
                ],
                [
                    'uuid',
                    $entity_keys['uuid'],
                ],
                [
                    'bundle',
                    $entity_keys['bundle'],
                ],
                [
                    'revision',
                    $entity_keys['revision'],
                ],
            ]));
            $this->entityType
                ->expects($this->any())
                ->method('getRevisionMetadataKeys')
                ->will($this->returnValue($revision_metadata_field_names));
            $this->setUpEntityStorage();
            $mapping = $this->entityStorage
                ->getTableMapping();
            $expected = [
                'entity_test',
                'entity_test_revision',
            ];
            $this->assertEquals($expected, $mapping->getTableNames());
            $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
            $expected = array_merge([
                $entity_keys['id'],
                $entity_keys['revision'],
            ], $revisionable_field_names, array_values($revision_metadata_field_names));
            $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
            $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
            $this->assertEquals([], $mapping->getExtraColumns('entity_test_revision'));
        }
    }
    
    /**
     * Tests getTableMapping() with a non-revisionable, translatable entity type.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingTranslatable(array $entity_keys) {
        // This allows to re-use the data provider.
        $entity_keys['langcode'] = 'langcode';
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('isTranslatable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getDataTable')
            ->will($this->returnValue('entity_test_field_data'));
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            [
                'id',
                $entity_keys['id'],
            ],
            [
                'uuid',
                $entity_keys['uuid'],
            ],
            [
                'bundle',
                $entity_keys['bundle'],
            ],
            [
                'langcode',
                $entity_keys['langcode'],
            ],
        ]));
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $expected = [
            'entity_test',
            'entity_test_field_data',
        ];
        $this->assertEquals($expected, $mapping->getTableNames());
        $expected = array_values(array_filter($entity_keys));
        $actual = $mapping->getFieldNames('entity_test');
        $this->assertEquals($expected, $actual);
        // The UUID is not stored on the data table.
        $expected = array_values(array_filter([
            $entity_keys['id'],
            $entity_keys['bundle'],
            $entity_keys['langcode'],
        ]));
        $actual = $mapping->getFieldNames('entity_test_field_data');
        $this->assertEquals($expected, $actual);
        $expected = [];
        $actual = $mapping->getExtraColumns('entity_test');
        $this->assertEquals($expected, $actual);
        $actual = $mapping->getExtraColumns('entity_test_field_data');
        $this->assertEquals($expected, $actual);
    }
    
    /**
     * Tests getTableMapping() with a translatable entity type with fields.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingTranslatableWithFields(array $entity_keys) {
        // This allows to re-use the data provider.
        $entity_keys['langcode'] = 'langcode';
        $base_field_names = [
            'title',
            'description',
            'owner',
        ];
        $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
        $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('isTranslatable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getDataTable')
            ->will($this->returnValue('entity_test_field_data'));
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            [
                'id',
                $entity_keys['id'],
            ],
            [
                'uuid',
                $entity_keys['uuid'],
            ],
            [
                'bundle',
                $entity_keys['bundle'],
            ],
            [
                'langcode',
                $entity_keys['langcode'],
            ],
        ]));
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $expected = [
            'entity_test',
            'entity_test_field_data',
        ];
        $this->assertEquals($expected, $mapping->getTableNames());
        $expected = array_values(array_filter($entity_keys));
        $actual = $mapping->getFieldNames('entity_test');
        $this->assertEquals($expected, $actual);
        // The UUID is not stored on the data table.
        $expected = array_merge(array_filter([
            $entity_keys['id'],
            $entity_keys['bundle'],
            $entity_keys['langcode'],
        ]), $base_field_names);
        $actual = $mapping->getFieldNames('entity_test_field_data');
        $this->assertEquals($expected, $actual);
        $expected = [];
        $actual = $mapping->getExtraColumns('entity_test');
        $this->assertEquals($expected, $actual);
        $actual = $mapping->getExtraColumns('entity_test_field_data');
        $this->assertEquals($expected, $actual);
    }
    
    /**
     * Tests getTableMapping() with a revisionable, translatable entity type.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingRevisionableTranslatable(array $entity_keys) {
        // This allows to re-use the data provider.
        $entity_keys = [
            'id' => $entity_keys['id'],
            'revision' => 'test_revision',
            'bundle' => $entity_keys['bundle'],
            'uuid' => $entity_keys['uuid'],
            'langcode' => 'langcode',
        ];
        $revision_metadata_keys = [
            'revision_created' => 'revision_timestamp',
            'revision_user' => 'revision_uid',
            'revision_log_message' => 'revision_log',
        ];
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('isRevisionable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('isTranslatable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getDataTable')
            ->will($this->returnValue('entity_test_field_data'));
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            [
                'id',
                $entity_keys['id'],
            ],
            [
                'uuid',
                $entity_keys['uuid'],
            ],
            [
                'bundle',
                $entity_keys['bundle'],
            ],
            [
                'revision',
                $entity_keys['revision'],
            ],
            [
                'langcode',
                $entity_keys['langcode'],
            ],
        ]));
        $this->entityType
            ->expects($this->any())
            ->method('getRevisionMetadataKeys')
            ->will($this->returnValue($revision_metadata_keys));
        $this->fieldDefinitions = $this->mockFieldDefinitions(array_values($revision_metadata_keys), [
            'isRevisionable' => TRUE,
        ]);
        $this->setUpEntityStorage();
        $mapping = $this->entityStorage
            ->getTableMapping();
        $expected = [
            'entity_test',
            'entity_test_field_data',
            'entity_test_revision',
            'entity_test_field_revision',
        ];
        $this->assertEquals($expected, $mapping->getTableNames());
        // The default language code is stored on the base table.
        $expected = array_values(array_filter([
            $entity_keys['id'],
            $entity_keys['revision'],
            $entity_keys['bundle'],
            $entity_keys['uuid'],
            $entity_keys['langcode'],
        ]));
        $actual = $mapping->getFieldNames('entity_test');
        $this->assertEquals($expected, $actual);
        // The revision table on the other hand does not store the bundle and the
        // UUID.
        $expected = array_values(array_filter([
            $entity_keys['id'],
            $entity_keys['revision'],
            $entity_keys['langcode'],
        ]));
        $expected = array_merge($expected, array_values($revision_metadata_keys));
        $actual = $mapping->getFieldNames('entity_test_revision');
        $this->assertEquals($expected, $actual);
        // The UUID is not stored on the data table.
        $expected = array_values(array_filter([
            $entity_keys['id'],
            $entity_keys['revision'],
            $entity_keys['bundle'],
            $entity_keys['langcode'],
        ]));
        $actual = $mapping->getFieldNames('entity_test_field_data');
        $this->assertEquals($expected, $actual);
        // The data revision also does not store the bundle.
        $expected = array_values(array_filter([
            $entity_keys['id'],
            $entity_keys['revision'],
            $entity_keys['langcode'],
        ]));
        $actual = $mapping->getFieldNames('entity_test_field_revision');
        $this->assertEquals($expected, $actual);
        $expected = [];
        $actual = $mapping->getExtraColumns('entity_test');
        $this->assertEquals($expected, $actual);
        $actual = $mapping->getExtraColumns('entity_test_revision');
        $this->assertEquals($expected, $actual);
        $actual = $mapping->getExtraColumns('entity_test_field_data');
        $this->assertEquals($expected, $actual);
        $actual = $mapping->getExtraColumns('entity_test_field_revision');
        $this->assertEquals($expected, $actual);
    }
    
    /**
     * Tests getTableMapping() with a complex entity type with fields.
     *
     * @param string[] $entity_keys
     *   A map of entity keys to use for the mocked entity type.
     *
     * @covers ::__construct
     * @covers ::getTableMapping
     *
     * @dataProvider providerTestGetTableMappingSimple()
     */
    public function testGetTableMappingRevisionableTranslatableWithFields(array $entity_keys) {
        // This allows to re-use the data provider.
        $entity_keys = [
            'id' => $entity_keys['id'],
            'revision' => 'test_revision',
            'bundle' => $entity_keys['bundle'],
            'uuid' => $entity_keys['uuid'],
            'langcode' => 'langcode',
        ];
        // PHPUnit does not allow for multiple data providers.
        $test_cases = [
            [],
            [
                'revision_created' => 'revision_timestamp',
            ],
            [
                'revision_user' => 'revision_uid',
            ],
            [
                'revision_log_message' => 'revision_log',
            ],
            [
                'revision_created' => 'revision_timestamp',
                'revision_user' => 'revision_uid',
            ],
            [
                'revision_created' => 'revision_timestamp',
                'revision_log_message' => 'revision_log',
            ],
            [
                'revision_user' => 'revision_uid',
                'revision_log_message' => 'revision_log',
            ],
            [
                'revision_created' => 'revision_timestamp',
                'revision_user' => 'revision_uid',
                'revision_log_message' => 'revision_log',
            ],
        ];
        foreach ($test_cases as $revision_metadata_field_names) {
            $this->setUp();
            $base_field_names = [
                'title',
            ];
            $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
            $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
            $revisionable_field_names = [
                'description',
                'owner',
            ];
            $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), [
                'isRevisionable' => TRUE,
            ]);
            $this->entityType
                ->expects($this->atLeastOnce())
                ->method('isRevisionable')
                ->will($this->returnValue(TRUE));
            $this->entityType
                ->expects($this->atLeastOnce())
                ->method('isTranslatable')
                ->will($this->returnValue(TRUE));
            $this->entityType
                ->expects($this->atLeastOnce())
                ->method('getDataTable')
                ->will($this->returnValue('entity_test_field_data'));
            $this->entityType
                ->expects($this->any())
                ->method('getKey')
                ->will($this->returnValueMap([
                [
                    'id',
                    $entity_keys['id'],
                ],
                [
                    'uuid',
                    $entity_keys['uuid'],
                ],
                [
                    'bundle',
                    $entity_keys['bundle'],
                ],
                [
                    'revision',
                    $entity_keys['revision'],
                ],
                [
                    'langcode',
                    $entity_keys['langcode'],
                ],
            ]));
            $this->entityType
                ->expects($this->any())
                ->method('getRevisionMetadataKeys')
                ->will($this->returnValue($revision_metadata_field_names));
            $this->setUpEntityStorage();
            $mapping = $this->entityStorage
                ->getTableMapping();
            $expected = [
                'entity_test',
                'entity_test_field_data',
                'entity_test_revision',
                'entity_test_field_revision',
            ];
            $this->assertEquals($expected, $mapping->getTableNames());
            $expected = [
                'entity_test',
                'entity_test_field_data',
                'entity_test_revision',
                'entity_test_field_revision',
            ];
            $this->assertEquals($expected, $mapping->getTableNames());
            // The default language code is not stored on the base table.
            $expected = array_values(array_filter([
                $entity_keys['id'],
                $entity_keys['revision'],
                $entity_keys['bundle'],
                $entity_keys['uuid'],
                $entity_keys['langcode'],
            ]));
            $actual = $mapping->getFieldNames('entity_test');
            $this->assertEquals($expected, $actual);
            // The revision table on the other hand does not store the bundle and the
            // UUID.
            $expected = array_merge(array_filter([
                $entity_keys['id'],
                $entity_keys['revision'],
                $entity_keys['langcode'],
            ]), array_values($revision_metadata_field_names));
            $actual = $mapping->getFieldNames('entity_test_revision');
            $this->assertEquals($expected, $actual);
            // The UUID is not stored on the data table.
            $expected = array_merge(array_filter([
                $entity_keys['id'],
                $entity_keys['revision'],
                $entity_keys['bundle'],
                $entity_keys['langcode'],
            ]), $base_field_names, $revisionable_field_names);
            $actual = $mapping->getFieldNames('entity_test_field_data');
            $this->assertEquals($expected, $actual);
            // The data revision also does not store the bundle.
            $expected = array_merge(array_filter([
                $entity_keys['id'],
                $entity_keys['revision'],
                $entity_keys['langcode'],
            ]), $revisionable_field_names);
            $actual = $mapping->getFieldNames('entity_test_field_revision');
            $this->assertEquals($expected, $actual);
            $expected = [];
            $actual = $mapping->getExtraColumns('entity_test');
            $this->assertEquals($expected, $actual);
            $actual = $mapping->getExtraColumns('entity_test_revision');
            $this->assertEquals($expected, $actual);
            $actual = $mapping->getExtraColumns('entity_test_field_data');
            $this->assertEquals($expected, $actual);
            $actual = $mapping->getExtraColumns('entity_test_field_revision');
            $this->assertEquals($expected, $actual);
        }
    }
    
    /**
     * @covers ::create
     */
    public function testCreate() {
        $language_manager = $this->createMock('Drupal\\Core\\Language\\LanguageManagerInterface');
        $language = new Language([
            'id' => 'en',
        ]);
        $language_manager->expects($this->any())
            ->method('getCurrentLanguage')
            ->will($this->returnValue($language));
        $entity = $this->getMockBuilder('Drupal\\Core\\Entity\\ContentEntityBase')
            ->disableOriginalConstructor()
            ->setMethods([
            'id',
        ])
            ->getMockForAbstractClass();
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('id')
            ->will($this->returnValue($this->entityTypeId));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getClass')
            ->will($this->returnValue(get_class($entity)));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getKeys')
            ->will($this->returnValue([
            'id' => 'id',
        ]));
        // ContentEntityStorageBase iterates over the entity which calls this method
        // internally in ContentEntityBase::getProperties().
        $this->entityFieldManager
            ->expects($this->once())
            ->method('getFieldDefinitions')
            ->will($this->returnValue([]));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('isRevisionable')
            ->will($this->returnValue(FALSE));
        $this->entityTypeManager
            ->expects($this->atLeastOnce())
            ->method('getDefinition')
            ->with($this->entityType
            ->id())
            ->will($this->returnValue($this->entityType));
        $this->setUpEntityStorage();
        $entity = $this->entityStorage
            ->create();
        $entity->expects($this->atLeastOnce())
            ->method('id')
            ->will($this->returnValue('foo'));
        $this->assertInstanceOf('Drupal\\Core\\Entity\\EntityInterface', $entity);
        $this->assertSame('foo', $entity->id());
        $this->assertTrue($entity->isNew());
    }
    
    /**
     * Returns a set of mock field definitions for the given names.
     *
     * @param array $field_names
     *   An array of field names.
     * @param array $methods
     *   (optional) An associative array of mock method return values keyed by
     *   method name.
     *
     * @return \Drupal\Tests\Core\Field\TestBaseFieldDefinitionInterface[]|\PHPUnit\Framework\MockObject\MockObject[]
     *   An array of mock base field definitions.
     */
    protected function mockFieldDefinitions(array $field_names, $methods = []) {
        $field_definitions = [];
        $definition = $this->createMock('Drupal\\Tests\\Core\\Field\\TestBaseFieldDefinitionInterface');
        // Assign common method return values.
        $methods += [
            'isBaseField' => TRUE,
        ];
        foreach ($methods as $method => $result) {
            $definition->expects($this->any())
                ->method($method)
                ->will($this->returnValue($result));
        }
        // Assign field names to mock definitions.
        foreach ($field_names as $field_name) {
            $field_definitions[$field_name] = clone $definition;
            $field_definitions[$field_name]->expects($this->any())
                ->method('getName')
                ->will($this->returnValue($field_name));
        }
        return $field_definitions;
    }
    
    /**
     * Sets up the content entity database storage.
     */
    protected function setUpEntityStorage() {
        $this->connection = $this->getMockBuilder('Drupal\\Core\\Database\\Connection')
            ->disableOriginalConstructor()
            ->getMock();
        $this->entityTypeManager
            ->expects($this->any())
            ->method('getDefinition')
            ->will($this->returnValue($this->entityType));
        $this->entityTypeManager
            ->expects($this->any())
            ->method('getActiveDefinition')
            ->will($this->returnValue($this->entityType));
        $this->entityFieldManager
            ->expects($this->any())
            ->method('getFieldStorageDefinitions')
            ->will($this->returnValue($this->fieldDefinitions));
        $this->entityFieldManager
            ->expects($this->any())
            ->method('getActiveFieldStorageDefinitions')
            ->will($this->returnValue($this->fieldDefinitions));
        $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager);
        $this->entityStorage
            ->setModuleHandler($this->moduleHandler);
    }
    
    /**
     * @covers ::doLoadMultiple
     * @covers ::buildCacheId
     * @covers ::getFromPersistentCache
     */
    public function testLoadMultiplePersistentCached() {
        $this->setUpModuleHandlerNoImplementations();
        $key = 'values:' . $this->entityTypeId . ':1';
        $id = 1;
        $entity = $this->getMockBuilder('\\Drupal\\Tests\\Core\\Entity\\Sql\\SqlContentEntityStorageTestEntityInterface')
            ->getMockForAbstractClass();
        $entity->expects($this->any())
            ->method('id')
            ->will($this->returnValue($id));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('isPersistentlyCacheable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('id')
            ->will($this->returnValue($this->entityTypeId));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getClass')
            ->will($this->returnValue(get_class($entity)));
        $this->cache
            ->expects($this->once())
            ->method('getMultiple')
            ->with([
            $key,
        ])
            ->will($this->returnValue([
            $key => (object) [
                'data' => $entity,
            ],
        ]));
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->setUpEntityStorage();
        $entities = $this->entityStorage
            ->loadMultiple([
            $id,
        ]);
        $this->assertEquals($entity, $entities[$id]);
    }
    
    /**
     * @covers ::doLoadMultiple
     * @covers ::buildCacheId
     * @covers ::getFromPersistentCache
     * @covers ::setPersistentCache
     */
    public function testLoadMultipleNoPersistentCache() {
        $this->setUpModuleHandlerNoImplementations();
        $id = 1;
        $entity = $this->getMockBuilder('\\Drupal\\Tests\\Core\\Entity\\Sql\\SqlContentEntityStorageTestEntityInterface')
            ->getMockForAbstractClass();
        $entity->expects($this->any())
            ->method('id')
            ->will($this->returnValue($id));
        $this->entityType
            ->expects($this->any())
            ->method('isPersistentlyCacheable')
            ->will($this->returnValue(FALSE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('id')
            ->will($this->returnValue($this->entityTypeId));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getClass')
            ->will($this->returnValue(get_class($entity)));
        // There should be no calls to the cache backend for an entity type without
        // persistent caching.
        $this->cache
            ->expects($this->never())
            ->method('getMultiple');
        $this->cache
            ->expects($this->never())
            ->method('set');
        $this->entityTypeManager
            ->expects($this->any())
            ->method('getActiveDefinition')
            ->will($this->returnValue($this->entityType));
        $entity_storage = $this->getMockBuilder('Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage')
            ->setConstructorArgs([
            $this->entityType,
            $this->connection,
            $this->entityFieldManager,
            $this->cache,
            $this->languageManager,
            new MemoryCache(),
            $this->entityTypeBundleInfo,
            $this->entityTypeManager,
        ])
            ->setMethods([
            'getFromStorage',
            'invokeStorageLoadHook',
            'initTableLayout',
        ])
            ->getMock();
        $entity_storage->method('invokeStorageLoadHook')
            ->willReturn(NULL);
        $entity_storage->method('initTableLayout')
            ->willReturn(NULL);
        $entity_storage->expects($this->once())
            ->method('getFromStorage')
            ->with([
            $id,
        ])
            ->will($this->returnValue([
            $id => $entity,
        ]));
        $entities = $entity_storage->loadMultiple([
            $id,
        ]);
        $this->assertEquals($entity, $entities[$id]);
    }
    
    /**
     * @covers ::doLoadMultiple
     * @covers ::buildCacheId
     * @covers ::getFromPersistentCache
     * @covers ::setPersistentCache
     */
    public function testLoadMultiplePersistentCacheMiss() {
        $this->setUpModuleHandlerNoImplementations();
        $id = 1;
        $entity = $this->getMockBuilder('\\Drupal\\Tests\\Core\\Entity\\Sql\\SqlContentEntityStorageTestEntityInterface')
            ->getMockForAbstractClass();
        $entity->expects($this->any())
            ->method('id')
            ->will($this->returnValue($id));
        $this->entityType
            ->expects($this->any())
            ->method('isPersistentlyCacheable')
            ->will($this->returnValue(TRUE));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('id')
            ->will($this->returnValue($this->entityTypeId));
        $this->entityType
            ->expects($this->atLeastOnce())
            ->method('getClass')
            ->will($this->returnValue(get_class($entity)));
        // In case of a cache miss, the entity is loaded from the storage and then
        // set in the cache.
        $key = 'values:' . $this->entityTypeId . ':1';
        $this->cache
            ->expects($this->once())
            ->method('getMultiple')
            ->with([
            $key,
        ])
            ->will($this->returnValue([]));
        $this->cache
            ->expects($this->once())
            ->method('set')
            ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, [
            $this->entityTypeId . '_values',
            'entity_field_info',
        ]);
        $this->entityTypeManager
            ->expects($this->any())
            ->method('getActiveDefinition')
            ->will($this->returnValue($this->entityType));
        $entity_storage = $this->getMockBuilder('Drupal\\Core\\Entity\\Sql\\SqlContentEntityStorage')
            ->setConstructorArgs([
            $this->entityType,
            $this->connection,
            $this->entityFieldManager,
            $this->cache,
            $this->languageManager,
            new MemoryCache(),
            $this->entityTypeBundleInfo,
            $this->entityTypeManager,
        ])
            ->setMethods([
            'getFromStorage',
            'invokeStorageLoadHook',
            'initTableLayout',
        ])
            ->getMock();
        $entity_storage->method('invokeStorageLoadHook')
            ->willReturn(NULL);
        $entity_storage->method('initTableLayout')
            ->willReturn(NULL);
        $entity_storage->expects($this->once())
            ->method('getFromStorage')
            ->with([
            $id,
        ])
            ->will($this->returnValue([
            $id => $entity,
        ]));
        $entities = $entity_storage->loadMultiple([
            $id,
        ]);
        $this->assertEquals($entity, $entities[$id]);
    }
    
    /**
     * @covers ::hasData
     */
    public function testHasData() {
        $query = $this->createMock('Drupal\\Core\\Entity\\Query\\QueryInterface');
        $query->expects($this->once())
            ->method('accessCheck')
            ->with(FALSE)
            ->willReturn($query);
        $query->expects($this->once())
            ->method('range')
            ->with(0, 1)
            ->willReturn($query);
        $query->expects($this->once())
            ->method('execute')
            ->willReturn([
            5,
        ]);
        $factory = $this->createMock(QueryFactoryInterface::class);
        $factory->expects($this->once())
            ->method('get')
            ->with($this->entityType, 'AND')
            ->willReturn($query);
        $this->container
            ->set('entity.query.sql', $factory);
        $database = $this->getMockBuilder('Drupal\\Core\\Database\\Connection')
            ->disableOriginalConstructor()
            ->getMock();
        $this->entityTypeManager
            ->expects($this->any())
            ->method('getDefinition')
            ->will($this->returnValue($this->entityType));
        $this->entityTypeManager
            ->expects($this->any())
            ->method('getActiveDefinition')
            ->will($this->returnValue($this->entityType));
        $this->entityFieldManager
            ->expects($this->any())
            ->method('getFieldStorageDefinitions')
            ->will($this->returnValue($this->fieldDefinitions));
        $this->entityFieldManager
            ->expects($this->any())
            ->method('getActiveFieldStorageDefinitions')
            ->will($this->returnValue($this->fieldDefinitions));
        $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager);
        $result = $this->entityStorage
            ->hasData();
        $this->assertTrue($result, 'hasData returned TRUE');
    }
    
    /**
     * Tests entity ID sanitization.
     */
    public function testCleanIds() {
        $valid_ids = [
            -1,
            0,
            1,
            '-1',
            '0',
            '1',
            0123,
            -0x1a,
            0x1afc,
            -0b111,
            0b101,
            '0123',
            '00123',
            '000123',
            '-0123',
            '-00123',
            '-000123',
            -10.0,
            -1.0,
            0.0,
            1.0,
            10.0,
            -10.0,
            -1.0,
            0.0,
            1.0,
            10.0,
        ];
        $this->fieldDefinitions = $this->mockFieldDefinitions([
            'id',
        ]);
        $this->fieldDefinitions['id']
            ->expects($this->any())
            ->method('getType')
            ->will($this->returnValue('integer'));
        $this->setUpEntityStorage();
        $this->entityType
            ->expects($this->any())
            ->method('getKey')
            ->will($this->returnValueMap([
            [
                'id',
                'id',
            ],
        ]));
        $method = new \ReflectionMethod($this->entityStorage, 'cleanIds');
        $method->setAccessible(TRUE);
        $this->assertEquals($valid_ids, $method->invoke($this->entityStorage, $valid_ids));
        $invalid_ids = [
            '--1',
            '-0x1A',
            '0x1AFC',
            '-0b111',
            '0b101',
            'a',
            FALSE,
            TRUE,
            NULL,
            '32acb',
            123.123,
            123.678,
        ];
        $this->assertEquals([], $method->invoke($this->entityStorage, $invalid_ids));
    }
    
    /**
     * @covers ::getFieldStorageDefinitions
     *
     * @expectedDeprecation SqlContentEntityStorage::getFieldStorageDefinitions() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions() instead. See https://www.drupal.org/node/3040966.
     *
     * @group legacy
     */
    public function testGetFieldStorageDefinitions() {
        $this->setUpEntityStorage();
        $this->entityStorage
            ->getFieldStorageDefinitions();
    }
    
    /**
     * Sets up the module handler with no implementations.
     */
    protected function setUpModuleHandlerNoImplementations() {
        $this->moduleHandler
            ->expects($this->any())
            ->method('getImplementations')
            ->will($this->returnValueMap([
            [
                'entity_load',
                [],
            ],
            [
                $this->entityTypeId . '_load',
                [],
            ],
        ]));
        $this->container
            ->set('module_handler', $this->moduleHandler);
    }

}

/**
 * Provides an entity with dummy implementations of static methods, because
 * those cannot be mocked.
 */
abstract class SqlContentEntityStorageTestEntityInterface implements EntityInterface {
    
    /**
     * {@inheritdoc}
     */
    public static function postLoad(EntityStorageInterface $storage, array &$entities) {
    }

}

Classes

Title Deprecated Summary
SqlContentEntityStorageTest @coversDefaultClass \Drupal\Core\Entity\Sql\SqlContentEntityStorage @group Entity
SqlContentEntityStorageTestEntityInterface Provides an entity with dummy implementations of static methods, because those cannot be mocked.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.