X-Git-Url: http://aleph1.co.uk/gitweb/?a=blobdiff_plain;ds=sidebyside;f=web%2Fcore%2Fmodules%2Fviews%2Ftests%2Fsrc%2FUnit%2FPlugin%2Fquery%2FSqlTest.php;fp=web%2Fcore%2Fmodules%2Fviews%2Ftests%2Fsrc%2FUnit%2FPlugin%2Fquery%2FSqlTest.php;h=02a5ce64c81d45a2deb8aa7d48c47730a0112389;hb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;hp=0000000000000000000000000000000000000000;hpb=57c063afa3f66b07c4bbddc2d6129a96d90f0aad;p=yaffs-website diff --git a/web/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php b/web/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php new file mode 100644 index 000000000..02a5ce64c --- /dev/null +++ b/web/core/modules/views/tests/src/Unit/Plugin/query/SqlTest.php @@ -0,0 +1,590 @@ +prophesize('Drupal\views\ViewExecutable')->reveal(); + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $view->result = $result; + + // Add a row with an entity. + $row = new ResultRow(); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:123']); + $entity = $prophecy->reveal(); + $row->_entity = $entity; + + $result[] = $row; + $view->result = $result; + + // Add a row with an entity and a relationship entity. + $row = new ResultRow(); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:124']); + $entity = $prophecy->reveal(); + $row->_entity = $entity; + + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:125']); + $entity = $prophecy->reveal(); + $row->_relationship_entities[] = $entity; + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheTags()->willReturn(['entity_test:126']); + $entity = $prophecy->reveal(); + $row->_relationship_entities[] = $entity; + + $result[] = $row; + $view->result = $result; + + $this->assertEquals(['entity_test:123', 'entity_test:124', 'entity_test:125', 'entity_test:126'], $query->getCacheTags()); + } + + /** + * @covers ::getCacheTags + * @covers ::getAllEntities + */ + public function testGetCacheMaxAge() { + $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal(); + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $view->result = []; + + // Add a row with an entity. + $row = new ResultRow(); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(10); + $entity = $prophecy->reveal(); + + $row->_entity = $entity; + $view->result[] = $row; + + // Add a row with an entity and a relationship entity. + $row = new ResultRow(); + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(20); + $entity = $prophecy->reveal(); + $row->_entity = $entity; + + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(30); + $entity = $prophecy->reveal(); + $row->_relationship_entities[] = $entity; + $prophecy = $this->prophesize('Drupal\Core\Entity\EntityInterface'); + $prophecy->getCacheMaxAge()->willReturn(40); + $entity = $prophecy->reveal(); + $row->_relationship_entities[] = $entity; + + $this->assertEquals(10, $query->getCacheMaxAge()); + } + + /** + * Sets up the views data in the container. + * + * @param \Drupal\views\ViewsData $views_data + * The views data. + */ + protected function setupViewsData(ViewsData $views_data) { + $container = \Drupal::hasContainer() ? \Drupal::getContainer() : new ContainerBuilder(); + $container->set('views.views_data', $views_data); + \Drupal::setContainer($container); + } + + /** + * Sets up the entity type manager in the container. + * + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. + */ + protected function setupEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) { + $container = \Drupal::hasContainer() ? \Drupal::getContainer() : new ContainerBuilder(); + $container->set('entity_type.manager', $entity_type_manager); + $container->set('entity.manager', $entity_type_manager); + \Drupal::setContainer($container); + } + + /** + * Sets up some test entity types and corresponding views data. + * + * @param \Drupal\Core\Entity\EntityInterface[][] $entities_by_type + * Test entities keyed by entity type and entity ID. + * @param \Drupal\Core\Entity\EntityInterface[][] $entities_by_type + * Test entities keyed by entity type and revision ID. + * + * @return \Prophecy\Prophecy\ObjectProphecy + */ + protected function setupEntityTypes($entities_by_type = [], $entity_revisions_by_type = []) { + $entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class); + $entity_type0 = new EntityType([ + 'label' => 'First', + 'id' => 'first', + 'base_table' => 'entity_first', + 'revision_table' => 'entity_first__revision', + 'entity_keys' => [ + 'id' => 'id', + 'revision' => 'vid', + ], + ]); + $entity_type1 = new EntityType([ + 'label' => 'second', + 'id' => 'second', + 'base_table' => 'entity_second', + 'revision_table' => 'entity_second__revision', + 'entity_keys' => [ + 'id' => 'id', + 'revision' => 'vid', + ], + ]); + + $entity_type_manager->getDefinitions()->willReturn([ + 'first' => $entity_type0, + 'second' => $entity_type1, + 'base_table' => 'entity_second', + ]); + + $entity_type_manager->getDefinition('first')->willReturn($entity_type0); + $entity_type_manager->getDefinition('second')->willReturn($entity_type1); + + // Setup the views data corresponding to the entity types. + $views_data = $this->prophesize(ViewsData::class); + $views_data->get('entity_first')->willReturn([ + 'table' => [ + 'entity type' => 'first', + 'entity revision' => FALSE, + ], + ]); + $views_data->get('entity_first__revision')->willReturn([ + 'table' => [ + 'entity type' => 'first', + 'entity revision' => TRUE, + ], + ]); + $views_data->get('entity_second')->willReturn([ + 'table' => [ + 'entity type' => 'second', + 'entity revision' => FALSE, + ], + ]); + $views_data->get('entity_second__revision')->willReturn([ + 'table' => [ + 'entity type' => 'second', + 'entity revision' => TRUE, + ], + ]); + $views_data->get('entity_first_field_data')->willReturn([ + 'table' => [ + 'entity type' => 'first', + 'entity revision' => FALSE, + ], + ]); + $this->setupViewsData($views_data->reveal()); + + // Setup the loading of entities and entity revisions. + $entity_storages = [ + 'first' => $this->prophesize(EntityStorageInterface::class), + 'second' => $this->prophesize(EntityStorageInterface::class), + ]; + + foreach ($entities_by_type as $entity_type_id => $entities) { + foreach ($entities as $entity_id => $entity) { + $entity_storages[$entity_type_id]->load($entity_id)->willReturn($entity); + } + $entity_storages[$entity_type_id]->loadMultiple(array_keys($entities))->willReturn($entities); + } + + foreach ($entity_revisions_by_type as $entity_type_id => $entity_revisions) { + foreach ($entity_revisions as $revision_id => $revision) { + $entity_storages[$entity_type_id]->loadRevision($revision_id)->willReturn($revision); + } + } + + $entity_type_manager->getStorage('first')->willReturn($entity_storages['first']); + $entity_type_manager->getStorage('second')->willReturn($entity_storages['second']); + + $this->setupEntityTypeManager($entity_type_manager->reveal()); + + return $entity_type_manager; + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithEmptyResult() { + $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal(); + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first'); + $view_entity->get('base_field')->willReturn('id'); + $view->storage = $view_entity->reveal(); + + $entity_type_manager = $this->setupEntityTypes(); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $query->addField('entity_first', 'id', 'id'); + $query->loadEntities($result); + $this->assertEmpty($result); + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithNoRelationshipAndNoRevision() { + $view = $this->prophesize('Drupal\views\ViewExecutable')->reveal(); + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first'); + $view_entity->get('base_field')->willReturn('id'); + $view->storage = $view_entity->reveal(); + + $entities = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 2 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_type_manager = $this->setupEntityTypes($entities); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $result[] = new ResultRow([ + 'id' => 1, + ]); + // Note: Let the same entity be returned multiple times, for example to + // support the translation usecase. + $result[] = new ResultRow([ + 'id' => 2, + ]); + $result[] = new ResultRow([ + 'id' => 2, + ]); + + $query->addField('entity_first', 'id', 'id'); + $query->loadEntities($result); + + $this->assertSame($entities['first'][1], $result[0]->_entity); + $this->assertSame($entities['first'][2], $result[1]->_entity); + $this->assertSame($entities['first'][2], $result[2]->_entity); + } + + /** + * Create a view with a relationship. + */ + protected function setupViewWithRelationships(ViewExecutable $view, $base = 'entity_second') { + // We don't use prophecy, because prophecy enforces methods. + $relationship = $this->getMockBuilder(RelationshipPluginBase::class)->disableOriginalConstructor()->getMock(); + $relationship->definition['base'] = $base; + $relationship->tableAlias = $base; + $relationship->alias = $base; + + $view->relationship[$base] = $relationship; + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithRelationship() { + // We don't use prophecy, because prophecy enforces methods. + $view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock(); + $this->setupViewWithRelationships($view); + + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first'); + $view_entity->get('base_field')->willReturn('id'); + $view->storage = $view_entity->reveal(); + + $entities = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 2 => $this->prophesize(EntityInterface::class)->reveal(), + ], + 'second' => [ + 11 => $this->prophesize(EntityInterface::class)->reveal(), + 12 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_type_manager = $this->setupEntityTypes($entities); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $result[] = new ResultRow([ + 'id' => 1, + 'entity_second__id' => 11, + ]); + // Provide an explicit NULL value, to test the case of a non required + // relationship. + $result[] = new ResultRow([ + 'id' => 2, + 'entity_second__id' => NULL, + ]); + $result[] = new ResultRow([ + 'id' => 2, + 'entity_second__id' => 12, + ]); + + $query->addField('entity_first', 'id', 'id'); + $query->addField('entity_second', 'id', 'entity_second__id'); + $query->loadEntities($result); + + $this->assertSame($entities['first'][1], $result[0]->_entity); + $this->assertSame($entities['first'][2], $result[1]->_entity); + $this->assertSame($entities['first'][2], $result[2]->_entity); + + $this->assertSame($entities['second'][11], $result[0]->_relationship_entities['entity_second']); + $this->assertEquals([], $result[1]->_relationship_entities); + $this->assertSame($entities['second'][12], $result[2]->_relationship_entities['entity_second']); + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithNonEntityRelationship() { + // We don't use prophecy, because prophecy enforces methods. + $view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock(); + $this->setupViewWithRelationships($view, 'entity_first_field_data'); + + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first'); + $view_entity->get('base_field')->willReturn('id'); + $view->storage = $view_entity->reveal(); + + $entities = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 2 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_type_manager = $this->setupEntityTypes($entities); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $result[] = new ResultRow([ + 'id' => 1, + ]); + $result[] = new ResultRow([ + 'id' => 2, + ]); + + $query->addField('entity_first', 'id', 'id'); + $query->loadEntities($result); + $entity_information = $query->getEntityTableInfo(); + + $this->assertSame($entities['first'][1], $result[0]->_entity); + $this->assertSame($entities['first'][2], $result[1]->_entity); + + $this->assertEquals([], $result[0]->_relationship_entities); + $this->assertEquals([], $result[1]->_relationship_entities); + + // This is an entity table and should be in $entity_information. + $this->assertContains('first', array_keys($entity_information)); + // This is not an entity table and should not be in $entity_information. + $this->assertNotContains('entity_first_field_data__entity_first_field_data', array_keys($entity_information)); + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithRevision() { + // We don't use prophecy, because prophecy enforces methods. + $view = $this->getMockBuilder(ViewExecutable::class) + ->disableOriginalConstructor() + ->getMock(); + + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first__revision'); + $view_entity->get('base_field')->willReturn('vid'); + $view->storage = $view_entity->reveal(); + + $entity_revisions = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 3 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_type_manager = $this->setupEntityTypes([], $entity_revisions); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $result[] = new ResultRow([ + 'vid' => 1, + ]); + $result[] = new ResultRow([ + 'vid' => 1, + ]); + $result[] = new ResultRow([ + 'vid' => 3, + ]); + + $query->addField('entity_first__revision', 'vid', 'vid'); + $query->loadEntities($result); + + $this->assertSame($entity_revisions['first'][1], $result[0]->_entity); + $this->assertSame($entity_revisions['first'][1], $result[1]->_entity); + $this->assertSame($entity_revisions['first'][3], $result[2]->_entity); + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithRevisionOfSameEntityType() { + // We don't use prophecy, because prophecy enforces methods. + $view = $this->getMockBuilder(ViewExecutable::class) + ->disableOriginalConstructor() + ->getMock(); + $this->setupViewWithRelationships($view, 'entity_first__revision'); + + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first'); + $view_entity->get('base_field')->willReturn('id'); + $view->storage = $view_entity->reveal(); + + $entity = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 2 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_revisions = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 2 => $this->prophesize(EntityInterface::class)->reveal(), + 3 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_type_manager = $this->setupEntityTypes($entity, $entity_revisions); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $result[] = new ResultRow([ + 'id' => 1, + 'entity_first__revision__vid' => 1, + ]); + $result[] = new ResultRow([ + 'id' => 2, + 'entity_first__revision__vid' => 2, + ]); + $result[] = new ResultRow([ + 'id' => 2, + 'entity_first__revision__vid' => 3, + ]); + + $query->addField('entity_first', 'id', 'id'); + $query->addField('entity_first__revision', 'vid', 'entity_first__revision__vid'); + $query->loadEntities($result); + + $this->assertSame($entity['first'][1], $result[0]->_entity); + $this->assertSame($entity['first'][2], $result[1]->_entity); + $this->assertSame($entity['first'][2], $result[2]->_entity); + $this->assertSame($entity_revisions['first'][1], $result[0]->_relationship_entities['entity_first__revision']); + $this->assertSame($entity_revisions['first'][2], $result[1]->_relationship_entities['entity_first__revision']); + $this->assertSame($entity_revisions['first'][3], $result[2]->_relationship_entities['entity_first__revision']); + } + + /** + * @covers ::loadEntities + * @covers ::assignEntitiesToResult + */ + public function testLoadEntitiesWithRelationshipAndRevision() { + // We don't use prophecy, because prophecy enforces methods. + $view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock(); + $this->setupViewWithRelationships($view); + + $view_entity = $this->prophesize(ViewEntityInterface::class); + $view_entity->get('base_table')->willReturn('entity_first__revision'); + $view_entity->get('base_field')->willReturn('vid'); + $view->storage = $view_entity->reveal(); + + $entities = [ + 'second' => [ + 11 => $this->prophesize(EntityInterface::class)->reveal(), + 12 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_revisions = [ + 'first' => [ + 1 => $this->prophesize(EntityInterface::class)->reveal(), + 3 => $this->prophesize(EntityInterface::class)->reveal(), + ], + ]; + $entity_type_manager = $this->setupEntityTypes($entities, $entity_revisions); + + $query = new Sql([], 'sql', [], $entity_type_manager->reveal()); + $query->view = $view; + + $result = []; + $result[] = new ResultRow([ + 'vid' => 1, + 'entity_second__id' => 11, + ]); + // Provide an explicit NULL value, to test the case of a non required + // relationship. + $result[] = new ResultRow([ + 'vid' => 1, + 'entity_second__id' => NULL, + ]); + $result[] = new ResultRow([ + 'vid' => 3, + 'entity_second__id' => 12, + ]); + + $query->addField('entity_first__revision', 'vid', 'vid'); + $query->addField('entity_second', 'id', 'entity_second__id'); + $query->loadEntities($result); + + $this->assertSame($entity_revisions['first'][1], $result[0]->_entity); + $this->assertSame($entity_revisions['first'][1], $result[1]->_entity); + $this->assertSame($entity_revisions['first'][3], $result[2]->_entity); + + $this->assertSame($entities['second'][11], $result[0]->_relationship_entities['entity_second']); + $this->assertEquals([], $result[1]->_relationship_entities); + $this->assertSame($entities['second'][12], $result[2]->_relationship_entities['entity_second']); + } + +}