Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Entity / Sql / SqlContentEntityStorageTest.php
1 <?php
2
3 /**
4  * @file
5  * Contains \Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTest.
6  */
7
8 namespace Drupal\Tests\Core\Entity\Sql;
9
10 use Drupal\Core\Cache\CacheBackendInterface;
11 use Drupal\Core\Entity\EntityFieldManagerInterface;
12 use Drupal\Core\Entity\EntityInterface;
13 use Drupal\Core\Entity\EntityManager;
14 use Drupal\Core\Entity\EntityStorageInterface;
15 use Drupal\Core\Entity\EntityTypeManagerInterface;
16 use Drupal\Core\Entity\Query\QueryFactoryInterface;
17 use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
18 use Drupal\Core\Language\Language;
19 use Drupal\Tests\UnitTestCase;
20 use Symfony\Component\DependencyInjection\ContainerBuilder;
21
22 /**
23  * @coversDefaultClass \Drupal\Core\Entity\Sql\SqlContentEntityStorage
24  * @group Entity
25  */
26 class SqlContentEntityStorageTest extends UnitTestCase {
27
28   /**
29    * The content entity database storage used in this test.
30    *
31    * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage|\PHPUnit_Framework_MockObject_MockObject
32    */
33   protected $entityStorage;
34
35   /**
36    * The mocked entity type used in this test.
37    *
38    * @var \Drupal\Core\Entity\ContentEntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
39    */
40   protected $entityType;
41
42   /**
43    * An array of field definitions used for this test, keyed by field name.
44    *
45    * @var \Drupal\Core\Field\BaseFieldDefinition[]|\PHPUnit_Framework_MockObject_MockObject[]
46    */
47   protected $fieldDefinitions = [];
48
49   /**
50    * The mocked entity manager used in this test.
51    *
52    * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
53    */
54   protected $entityManager;
55
56   /**
57    * The mocked entity type manager used in this test.
58    *
59    * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject
60    */
61   protected $entityTypeManager;
62
63   /**
64    * The mocked entity field manager used in this test.
65    *
66    * @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit_Framework_MockObject_MockObject
67    */
68   protected $entityFieldManager;
69
70   /**
71    * The entity type ID.
72    *
73    * @var string
74    */
75   protected $entityTypeId = 'entity_test';
76
77   /**
78    * The dependency injection container.
79    *
80    * @var \Symfony\Component\DependencyInjection\ContainerBuilder
81    */
82   protected $container;
83
84   /**
85    * The module handler.
86    *
87    * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
88    */
89   protected $moduleHandler;
90
91   /**
92    * The cache backend to use.
93    *
94    * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
95    */
96   protected $cache;
97
98   /**
99    * The language manager.
100    *
101    * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
102    */
103   protected $languageManager;
104
105   /**
106    * The database connection to use.
107    *
108    * @var \Drupal\Core\Database\Connection|\PHPUnit_Framework_MockObject_MockObject
109    */
110   protected $connection;
111
112   /**
113    * {@inheritdoc}
114    */
115   protected function setUp() {
116     $this->entityType = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface');
117     $this->entityType->expects($this->any())
118       ->method('id')
119       ->will($this->returnValue($this->entityTypeId));
120
121     $this->container = new ContainerBuilder();
122     \Drupal::setContainer($this->container);
123
124     $this->entityManager = new EntityManager();
125     // Inject the container into entity.manager so it can defer to
126     // entity_type.manager and other services.
127     $this->entityManager->setContainer($this->container);
128     $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class);
129     $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class);
130     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
131     $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
132     $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
133     $this->languageManager->expects($this->any())
134       ->method('getDefaultLanguage')
135       ->will($this->returnValue(new Language(['langcode' => 'en'])));
136     $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
137       ->disableOriginalConstructor()
138       ->getMock();
139
140     $this->container->set('entity.manager', $this->entityManager);
141     $this->container->set('entity_type.manager', $this->entityTypeManager);
142     $this->container->set('entity_field.manager', $this->entityFieldManager);
143   }
144
145   /**
146    * Tests SqlContentEntityStorage::getBaseTable().
147    *
148    * @param string $base_table
149    *   The base table to be returned by the mocked entity type.
150    * @param string $expected
151    *   The expected return value of
152    *   SqlContentEntityStorage::getBaseTable().
153    *
154    * @covers ::__construct
155    * @covers ::getBaseTable
156    *
157    * @dataProvider providerTestGetBaseTable
158    */
159   public function testGetBaseTable($base_table, $expected) {
160     $this->entityType->expects($this->once())
161       ->method('getBaseTable')
162       ->willReturn($base_table);
163
164     $this->setUpEntityStorage();
165
166     $this->assertSame($expected, $this->entityStorage->getBaseTable());
167   }
168
169   /**
170    * Provides test data for testGetBaseTable().
171    *
172    * @return array[]
173    *   An nested array where each inner array has the base table to be returned
174    *   by the mocked entity type as the first value and the expected return
175    *   value of SqlContentEntityStorage::getBaseTable() as the second
176    *   value.
177    */
178   public function providerTestGetBaseTable() {
179     return [
180       // Test that the entity type's base table is used, if provided.
181       ['entity_test', 'entity_test'],
182       // Test that the storage falls back to the entity type ID.
183       [NULL, 'entity_test'],
184     ];
185   }
186
187   /**
188    * Tests SqlContentEntityStorage::getRevisionTable().
189    *
190    * @param string $revision_table
191    *   The revision table to be returned by the mocked entity type.
192    * @param string $expected
193    *   The expected return value of
194    *   SqlContentEntityStorage::getRevisionTable().
195    *
196    * @covers ::__construct
197    * @covers ::getRevisionTable
198    *
199    * @dataProvider providerTestGetRevisionTable
200    */
201   public function testGetRevisionTable($revision_table, $expected) {
202     $this->entityType->expects($this->once())
203       ->method('isRevisionable')
204       ->will($this->returnValue(TRUE));
205     $this->entityType->expects($this->once())
206       ->method('getRevisionTable')
207       ->will($this->returnValue($revision_table));
208
209     $this->setUpEntityStorage();
210
211     $this->assertSame($expected, $this->entityStorage->getRevisionTable());
212   }
213
214   /**
215    * Provides test data for testGetRevisionTable().
216    *
217    * @return array[]
218    *   An nested array where each inner array has the revision table to be
219    *   returned by the mocked entity type as the first value and the expected
220    *   return value of SqlContentEntityStorage::getRevisionTable() as the
221    *   second value.
222    */
223   public function providerTestGetRevisionTable() {
224     return [
225       // Test that the entity type's revision table is used, if provided.
226       ['entity_test_revision', 'entity_test_revision'],
227       // Test that the storage falls back to the entity type ID with a
228       // '_revision' suffix.
229       [NULL, 'entity_test_revision'],
230     ];
231   }
232
233   /**
234    * Tests SqlContentEntityStorage::getDataTable().
235    *
236    * @covers ::__construct
237    * @covers ::getDataTable
238    */
239   public function testGetDataTable() {
240     $this->entityType->expects($this->once())
241       ->method('isTranslatable')
242       ->will($this->returnValue(TRUE));
243     $this->entityType->expects($this->exactly(1))
244       ->method('getDataTable')
245       ->will($this->returnValue('entity_test_field_data'));
246
247     $this->setUpEntityStorage();
248
249     $this->assertSame('entity_test_field_data', $this->entityStorage->getDataTable());
250   }
251
252   /**
253    * Tests SqlContentEntityStorage::getRevisionDataTable().
254    *
255    * @param string $revision_data_table
256    *   The revision data table to be returned by the mocked entity type.
257    * @param string $expected
258    *   The expected return value of
259    *   SqlContentEntityStorage::getRevisionDataTable().
260    *
261    * @covers ::__construct
262    * @covers ::getRevisionDataTable
263    *
264    * @dataProvider providerTestGetRevisionDataTable
265    */
266   public function testGetRevisionDataTable($revision_data_table, $expected) {
267     $this->entityType->expects($this->once())
268       ->method('isRevisionable')
269       ->will($this->returnValue(TRUE));
270     $this->entityType->expects($this->once())
271       ->method('isTranslatable')
272       ->will($this->returnValue(TRUE));
273     $this->entityType->expects($this->exactly(1))
274       ->method('getDataTable')
275       ->will($this->returnValue('entity_test_field_data'));
276     $this->entityType->expects($this->once())
277       ->method('getRevisionDataTable')
278       ->will($this->returnValue($revision_data_table));
279
280     $this->setUpEntityStorage();
281
282     $actual = $this->entityStorage->getRevisionDataTable();
283     $this->assertSame($expected, $actual);
284   }
285
286   /**
287    * Provides test data for testGetRevisionDataTable().
288    *
289    * @return array[]
290    *   An nested array where each inner array has the revision data table to be
291    *   returned by the mocked entity type as the first value and the expected
292    *   return value of SqlContentEntityStorage::getRevisionDataTable() as
293    *   the second value.
294    */
295   public function providerTestGetRevisionDataTable() {
296     return [
297       // Test that the entity type's revision data table is used, if provided.
298       ['entity_test_field_revision', 'entity_test_field_revision'],
299       // Test that the storage falls back to the entity type ID with a
300       // '_field_revision' suffix.
301       [NULL, 'entity_test_field_revision'],
302     ];
303   }
304
305   /**
306    * Tests ContentEntityDatabaseStorage::onEntityTypeCreate().
307    *
308    * @covers ::__construct
309    * @covers ::onEntityTypeCreate
310    * @covers ::getTableMapping
311    */
312   public function testOnEntityTypeCreate() {
313     $columns = [
314       'value' => [
315         'type' => 'int',
316       ],
317     ];
318
319     $this->fieldDefinitions = $this->mockFieldDefinitions(['id']);
320     $this->fieldDefinitions['id']->expects($this->any())
321       ->method('getColumns')
322       ->will($this->returnValue($columns));
323     $this->fieldDefinitions['id']->expects($this->once())
324       ->method('getSchema')
325       ->will($this->returnValue(['columns' => $columns]));
326
327     $this->entityType->expects($this->once())
328       ->method('getKeys')
329       ->will($this->returnValue(['id' => 'id']));
330     $this->entityType->expects($this->any())
331       ->method('getKey')
332       ->will($this->returnValueMap([
333         // EntityStorageBase::__construct()
334         ['id', 'id'],
335         // ContentEntityStorageBase::__construct()
336         ['uuid', NULL],
337         ['bundle', NULL],
338         // SqlContentEntityStorageSchema::initializeBaseTable()
339         ['id' => 'id'],
340         // SqlContentEntityStorageSchema::processBaseTable()
341         ['id' => 'id'],
342       ]));
343
344     $this->setUpEntityStorage();
345
346     $expected = [
347       'description' => 'The base table for entity_test entities.',
348       'fields' => [
349         'id' => [
350           'type' => 'serial',
351           'not null' => TRUE,
352         ],
353       ],
354       'primary key' => ['id'],
355       'unique keys' => [],
356       'indexes' => [],
357       'foreign keys' => [],
358     ];
359
360     $schema_handler = $this->getMockBuilder('Drupal\Core\Database\Schema')
361       ->disableOriginalConstructor()
362       ->getMock();
363     $schema_handler->expects($this->any())
364       ->method('createTable')
365       ->with($this->equalTo('entity_test'), $this->equalTo($expected));
366
367     $this->connection->expects($this->once())
368       ->method('schema')
369       ->will($this->returnValue($schema_handler));
370
371     $storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
372       ->setConstructorArgs([$this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager])
373       ->setMethods(['getStorageSchema'])
374       ->getMock();
375
376     $key_value = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
377     $schema_handler = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema')
378       ->setConstructorArgs([$this->entityManager, $this->entityType, $storage, $this->connection])
379       ->setMethods(['installedStorageSchema', 'createSharedTableSchema'])
380       ->getMock();
381     $schema_handler
382       ->expects($this->any())
383       ->method('installedStorageSchema')
384       ->will($this->returnValue($key_value));
385
386     $storage
387       ->expects($this->any())
388       ->method('getStorageSchema')
389       ->will($this->returnValue($schema_handler));
390
391     $storage->onEntityTypeCreate($this->entityType);
392   }
393
394   /**
395    * Tests getTableMapping() with an empty entity type.
396    *
397    * @covers ::__construct
398    * @covers ::getTableMapping
399    */
400   public function testGetTableMappingEmpty() {
401     $this->setUpEntityStorage();
402
403     $mapping = $this->entityStorage->getTableMapping();
404     $this->assertSame(['entity_test'], $mapping->getTableNames());
405     $this->assertSame([], $mapping->getFieldNames('entity_test'));
406     $this->assertSame([], $mapping->getExtraColumns('entity_test'));
407   }
408
409   /**
410    * Tests getTableMapping() with a simple entity type.
411    *
412    * @param string[] $entity_keys
413    *   A map of entity keys to use for the mocked entity type.
414    *
415    * @covers ::__construct
416    * @covers ::getTableMapping
417    *
418    * @dataProvider providerTestGetTableMappingSimple()
419    */
420   public function testGetTableMappingSimple(array $entity_keys) {
421     $this->entityType->expects($this->any())
422       ->method('getKey')
423       ->will($this->returnValueMap([
424         ['id', $entity_keys['id']],
425         ['uuid', $entity_keys['uuid']],
426         ['bundle', $entity_keys['bundle']],
427       ]));
428
429     $this->setUpEntityStorage();
430
431     $mapping = $this->entityStorage->getTableMapping();
432
433     $this->assertEquals(['entity_test'], $mapping->getTableNames());
434
435     $expected = array_values(array_filter($entity_keys));
436     $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
437
438     $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
439   }
440
441   /**
442    * Tests getTableMapping() with a simple entity type with some base fields.
443    *
444    * @param string[] $entity_keys
445    *   A map of entity keys to use for the mocked entity type.
446    *
447    * @covers ::__construct
448    * @covers ::getTableMapping
449    *
450    * @dataProvider providerTestGetTableMappingSimple()
451    */
452   public function testGetTableMappingSimpleWithFields(array $entity_keys) {
453     $base_field_names = ['title', 'description', 'owner'];
454     $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
455     $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
456     $this->setUpEntityStorage();
457
458     $mapping = $this->entityStorage->getTableMapping();
459     $this->assertEquals(['entity_test'], $mapping->getTableNames());
460     $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
461     $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
462   }
463
464   /**
465    * Provides test data for testGetTableMappingSimple().
466    *
467    * @return array[]
468    *   A nested array, where each inner array has a single value being a  map of
469    *   entity keys to use for the mocked entity type.
470    */
471   public function providerTestGetTableMappingSimple() {
472     return [
473       [['id' => 'test_id', 'bundle' => NULL, 'uuid' => NULL]],
474       [['id' => 'test_id', 'bundle' => 'test_bundle', 'uuid' => NULL]],
475       [['id' => 'test_id', 'bundle' => NULL, 'uuid' => 'test_uuid']],
476       [['id' => 'test_id', 'bundle' => 'test_bundle', 'uuid' => 'test_uuid']],
477     ];
478   }
479
480   /**
481    * Tests getTableMapping() with a base field that requires a dedicated table.
482    *
483    * @covers ::__construct
484    * @covers ::getTableMapping
485    */
486   public function testGetTableMappingSimpleWithDedicatedStorageFields() {
487     $base_field_names = ['multi_valued_base_field'];
488
489     // Set up one entity key in order to have a base table.
490     $this->fieldDefinitions = $this->mockFieldDefinitions(['test_id']);
491
492     // Set up the multi-valued base field.
493     $this->fieldDefinitions += $this->mockFieldDefinitions($base_field_names, [
494       'hasCustomStorage' => FALSE,
495       'isMultiple' => TRUE,
496       'getTargetEntityTypeId' => 'entity_test',
497     ]);
498
499     $this->setUpEntityStorage();
500
501     $mapping = $this->entityStorage->getTableMapping();
502     $this->assertEquals(['entity_test', 'entity_test__multi_valued_base_field'], $mapping->getTableNames());
503     $this->assertEquals($base_field_names, $mapping->getFieldNames('entity_test__multi_valued_base_field'));
504
505     $extra_columns = [
506       'bundle',
507       'deleted',
508       'entity_id',
509       'revision_id',
510       'langcode',
511       'delta',
512     ];
513     $this->assertEquals($extra_columns, $mapping->getExtraColumns('entity_test__multi_valued_base_field'));
514   }
515
516   /**
517    * Tests getTableMapping() with a revisionable, non-translatable entity type.
518    *
519    * @param string[] $entity_keys
520    *   A map of entity keys to use for the mocked entity type.
521    *
522    * @covers ::__construct
523    * @covers ::getTableMapping
524    *
525    * @dataProvider providerTestGetTableMappingSimple()
526    */
527   public function testGetTableMappingRevisionable(array $entity_keys) {
528     // This allows to re-use the data provider.
529     $entity_keys = [
530       'id' => $entity_keys['id'],
531       'revision' => 'test_revision',
532       'bundle' => $entity_keys['bundle'],
533       'uuid' => $entity_keys['uuid'],
534     ];
535
536     $this->entityType->expects($this->exactly(2))
537       ->method('isRevisionable')
538       ->will($this->returnValue(TRUE));
539     $this->entityType->expects($this->any())
540       ->method('getKey')
541       ->will($this->returnValueMap([
542         ['id', $entity_keys['id']],
543         ['uuid', $entity_keys['uuid']],
544         ['bundle', $entity_keys['bundle']],
545         ['revision', $entity_keys['revision']],
546       ]));
547     $this->entityType->expects($this->any())
548       ->method('getRevisionMetadataKeys')
549       ->will($this->returnValue([]));
550
551     $this->setUpEntityStorage();
552
553     $mapping = $this->entityStorage->getTableMapping();
554
555     $expected = ['entity_test', 'entity_test_revision'];
556     $this->assertEquals($expected, $mapping->getTableNames());
557
558     $expected = array_values(array_filter($entity_keys));
559     $this->assertEquals($expected, $mapping->getFieldNames('entity_test'));
560     $expected = [$entity_keys['id'], $entity_keys['revision']];
561     $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
562
563     $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
564     $this->assertEquals([], $mapping->getExtraColumns('entity_test_revision'));
565   }
566
567   /**
568    * Tests getTableMapping() with a revisionable entity type with fields.
569    *
570    * @param string[] $entity_keys
571    *   A map of entity keys to use for the mocked entity type.
572    *
573    * @covers ::__construct
574    * @covers ::getTableMapping
575    *
576    * @dataProvider providerTestGetTableMappingSimple()
577    */
578   public function testGetTableMappingRevisionableWithFields(array $entity_keys) {
579     // This allows to re-use the data provider.
580     $entity_keys = [
581       'id' => $entity_keys['id'],
582       'revision' => 'test_revision',
583       'bundle' => $entity_keys['bundle'],
584       'uuid' => $entity_keys['uuid'],
585     ];
586
587     // PHPUnit does not allow for multiple data providers.
588     $test_cases = [
589       [],
590       ['revision_created' => 'revision_timestamp'],
591       ['revision_user' => 'revision_uid'],
592       ['revision_log_message' => 'revision_log'],
593       ['revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid'],
594       ['revision_created' => 'revision_timestamp', 'revision_log_message' => 'revision_log'],
595       ['revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'],
596       ['revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'],
597     ];
598     foreach ($test_cases as $revision_metadata_field_names) {
599       $this->setUp();
600
601       $base_field_names = ['title'];
602       $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
603       $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
604
605       $revisionable_field_names = ['description', 'owner'];
606       $field_names = array_merge($field_names, $revisionable_field_names);
607       $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), ['isRevisionable' => TRUE]);
608
609       $this->entityType->expects($this->exactly(2))
610         ->method('isRevisionable')
611         ->will($this->returnValue(TRUE));
612       $this->entityType->expects($this->any())
613         ->method('getKey')
614         ->will($this->returnValueMap([
615           ['id', $entity_keys['id']],
616           ['uuid', $entity_keys['uuid']],
617           ['bundle', $entity_keys['bundle']],
618           ['revision', $entity_keys['revision']],
619         ]));
620
621       $this->entityType->expects($this->any())
622         ->method('getRevisionMetadataKeys')
623         ->will($this->returnValue($revision_metadata_field_names));
624
625       $this->setUpEntityStorage();
626
627       $mapping = $this->entityStorage->getTableMapping();
628
629       $expected = ['entity_test', 'entity_test_revision'];
630       $this->assertEquals($expected, $mapping->getTableNames());
631
632       $this->assertEquals($field_names, $mapping->getFieldNames('entity_test'));
633       $expected = array_merge(
634         [$entity_keys['id'], $entity_keys['revision']],
635         $revisionable_field_names,
636         array_values($revision_metadata_field_names)
637       );
638       $this->assertEquals($expected, $mapping->getFieldNames('entity_test_revision'));
639
640       $this->assertEquals([], $mapping->getExtraColumns('entity_test'));
641       $this->assertEquals([], $mapping->getExtraColumns('entity_test_revision'));
642     }
643   }
644
645   /**
646    * Tests getTableMapping() with a non-revisionable, translatable entity type.
647    *
648    * @param string[] $entity_keys
649    *   A map of entity keys to use for the mocked entity type.
650    *
651    * @covers ::__construct
652    * @covers ::getTableMapping
653    *
654    * @dataProvider providerTestGetTableMappingSimple()
655    */
656   public function testGetTableMappingTranslatable(array $entity_keys) {
657     // This allows to re-use the data provider.
658     $entity_keys['langcode'] = 'langcode';
659
660     $this->entityType->expects($this->atLeastOnce())
661       ->method('isTranslatable')
662       ->will($this->returnValue(TRUE));
663     $this->entityType->expects($this->atLeastOnce())
664       ->method('getDataTable')
665       ->will($this->returnValue('entity_test_field_data'));
666     $this->entityType->expects($this->any())
667       ->method('getKey')
668       ->will($this->returnValueMap([
669         ['id', $entity_keys['id']],
670         ['uuid', $entity_keys['uuid']],
671         ['bundle', $entity_keys['bundle']],
672         ['langcode', $entity_keys['langcode']],
673       ]));
674
675     $this->setUpEntityStorage();
676
677     $mapping = $this->entityStorage->getTableMapping();
678
679     $expected = ['entity_test', 'entity_test_field_data'];
680     $this->assertEquals($expected, $mapping->getTableNames());
681
682     $expected = array_values(array_filter($entity_keys));
683     $actual = $mapping->getFieldNames('entity_test');
684     $this->assertEquals($expected, $actual);
685     // The UUID is not stored on the data table.
686     $expected = array_values(array_filter([
687       $entity_keys['id'],
688       $entity_keys['bundle'],
689       $entity_keys['langcode'],
690     ]));
691     $actual = $mapping->getFieldNames('entity_test_field_data');
692     $this->assertEquals($expected, $actual);
693
694     $expected = [];
695     $actual = $mapping->getExtraColumns('entity_test');
696     $this->assertEquals($expected, $actual);
697     $actual = $mapping->getExtraColumns('entity_test_field_data');
698     $this->assertEquals($expected, $actual);
699   }
700
701   /**
702    * Tests getTableMapping() with a translatable entity type with fields.
703    *
704    * @param string[] $entity_keys
705    *   A map of entity keys to use for the mocked entity type.
706    *
707    * @covers ::__construct
708    * @covers ::getTableMapping
709    *
710    * @dataProvider providerTestGetTableMappingSimple()
711    */
712   public function testGetTableMappingTranslatableWithFields(array $entity_keys) {
713     // This allows to re-use the data provider.
714     $entity_keys['langcode'] = 'langcode';
715
716     $base_field_names = ['title', 'description', 'owner'];
717     $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
718     $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
719
720     $this->entityType->expects($this->atLeastOnce())
721       ->method('isTranslatable')
722       ->will($this->returnValue(TRUE));
723     $this->entityType->expects($this->atLeastOnce())
724       ->method('getDataTable')
725       ->will($this->returnValue('entity_test_field_data'));
726     $this->entityType->expects($this->any())
727       ->method('getKey')
728       ->will($this->returnValueMap([
729         ['id', $entity_keys['id']],
730         ['uuid', $entity_keys['uuid']],
731         ['bundle', $entity_keys['bundle']],
732         ['langcode', $entity_keys['langcode']],
733       ]));
734
735     $this->setUpEntityStorage();
736
737     $mapping = $this->entityStorage->getTableMapping();
738
739     $expected = ['entity_test', 'entity_test_field_data'];
740     $this->assertEquals($expected, $mapping->getTableNames());
741
742     $expected = array_values(array_filter($entity_keys));
743     $actual = $mapping->getFieldNames('entity_test');
744     $this->assertEquals($expected, $actual);
745     // The UUID is not stored on the data table.
746     $expected = array_merge(array_filter([
747       $entity_keys['id'],
748       $entity_keys['bundle'],
749       $entity_keys['langcode'],
750     ]), $base_field_names);
751     $actual = $mapping->getFieldNames('entity_test_field_data');
752     $this->assertEquals($expected, $actual);
753
754     $expected = [];
755     $actual = $mapping->getExtraColumns('entity_test');
756     $this->assertEquals($expected, $actual);
757     $actual = $mapping->getExtraColumns('entity_test_field_data');
758     $this->assertEquals($expected, $actual);
759   }
760
761   /**
762    * Tests getTableMapping() with a revisionable, translatable entity type.
763    *
764    * @param string[] $entity_keys
765    *   A map of entity keys to use for the mocked entity type.
766    *
767    * @covers ::__construct
768    * @covers ::getTableMapping
769    *
770    * @dataProvider providerTestGetTableMappingSimple()
771    */
772   public function testGetTableMappingRevisionableTranslatable(array $entity_keys) {
773     // This allows to re-use the data provider.
774     $entity_keys = [
775       'id' => $entity_keys['id'],
776       'revision' => 'test_revision',
777       'bundle' => $entity_keys['bundle'],
778       'uuid' => $entity_keys['uuid'],
779       'langcode' => 'langcode',
780     ];
781     $revision_metadata_keys = [
782       'revision_created' => 'revision_timestamp',
783       'revision_user' => 'revision_uid',
784       'revision_log_message' => 'revision_log'
785     ];
786
787     $this->entityType->expects($this->atLeastOnce())
788       ->method('isRevisionable')
789       ->will($this->returnValue(TRUE));
790     $this->entityType->expects($this->atLeastOnce())
791       ->method('isTranslatable')
792       ->will($this->returnValue(TRUE));
793     $this->entityType->expects($this->atLeastOnce())
794       ->method('getDataTable')
795       ->will($this->returnValue('entity_test_field_data'));
796     $this->entityType->expects($this->any())
797       ->method('getKey')
798       ->will($this->returnValueMap([
799         ['id', $entity_keys['id']],
800         ['uuid', $entity_keys['uuid']],
801         ['bundle', $entity_keys['bundle']],
802         ['revision', $entity_keys['revision']],
803         ['langcode', $entity_keys['langcode']],
804       ]));
805     $this->entityType->expects($this->any())
806       ->method('getRevisionMetadataKeys')
807       ->will($this->returnValue($revision_metadata_keys));
808
809     $this->setUpEntityStorage();
810
811     $mapping = $this->entityStorage->getTableMapping();
812
813     $expected = [
814       'entity_test',
815       'entity_test_field_data',
816       'entity_test_revision',
817       'entity_test_field_revision',
818     ];
819     $this->assertEquals($expected, $mapping->getTableNames());
820
821     // The default language code is stored on the base table.
822     $expected = array_values(array_filter([
823       $entity_keys['id'],
824       $entity_keys['revision'],
825       $entity_keys['bundle'],
826       $entity_keys['uuid'],
827       $entity_keys['langcode'],
828     ]));
829     $actual = $mapping->getFieldNames('entity_test');
830     $this->assertEquals($expected, $actual);
831     // The revision table on the other hand does not store the bundle and the
832     // UUID.
833     $expected = array_values(array_filter([
834       $entity_keys['id'],
835       $entity_keys['revision'],
836       $entity_keys['langcode'],
837     ]));
838     $expected = array_merge($expected, array_values($revision_metadata_keys));
839     $actual = $mapping->getFieldNames('entity_test_revision');
840     $this->assertEquals($expected, $actual);
841     // The UUID is not stored on the data table.
842     $expected = array_values(array_filter([
843       $entity_keys['id'],
844       $entity_keys['revision'],
845       $entity_keys['bundle'],
846       $entity_keys['langcode'],
847     ]));
848     $actual = $mapping->getFieldNames('entity_test_field_data');
849     $this->assertEquals($expected, $actual);
850     // The data revision also does not store the bundle.
851     $expected = array_values(array_filter([
852       $entity_keys['id'],
853       $entity_keys['revision'],
854       $entity_keys['langcode'],
855     ]));
856     $actual = $mapping->getFieldNames('entity_test_field_revision');
857     $this->assertEquals($expected, $actual);
858
859     $expected = [];
860     $actual = $mapping->getExtraColumns('entity_test');
861     $this->assertEquals($expected, $actual);
862     $actual = $mapping->getExtraColumns('entity_test_revision');
863     $this->assertEquals($expected, $actual);
864     $actual = $mapping->getExtraColumns('entity_test_field_data');
865     $this->assertEquals($expected, $actual);
866     $actual = $mapping->getExtraColumns('entity_test_field_revision');
867     $this->assertEquals($expected, $actual);
868   }
869
870   /**
871    * Tests getTableMapping() with a complex entity type with fields.
872    *
873    * @param string[] $entity_keys
874    *   A map of entity keys to use for the mocked entity type.
875    *
876    * @covers ::__construct
877    * @covers ::getTableMapping
878    *
879    * @dataProvider providerTestGetTableMappingSimple()
880    */
881   public function testGetTableMappingRevisionableTranslatableWithFields(array $entity_keys) {
882     // This allows to re-use the data provider.
883     $entity_keys = [
884       'id' => $entity_keys['id'],
885       'revision' => 'test_revision',
886       'bundle' => $entity_keys['bundle'],
887       'uuid' => $entity_keys['uuid'],
888       'langcode' => 'langcode',
889     ];
890
891     // PHPUnit does not allow for multiple data providers.
892     $test_cases = [
893       [],
894       ['revision_created' => 'revision_timestamp'],
895       ['revision_user' => 'revision_uid'],
896       ['revision_log_message' => 'revision_log'],
897       ['revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid'],
898       ['revision_created' => 'revision_timestamp', 'revision_log_message' => 'revision_log'],
899       ['revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'],
900       ['revision_created' => 'revision_timestamp', 'revision_user' => 'revision_uid', 'revision_log_message' => 'revision_log'],
901     ];
902     foreach ($test_cases as $revision_metadata_field_names) {
903       $this->setUp();
904
905       $base_field_names = ['title'];
906       $field_names = array_merge(array_values(array_filter($entity_keys)), $base_field_names);
907       $this->fieldDefinitions = $this->mockFieldDefinitions($field_names);
908
909       $revisionable_field_names = ['description', 'owner'];
910       $this->fieldDefinitions += $this->mockFieldDefinitions(array_merge($revisionable_field_names, array_values($revision_metadata_field_names)), ['isRevisionable' => TRUE]);
911
912       $this->entityType->expects($this->atLeastOnce())
913         ->method('isRevisionable')
914         ->will($this->returnValue(TRUE));
915       $this->entityType->expects($this->atLeastOnce())
916         ->method('isTranslatable')
917         ->will($this->returnValue(TRUE));
918       $this->entityType->expects($this->atLeastOnce())
919         ->method('getDataTable')
920         ->will($this->returnValue('entity_test_field_data'));
921       $this->entityType->expects($this->any())
922         ->method('getKey')
923         ->will($this->returnValueMap([
924           ['id', $entity_keys['id']],
925           ['uuid', $entity_keys['uuid']],
926           ['bundle', $entity_keys['bundle']],
927           ['revision', $entity_keys['revision']],
928           ['langcode', $entity_keys['langcode']],
929         ]));
930       $this->entityType->expects($this->any())
931         ->method('getRevisionMetadataKeys')
932         ->will($this->returnValue($revision_metadata_field_names));
933
934       $this->setUpEntityStorage();
935
936       $mapping = $this->entityStorage->getTableMapping();
937
938       $expected = [
939         'entity_test',
940         'entity_test_field_data',
941         'entity_test_revision',
942         'entity_test_field_revision',
943       ];
944       $this->assertEquals($expected, $mapping->getTableNames());
945
946       $expected = [
947         'entity_test',
948         'entity_test_field_data',
949         'entity_test_revision',
950         'entity_test_field_revision',
951       ];
952       $this->assertEquals($expected, $mapping->getTableNames());
953
954       // The default language code is not stored on the base table.
955       $expected = array_values(array_filter([
956         $entity_keys['id'],
957         $entity_keys['revision'],
958         $entity_keys['bundle'],
959         $entity_keys['uuid'],
960         $entity_keys['langcode'],
961       ]));
962       $actual = $mapping->getFieldNames('entity_test');
963       $this->assertEquals($expected, $actual);
964       // The revision table on the other hand does not store the bundle and the
965       // UUID.
966       $expected = array_merge(array_filter([
967         $entity_keys['id'],
968         $entity_keys['revision'],
969         $entity_keys['langcode'],
970       ]), array_values($revision_metadata_field_names));
971       $actual = $mapping->getFieldNames('entity_test_revision');
972       $this->assertEquals($expected, $actual);
973       // The UUID is not stored on the data table.
974       $expected = array_merge(array_filter([
975         $entity_keys['id'],
976         $entity_keys['revision'],
977         $entity_keys['bundle'],
978         $entity_keys['langcode'],
979       ]), $base_field_names, $revisionable_field_names);
980       $actual = $mapping->getFieldNames('entity_test_field_data');
981       $this->assertEquals($expected, $actual);
982       // The data revision also does not store the bundle.
983       $expected = array_merge(array_filter([
984         $entity_keys['id'],
985         $entity_keys['revision'],
986         $entity_keys['langcode'],
987       ]), $revisionable_field_names);
988       $actual = $mapping->getFieldNames('entity_test_field_revision');
989       $this->assertEquals($expected, $actual);
990
991       $expected = [];
992       $actual = $mapping->getExtraColumns('entity_test');
993       $this->assertEquals($expected, $actual);
994       $actual = $mapping->getExtraColumns('entity_test_revision');
995       $this->assertEquals($expected, $actual);
996       $actual = $mapping->getExtraColumns('entity_test_field_data');
997       $this->assertEquals($expected, $actual);
998       $actual = $mapping->getExtraColumns('entity_test_field_revision');
999       $this->assertEquals($expected, $actual);
1000     }
1001   }
1002
1003   /**
1004    * @covers ::create
1005    */
1006   public function testCreate() {
1007     $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
1008
1009     $language = new Language(['id' => 'en']);
1010     $language_manager->expects($this->any())
1011       ->method('getCurrentLanguage')
1012       ->will($this->returnValue($language));
1013
1014     $this->container->set('language_manager', $language_manager);
1015     $this->container->set('module_handler', $this->moduleHandler);
1016
1017     $entity = $this->getMockBuilder('Drupal\Core\Entity\ContentEntityBase')
1018       ->disableOriginalConstructor()
1019       ->setMethods(['id'])
1020       ->getMockForAbstractClass();
1021
1022     $this->entityType->expects($this->atLeastOnce())
1023       ->method('id')
1024       ->will($this->returnValue($this->entityTypeId));
1025     $this->entityType->expects($this->atLeastOnce())
1026       ->method('getClass')
1027       ->will($this->returnValue(get_class($entity)));
1028     $this->entityType->expects($this->atLeastOnce())
1029       ->method('getKeys')
1030       ->will($this->returnValue(['id' => 'id']));
1031
1032     // ContentEntityStorageBase iterates over the entity which calls this method
1033     // internally in ContentEntityBase::getProperties().
1034     $this->entityFieldManager->expects($this->once())
1035       ->method('getFieldDefinitions')
1036       ->will($this->returnValue([]));
1037
1038     $this->entityType->expects($this->atLeastOnce())
1039       ->method('isRevisionable')
1040       ->will($this->returnValue(FALSE));
1041     $this->entityTypeManager->expects($this->atLeastOnce())
1042       ->method('getDefinition')
1043       ->with($this->entityType->id())
1044       ->will($this->returnValue($this->entityType));
1045
1046     $this->setUpEntityStorage();
1047
1048     $entity = $this->entityStorage->create();
1049     $entity->expects($this->atLeastOnce())
1050       ->method('id')
1051       ->will($this->returnValue('foo'));
1052
1053     $this->assertInstanceOf('Drupal\Core\Entity\EntityInterface', $entity);
1054     $this->assertSame('foo', $entity->id());
1055     $this->assertTrue($entity->isNew());
1056   }
1057
1058   /**
1059    * Returns a set of mock field definitions for the given names.
1060    *
1061    * @param array $field_names
1062    *   An array of field names.
1063    * @param array $methods
1064    *   (optional) An associative array of mock method return values keyed by
1065    *   method name.
1066    *
1067    * @return \Drupal\Tests\Core\Field\TestBaseFieldDefinitionInterface[]|\PHPUnit_Framework_MockObject_MockObject[]
1068    *   An array of mock base field definitions.
1069    */
1070   protected function mockFieldDefinitions(array $field_names, $methods = []) {
1071     $field_definitions = [];
1072     $definition = $this->getMock('Drupal\Tests\Core\Field\TestBaseFieldDefinitionInterface');
1073
1074     // Assign common method return values.
1075     $methods += [
1076       'isBaseField' => TRUE,
1077     ];
1078     foreach ($methods as $method => $result) {
1079       $definition
1080         ->expects($this->any())
1081         ->method($method)
1082         ->will($this->returnValue($result));
1083     }
1084
1085     // Assign field names to mock definitions.
1086     foreach ($field_names as $field_name) {
1087       $field_definitions[$field_name] = clone $definition;
1088       $field_definitions[$field_name]
1089         ->expects($this->any())
1090         ->method('getName')
1091         ->will($this->returnValue($field_name));
1092     }
1093
1094     return $field_definitions;
1095   }
1096
1097   /**
1098    * Sets up the content entity database storage.
1099    */
1100   protected function setUpEntityStorage() {
1101     $this->connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
1102       ->disableOriginalConstructor()
1103       ->getMock();
1104
1105     $this->entityTypeManager->expects($this->any())
1106       ->method('getDefinition')
1107       ->will($this->returnValue($this->entityType));
1108
1109     $this->entityFieldManager->expects($this->any())
1110       ->method('getFieldStorageDefinitions')
1111       ->will($this->returnValue($this->fieldDefinitions));
1112
1113     $this->entityFieldManager->expects($this->any())
1114       ->method('getBaseFieldDefinitions')
1115       ->will($this->returnValue($this->fieldDefinitions));
1116
1117     $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager);
1118   }
1119
1120   /**
1121    * @covers ::doLoadMultiple
1122    * @covers ::buildCacheId
1123    * @covers ::getFromPersistentCache
1124    */
1125   public function testLoadMultiplePersistentCached() {
1126     $this->setUpModuleHandlerNoImplementations();
1127
1128     $key = 'values:' . $this->entityTypeId . ':1';
1129     $id = 1;
1130     $entity = $this->getMockBuilder('\Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTestEntityInterface')
1131       ->getMockForAbstractClass();
1132     $entity->expects($this->any())
1133       ->method('id')
1134       ->will($this->returnValue($id));
1135
1136     $this->entityType->expects($this->atLeastOnce())
1137       ->method('isPersistentlyCacheable')
1138       ->will($this->returnValue(TRUE));
1139     $this->entityType->expects($this->atLeastOnce())
1140       ->method('id')
1141       ->will($this->returnValue($this->entityTypeId));
1142     $this->entityType->expects($this->atLeastOnce())
1143       ->method('getClass')
1144       ->will($this->returnValue(get_class($entity)));
1145
1146     $this->cache->expects($this->once())
1147       ->method('getMultiple')
1148       ->with([$key])
1149       ->will($this->returnValue([$key => (object) ['data' => $entity]]));
1150     $this->cache->expects($this->never())
1151       ->method('set');
1152
1153     $this->setUpEntityStorage();
1154     $entities = $this->entityStorage->loadMultiple([$id]);
1155     $this->assertEquals($entity, $entities[$id]);
1156   }
1157
1158   /**
1159    * @covers ::doLoadMultiple
1160    * @covers ::buildCacheId
1161    * @covers ::getFromPersistentCache
1162    * @covers ::setPersistentCache
1163    */
1164   public function testLoadMultipleNoPersistentCache() {
1165     $this->setUpModuleHandlerNoImplementations();
1166
1167     $id = 1;
1168     $entity = $this->getMockBuilder('\Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTestEntityInterface')
1169       ->getMockForAbstractClass();
1170     $entity->expects($this->any())
1171       ->method('id')
1172       ->will($this->returnValue($id));
1173
1174     $this->entityType->expects($this->any())
1175       ->method('isPersistentlyCacheable')
1176       ->will($this->returnValue(FALSE));
1177     $this->entityType->expects($this->atLeastOnce())
1178       ->method('id')
1179       ->will($this->returnValue($this->entityTypeId));
1180     $this->entityType->expects($this->atLeastOnce())
1181       ->method('getClass')
1182       ->will($this->returnValue(get_class($entity)));
1183
1184     // There should be no calls to the cache backend for an entity type without
1185     // persistent caching.
1186     $this->cache->expects($this->never())
1187       ->method('getMultiple');
1188     $this->cache->expects($this->never())
1189       ->method('set');
1190
1191     $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
1192       ->setConstructorArgs([$this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager])
1193       ->setMethods(['getFromStorage', 'invokeStorageLoadHook'])
1194       ->getMock();
1195     $entity_storage->method('invokeStorageLoadHook')
1196       ->willReturn(NULL);
1197     $entity_storage->expects($this->once())
1198       ->method('getFromStorage')
1199       ->with([$id])
1200       ->will($this->returnValue([$id => $entity]));
1201
1202     $entities = $entity_storage->loadMultiple([$id]);
1203     $this->assertEquals($entity, $entities[$id]);
1204   }
1205
1206   /**
1207    * @covers ::doLoadMultiple
1208    * @covers ::buildCacheId
1209    * @covers ::getFromPersistentCache
1210    * @covers ::setPersistentCache
1211    */
1212   public function testLoadMultiplePersistentCacheMiss() {
1213     $this->setUpModuleHandlerNoImplementations();
1214
1215     $id = 1;
1216     $entity = $this->getMockBuilder('\Drupal\Tests\Core\Entity\Sql\SqlContentEntityStorageTestEntityInterface')
1217       ->getMockForAbstractClass();
1218     $entity->expects($this->any())
1219       ->method('id')
1220       ->will($this->returnValue($id));
1221
1222     $this->entityType->expects($this->any())
1223       ->method('isPersistentlyCacheable')
1224       ->will($this->returnValue(TRUE));
1225     $this->entityType->expects($this->atLeastOnce())
1226       ->method('id')
1227       ->will($this->returnValue($this->entityTypeId));
1228     $this->entityType->expects($this->atLeastOnce())
1229       ->method('getClass')
1230       ->will($this->returnValue(get_class($entity)));
1231
1232     // In case of a cache miss, the entity is loaded from the storage and then
1233     // set in the cache.
1234     $key = 'values:' . $this->entityTypeId . ':1';
1235     $this->cache->expects($this->once())
1236       ->method('getMultiple')
1237       ->with([$key])
1238       ->will($this->returnValue([]));
1239     $this->cache->expects($this->once())
1240       ->method('set')
1241       ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, [$this->entityTypeId . '_values', 'entity_field_info']);
1242
1243     $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
1244       ->setConstructorArgs([$this->entityType, $this->connection, $this->entityManager, $this->cache, $this->languageManager])
1245       ->setMethods(['getFromStorage', 'invokeStorageLoadHook'])
1246       ->getMock();
1247     $entity_storage->method('invokeStorageLoadHook')
1248       ->willReturn(NULL);
1249     $entity_storage->expects($this->once())
1250       ->method('getFromStorage')
1251       ->with([$id])
1252       ->will($this->returnValue([$id => $entity]));
1253
1254     $entities = $entity_storage->loadMultiple([$id]);
1255     $this->assertEquals($entity, $entities[$id]);
1256   }
1257
1258   /**
1259    * @covers ::hasData
1260    */
1261   public function testHasData() {
1262     $query = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
1263     $query->expects(($this->once()))
1264       ->method('accessCheck')
1265       ->with(FALSE)
1266       ->willReturn($query);
1267     $query->expects(($this->once()))
1268       ->method('range')
1269       ->with(0, 1)
1270       ->willReturn($query);
1271     $query->expects(($this->once()))
1272       ->method('execute')
1273       ->willReturn([5]);
1274
1275     $factory = $this->getMock(QueryFactoryInterface::class);
1276     $factory->expects($this->once())
1277       ->method('get')
1278       ->with($this->entityType, 'AND')
1279       ->willReturn($query);
1280
1281     $this->container->set('entity.query.sql', $factory);
1282
1283     $database = $this->getMockBuilder('Drupal\Core\Database\Connection')
1284       ->disableOriginalConstructor()
1285       ->getMock();
1286
1287     $this->entityTypeManager->expects($this->any())
1288       ->method('getDefinition')
1289       ->will($this->returnValue($this->entityType));
1290
1291     $this->entityFieldManager->expects($this->any())
1292       ->method('getFieldStorageDefinitions')
1293       ->will($this->returnValue($this->fieldDefinitions));
1294
1295     $this->entityFieldManager->expects($this->any())
1296       ->method('getBaseFieldDefinitions')
1297       ->will($this->returnValue($this->fieldDefinitions));
1298
1299     $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityManager, $this->cache, $this->languageManager);
1300
1301     $result = $this->entityStorage->hasData();
1302
1303     $this->assertTrue($result, 'hasData returned TRUE');
1304   }
1305
1306   /**
1307    * Tests entity ID sanitization.
1308    */
1309   public function testCleanIds() {
1310     $valid_ids = [
1311       -1,
1312       0,
1313       1,
1314       '-1',
1315       '0',
1316       '1',
1317       0123,
1318       -0x1A,
1319       0x1AFC,
1320       -0b111,
1321       0b101,
1322       '0123',
1323       '00123',
1324       '000123',
1325       '-0123',
1326       '-00123',
1327       '-000123',
1328       -10.0,
1329       -1.0,
1330       0.0,
1331       1.0,
1332       10.0,
1333       -10.00,
1334       -1.00,
1335       0.00,
1336       1.00,
1337       10.00,
1338     ];
1339
1340     $this->fieldDefinitions = $this->mockFieldDefinitions(['id']);
1341     $this->fieldDefinitions['id']->expects($this->any())
1342       ->method('getType')
1343       ->will($this->returnValue('integer'));
1344
1345     $this->setUpEntityStorage();
1346
1347     $this->entityType->expects($this->any())
1348       ->method('getKey')
1349       ->will($this->returnValueMap(
1350         [['id', 'id']]
1351       ));
1352
1353     $method = new \ReflectionMethod($this->entityStorage, 'cleanIds');
1354     $method->setAccessible(TRUE);
1355     $this->assertEquals($valid_ids, $method->invoke($this->entityStorage, $valid_ids));
1356
1357     $invalid_ids = [
1358       '--1',
1359       '-0x1A',
1360       '0x1AFC',
1361       '-0b111',
1362       '0b101',
1363       'a',
1364       FALSE,
1365       TRUE,
1366       NULL,
1367       '32acb',
1368       123.123,
1369       123.678,
1370     ];
1371     $this->assertEquals([], $method->invoke($this->entityStorage, $invalid_ids));
1372
1373   }
1374
1375   /**
1376    * Sets up the module handler with no implementations.
1377    */
1378   protected function setUpModuleHandlerNoImplementations() {
1379     $this->moduleHandler->expects($this->any())
1380       ->method('getImplementations')
1381       ->will($this->returnValueMap([
1382         ['entity_load', []],
1383         [$this->entityTypeId . '_load', []]
1384       ]));
1385
1386     $this->container->set('module_handler', $this->moduleHandler);
1387   }
1388
1389 }
1390
1391 /**
1392  * Provides an entity with dummy implementations of static methods, because
1393  * those cannot be mocked.
1394  */
1395 abstract class SqlContentEntityStorageTestEntityInterface implements EntityInterface {
1396
1397   /**
1398    * {@inheritdoc}
1399    */
1400   public static function postLoad(EntityStorageInterface $storage, array &$entities) {
1401   }
1402
1403 }