More updates to stop using dev or alpha or beta versions.
[yaffs-website] / web / core / tests / Drupal / Tests / Core / Entity / Sql / SqlContentEntityStorageSchemaTest.php
1 <?php
2
3 namespace Drupal\Tests\Core\Entity\Sql;
4
5 use Drupal\Core\Entity\ContentEntityType;
6 use Drupal\Core\Entity\ContentEntityTypeInterface;
7 use Drupal\Core\Entity\Sql\DefaultTableMapping;
8 use Drupal\Tests\UnitTestCase;
9
10 /**
11  * @coversDefaultClass \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema
12  * @group Entity
13  */
14 class SqlContentEntityStorageSchemaTest extends UnitTestCase {
15
16   /**
17    * The mocked DB schema handler.
18    *
19    * @var \Drupal\Core\Database\Schema|\PHPUnit_Framework_MockObject_MockObject
20    */
21   protected $dbSchemaHandler;
22
23   /**
24    * The mocked entity manager used in this test.
25    *
26    * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
27    */
28   protected $entityManager;
29
30   /**
31    * The mocked entity type used in this test.
32    *
33    * @var \Drupal\Core\Entity\ContentEntityTypeInterface
34    */
35   protected $entityType;
36
37   /**
38    * The mocked SQL storage used in this test.
39    *
40    * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage|\PHPUnit_Framework_MockObject_MockObject
41    */
42   protected $storage;
43
44   /**
45    * The mocked field definitions used in this test.
46    *
47    * @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]|\PHPUnit_Framework_MockObject_MockObject[]
48    */
49   protected $storageDefinitions;
50
51   /**
52    * The storage schema handler used in this test.
53    *
54    * @var \Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema|\PHPUnit_Framework_MockObject_MockObject.
55    */
56   protected $storageSchema;
57
58   /**
59    * {@inheritdoc}
60    */
61   protected function setUp() {
62     $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
63     $this->storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
64       ->disableOriginalConstructor()
65       ->getMock();
66
67     $this->storage->expects($this->any())
68       ->method('getBaseTable')
69       ->will($this->returnValue('entity_test'));
70
71     // Add an ID field. This also acts as a test for a simple, single-column
72     // field.
73     $this->setUpStorageDefinition('id', [
74       'columns' => [
75         'value' => [
76           'type' => 'int',
77         ],
78       ],
79     ]);
80   }
81
82   /**
83    * Tests the schema for non-revisionable, non-translatable entities.
84    *
85    * @covers ::__construct
86    * @covers ::getEntitySchemaTables
87    * @covers ::initializeBaseTable
88    * @covers ::addTableDefaults
89    * @covers ::getEntityIndexName
90    * @covers ::getFieldIndexes
91    * @covers ::getFieldUniqueKeys
92    * @covers ::getFieldForeignKeys
93    * @covers ::getFieldSchemaData
94    * @covers ::processBaseTable
95    * @covers ::processIdentifierSchema
96    */
97   public function testGetSchemaBase() {
98     $this->entityType = new ContentEntityType([
99       'id' => 'entity_test',
100       'entity_keys' => ['id' => 'id'],
101     ]);
102
103     // Add a field with a 'length' constraint.
104     $this->setUpStorageDefinition('name', [
105       'columns' => [
106         'value' => [
107           'type' => 'varchar',
108           'length' => 255,
109         ],
110       ],
111     ]);
112     // Add a multi-column field.
113     $this->setUpStorageDefinition('description', [
114       'columns' => [
115         'value' => [
116           'type' => 'text',
117         ],
118         'format' => [
119           'type' => 'varchar',
120         ],
121       ],
122     ]);
123     // Add a field with a unique key.
124     $this->setUpStorageDefinition('uuid', [
125       'columns' => [
126         'value' => [
127           'type' => 'varchar',
128           'length' => 128,
129         ],
130       ],
131       'unique keys' => [
132         'value' => ['value'],
133       ],
134     ]);
135     // Add a field with a unique key, specified as column name and length.
136     $this->setUpStorageDefinition('hash', [
137       'columns' => [
138         'value' => [
139           'type' => 'varchar',
140           'length' => 20,
141         ],
142       ],
143       'unique keys' => [
144         'value' => [['value', 10]],
145       ],
146     ]);
147     // Add a field with a multi-column unique key.
148     $this->setUpStorageDefinition('email', [
149       'columns' => [
150         'username' => [
151           'type' => 'varchar',
152         ],
153         'hostname' => [
154           'type' => 'varchar',
155         ],
156         'domain' => [
157           'type' => 'varchar',
158         ]
159       ],
160       'unique keys' => [
161         'email' => ['username', 'hostname', ['domain', 3]],
162       ],
163     ]);
164     // Add a field with an index.
165     $this->setUpStorageDefinition('owner', [
166       'columns' => [
167         'target_id' => [
168           'type' => 'int',
169         ],
170       ],
171       'indexes' => [
172         'target_id' => ['target_id'],
173       ],
174     ]);
175     // Add a field with an index, specified as column name and length.
176     $this->setUpStorageDefinition('translator', [
177       'columns' => [
178         'target_id' => [
179           'type' => 'int',
180         ],
181       ],
182       'indexes' => [
183         'target_id' => [['target_id', 10]],
184       ],
185     ]);
186     // Add a field with a multi-column index.
187     $this->setUpStorageDefinition('location', [
188       'columns' => [
189         'country' => [
190           'type' => 'varchar',
191         ],
192         'state' => [
193           'type' => 'varchar',
194         ],
195         'city' => [
196           'type' => 'varchar',
197         ]
198       ],
199       'indexes' => [
200         'country_state_city' => ['country', 'state', ['city', 10]],
201       ],
202     ]);
203     // Add a field with a foreign key.
204     $this->setUpStorageDefinition('editor', [
205       'columns' => [
206         'target_id' => [
207           'type' => 'int',
208         ],
209       ],
210       'foreign keys' => [
211         'user_id' => [
212           'table' => 'users',
213           'columns' => ['target_id' => 'uid'],
214         ],
215       ],
216     ]);
217     // Add a multi-column field with a foreign key.
218     $this->setUpStorageDefinition('editor_revision', [
219       'columns' => [
220         'target_id' => [
221           'type' => 'int',
222         ],
223         'target_revision_id' => [
224           'type' => 'int',
225         ],
226       ],
227       'foreign keys' => [
228         'user_id' => [
229           'table' => 'users',
230           'columns' => ['target_id' => 'uid'],
231         ],
232       ],
233     ]);
234     // Add a field with a really long index.
235     $this->setUpStorageDefinition('long_index_name', [
236       'columns' => [
237         'long_index_name' => [
238           'type' => 'int',
239         ],
240       ],
241       'indexes' => [
242         'long_index_name_really_long_long_name' => [['long_index_name', 10]],
243       ],
244     ]);
245
246     $expected = [
247       'entity_test' => [
248         'description' => 'The base table for entity_test entities.',
249         'fields' => [
250           'id' => [
251             'type' => 'serial',
252             'not null' => TRUE,
253           ],
254           'name' => [
255             'type' => 'varchar',
256             'length' => 255,
257             'not null' => FALSE,
258           ],
259           'description__value' => [
260             'type' => 'text',
261             'not null' => FALSE,
262           ],
263           'description__format' => [
264             'type' => 'varchar',
265             'not null' => FALSE,
266           ],
267           'uuid' => [
268             'type' => 'varchar',
269             'length' => 128,
270             'not null' => FALSE,
271           ],
272           'hash' => [
273             'type' => 'varchar',
274             'length' => 20,
275             'not null' => FALSE,
276           ],
277           'email__username' => [
278             'type' => 'varchar',
279             'not null' => FALSE,
280           ],
281           'email__hostname' => [
282             'type' => 'varchar',
283             'not null' => FALSE,
284           ],
285           'email__domain' => [
286             'type' => 'varchar',
287             'not null' => FALSE,
288           ],
289           'owner' => [
290             'type' => 'int',
291             'not null' => FALSE,
292           ],
293           'translator' => [
294             'type' => 'int',
295             'not null' => FALSE,
296           ],
297           'location__country' => [
298             'type' => 'varchar',
299             'not null' => FALSE,
300           ],
301           'location__state' => [
302             'type' => 'varchar',
303             'not null' => FALSE,
304           ],
305           'location__city' => [
306             'type' => 'varchar',
307             'not null' => FALSE,
308           ],
309           'editor' => [
310             'type' => 'int',
311             'not null' => FALSE,
312           ],
313           'editor_revision__target_id' => [
314             'type' => 'int',
315             'not null' => FALSE,
316           ],
317           'editor_revision__target_revision_id' => [
318             'type' => 'int',
319             'not null' => FALSE,
320           ],
321           'long_index_name' => [
322             'type' => 'int',
323             'not null' => FALSE,
324           ],
325         ],
326         'primary key' => ['id'],
327         'unique keys' => [
328           'entity_test_field__uuid__value' => ['uuid'],
329           'entity_test_field__hash__value' => [['hash', 10]],
330           'entity_test_field__email__email' => [
331             'email__username',
332             'email__hostname',
333             ['email__domain', 3],
334           ],
335         ],
336         'indexes' => [
337           'entity_test_field__owner__target_id' => ['owner'],
338           'entity_test_field__translator__target_id' => [
339             ['translator', 10],
340           ],
341           'entity_test_field__location__country_state_city' => [
342             'location__country',
343             'location__state',
344             ['location__city', 10],
345           ],
346           'entity_test__b588603cb9' => [
347             ['long_index_name', 10],
348           ],
349
350         ],
351         'foreign keys' => [
352           'entity_test_field__editor__user_id' => [
353             'table' => 'users',
354             'columns' => ['editor' => 'uid'],
355           ],
356           'entity_test_field__editor_revision__user_id' => [
357             'table' => 'users',
358             'columns' => ['editor_revision__target_id' => 'uid'],
359           ],
360         ],
361       ],
362     ];
363
364     $this->setUpStorageSchema($expected);
365
366     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
367     $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
368     $table_mapping->setExtraColumns('entity_test', ['default_langcode']);
369
370     $this->storage->expects($this->any())
371       ->method('getTableMapping')
372       ->will($this->returnValue($table_mapping));
373
374     $this->assertNull(
375       $this->storageSchema->onEntityTypeCreate($this->entityType)
376     );
377   }
378
379   /**
380    * Tests the schema for revisionable, non-translatable entities.
381    *
382    * @covers ::__construct
383    * @covers ::getEntitySchemaTables
384    * @covers ::initializeBaseTable
385    * @covers ::initializeRevisionTable
386    * @covers ::addTableDefaults
387    * @covers ::getEntityIndexName
388    * @covers ::processRevisionTable
389    * @covers ::processIdentifierSchema
390    */
391   public function testGetSchemaRevisionable() {
392     $this->entityType = new ContentEntityType([
393       'id' => 'entity_test',
394       'entity_keys' => [
395         'id' => 'id',
396         'revision' => 'revision_id',
397       ],
398     ]);
399
400     $this->storage->expects($this->exactly(2))
401       ->method('getRevisionTable')
402       ->will($this->returnValue('entity_test_revision'));
403
404     $this->setUpStorageDefinition('revision_id', [
405       'columns' => [
406         'value' => [
407           'type' => 'int',
408         ],
409       ],
410     ]);
411
412     $expected = [
413       'entity_test' => [
414         'description' => 'The base table for entity_test entities.',
415         'fields' => [
416           'id' => [
417             'type' => 'serial',
418             'not null' => TRUE,
419           ],
420           'revision_id' => [
421             'type' => 'int',
422             'not null' => FALSE,
423           ]
424         ],
425         'primary key' => ['id'],
426         'unique keys' => [
427           'entity_test__revision_id' => ['revision_id'],
428         ],
429         'indexes' => [],
430         'foreign keys' => [
431           'entity_test__revision' => [
432             'table' => 'entity_test_revision',
433             'columns' => ['revision_id' => 'revision_id'],
434           ]
435         ],
436       ],
437       'entity_test_revision' => [
438         'description' => 'The revision table for entity_test entities.',
439         'fields' => [
440           'id' => [
441             'type' => 'int',
442             'not null' => TRUE,
443           ],
444           'revision_id' => [
445             'type' => 'serial',
446             'not null' => TRUE,
447           ],
448         ],
449         'primary key' => ['revision_id'],
450         'unique keys' => [],
451         'indexes' => [
452           'entity_test__id' => ['id'],
453         ],
454         'foreign keys' => [
455           'entity_test__revisioned' => [
456             'table' => 'entity_test',
457             'columns' => ['id' => 'id'],
458           ],
459         ],
460       ],
461     ];
462
463     $this->setUpStorageSchema($expected);
464
465     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
466     $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
467     $table_mapping->setFieldNames('entity_test_revision', array_keys($this->storageDefinitions));
468
469     $this->storage->expects($this->any())
470       ->method('getTableMapping')
471       ->will($this->returnValue($table_mapping));
472
473     $this->storageSchema->onEntityTypeCreate($this->entityType);
474   }
475
476   /**
477    * Tests the schema for non-revisionable, translatable entities.
478    *
479    * @covers ::__construct
480    * @covers ::getEntitySchemaTables
481    * @covers ::initializeDataTable
482    * @covers ::addTableDefaults
483    * @covers ::getEntityIndexName
484    * @covers ::processDataTable
485    */
486   public function testGetSchemaTranslatable() {
487     $this->entityType = new ContentEntityType([
488       'id' => 'entity_test',
489       'entity_keys' => [
490         'id' => 'id',
491         'langcode' => 'langcode',
492       ],
493     ]);
494
495     $this->storage->expects($this->any())
496       ->method('getDataTable')
497       ->will($this->returnValue('entity_test_field_data'));
498
499     $this->setUpStorageDefinition('langcode', [
500       'columns' => [
501         'value' => [
502           'type' => 'varchar',
503         ],
504       ],
505     ]);
506
507     $this->setUpStorageDefinition('default_langcode', [
508       'columns' => [
509         'value' => [
510           'type' => 'int',
511           'size' => 'tiny',
512         ],
513       ],
514     ]);
515
516     $expected = [
517       'entity_test' => [
518         'description' => 'The base table for entity_test entities.',
519         'fields' => [
520           'id' => [
521             'type' => 'serial',
522             'not null' => TRUE,
523           ],
524           'langcode' => [
525             'type' => 'varchar',
526             'not null' => TRUE,
527           ]
528         ],
529         'primary key' => ['id'],
530         'unique keys' => [],
531         'indexes' => [],
532         'foreign keys' => [],
533       ],
534       'entity_test_field_data' => [
535         'description' => 'The data table for entity_test entities.',
536         'fields' => [
537           'id' => [
538             'type' => 'int',
539             'not null' => TRUE,
540           ],
541           'langcode' => [
542             'type' => 'varchar',
543             'not null' => TRUE,
544           ],
545           'default_langcode' => [
546             'type' => 'int',
547             'size' => 'tiny',
548             'not null' => TRUE,
549           ],
550         ],
551         'primary key' => ['id', 'langcode'],
552         'unique keys' => [],
553         'indexes' => [
554           'entity_test__id__default_langcode__langcode' => [
555             0 => 'id',
556             1 => 'default_langcode',
557             2 => 'langcode',
558           ],
559         ],
560         'foreign keys' => [
561           'entity_test' => [
562             'table' => 'entity_test',
563             'columns' => ['id' => 'id'],
564           ],
565         ],
566       ],
567     ];
568
569     $this->setUpStorageSchema($expected);
570
571     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
572     $non_data_fields = array_keys($this->storageDefinitions);
573     unset($non_data_fields[array_search('default_langcode', $non_data_fields)]);
574     $table_mapping->setFieldNames('entity_test', $non_data_fields);
575     $table_mapping->setFieldNames('entity_test_field_data', array_keys($this->storageDefinitions));
576
577     $this->storage->expects($this->any())
578       ->method('getTableMapping')
579       ->will($this->returnValue($table_mapping));
580
581     $this->assertNull(
582       $this->storageSchema->onEntityTypeCreate($this->entityType)
583     );
584   }
585
586   /**
587    * Tests the schema for revisionable, translatable entities.
588    *
589    * @covers ::__construct
590    * @covers ::getEntitySchemaTables
591    * @covers ::initializeDataTable
592    * @covers ::addTableDefaults
593    * @covers ::getEntityIndexName
594    * @covers ::initializeRevisionDataTable
595    * @covers ::processRevisionDataTable
596    */
597   public function testGetSchemaRevisionableTranslatable() {
598     $this->entityType = new ContentEntityType([
599       'id' => 'entity_test',
600       'entity_keys' => [
601         'id' => 'id',
602         'revision' => 'revision_id',
603         'langcode' => 'langcode',
604       ],
605     ]);
606
607     $this->storage->expects($this->exactly(3))
608       ->method('getRevisionTable')
609       ->will($this->returnValue('entity_test_revision'));
610     $this->storage->expects($this->once())
611       ->method('getDataTable')
612       ->will($this->returnValue('entity_test_field_data'));
613     $this->storage->expects($this->once())
614       ->method('getRevisionDataTable')
615       ->will($this->returnValue('entity_test_revision_field_data'));
616
617     $this->setUpStorageDefinition('revision_id', [
618       'columns' => [
619         'value' => [
620           'type' => 'int',
621         ],
622       ],
623     ]);
624     $this->setUpStorageDefinition('langcode', [
625       'columns' => [
626         'value' => [
627           'type' => 'varchar',
628         ],
629       ],
630     ]);
631     $this->setUpStorageDefinition('default_langcode', [
632       'columns' => [
633         'value' => [
634           'type' => 'int',
635           'size' => 'tiny',
636         ],
637       ],
638     ]);
639
640     $expected = [
641       'entity_test' => [
642         'description' => 'The base table for entity_test entities.',
643         'fields' => [
644           'id' => [
645             'type' => 'serial',
646             'not null' => TRUE,
647           ],
648           'revision_id' => [
649             'type' => 'int',
650             'not null' => FALSE,
651           ],
652           'langcode' => [
653             'type' => 'varchar',
654             'not null' => TRUE,
655           ]
656         ],
657         'primary key' => ['id'],
658         'unique keys' => [
659           'entity_test__revision_id' => ['revision_id'],
660         ],
661         'indexes' => [],
662         'foreign keys' => [
663           'entity_test__revision' => [
664             'table' => 'entity_test_revision',
665             'columns' => ['revision_id' => 'revision_id'],
666           ],
667         ],
668       ],
669       'entity_test_revision' => [
670         'description' => 'The revision table for entity_test entities.',
671         'fields' => [
672           'id' => [
673             'type' => 'int',
674             'not null' => TRUE,
675           ],
676           'revision_id' => [
677             'type' => 'serial',
678             'not null' => TRUE,
679           ],
680           'langcode' => [
681             'type' => 'varchar',
682             'not null' => TRUE,
683           ],
684         ],
685         'primary key' => ['revision_id'],
686         'unique keys' => [],
687         'indexes' => [
688           'entity_test__id' => ['id'],
689         ],
690         'foreign keys' => [
691           'entity_test__revisioned' => [
692             'table' => 'entity_test',
693             'columns' => ['id' => 'id'],
694           ],
695         ],
696       ],
697       'entity_test_field_data' => [
698         'description' => 'The data table for entity_test entities.',
699         'fields' => [
700           'id' => [
701             'type' => 'int',
702             'not null' => TRUE,
703           ],
704           'revision_id' => [
705             'type' => 'int',
706             'not null' => TRUE,
707           ],
708           'langcode' => [
709             'type' => 'varchar',
710             'not null' => TRUE,
711           ],
712           'default_langcode' => [
713             'type' => 'int',
714             'size' => 'tiny',
715             'not null' => TRUE,
716           ],
717         ],
718         'primary key' => ['id', 'langcode'],
719         'unique keys' => [],
720         'indexes' => [
721           'entity_test__revision_id' => ['revision_id'],
722           'entity_test__id__default_langcode__langcode' => [
723             0 => 'id',
724             1 => 'default_langcode',
725             2 => 'langcode',
726           ],
727         ],
728         'foreign keys' => [
729           'entity_test' => [
730             'table' => 'entity_test',
731             'columns' => ['id' => 'id'],
732           ],
733         ],
734       ],
735       'entity_test_revision_field_data' => [
736         'description' => 'The revision data table for entity_test entities.',
737         'fields' => [
738           'id' => [
739             'type' => 'int',
740             'not null' => TRUE,
741           ],
742           'revision_id' => [
743             'type' => 'int',
744             'not null' => TRUE,
745           ],
746           'langcode' => [
747             'type' => 'varchar',
748             'not null' => TRUE,
749           ],
750           'default_langcode' => [
751             'type' => 'int',
752             'size' => 'tiny',
753             'not null' => TRUE,
754           ],
755         ],
756         'primary key' => ['revision_id', 'langcode'],
757         'unique keys' => [],
758         'indexes' => [
759           'entity_test__id__default_langcode__langcode' => [
760             0 => 'id',
761             1 => 'default_langcode',
762             2 => 'langcode',
763           ],
764         ],
765         'foreign keys' => [
766           'entity_test' => [
767             'table' => 'entity_test',
768             'columns' => ['id' => 'id'],
769           ],
770           'entity_test__revision' => [
771             'table' => 'entity_test_revision',
772             'columns' => ['revision_id' => 'revision_id'],
773           ],
774         ],
775       ],
776     ];
777
778     $this->setUpStorageSchema($expected);
779
780     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
781     $non_data_fields = array_keys($this->storageDefinitions);
782     unset($non_data_fields[array_search('default_langcode', $non_data_fields)]);
783     $table_mapping->setFieldNames('entity_test', $non_data_fields);
784     $table_mapping->setFieldNames('entity_test_revision', $non_data_fields);
785     $table_mapping->setFieldNames('entity_test_field_data', array_keys($this->storageDefinitions));
786     $table_mapping->setFieldNames('entity_test_revision_field_data', array_keys($this->storageDefinitions));
787
788     $this->storage->expects($this->any())
789       ->method('getTableMapping')
790       ->will($this->returnValue($table_mapping));
791
792     $this->storageSchema->onEntityTypeCreate($this->entityType);
793   }
794
795   /**
796    * Tests the schema for a field dedicated table.
797    *
798    * @covers ::onFieldStorageDefinitionCreate
799    * @covers ::getDedicatedTableSchema
800    * @covers ::createDedicatedTableSchema
801    */
802   public function testDedicatedTableSchema() {
803     $entity_type_id = 'entity_test';
804     $this->entityType = new ContentEntityType([
805       'id' => 'entity_test',
806       'entity_keys' => ['id' => 'id'],
807     ]);
808
809     // Setup a field having a dedicated schema.
810     $field_name = $this->getRandomGenerator()->name();
811     $this->setUpStorageDefinition($field_name, [
812       'columns' => [
813         'shape' => [
814           'type' => 'varchar',
815           'length' => 32,
816           'not null' => FALSE,
817         ],
818         'color' => [
819           'type' => 'varchar',
820           'length' => 32,
821           'not null' => FALSE,
822         ],
823         'area' => [
824           'type' => 'int',
825           'unsigned' => TRUE,
826           'not null' => TRUE,
827         ],
828         'depth' => [
829           'type' => 'int',
830           'unsigned' => TRUE,
831           'not null' => TRUE,
832         ],
833       ],
834       'foreign keys' => [
835         'color' => [
836           'table' => 'color',
837           'columns' => [
838             'color' => 'id'
839           ],
840         ],
841       ],
842       'unique keys' => [
843         'area' => ['area'],
844         'shape' => [['shape', 10]],
845       ],
846       'indexes' => [
847         'depth' => ['depth'],
848         'color' => [['color', 3]],
849       ],
850     ]);
851
852     $field_storage = $this->storageDefinitions[$field_name];
853     $field_storage
854       ->expects($this->any())
855       ->method('getType')
856       ->will($this->returnValue('shape'));
857     $field_storage
858       ->expects($this->any())
859       ->method('getTargetEntityTypeId')
860       ->will($this->returnValue($entity_type_id));
861     $field_storage
862       ->expects($this->any())
863       ->method('isMultiple')
864       ->will($this->returnValue(TRUE));
865
866     $this->storageDefinitions['id']
867       ->expects($this->any())
868       ->method('getType')
869       ->will($this->returnValue('integer'));
870
871     $expected = [
872       $entity_type_id . '__' . $field_name => [
873         'description' => "Data storage for $entity_type_id field $field_name.",
874         'fields' => [
875           'bundle' => [
876             'type' => 'varchar_ascii',
877             'length' => 128,
878             'not null' => TRUE,
879             'default' => '',
880             'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
881           ],
882           'deleted' => [
883             'type' => 'int',
884             'size' => 'tiny',
885             'not null' => TRUE,
886             'default' => 0,
887             'description' => 'A boolean indicating whether this data item has been deleted',
888           ],
889           'entity_id' => [
890             'type' => 'int',
891             'unsigned' => TRUE,
892             'not null' => TRUE,
893             'description' => 'The entity id this data is attached to',
894           ],
895           'revision_id' => [
896             'type' => 'int',
897             'unsigned' => TRUE,
898             'not null' => TRUE,
899             'description' => 'The entity revision id this data is attached to, which for an unversioned entity type is the same as the entity id',
900           ],
901           'langcode' => [
902             'type' => 'varchar_ascii',
903             'length' => 32,
904             'not null' => TRUE,
905             'default' => '',
906             'description' => 'The language code for this data item.',
907           ],
908           'delta' => [
909             'type' => 'int',
910             'unsigned' => TRUE,
911             'not null' => TRUE,
912             'description' => 'The sequence number for this data item, used for multi-value fields',
913           ],
914           $field_name . '_shape' => [
915             'type' => 'varchar',
916             'length' => 32,
917             'not null' => FALSE,
918           ],
919           $field_name . '_color' => [
920             'type' => 'varchar',
921             'length' => 32,
922             'not null' => FALSE,
923           ],
924           $field_name . '_area' => [
925             'type' => 'int',
926             'unsigned' => TRUE,
927             'not null' => TRUE,
928           ],
929           $field_name . '_depth' => [
930             'type' => 'int',
931             'unsigned' => TRUE,
932             'not null' => TRUE,
933           ],
934         ],
935         'primary key' => ['entity_id', 'deleted', 'delta', 'langcode'],
936         'indexes' => [
937           'bundle' => ['bundle'],
938           'revision_id' => ['revision_id'],
939           $field_name . '_depth' => [$field_name . '_depth'],
940           $field_name . '_color' => [[$field_name . '_color', 3]],
941         ],
942         'unique keys' => [
943            $field_name . '_area' => [$field_name . '_area'],
944            $field_name . '_shape' => [[$field_name . '_shape', 10]],
945         ],
946         'foreign keys' => [
947           $field_name . '_color' => [
948             'table' => 'color',
949             'columns' => [
950               $field_name . '_color' => 'id',
951             ],
952           ],
953         ],
954       ],
955     ];
956
957     $this->setUpStorageSchema($expected);
958
959     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
960     $table_mapping->setFieldNames($entity_type_id, array_keys($this->storageDefinitions));
961     $table_mapping->setExtraColumns($entity_type_id, ['default_langcode']);
962
963     $this->storage->expects($this->any())
964       ->method('getTableMapping')
965       ->will($this->returnValue($table_mapping));
966
967     $this->assertNull(
968       $this->storageSchema->onFieldStorageDefinitionCreate($field_storage)
969     );
970   }
971
972   /**
973    * Tests the schema for a field dedicated table for an entity with a string identifier.
974    *
975    * @covers ::onFieldStorageDefinitionCreate
976    * @covers ::getDedicatedTableSchema
977    * @covers ::createDedicatedTableSchema
978    */
979   public function testDedicatedTableSchemaForEntityWithStringIdentifier() {
980     $entity_type_id = 'entity_test';
981     $this->entityType = new ContentEntityType([
982       'id' => 'entity_test',
983       'entity_keys' => ['id' => 'id'],
984     ]);
985
986     // Setup a field having a dedicated schema.
987     $field_name = $this->getRandomGenerator()->name();
988     $this->setUpStorageDefinition($field_name, [
989       'columns' => [
990         'shape' => [
991           'type' => 'varchar',
992           'length' => 32,
993           'not null' => FALSE,
994         ],
995         'color' => [
996           'type' => 'varchar',
997           'length' => 32,
998           'not null' => FALSE,
999         ],
1000       ],
1001       'foreign keys' => [
1002         'color' => [
1003           'table' => 'color',
1004           'columns' => [
1005             'color' => 'id'
1006           ],
1007         ],
1008       ],
1009       'unique keys' => [],
1010       'indexes' => [],
1011     ]);
1012
1013     $field_storage = $this->storageDefinitions[$field_name];
1014     $field_storage
1015       ->expects($this->any())
1016       ->method('getType')
1017       ->will($this->returnValue('shape'));
1018     $field_storage
1019       ->expects($this->any())
1020       ->method('getTargetEntityTypeId')
1021       ->will($this->returnValue($entity_type_id));
1022     $field_storage
1023       ->expects($this->any())
1024       ->method('isMultiple')
1025       ->will($this->returnValue(TRUE));
1026
1027     $this->storageDefinitions['id']
1028       ->expects($this->any())
1029       ->method('getType')
1030       ->will($this->returnValue('string'));
1031
1032     $expected = [
1033       $entity_type_id . '__' . $field_name => [
1034         'description' => "Data storage for $entity_type_id field $field_name.",
1035         'fields' => [
1036           'bundle' => [
1037             'type' => 'varchar_ascii',
1038             'length' => 128,
1039             'not null' => TRUE,
1040             'default' => '',
1041             'description' => 'The field instance bundle to which this row belongs, used when deleting a field instance',
1042           ],
1043           'deleted' => [
1044             'type' => 'int',
1045             'size' => 'tiny',
1046             'not null' => TRUE,
1047             'default' => 0,
1048             'description' => 'A boolean indicating whether this data item has been deleted',
1049           ],
1050           'entity_id' => [
1051             'type' => 'varchar_ascii',
1052             'length' => 128,
1053             'not null' => TRUE,
1054             'description' => 'The entity id this data is attached to',
1055           ],
1056           'revision_id' => [
1057             'type' => 'varchar_ascii',
1058             'length' => 128,
1059             'not null' => TRUE,
1060             'description' => 'The entity revision id this data is attached to, which for an unversioned entity type is the same as the entity id',
1061           ],
1062           'langcode' => [
1063             'type' => 'varchar_ascii',
1064             'length' => 32,
1065             'not null' => TRUE,
1066             'default' => '',
1067             'description' => 'The language code for this data item.',
1068           ],
1069           'delta' => [
1070             'type' => 'int',
1071             'unsigned' => TRUE,
1072             'not null' => TRUE,
1073             'description' => 'The sequence number for this data item, used for multi-value fields',
1074           ],
1075           $field_name . '_shape' => [
1076             'type' => 'varchar',
1077             'length' => 32,
1078             'not null' => FALSE,
1079           ],
1080           $field_name . '_color' => [
1081             'type' => 'varchar',
1082             'length' => 32,
1083             'not null' => FALSE,
1084           ],
1085         ],
1086         'primary key' => ['entity_id', 'deleted', 'delta', 'langcode'],
1087         'indexes' => [
1088           'bundle' => ['bundle'],
1089           'revision_id' => ['revision_id'],
1090         ],
1091         'foreign keys' => [
1092           $field_name . '_color' => [
1093             'table' => 'color',
1094             'columns' => [
1095               $field_name . '_color' => 'id',
1096             ],
1097           ],
1098         ],
1099       ],
1100     ];
1101
1102     $this->setUpStorageSchema($expected);
1103
1104     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
1105     $table_mapping->setFieldNames($entity_type_id, array_keys($this->storageDefinitions));
1106     $table_mapping->setExtraColumns($entity_type_id, ['default_langcode']);
1107
1108     $this->storage->expects($this->any())
1109       ->method('getTableMapping')
1110       ->will($this->returnValue($table_mapping));
1111
1112     $this->assertNull(
1113       $this->storageSchema->onFieldStorageDefinitionCreate($field_storage)
1114     );
1115   }
1116
1117   public function providerTestRequiresEntityDataMigration() {
1118     $updated_entity_type_definition = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
1119     $updated_entity_type_definition->expects($this->any())
1120       ->method('getStorageClass')
1121       // A class that exists, *any* class.
1122       ->willReturn('\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema');
1123     $original_entity_type_definition = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
1124     $original_entity_type_definition->expects($this->any())
1125       ->method('getStorageClass')
1126       // A class that exists, *any* class.
1127       ->willReturn('\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema');
1128     $original_entity_type_definition_other_nonexisting = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
1129     $original_entity_type_definition_other_nonexisting->expects($this->any())
1130       ->method('getStorageClass')
1131       ->willReturn('bar');
1132     $original_entity_type_definition_other_existing = $this->getMock('\Drupal\Core\Entity\EntityTypeInterface');
1133     $original_entity_type_definition_other_existing->expects($this->any())
1134       ->method('getStorageClass')
1135       // A class that exists, *any* class.
1136       ->willReturn('\Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema');
1137
1138     return [
1139       // Case 1: same storage class, ::hasData() === TRUE.
1140       [$updated_entity_type_definition, $original_entity_type_definition, TRUE, TRUE, TRUE],
1141       // Case 2: same storage class, ::hasData() === FALSE.
1142       [$updated_entity_type_definition, $original_entity_type_definition, FALSE, TRUE, FALSE],
1143       // Case 3: different storage class, original storage class does not exist.
1144       [$updated_entity_type_definition, $original_entity_type_definition_other_nonexisting, NULL, TRUE, TRUE],
1145       // Case 4: different storage class, original storage class exists,
1146       // ::hasData() === TRUE.
1147       [$updated_entity_type_definition, $original_entity_type_definition_other_existing, TRUE, TRUE, TRUE],
1148       // Case 5: different storage class, original storage class exists,
1149       // ::hasData() === FALSE.
1150       [$updated_entity_type_definition, $original_entity_type_definition_other_existing, FALSE, TRUE, FALSE],
1151       // Case 6: same storage class, ::hasData() === TRUE, no structure changes.
1152       [$updated_entity_type_definition, $original_entity_type_definition, TRUE, FALSE, FALSE],
1153       // Case 7: different storage class, original storage class exists,
1154       // ::hasData() === TRUE, no structure changes.
1155       [$updated_entity_type_definition, $original_entity_type_definition_other_existing, TRUE, FALSE, FALSE],
1156     ];
1157   }
1158
1159   /**
1160    * @covers ::requiresEntityDataMigration
1161    *
1162    * @dataProvider providerTestRequiresEntityDataMigration
1163    */
1164   public function testRequiresEntityDataMigration($updated_entity_type_definition, $original_entity_type_definition, $original_storage_has_data, $shared_table_structure_changed, $migration_required) {
1165     $this->entityType = new ContentEntityType([
1166       'id' => 'entity_test',
1167       'entity_keys' => ['id' => 'id'],
1168     ]);
1169
1170     $original_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage')
1171       ->disableOriginalConstructor()
1172       ->getMock();
1173
1174     $original_storage->expects($this->exactly(is_null($original_storage_has_data) || !$shared_table_structure_changed ? 0 : 1))
1175       ->method('hasData')
1176       ->willReturn($original_storage_has_data);
1177
1178     // Assert hasData() is never called on the new storage definition.
1179     $this->storage->expects($this->never())
1180       ->method('hasData');
1181
1182     $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
1183       ->disableOriginalConstructor()
1184       ->getMock();
1185
1186     $this->entityManager->expects($this->any())
1187       ->method('createHandlerInstance')
1188       ->willReturn($original_storage);
1189
1190     $this->storageSchema = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema')
1191       ->setConstructorArgs([$this->entityManager, $this->entityType, $this->storage, $connection])
1192       ->setMethods(['installedStorageSchema', 'hasSharedTableStructureChange'])
1193       ->getMock();
1194
1195     $this->storageSchema->expects($this->any())
1196       ->method('hasSharedTableStructureChange')
1197       ->with($updated_entity_type_definition, $original_entity_type_definition)
1198       ->willReturn($shared_table_structure_changed);
1199
1200     $this->assertEquals($migration_required, $this->storageSchema->requiresEntityDataMigration($updated_entity_type_definition, $original_entity_type_definition));
1201   }
1202
1203   /**
1204    * Data provider for ::testRequiresEntityStorageSchemaChanges().
1205    */
1206   public function providerTestRequiresEntityStorageSchemaChanges() {
1207
1208     $cases = [];
1209
1210     $updated_entity_type_definition = $this->getMock('\Drupal\Core\Entity\ContentEntityTypeInterface');
1211     $original_entity_type_definition = $this->getMock('\Drupal\Core\Entity\ContentEntityTypeInterface');
1212
1213     $updated_entity_type_definition->expects($this->any())
1214       ->method('id')
1215       ->willReturn('entity_test');
1216     $updated_entity_type_definition->expects($this->any())
1217       ->method('getKey')
1218       ->willReturn('id');
1219     $original_entity_type_definition->expects($this->any())
1220       ->method('id')
1221       ->willReturn('entity_test');
1222     $original_entity_type_definition->expects($this->any())
1223       ->method('getKey')
1224       ->willReturn('id');
1225
1226     // Storage class changes should not impact this at all, and should not be
1227     // checked.
1228     $updated = clone $updated_entity_type_definition;
1229     $original = clone $original_entity_type_definition;
1230     $updated->expects($this->never())
1231       ->method('getStorageClass');
1232     $original->expects($this->never())
1233       ->method('getStorageClass');
1234
1235     // Case 1: No shared table changes should not require change.
1236     $cases[] = [$updated, $original, FALSE, FALSE, FALSE];
1237
1238     // Case 2: A change in the entity schema should result in required changes.
1239     $cases[] = [$updated, $original, TRUE, TRUE, FALSE];
1240
1241     // Case 3: Has shared table changes should result in required changes.
1242     $cases[] = [$updated, $original, TRUE, FALSE, TRUE];
1243
1244     // Case 4: Changing translation should result in required changes.
1245     $updated = clone $updated_entity_type_definition;
1246     $updated->expects($this->once())
1247       ->method('isTranslatable')
1248       ->willReturn(FALSE);
1249     $original = clone $original_entity_type_definition;
1250     $original->expects($this->once())
1251       ->method('isTranslatable')
1252       ->willReturn(TRUE);
1253     $cases[] = [$updated, $original, TRUE, FALSE, FALSE];
1254
1255     // Case 5: Changing revisionable should result in required changes.
1256     $updated = clone $updated_entity_type_definition;
1257     $updated->expects($this->once())
1258       ->method('isRevisionable')
1259       ->willReturn(FALSE);
1260     $original = clone $original_entity_type_definition;
1261     $original->expects($this->once())
1262       ->method('isRevisionable')
1263       ->willReturn(TRUE);
1264     $cases[] = [$updated, $original, TRUE, FALSE, FALSE];
1265
1266     return $cases;
1267   }
1268
1269   /**
1270    * @covers ::requiresEntityStorageSchemaChanges
1271    *
1272    * @dataProvider providerTestRequiresEntityStorageSchemaChanges
1273    */
1274   public function testRequiresEntityStorageSchemaChanges(ContentEntityTypeInterface $updated, ContentEntityTypeInterface $original, $requires_change, $change_schema, $change_shared_table) {
1275
1276     $this->entityType = new ContentEntityType([
1277       'id' => 'entity_test',
1278       'entity_keys' => ['id' => 'id'],
1279     ]);
1280
1281     $this->setUpStorageSchema();
1282     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
1283     $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
1284     $table_mapping->setExtraColumns('entity_test', ['default_langcode']);
1285     $this->storage->expects($this->any())
1286       ->method('getTableMapping')
1287       ->will($this->returnValue($table_mapping));
1288
1289     // Setup storage schema.
1290     if ($change_schema) {
1291       $this->storageSchema->expects($this->once())
1292         ->method('loadEntitySchemaData')
1293         ->willReturn([]);
1294     }
1295     else {
1296       $expected = [
1297         'entity_test' => [
1298           'primary key' => ['id'],
1299         ],
1300       ];
1301       $this->storageSchema->expects($this->any())
1302         ->method('loadEntitySchemaData')
1303         ->willReturn($expected);
1304     }
1305
1306     if ($change_shared_table) {
1307       $this->storageSchema->expects($this->once())
1308         ->method('hasSharedTableNameChanges')
1309         ->willReturn(TRUE);
1310     }
1311
1312     $this->assertEquals($requires_change, $this->storageSchema->requiresEntityStorageSchemaChanges($updated, $original));
1313   }
1314
1315   /**
1316    * Sets up the storage schema object to test.
1317    *
1318    * This uses the field definitions set in $this->storageDefinitions.
1319    *
1320    * @param array $expected
1321    *   (optional) An associative array describing the expected entity schema to
1322    *   be created. Defaults to expecting nothing.
1323    */
1324   protected function setUpStorageSchema(array $expected = []) {
1325     $this->entityManager->expects($this->any())
1326       ->method('getDefinition')
1327       ->with($this->entityType->id())
1328       ->will($this->returnValue($this->entityType));
1329
1330     $this->entityManager->expects($this->any())
1331       ->method('getFieldStorageDefinitions')
1332       ->with($this->entityType->id())
1333       ->will($this->returnValue($this->storageDefinitions));
1334
1335     $this->dbSchemaHandler = $this->getMockBuilder('Drupal\Core\Database\Schema')
1336       ->disableOriginalConstructor()
1337       ->getMock();
1338
1339     if ($expected) {
1340       $invocation_count = 0;
1341       $expected_table_names = array_keys($expected);
1342       $expected_table_schemas = array_values($expected);
1343
1344       $this->dbSchemaHandler->expects($this->any())
1345         ->method('createTable')
1346         ->with(
1347           $this->callback(function ($table_name) use (&$invocation_count, $expected_table_names) {
1348             return $expected_table_names[$invocation_count] == $table_name;
1349           }),
1350           $this->callback(function ($table_schema) use (&$invocation_count, $expected_table_schemas) {
1351             return $expected_table_schemas[$invocation_count] == $table_schema;
1352           })
1353         )
1354         ->will($this->returnCallback(function () use (&$invocation_count) {
1355           $invocation_count++;
1356         }));
1357     }
1358
1359     $connection = $this->getMockBuilder('Drupal\Core\Database\Connection')
1360       ->disableOriginalConstructor()
1361       ->getMock();
1362     $connection->expects($this->any())
1363       ->method('schema')
1364       ->will($this->returnValue($this->dbSchemaHandler));
1365
1366     $key_value = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
1367     $this->storageSchema = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema')
1368       ->setConstructorArgs([$this->entityManager, $this->entityType, $this->storage, $connection])
1369       ->setMethods(['installedStorageSchema', 'loadEntitySchemaData', 'hasSharedTableNameChanges', 'isTableEmpty'])
1370       ->getMock();
1371     $this->storageSchema
1372       ->expects($this->any())
1373       ->method('installedStorageSchema')
1374       ->will($this->returnValue($key_value));
1375     $this->storageSchema
1376       ->expects($this->any())
1377       ->method('isTableEmpty')
1378       ->willReturn(FALSE);
1379   }
1380
1381   /**
1382    * Sets up a field definition.
1383    *
1384    * @param string $field_name
1385    *   The field name.
1386    * @param array $schema
1387    *   The schema array of the field definition, as returned from
1388    *   FieldStorageDefinitionInterface::getSchema().
1389    */
1390   public function setUpStorageDefinition($field_name, array $schema) {
1391     $this->storageDefinitions[$field_name] = $this->getMock('Drupal\Tests\Core\Field\TestBaseFieldDefinitionInterface');
1392     $this->storageDefinitions[$field_name]->expects($this->any())
1393       ->method('isBaseField')
1394       ->will($this->returnValue(TRUE));
1395     // getName() is called once for each table.
1396     $this->storageDefinitions[$field_name]->expects($this->any())
1397       ->method('getName')
1398       ->will($this->returnValue($field_name));
1399     // getSchema() is called once for each table.
1400     $this->storageDefinitions[$field_name]->expects($this->any())
1401       ->method('getSchema')
1402       ->will($this->returnValue($schema));
1403     $this->storageDefinitions[$field_name]->expects($this->any())
1404       ->method('getColumns')
1405       ->will($this->returnValue($schema['columns']));
1406     // Add property definitions.
1407     if (!empty($schema['columns'])) {
1408       $property_definitions = [];
1409       foreach ($schema['columns'] as $column => $info) {
1410         $property_definitions[$column] = $this->getMock('Drupal\Core\TypedData\DataDefinitionInterface');
1411         $property_definitions[$column]->expects($this->any())
1412           ->method('isRequired')
1413           ->will($this->returnValue(!empty($info['not null'])));
1414       }
1415       $this->storageDefinitions[$field_name]->expects($this->any())
1416         ->method('getPropertyDefinitions')
1417         ->will($this->returnValue($property_definitions));
1418     }
1419   }
1420
1421   /**
1422    * ::onEntityTypeUpdate
1423    */
1424   public function testonEntityTypeUpdateWithNewIndex() {
1425     $this->entityType = $original_entity_type = new ContentEntityType([
1426       'id' => 'entity_test',
1427       'entity_keys' => ['id' => 'id'],
1428     ]);
1429
1430     // Add a field with a really long index.
1431     $this->setUpStorageDefinition('long_index_name', [
1432       'columns' => [
1433         'long_index_name' => [
1434           'type' => 'int',
1435         ],
1436       ],
1437       'indexes' => [
1438         'long_index_name_really_long_long_name' => [['long_index_name', 10]],
1439       ],
1440     ]);
1441
1442     $expected = [
1443       'entity_test' => [
1444         'description' => 'The base table for entity_test entities.',
1445         'fields' => [
1446           'id' => [
1447             'type' => 'serial',
1448             'not null' => TRUE,
1449           ],
1450           'long_index_name' => [
1451             'type' => 'int',
1452             'not null' => FALSE,
1453           ],
1454         ],
1455         'indexes' => [
1456           'entity_test__b588603cb9' => [
1457             ['long_index_name', 10],
1458           ],
1459         ],
1460       ],
1461     ];
1462
1463     $this->setUpStorageSchema($expected);
1464
1465     $table_mapping = new DefaultTableMapping($this->entityType, $this->storageDefinitions);
1466     $table_mapping->setFieldNames('entity_test', array_keys($this->storageDefinitions));
1467     $table_mapping->setExtraColumns('entity_test', ['default_langcode']);
1468
1469     $this->storage->expects($this->any())
1470       ->method('getTableMapping')
1471       ->will($this->returnValue($table_mapping));
1472
1473     $this->storageSchema->expects($this->any())
1474       ->method('loadEntitySchemaData')
1475       ->willReturn([
1476         'entity_test' => [
1477           'indexes' => [
1478             // A changed index definition.
1479             'entity_test__b588603cb9' => ['longer_index_name'],
1480             // An index that has been removed.
1481             'entity_test__removed_field' => ['removed_field'],
1482           ],
1483         ],
1484       ]);
1485
1486     // The original indexes should be dropped before the new one is added.
1487     $this->dbSchemaHandler->expects($this->at(0))
1488       ->method('dropIndex')
1489       ->with('entity_test', 'entity_test__b588603cb9');
1490     $this->dbSchemaHandler->expects($this->at(1))
1491       ->method('dropIndex')
1492       ->with('entity_test', 'entity_test__removed_field');
1493
1494     $this->dbSchemaHandler->expects($this->atLeastOnce())
1495       ->method('fieldExists')
1496       ->willReturn(TRUE);
1497     $this->dbSchemaHandler->expects($this->atLeastOnce())
1498       ->method('addIndex')
1499       ->with('entity_test', 'entity_test__b588603cb9', [['long_index_name', 10]], $this->callback(function ($actual_value) use ($expected) {
1500         $this->assertEquals($expected['entity_test']['indexes'], $actual_value['indexes']);
1501         $this->assertEquals($expected['entity_test']['fields'], $actual_value['fields']);
1502         // If the parameters don't match, the assertions above will throw an
1503         // exception.
1504         return TRUE;
1505       }));
1506
1507     $this->assertNull(
1508       $this->storageSchema->onEntityTypeUpdate($this->entityType, $original_entity_type)
1509     );
1510   }
1511
1512 }