graphql_core_schema-1.0.x-dev/tests/src/Kernel/GenerateSchemaTest.php

tests/src/Kernel/GenerateSchemaTest.php
<?php

// phpcs:disable Drupal.NamingConventions.ValidVariableName.LowerStart

namespace Drupal\Tests\graphql_core_schema\Kernel;

use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\graphql_core_schema\EntitySchemaHelper;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\field\Traits\EntityReferenceFieldCreationTrait;
use Drupal\Tests\graphql_core_schema\Traits\CoreComposableServerBuilder;
use GraphQL\Error\Error;
use GraphQL\Type\Definition\InterfaceType;
use GraphQL\Type\Definition\ListOfType;

/**
 * Tests the graphql_core_schema schema generation.
 *
 * @group graphql_core_schema
 */
class GenerateSchemaTest extends CoreComposableKernelTestBase {

  use EntityReferenceFieldCreationTrait;

  /**
   * Test that the base schema is generated correctly.
   */
  public function testBaseSchema(): void {
    $builder = new CoreComposableServerBuilder();
    $builder->enableEntityType('node')->enableBaseEntityField('label');
    $server = $builder->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\InterfaceType $Entity */
    $Entity = $schema->getType('Entity');
    $this->assertNotNull($Entity);
    $this->assertNotNull($Entity->getField('id'), 'Generates the ID field for the Entity interface.');
    $this->assertNotNull($Entity->getField('label'), 'Generates an enabled base entity field.');
    $this->assertFalse($Entity->hasField('entityBundle'), 'Does not generate a base entity field that is not enabled.');

    /** @var \GraphQL\Type\Definition\InterfaceType $EntityDescribable */
    $EntityDescribable = $schema->getType('EntityDescribable');
    $this->assertNotNull($EntityDescribable);
    $EntityDescribable_entityDescription = $EntityDescribable->getField('entityDescription');
    $this->assertNotNull($EntityDescribable_entityDescription);
    $this->assertEquals('String', $EntityDescribable_entityDescription->getType()->name);

    /** @var \GraphQL\Type\Definition\InterfaceType $EntityLinkable */
    $EntityLinkable = $schema->getType('EntityLinkable');
    $this->assertNotNull($EntityLinkable);
    $this->assertNotNull($EntityLinkable->getField('url'));
    $this->assertEquals('Url', $EntityLinkable->getField('url')->getType()->name);

    /** @var \GraphQL\Type\Definition\InterfaceType $EntityTranslatable */
    $EntityTranslatable = $schema->getType('EntityTranslatable');
    $this->assertNotNull($EntityTranslatable);
    $EntityTranslatable_translations = $EntityTranslatable->getField('translations');
    $this->assertNotNull($EntityTranslatable_translations);
    /** @var \GraphQL\Type\Definition\ListOfType $translations_list_type */
    $translations_list_type = $EntityTranslatable_translations->getType();
    $translations_list_item_type = $translations_list_type->getOfType();
    $this->assertEquals('EntityTranslatable', $translations_list_item_type->name);

    /** @var \GraphQL\Type\Definition\ObjectType $Query */
    $Query = $schema->getType('Query');
    $this->assertTrue($Query->hasField('ping'));
    /** @var \GraphQL\Type\Definition\ObjectType $Mutation */
    $Mutation = $schema->getType('Mutation');
    $this->assertTrue($Mutation->hasField('ping'));
  }

  /**
   * Generates the enum for langcodes.
   */
  public function testLangcodeEnum(): void {
    $builder = new CoreComposableServerBuilder();
    $builder->enableEntityType('node')->enableBaseEntityField('label');
    $server = $builder->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\EnumType $Langcode */
    $Langcode = $schema->getType('Langcode');
    $this->assertNotNull($Langcode->getValue('DE'));
    $this->assertNotNull($Langcode->getValue('IT'));
    $this->assertEquals('German', $Langcode->getValue('DE')->description);
    $this->assertEquals('Italian', $Langcode->getValue('IT')->description);
  }

  /**
   * Tests the ConfigurableLanguage.direction type.
   */
  public function testLanguageDirection(): void {
    $builder = new CoreComposableServerBuilder();
    $builder->enableEntityType('configurable_language', ['direction'])
      ->enableBaseEntityField('label');
    $server = $builder->createServer();
    $schema = $this->getSchema($server);
    $type = $schema->getType('ConfigurableLanguage')->getField('direction')->getType()->toString();
    $this->assertEquals('String', $type);
  }

