function ValidKeysConstraintValidatorTest::testValidation

Same name and namespace in other branches
  1. 11.x core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php \Drupal\KernelTests\Core\TypedData\ValidKeysConstraintValidatorTest::testValidation()

Tests the ValidKeys constraint validator.

File

core/tests/Drupal/KernelTests/Core/TypedData/ValidKeysConstraintValidatorTest.php, line 232

Class

ValidKeysConstraintValidatorTest
Tests the ValidKeys validation constraint.

Namespace

Drupal\KernelTests\Core\TypedData

Code

public function testValidation() : void {
  // Create a data definition that specifies certain allowed keys.
  $definition = MapDataDefinition::create('mapping')->addConstraint('ValidKeys', [
    'north',
    'south',
    'west',
  ]);
  $definition['mapping'] = [
    'north' => [
      'type' => 'string',
      'requiredKey' => FALSE,
    ],
    'east' => [
      'type' => 'string',
      'requiredKey' => FALSE,
    ],
    'south' => [
      'type' => 'string',
      'requiredKey' => FALSE,
    ],
    'west' => [
      'type' => 'string',
      'requiredKey' => FALSE,
    ],
  ];
  // @todo Remove this line in https://www.drupal.org/project/drupal/issues/3403782
  $definition->setClass('Drupal\\Core\\Config\\Schema\\Mapping');
  /** @var \Drupal\Core\TypedData\TypedDataManagerInterface $typed_config */
  $typed_config = $this->container
    ->get('config.typed');
  // @see \Drupal\Core\Config\TypedConfigManager::buildDataDefinition()
  // @see \Drupal\Core\TypedData\TypedDataManager::createDataDefinition()
  $definition->setTypedDataManager($typed_config);
  // Passing a non-array value should raise an exception.
  try {
    // TRICKY: we must clone the definition because the instance is modified
    // when processing.
    // @see \Drupal\Core\Config\Schema\Mapping::processRequiredKeyFlags()
    $typed_config->create(clone $definition, 2501)
      ->validate();
    $this->fail('Expected an exception but none was raised.');
  } catch (UnexpectedTypeException $e) {
    $this->assertSame('Expected argument of type "array", "int" given', $e->getMessage());
  }
  // Empty arrays are valid.
  $this->assertCount(0, $typed_config->create(clone $definition, [])
    ->validate());
  // Indexed arrays are never valid.
  $violations = $typed_config->create(clone $definition, [
    'north',
    'south',
  ])
    ->validate();
  $this->assertCount(1, $violations);
  $this->assertSame('Numerically indexed arrays are not allowed.', (string) $violations->get(0)
    ->getMessage());
  // Arrays with automatically assigned keys, AND a valid key, should be
  // considered invalid overall.
  $violations = $typed_config->create(clone $definition, [
    'north',
    'south' => 'west',
  ])
    ->validate();
  $this->assertCount(1, $violations);
  $this->assertSame("'0' is not a supported key.", (string) $violations->get(0)
    ->getMessage());
  // Associative arrays with an invalid key should be invalid.
  $violations = $typed_config->create(clone $definition, [
    'north' => 'south',
    'east' => 'west',
  ])
    ->validate();
  $this->assertCount(1, $violations);
  $this->assertSame("'east' is not a supported key.", (string) $violations->get(0)
    ->getMessage());
  // If the array only contains the allowed keys, it's fine.
  $value = [
    'north' => 'Boston',
    'south' => 'Atlanta',
    'west' => 'San Francisco',
  ];
  $violations = $typed_config->create(clone $definition, $value)
    ->validate();
  $this->assertCount(0, $violations);
  // If, in the mapping definition, some keys do NOT have
  // `requiredKey: false` set, then they MUST be set. In other
  // words, all keys are required unless they individually
  // specify otherwise.
  // First test without changing the value: no error should occur because all
  // keys passed to the ValidKeys constraint have a value.
  unset($definition['mapping']['south']['requiredKey']);
  unset($definition['mapping']['east']['requiredKey']);
  $violations = $typed_config->create(clone $definition, $value)
    ->validate();
  $this->assertCount(0, $violations);
  // If in the mapping definition some keys that do NOT have
  // `requiredKey: false` set, then they MUST be set.
  // First test without changing the value: no error should occur because all
  // keys passed to the ValidKeys constraint have a value.
  unset($definition['mapping']['south']['requiredKey']);
  unset($definition['mapping']['east']['requiredKey']);
  $violations = $typed_config->create(clone $definition, $value)
    ->validate();
  $this->assertCount(0, $violations);
  // Then remove the required key-value pair: this must trigger an error, but
  // only if the root type has opted in.
  unset($value['south']);
  $violations = $typed_config->create(clone $definition, $value)
    ->validate();
  $this->assertCount(0, $violations);
  $definition->addConstraint('FullyValidatable', NULL);
  $violations = $typed_config->create(clone $definition, $value)
    ->validate();
  $this->assertCount(1, $violations);
  $this->assertSame("'south' is a required key.", (string) $violations->get(0)
    ->getMessage());
}

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