PageRenderTime 50ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/core/tests/Drupal/KernelTests/Core/Entity/EntityValidationTest.php

http://github.com/drupal/drupal
PHP | 260 lines | 140 code | 41 blank | 79 comment | 1 complexity | 7e6f43f67eb284b0937629efcf5ce2b6 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1
  1. <?php
  2. namespace Drupal\KernelTests\Core\Entity;
  3. use Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase;
  4. use Drupal\language\Entity\ConfigurableLanguage;
  5. /**
  6. * Tests the Entity Validation API.
  7. *
  8. * @group Entity
  9. */
  10. class EntityValidationTest extends EntityKernelTestBase {
  11. /**
  12. * Modules to enable.
  13. *
  14. * @var array
  15. */
  16. public static $modules = ['filter', 'text', 'language'];
  17. /**
  18. * @var string
  19. */
  20. protected $entityName;
  21. /**
  22. * @var \Drupal\user\Entity\User
  23. */
  24. protected $entityUser;
  25. /**
  26. * @var string
  27. */
  28. protected $entityFieldText;
  29. /**
  30. * {@inheritdoc}
  31. */
  32. protected function setUp() {
  33. parent::setUp();
  34. // Enable an additional language.
  35. ConfigurableLanguage::createFromLangcode('de')
  36. ->save();
  37. $this->installEntitySchema('entity_test_mul');
  38. $this->installEntitySchema('entity_test_mul_langcode_key');
  39. $this->installEntitySchema('entity_test_mul_changed');
  40. $this->installEntitySchema('entity_test_rev');
  41. $this->installEntitySchema('entity_test_mulrev');
  42. $this->installEntitySchema('entity_test_mulrev_changed');
  43. // Create the test field.
  44. module_load_install('entity_test');
  45. entity_test_install();
  46. // Install required default configuration for filter module.
  47. $this->installConfig(['system', 'filter']);
  48. }
  49. /**
  50. * Creates a test entity.
  51. *
  52. * @param string $entity_type
  53. * An entity type.
  54. *
  55. * @return \Drupal\Core\Entity\EntityInterface
  56. * The created test entity.
  57. */
  58. protected function createTestEntity($entity_type) {
  59. $this->entityName = $this->randomMachineName();
  60. $this->entityUser = $this->createUser();
  61. // Pass in the value of the name field when creating. With the user
  62. // field we test setting a field after creation.
  63. $entity = $this->container->get('entity_type.manager')
  64. ->getStorage($entity_type)
  65. ->create();
  66. $entity->user_id->target_id = $this->entityUser->id();
  67. $entity->name->value = $this->entityName;
  68. // Set a value for the test field.
  69. if ($entity->hasField('field_test_text')) {
  70. $this->entityFieldText = $this->randomMachineName();
  71. $entity->field_test_text->value = $this->entityFieldText;
  72. }
  73. return $entity;
  74. }
  75. /**
  76. * Tests validating test entity types.
  77. */
  78. public function testValidation() {
  79. // Ensure that the constraint manager is marked as cached cleared.
  80. // Use the protected property on the cache_clearer first to check whether
  81. // the constraint manager is added there.
  82. // Ensure that the proxy class is initialized, which has the necessary
  83. // method calls attached.
  84. \Drupal::service('plugin.cache_clearer');
  85. $plugin_cache_clearer = \Drupal::service('drupal.proxy_original_service.plugin.cache_clearer');
  86. $get_cached_discoveries = function () {
  87. return $this->cachedDiscoveries;
  88. };
  89. $get_cached_discoveries = $get_cached_discoveries->bindTo($plugin_cache_clearer, $plugin_cache_clearer);
  90. $cached_discoveries = $get_cached_discoveries();
  91. $cached_discovery_classes = [];
  92. foreach ($cached_discoveries as $cached_discovery) {
  93. $cached_discovery_classes[] = get_class($cached_discovery);
  94. }
  95. $this->assertTrue(in_array('Drupal\Core\Validation\ConstraintManager', $cached_discovery_classes));
  96. // All entity variations have to have the same results.
  97. foreach (entity_test_entity_types() as $entity_type) {
  98. $this->checkValidation($entity_type);
  99. }
  100. }
  101. /**
  102. * Executes the validation test set for a defined entity type.
  103. *
  104. * @param string $entity_type
  105. * The entity type to run the tests with.
  106. */
  107. protected function checkValidation($entity_type) {
  108. $entity = $this->createTestEntity($entity_type);
  109. $violations = $entity->validate();
  110. $this->assertEqual($violations->count(), 0, 'Validation passes.');
  111. // Test triggering a fail for each of the constraints specified.
  112. $test_entity = clone $entity;
  113. $test_entity->id->value = -1;
  114. $violations = $test_entity->validate();
  115. $this->assertEqual($violations->count(), 1, 'Validation failed.');
  116. $this->assertEqual($violations[0]->getMessage(), t('%name: The integer must be larger or equal to %min.', ['%name' => 'ID', '%min' => 0]));
  117. $test_entity = clone $entity;
  118. $test_entity->uuid->value = $this->randomString(129);
  119. $violations = $test_entity->validate();
  120. $this->assertEqual($violations->count(), 1, 'Validation failed.');
  121. $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', ['%name' => 'UUID', '@max' => 128]));
  122. $test_entity = clone $entity;
  123. $langcode_key = $this->entityTypeManager->getDefinition($entity_type)->getKey('langcode');
  124. $test_entity->{$langcode_key}->value = $this->randomString(13);
  125. $violations = $test_entity->validate();
  126. // This should fail on AllowedValues and Length constraints.
  127. $this->assertEqual($violations->count(), 2, 'Validation failed.');
  128. $this->assertEqual($violations[0]->getMessage(), t('This value is too long. It should have %limit characters or less.', ['%limit' => '12']));
  129. $this->assertEqual($violations[1]->getMessage(), t('The value you selected is not a valid choice.'));
  130. $test_entity = clone $entity;
  131. $test_entity->type->value = NULL;
  132. $violations = $test_entity->validate();
  133. $this->assertEqual($violations->count(), 1, 'Validation failed.');
  134. $this->assertEqual($violations[0]->getMessage(), t('This value should not be null.'));
  135. $test_entity = clone $entity;
  136. $test_entity->name->value = $this->randomString(33);
  137. $violations = $test_entity->validate();
  138. $this->assertEqual($violations->count(), 1, 'Validation failed.');
  139. $this->assertEqual($violations[0]->getMessage(), t('%name: may not be longer than @max characters.', ['%name' => 'Name', '@max' => 32]));
  140. // Make sure the information provided by a violation is correct.
  141. $violation = $violations[0];
  142. $this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.');
  143. $this->assertEqual($violation->getPropertyPath(), 'name.0.value', 'Violation property path is correct.');
  144. $this->assertEqual($violation->getInvalidValue(), $test_entity->name->value, 'Violation contains invalid value.');
  145. $test_entity = clone $entity;
  146. $test_entity->set('user_id', 9999);
  147. $violations = $test_entity->validate();
  148. $this->assertEqual($violations->count(), 1, 'Validation failed.');
  149. $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', ['%type' => 'user', '%id' => 9999]));
  150. $test_entity = clone $entity;
  151. $test_entity->field_test_text->format = $this->randomString(33);
  152. $violations = $test_entity->validate();
  153. $this->assertEqual($violations->count(), 1, 'Validation failed.');
  154. $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.'));
  155. // Make sure the information provided by a violation is correct.
  156. $violation = $violations[0];
  157. $this->assertEqual($violation->getRoot()->getValue(), $test_entity, 'Violation root is entity.');
  158. $this->assertEqual($violation->getPropertyPath(), 'field_test_text.0.format', 'Violation property path is correct.');
  159. $this->assertEqual($violation->getInvalidValue(), $test_entity->field_test_text->format, 'Violation contains invalid value.');
  160. }
  161. /**
  162. * Tests composite constraints.
  163. */
  164. public function testCompositeConstraintValidation() {
  165. $entity = $this->createTestEntity('entity_test_composite_constraint');
  166. $violations = $entity->validate();
  167. $this->assertEqual($violations->count(), 0);
  168. // Trigger violation condition.
  169. $entity->name->value = 'test';
  170. $entity->type->value = 'test2';
  171. $violations = $entity->validate();
  172. $this->assertEqual($violations->count(), 1);
  173. // Make sure we can determine this is composite constraint.
  174. $constraint = $violations[0]->getConstraint();
  175. $this->assertTrue($constraint instanceof CompositeConstraintBase, 'Constraint is composite constraint.');
  176. $this->assertEqual('type', $violations[0]->getPropertyPath());
  177. /** @var \Drupal\Core\Entity\Plugin\Validation\Constraint\CompositeConstraintBase $constraint */
  178. $this->assertEqual($constraint->coversFields(), ['name', 'type'], 'Information about covered fields can be retrieved.');
  179. }
  180. /**
  181. * Tests the EntityChangedConstraintValidator with multiple translations.
  182. */
  183. public function testEntityChangedConstraintOnConcurrentMultilingualEditing() {
  184. $this->installEntitySchema('entity_test_mulrev_changed');
  185. $storage = \Drupal::entityTypeManager()
  186. ->getStorage('entity_test_mulrev_changed');
  187. // Create a test entity.
  188. $entity = $this->createTestEntity('entity_test_mulrev_changed');
  189. $entity->save();
  190. $entity->setChangedTime($entity->getChangedTime() - 1);
  191. $violations = $entity->validate();
  192. $this->assertEquals(1, $violations->count());
  193. $this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
  194. $entity = $storage->loadUnchanged($entity->id());
  195. $translation = $entity->addTranslation('de');
  196. $entity->save();
  197. // Ensure that the new translation has a newer changed timestamp than the
  198. // default translation.
  199. $this->assertGreaterThan($entity->getChangedTime(), $translation->getChangedTime());
  200. // Simulate concurrent form editing by saving the entity with an altered
  201. // non-translatable field in order for the changed timestamp to be updated
  202. // across all entity translations.
  203. $original_entity_time = $entity->getChangedTime();
  204. $entity->set('not_translatable', $this->randomString());
  205. $entity->save();
  206. // Simulate form submission of an uncached form by setting the previous
  207. // timestamp of an entity translation on the saved entity object. This
  208. // happens in the entity form API where we put the changed timestamp of
  209. // the entity in a form hidden value and then set it on the entity which on
  210. // form submit is loaded from the storage if the form is not yet cached.
  211. $entity->setChangedTime($original_entity_time);
  212. // Setting the changed timestamp from the user input on the entity loaded
  213. // from the storage is used as a prevention from saving a form built with a
  214. // previous version of the entity and thus reverting changes by other users.
  215. $violations = $entity->validate();
  216. $this->assertEquals(1, $violations->count());
  217. $this->assertEqual($violations[0]->getMessage(), 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
  218. }
  219. }