  /**
   * Tests the ConfigurableLanguage.direction with language_schema extension.
   */
  public function testLanguageDirectionWithLanguageExtension(): void {
    $builder = new CoreComposableServerBuilder();
    $builder->enableEntityType('configurable_language', ['direction'])
      ->enableBaseEntityField('label')
      ->enableExtension('language_schema');
    $server = $builder->createServer();
    $schema = $this->getSchema($server);

    $type = $schema->getType('ConfigurableLanguage')->getField('direction')->getType()->toString();
    $this->assertEquals('LanguageDirection!', $type);
  }

  /**
   * Generates the enum for entity types.
   */
  public function testEntityTypeEnum(): void {
    $builder = new CoreComposableServerBuilder();
    $server = $builder
      ->enableEntityType('node')
      ->enableEntityType('node_type')
      ->enableEntityType('user')
      ->enableEntityType('taxonomy_term')
      ->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\EnumType $EntityType */
    $EntityType = $schema->getType('EntityType');
    $this->assertNotNull($EntityType->getValue('NODE'));
    $this->assertNotNull($EntityType->getValue('NODE_TYPE'));
    $this->assertNotNull($EntityType->getValue('USER'));
    $this->assertNotNull($EntityType->getValue('TAXONOMY_TERM'));
  }

  /**
   * Generates the enum for date formats.
   */
  public function testDateFormatEnum(): void {
    $builder = new CoreComposableServerBuilder();
    $server = $builder
      ->enableEntityType('node')
      ->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\EnumType $EntityType */
    $EntityType = $schema->getType('DrupalDateFormat');
    $this->assertNotNull($EntityType->getValue('LONG'));
    $this->assertNotNull($EntityType->getValue('SHORT'));
    $this->assertNotNull($EntityType->getValue('FALLBACK'));
  }

  /**
   * Test that entity fields are generated.
   */
  public function testEntityFields(): void {
    NodeType::create(['type' => 'article'])->save();
    $field_storage = FieldStorageConfig::create([
      'field_name' => 'field_author',
      'entity_type' => 'node',
      'type' => 'entity_reference',
    ]);
    $field_storage->save();

    FieldConfig::create([
      'field_name' => 'field_author',
      'field_storage' => $field_storage,
      'entity_type' => 'node',
      'bundle' => 'article',
      'label' => 'Author',
    ])->save();

    $builder = new CoreComposableServerBuilder();
    $builder->enableEntityType('node', ['field_author'], ['article']);
    $server = $builder->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\InterfaceType $Node */
    $Node = $schema->getType('Node');
    $this->assertNotNull($Node);

    /** @var \GraphQL\Type\Definition\InterfaceType $NodeArticle */
    $NodeArticle = $schema->getType('NodeArticle');
    $this->assertNotNull($NodeArticle);
    $NodeArticle_fieldAuthor = $NodeArticle->getField('fieldAuthorRawField');
    $this->assertNotNull($NodeArticle_fieldAuthor);
  }

  /**
   * Test that disabled entity types are not created.
   */
  public function testNotGenerateDisabledTypes(): void {
    Vocabulary::create(['vid' => 'tags'])->save();
    NodeType::create(['type' => 'article'])->save();

    $builder = new CoreComposableServerBuilder();
    $server = $builder->enableEntityType('node')->createServer();
    $schema = $this->getSchema($server);
    $this->expectException(Error::class);
    $schema->getType('TaxonomyTerm');
  }

  /**
   * Tests that only requested bundle types are generated.
   */
  public function testGenerateRequestedBundleTypesOnly(): void {
    NodeType::create(['type' => 'article'])->save();
    NodeType::create(['type' => 'page'])->save();

    $builder = new CoreComposableServerBuilder();
    $server = $builder
      ->enableEntityType('node', [], ['article'])
      ->createServer();
    $schema = $this->getSchema($server);

    $this->assertNotNull($schema->getType('NodeArticle'));
    $this->expectException(Error::class);
    $this->expectExceptionMessage('Type "NodePage" not found in document.');
    $schema->getType('NodePage');
  }

