Chris@0: installEntitySchema('user'); Chris@0: $this->installSchema('system', ['sequences']); Chris@0: Chris@0: // Make sure that the default roles exist. Chris@0: $this->installConfig(['user']); Chris@0: Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests user name validation. Chris@0: */ Chris@0: public function testUsernames() { Chris@0: $test_cases = [ Chris@0: // '' => ['', 'assert']. Chris@0: 'foo' => ['Valid username', 'assertNull'], Chris@0: 'FOO' => ['Valid username', 'assertNull'], Chris@0: 'Foo O\'Bar' => ['Valid username', 'assertNull'], Chris@0: 'foo@bar' => ['Valid username', 'assertNull'], Chris@0: 'foo@example.com' => ['Valid username', 'assertNull'], Chris@0: // invalid domains are allowed in usernames. Chris@0: 'foo@-example.com' => ['Valid username', 'assertNull'], Chris@0: 'þòøÇߪř€' => ['Valid username', 'assertNull'], Chris@0: // '+' symbol is allowed. Chris@0: 'foo+bar' => ['Valid username', 'assertNull'], Chris@0: // runes. Chris@0: 'ᚠᛇᚻ᛫ᛒᛦᚦ' => ['Valid UTF8 username', 'assertNull'], Chris@0: ' foo' => ['Invalid username that starts with a space', 'assertNotNull'], Chris@0: 'foo ' => ['Invalid username that ends with a space', 'assertNotNull'], Chris@0: 'foo bar' => ['Invalid username that contains 2 spaces \'  \'', 'assertNotNull'], Chris@0: '' => ['Invalid empty username', 'assertNotNull'], Chris@0: 'foo/' => ['Invalid username containing invalid chars', 'assertNotNull'], Chris@0: // NULL. Chris@0: 'foo' . chr(0) . 'bar' => ['Invalid username containing chr(0)', 'assertNotNull'], Chris@0: // CR. Chris@0: 'foo' . chr(13) . 'bar' => ['Invalid username containing chr(13)', 'assertNotNull'], Chris@18: str_repeat('x', UserInterface::USERNAME_MAX_LENGTH + 1) => ['Invalid excessively long username', 'assertNotNull'], Chris@0: ]; Chris@0: foreach ($test_cases as $name => $test_case) { Chris@0: list($description, $test) = $test_case; Chris@0: $result = user_validate_name($name); Chris@0: $this->$test($result, $description . ' (' . $name . ')'); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Runs entity validation checks. Chris@0: */ Chris@0: public function testValidation() { Chris@0: $user = User::create([ Chris@0: 'name' => 'test', Chris@0: 'mail' => 'test@example.com', Chris@0: ]); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 0, 'No violations when validating a default user.'); Chris@0: Chris@0: // Only test one example invalid name here, the rest is already covered in Chris@0: // the testUsernames() method in this class. Chris@0: $name = $this->randomMachineName(61); Chris@0: $user->set('name', $name); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1, 'Violation found when name is too long.'); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'name'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('The username %name is too long: it must be %max characters or less.', ['%name' => $name, '%max' => 60])); Chris@0: Chris@0: // Create a second test user to provoke a name collision. Chris@0: $user2 = User::create([ Chris@0: 'name' => 'existing', Chris@0: 'mail' => 'existing@example.com', Chris@0: ]); Chris@0: $user2->save(); Chris@0: $user->set('name', 'existing'); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1, 'Violation found on name collision.'); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'name'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('The username %name is already taken.', ['%name' => 'existing'])); Chris@0: Chris@0: // Make the name valid. Chris@0: $user->set('name', $this->randomMachineName()); Chris@0: Chris@0: $user->set('mail', 'invalid'); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1, 'Violation found when email is invalid'); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.')); Chris@0: Chris@0: $mail = $this->randomMachineName(Email::EMAIL_MAX_LENGTH - 11) . '@example.com'; Chris@0: $user->set('mail', $mail); Chris@0: $violations = $user->validate(); Chris@0: // @todo There are two violations because EmailItem::getConstraints() Chris@0: // overlaps with the implicit constraint of the 'email' property type used Chris@0: // in EmailItem::propertyDefinitions(). Resolve this in Chris@0: // https://www.drupal.org/node/2023465. Chris@0: $this->assertEqual(count($violations), 2, 'Violations found when email is too long'); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('%name: the email address can not be longer than @max characters.', ['%name' => $user->get('mail')->getFieldDefinition()->getLabel(), '@max' => Email::EMAIL_MAX_LENGTH])); Chris@0: $this->assertEqual($violations[1]->getPropertyPath(), 'mail.0.value'); Chris@0: $this->assertEqual($violations[1]->getMessage(), t('This value is not a valid email address.')); Chris@0: Chris@0: // Provoke an email collision with an existing user. Chris@0: $user->set('mail', 'existing@example.com'); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1, 'Violation found when email already exists.'); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'mail'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('The email address %mail is already taken.', ['%mail' => 'existing@example.com'])); Chris@0: $user->set('mail', NULL); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1, 'Email addresses may not be removed'); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'mail'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('@name field is required.', ['@name' => $user->getFieldDefinition('mail')->getLabel()])); Chris@0: $user->set('mail', 'someone@example.com'); Chris@0: Chris@0: $user->set('timezone', $this->randomString(33)); Chris@0: $this->assertLengthViolation($user, 'timezone', 32, 2, 1); Chris@0: $user->set('timezone', 'invalid zone'); Chris@0: $this->assertAllowedValuesViolation($user, 'timezone'); Chris@0: $user->set('timezone', NULL); Chris@0: Chris@0: $user->set('init', 'invalid'); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1, 'Violation found when init email is invalid'); Chris@0: $user->set('init', NULL); Chris@0: Chris@0: $user->set('langcode', 'invalid'); Chris@0: $this->assertAllowedValuesViolation($user, 'langcode'); Chris@0: $user->set('langcode', NULL); Chris@0: Chris@0: // Only configurable langcodes are allowed for preferred languages. Chris@0: $user->set('preferred_langcode', Language::LANGCODE_NOT_SPECIFIED); Chris@0: $this->assertAllowedValuesViolation($user, 'preferred_langcode'); Chris@0: $user->set('preferred_langcode', NULL); Chris@0: Chris@0: $user->set('preferred_admin_langcode', Language::LANGCODE_NOT_SPECIFIED); Chris@0: $this->assertAllowedValuesViolation($user, 'preferred_admin_langcode'); Chris@0: $user->set('preferred_admin_langcode', NULL); Chris@0: Chris@0: Role::create(['id' => 'role1'])->save(); Chris@0: Role::create(['id' => 'role2'])->save(); Chris@0: Chris@0: // Test cardinality of user roles. Chris@0: $user = User::create([ Chris@0: 'name' => 'role_test', Chris@0: 'mail' => 'test@example.com', Chris@0: 'roles' => ['role1', 'role2'], Chris@0: ]); Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 0); Chris@0: Chris@0: $user->roles[1]->target_id = 'unknown_role'; Chris@0: $violations = $user->validate(); Chris@0: $this->assertEqual(count($violations), 1); Chris@0: $this->assertEqual($violations[0]->getPropertyPath(), 'roles.1.target_id'); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%entity_type: %name) does not exist.', ['%entity_type' => 'user_role', '%name' => 'unknown_role'])); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Verifies that a length violation exists for the given field. Chris@0: * Chris@0: * @param \Drupal\core\Entity\EntityInterface $entity Chris@0: * The entity object to validate. Chris@0: * @param string $field_name Chris@0: * The field that violates the maximum length. Chris@0: * @param int $length Chris@0: * Number of characters that was exceeded. Chris@0: * @param int $count Chris@0: * (optional) The number of expected violations. Defaults to 1. Chris@0: * @param int $expected_index Chris@0: * (optional) The index at which to expect the violation. Defaults to 0. Chris@0: */ Chris@0: protected function assertLengthViolation(EntityInterface $entity, $field_name, $length, $count = 1, $expected_index = 0) { Chris@0: $violations = $entity->validate(); Chris@0: $this->assertEqual(count($violations), $count, "Violation found when $field_name is too long."); Chris@0: $this->assertEqual($violations[$expected_index]->getPropertyPath(), "$field_name.0.value"); Chris@0: $field_label = $entity->get($field_name)->getFieldDefinition()->getLabel(); Chris@0: $this->assertEqual($violations[$expected_index]->getMessage(), t('%name: may not be longer than @max characters.', ['%name' => $field_label, '@max' => $length])); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Verifies that a AllowedValues violation exists for the given field. Chris@0: * Chris@0: * @param \Drupal\core\Entity\EntityInterface $entity Chris@0: * The entity object to validate. Chris@0: * @param string $field_name Chris@0: * The name of the field to verify. Chris@0: */ Chris@0: protected function assertAllowedValuesViolation(EntityInterface $entity, $field_name) { Chris@0: $violations = $entity->validate(); Chris@0: $this->assertEqual(count($violations), 1, "Allowed values violation for $field_name found."); Chris@17: $this->assertEqual($violations[0]->getPropertyPath(), $field_name === 'langcode' ? "$field_name.0" : "$field_name.0.value"); Chris@0: $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.')); Chris@0: } Chris@0: Chris@0: }