  /**
   * Test that bundle type names can be overridden in config.
   */
  public function testGenerateOverriddenBundleTypeNames(): void {
    Vocabulary::create(['vid' => 'tags'])->save();
    NodeType::create(['type' => 'article'])->save();

    $builder = new CoreComposableServerBuilder();
    $builder
      ->enableEntityType('node')
      ->setBundleConfig('node', 'article', ['enabled' => TRUE, 'type_name' => 'Article'])
      ->enableEntityType('taxonomy_term')
      ->setBundleConfig('taxonomy_term', 'tags', ['enabled' => TRUE, 'type_name' => 'Tag']);
    $server = $builder->createServer();
    $schema = $this->getSchema($server);

    $expected_type_interfaces = [
      'Article' => ['Node', 'Entity', 'EntityLinkable', 'EntityRevisionable'],
      'Tag' => ['TaxonomyTerm', 'Entity', 'EntityLinkable', 'EntityRevisionable'],
    ];
    foreach ($expected_type_interfaces as $overriddenTypeName => $expectedInterfaces) {
      $type = $schema->getType($overriddenTypeName);
      $this->assertNotNull($type, "The type '$overriddenTypeName' is generated.");
      $actual_interfaces = array_map(fn (InterfaceType $interface) => $interface->name, $type->getInterfaces());
      sort($actual_interfaces);
      sort($expectedInterfaces);
      $this->assertEquals($expectedInterfaces, $actual_interfaces, "The type '$overriddenTypeName' implements the expected interfaces.");
    }

    // Sanity check. Since we are overriding the type, the default type name
    // 'NodeArticle' should not be included in the schema.
    $this->expectException(Error::class);
    $schema->getType('NodeArticle');
  }

  /**
   * Tests that types of entity_reference fields don't use bundles that are not enabled.
   */
  public function testGenerateRequestedBundleTypesOnlyInReferences(): void {
    NodeType::create(['type' => 'article'])->save();
    Vocabulary::create(['vid' => 'tag'])->save();
    Vocabulary::create(['vid' => 'location'])->save();

    $this->createEntityReferenceField(
      'node',
      'article',
      'no_bundle_type',
      'Tag',
      'taxonomy_term',
      'default',
      ['target_bundles' => ['tag' => 'tag']],
      1
    );

    $this->createEntityReferenceField(
      'node',
      'article',
      'has_bundle_type',
      'Location',
      'taxonomy_term',
      'default',
      ['target_bundles' => ['location' => 'location']],
      1
    );

    $builder = new CoreComposableServerBuilder();
    $server = $builder
      ->enableEntityType('node', ['no_bundle_type', 'has_bundle_type'], ['article'])
      ->enableEntityType('taxonomy_term', [], ['location'])
      ->enableValueFields()
      ->createServer();
    $schema = $this->getSchema($server);
    /** @var \GraphQL\Type\Definition\ObjectType $NodeArticle */
    $NodeArticle = $schema->getType('NodeArticle');

    $noBundleType = $NodeArticle->getField('noBundleType');
    $this->assertEquals('TaxonomyTerm', $noBundleType->getType()->name, 'Does not use a type for a bundle that is not enabled.');

    $hasBundleType = $NodeArticle->getField('hasBundleType');
    $this->assertEquals('TaxonomyTermLocation', $hasBundleType->getType()->name, 'Uses a bundle specific type when the bundle is enabled.');
  }

  /**
   * Test that entity reference fields are generated correctly.
   */
  public function testEntityReferenceFields(): void {
    Vocabulary::create(['vid' => 'tag'])->save();
    Vocabulary::create(['vid' => 'location'])->save();
    NodeType::create(['type' => 'article'])->save();
    $this->createEntityReferenceField(
      'node',
      'article',
      'single_term',
      'Location',
      'taxonomy_term',
      'default',
      ['target_bundles' => ['location' => 'location']],
      1
    );

    $this->createEntityReferenceField(
      'node',
      'article',
      'multiple_terms',
      'Tags',
      'taxonomy_term',
      'default',
      ['target_bundles' => ['tag' => 'tag']],
      6
    );

    $this->createEntityReferenceField(
      'node',
      'article',
      'multiple_vocab_single_term',
      'Multiple',
      'taxonomy_term',
      'default',
      ['target_bundles' => ['tag' => 'tag', 'location' => 'location']],
      1
    );

    $this->createEntityReferenceField(
      'node',
      'article',
      'multiple_vocab_multiple_terms',
      'Multiple Vocabularies',
      'taxonomy_term',
      'default',
      ['target_bundles' => ['tag' => 'tag', 'location' => 'location']],
      6
    );

    $builder = new CoreComposableServerBuilder();
    $server = $builder
      ->enableEntityType('node', [
        'single_term',
        'multiple_terms',
        'multiple_vocab_single_term',
        'multiple_vocab_multiple_terms',
      ], ['article'])
      ->enableEntityType('taxonomy_term', [], ['tag', 'location'])
      ->enableValueFields()
      ->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\ObjectType $NodeArticle */
    $NodeArticle = $schema->getType('NodeArticle');
    $singleTerm = $NodeArticle->getField('singleTerm');
    $this->assertEquals('TaxonomyTermLocation', $singleTerm->getType()->name, 'Generates a value field with the target bundle as the type.');

    $multipleTerms = $NodeArticle->getField('multipleTerms');
    $this->assertEquals('TaxonomyTermTag', $multipleTerms->getType()->getOfType()->name, 'Generates a value field with the target bundle as the type.');

    $multipleVocabSingleTerm = $NodeArticle->getField('multipleVocabSingleTerm');
    $this->assertEquals('TaxonomyTerm', $multipleVocabSingleTerm->getType()->name, 'Generates a value field with the target entity type interface as the type.');

    $multipleVocabMultipleTerms = $NodeArticle->getField('multipleVocabMultipleTerms');
    $this->assertEquals('TaxonomyTerm', $multipleVocabMultipleTerms->getType()->getOfType()->name, 'Generates a value field with the target entity type interface as the type.');
  }

  /**
   * Generates value fields for string field items.
   */
  public function testScalarValueFields(): void {
    $this->enableModules(['telephone', 'options']);
    NodeType::create(['type' => 'article'])->save();

    $fieldNames = [];
    $fields = [
      'string' => 'String',
      'string_long' => 'String',
      'text' => 'String',
      'text_with_summary' => 'String',
      'text_long' => 'String',
      'uri' => 'String',
      'email' => 'String',
      'telephone' => 'String',
      'list_string' => 'String',
      'timestamp' => 'String',
      'changed' => 'String',
      'created' => 'String',
      'boolean' => 'Boolean',
      'decimal' => 'Float',
      'float' => 'Float',
      'integer' => 'Int',
      'map' => 'MapData',
      'language' => 'LanguageInterface',
    ];

    for ($cardinality = 1; $cardinality <= 2; $cardinality++) {
      foreach ($fields as $drupalType => $graphqlType) {
        $fieldName = 'field_' . $drupalType . '_' . $cardinality;
        $fieldNames[] = $fieldName;
        $field_storage = FieldStorageConfig::create([
          'field_name' => $fieldName,
          'entity_type' => 'node',
          'type' => $drupalType,
          'cardinality' => $cardinality,
        ]);
        $field_storage->save();

        FieldConfig::create([
          'field_name' => $fieldName,
          'field_storage' => $field_storage,
          'entity_type' => 'node',
          'bundle' => 'article',
          'label' => $fieldName,
        ])->save();
      }
    }

    $builder = new CoreComposableServerBuilder();
    $server = $builder->enableEntityType('node', $fieldNames, ['article'])->enableValueFields()->createServer();
    $schema = $this->getSchema($server);
    /** @var \GraphQL\Type\Definition\ObjectType $NodeArticle */
    $NodeArticle = $schema->getType('NodeArticle');

    foreach ($fields as $drupalType => $graphqlType) {
      $fieldNameSingle = EntitySchemaHelper::toCamelCase('field_' . $drupalType . '_1');
      $fieldSingle = $NodeArticle->getField($fieldNameSingle);
      $this->assertEquals($graphqlType, $fieldSingle->getType()->name, "Value field for '$drupalType' generates type '$graphqlType'.");

      $fieldNameMultiple = EntitySchemaHelper::toCamelCase('field_' . $drupalType . '_2');
      $fieldMultiple = $NodeArticle->getField($fieldNameMultiple);
      /** @var ListOfType $fieldMultipleType */
      $fieldMultipleType = $fieldMultiple->getType();
      $this->assertInstanceOf(ListOfType::class, $fieldMultipleType);
      $this->assertEquals($graphqlType, $fieldMultipleType->getOfType()->name, "List value field for '$drupalType' generates type '[$graphqlType]'.");
    }

    $textWithSummary = $NodeArticle->getField('fieldTextWithSummary1');
    $arg = $textWithSummary->getArg('summary');
    $this->assertNotNull($arg, 'Adds the summary argument to text_with_summary value fields.');
    $this->assertEquals('Boolean', $arg->getType()->name);
  }

  /**
   * Uses interfaces for field item types that share a value property of the same scalar type.
   */
  public function testFieldItemInterfaces(): void {
    $this->enableModules(['telephone', 'options']);
    NodeType::create(['type' => 'article'])->save();

    $fieldNames = [];
    $fields = [
      'changed' => 'FieldItemTypeTimestampInterface',
      'timestamp' => 'FieldItemTypeTimestampInterface',
      'text' => 'FieldItemTypeStringInterface',
      'string' => 'FieldItemTypeStringInterface',
      'integer' => 'FieldItemTypeIntegerInterface',
    ];

    foreach ($fields as $drupalType => $graphqlType) {
      $fieldName = 'field_' . $drupalType;
      $fieldNames[] = $fieldName;
      $field_storage = FieldStorageConfig::create([
        'field_name' => $fieldName,
        'entity_type' => 'node',
        'type' => $drupalType,
      ]);
      $field_storage->save();

      FieldConfig::create([
        'field_name' => $fieldName,
        'field_storage' => $field_storage,
        'entity_type' => 'node',
        'bundle' => 'article',
        'label' => $fieldName,
      ])->save();
    }

    $builder = new CoreComposableServerBuilder();
    $server = $builder->enableEntityType('node', $fieldNames, ['article'])->createServer();
    $schema = $this->getSchema($server);
    /** @var \GraphQL\Type\Definition\ObjectType $NodeArticle */
    $NodeArticle = $schema->getType('NodeArticle');

    foreach ($fields as $drupalType => $interfaceName) {
      $fieldName = EntitySchemaHelper::toCamelCase('field_' . $drupalType . '_raw_field');
      $field = $NodeArticle->getField($fieldName);

      /** @var \GraphQL\Type\Definition\ObjectType $fieldType */
      $fieldType = $field->getType();

      /** @var \GraphQL\Type\Definition\ObjectType $fieldItemType */
      $fieldItemType = $fieldType->getField('first')->getType();

      // The names of the interfaces this type implements.
      $implementingInterfaces = array_map(function (InterfaceType $interface) {
        return $interface->name;
      }, $fieldItemType->getInterfaces());

      $this->assertContains(
        $interfaceName,
        $implementingInterfaces,
        "Field items of type '$drupalType' implement interface '$interfaceName'"
      );
    }
  }

  /**
   * Test a schema that generates complex data types.
   */
  public function testConfigEntities(): void {
    $server = $this
      ->getCoreComposableServerBuilder()
      ->enableValueFields()
      ->enableEntityType('node_type', [
        'preview_mode',
        'status',
        'type',
        'help',
        'name',
        'display_submitted',
        'new_revision',
      ])
      ->createServer();

    $schema = $this->getSchema($server);

    $tests = [
      'previewMode' => 'Int',
      'status' => 'Boolean',
      'type' => 'String',
      'help' => 'String',
      'name' => 'String',
      'displaySubmitted' => 'Boolean',
      'newRevision' => 'Boolean',
    ];

    /** @var \GraphQL\Type\Definition\ObjectType $NodeType */
    $NodeType = $schema->getType('NodeType');

    foreach ($tests as $fieldName => $expectedType) {
      $field = $NodeType->getField($fieldName);
      $type = $field->getType();
      $this->assertEquals($expectedType, $type->name);
    }
  }

  /**
   * Test to make sure that generating a type that references itself works.
   */
  public function testTypeSelfReference(): void {
    NodeType::create(['type' => 'article'])->save();

    $this->createEntityReferenceField('node', 'article', 'field_node', NULL, 'node', 'default', ['target_bundles' => ['article']], 1);

    $server = $this
      ->getCoreComposableServerBuilder()
      ->enableEntityType('node', ['field_node'], ['article'])
      ->enableEntityType('user')
      ->enableEntityType('user_role')
      ->enableValueFields()
      ->createServer();
    $schema = $this->getSchema($server);

    /** @var \GraphQL\Type\Definition\ObjectType $NodeArticle */
    $NodeArticle = $schema->getType('NodeArticle');
    $this->assertNotNull($NodeArticle);
    $this->assertNotNull($NodeArticle->getField('fieldNode'), 'Generates the field with a self reference.');
    $this->assertEquals('NodeArticle', $NodeArticle->getField('fieldNode')->getType()->name, 'Generates the field with a self reference.');
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc