Version 1
[yaffs-website] / web / core / modules / user / tests / src / Kernel / UserValidationTest.php
1 <?php
2
3 namespace Drupal\Tests\user\Kernel;
4
5 use Drupal\Core\Entity\EntityInterface;
6 use Drupal\Core\Language\Language;
7 use Drupal\Core\Render\Element\Email;
8 use Drupal\KernelTests\KernelTestBase;
9 use Drupal\user\Entity\Role;
10 use Drupal\user\Entity\User;
11
12 /**
13  * Verify that user validity checks behave as designed.
14  *
15  * @group user
16  */
17 class UserValidationTest extends KernelTestBase {
18
19   /**
20    * Modules to enable.
21    *
22    * @var array
23    */
24   public static $modules = ['field', 'user', 'system'];
25
26   /**
27    * {@inheritdoc}
28    */
29   protected function setUp() {
30     parent::setUp();
31     $this->installEntitySchema('user');
32     $this->installSchema('system', ['sequences']);
33
34     // Make sure that the default roles exist.
35     $this->installConfig(['user']);
36
37   }
38
39   /**
40    * Tests user name validation.
41    */
42   public function testUsernames() {
43     $test_cases = [ // '<username>' => array('<description>', 'assert<testName>'),
44       'foo'                    => ['Valid username', 'assertNull'],
45       'FOO'                    => ['Valid username', 'assertNull'],
46       'Foo O\'Bar'             => ['Valid username', 'assertNull'],
47       'foo@bar'                => ['Valid username', 'assertNull'],
48       'foo@example.com'        => ['Valid username', 'assertNull'],
49       'foo@-example.com'       => ['Valid username', 'assertNull'], // invalid domains are allowed in usernames
50       'þòøÇߪř€'               => ['Valid username', 'assertNull'],
51       'foo+bar'                => ['Valid username', 'assertNull'], // '+' symbol is allowed
52       'ᚠᛇᚻ᛫ᛒᛦᚦ'                => ['Valid UTF8 username', 'assertNull'], // runes
53       ' foo'                   => ['Invalid username that starts with a space', 'assertNotNull'],
54       'foo '                   => ['Invalid username that ends with a space', 'assertNotNull'],
55       'foo  bar'               => ['Invalid username that contains 2 spaces \'&nbsp;&nbsp;\'', 'assertNotNull'],
56       ''                       => ['Invalid empty username', 'assertNotNull'],
57       'foo/'                   => ['Invalid username containing invalid chars', 'assertNotNull'],
58       'foo' . chr(0) . 'bar'   => ['Invalid username containing chr(0)', 'assertNotNull'], // NULL
59       'foo' . chr(13) . 'bar'  => ['Invalid username containing chr(13)', 'assertNotNull'], // CR
60       str_repeat('x', USERNAME_MAX_LENGTH + 1) => ['Invalid excessively long username', 'assertNotNull'],
61     ];
62     foreach ($test_cases as $name => $test_case) {
63       list($description, $test) = $test_case;
64       $result = user_validate_name($name);
65       $this->$test($result, $description . ' (' . $name . ')');
66     }
67   }
68
69   /**
70    * Runs entity validation checks.
71    */
72   public function testValidation() {
73     $user = User::create([
74       'name' => 'test',
75       'mail' => 'test@example.com',
76     ]);
77     $violations = $user->validate();
78     $this->assertEqual(count($violations), 0, 'No violations when validating a default user.');
79
80     // Only test one example invalid name here, the rest is already covered in
81     // the testUsernames() method in this class.
82     $name = $this->randomMachineName(61);
83     $user->set('name', $name);
84     $violations = $user->validate();
85     $this->assertEqual(count($violations), 1, 'Violation found when name is too long.');
86     $this->assertEqual($violations[0]->getPropertyPath(), 'name');
87     $this->assertEqual($violations[0]->getMessage(), t('The username %name is too long: it must be %max characters or less.', ['%name' => $name, '%max' => 60]));
88
89     // Create a second test user to provoke a name collision.
90     $user2 = User::create([
91       'name' => 'existing',
92       'mail' => 'existing@example.com',
93     ]);
94     $user2->save();
95     $user->set('name', 'existing');
96     $violations = $user->validate();
97     $this->assertEqual(count($violations), 1, 'Violation found on name collision.');
98     $this->assertEqual($violations[0]->getPropertyPath(), 'name');
99     $this->assertEqual($violations[0]->getMessage(), t('The username %name is already taken.', ['%name' => 'existing']));
100
101     // Make the name valid.
102     $user->set('name', $this->randomMachineName());
103
104     $user->set('mail', 'invalid');
105     $violations = $user->validate();
106     $this->assertEqual(count($violations), 1, 'Violation found when email is invalid');
107     $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
108     $this->assertEqual($violations[0]->getMessage(), t('This value is not a valid email address.'));
109
110     $mail = $this->randomMachineName(Email::EMAIL_MAX_LENGTH - 11) . '@example.com';
111     $user->set('mail', $mail);
112     $violations = $user->validate();
113     // @todo There are two violations because EmailItem::getConstraints()
114     //   overlaps with the implicit constraint of the 'email' property type used
115     //   in EmailItem::propertyDefinitions(). Resolve this in
116     //   https://www.drupal.org/node/2023465.
117     $this->assertEqual(count($violations), 2, 'Violations found when email is too long');
118     $this->assertEqual($violations[0]->getPropertyPath(), 'mail.0.value');
119     $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]));
120     $this->assertEqual($violations[1]->getPropertyPath(), 'mail.0.value');
121     $this->assertEqual($violations[1]->getMessage(), t('This value is not a valid email address.'));
122
123     // Provoke an email collision with an existing user.
124     $user->set('mail', 'existing@example.com');
125     $violations = $user->validate();
126     $this->assertEqual(count($violations), 1, 'Violation found when email already exists.');
127     $this->assertEqual($violations[0]->getPropertyPath(), 'mail');
128     $this->assertEqual($violations[0]->getMessage(), t('The email address %mail is already taken.', ['%mail' => 'existing@example.com']));
129     $user->set('mail', NULL);
130     $violations = $user->validate();
131     $this->assertEqual(count($violations), 1, 'Email addresses may not be removed');
132     $this->assertEqual($violations[0]->getPropertyPath(), 'mail');
133     $this->assertEqual($violations[0]->getMessage(), t('@name field is required.', ['@name' => $user->getFieldDefinition('mail')->getLabel()]));
134     $user->set('mail', 'someone@example.com');
135
136     $user->set('timezone', $this->randomString(33));
137     $this->assertLengthViolation($user, 'timezone', 32, 2, 1);
138     $user->set('timezone', 'invalid zone');
139     $this->assertAllowedValuesViolation($user, 'timezone');
140     $user->set('timezone', NULL);
141
142     $user->set('init', 'invalid');
143     $violations = $user->validate();
144     $this->assertEqual(count($violations), 1, 'Violation found when init email is invalid');
145     $user->set('init', NULL);
146
147     $user->set('langcode', 'invalid');
148     $this->assertAllowedValuesViolation($user, 'langcode');
149     $user->set('langcode', NULL);
150
151     // Only configurable langcodes are allowed for preferred languages.
152     $user->set('preferred_langcode', Language::LANGCODE_NOT_SPECIFIED);
153     $this->assertAllowedValuesViolation($user, 'preferred_langcode');
154     $user->set('preferred_langcode', NULL);
155
156     $user->set('preferred_admin_langcode', Language::LANGCODE_NOT_SPECIFIED);
157     $this->assertAllowedValuesViolation($user, 'preferred_admin_langcode');
158     $user->set('preferred_admin_langcode', NULL);
159
160     Role::create(['id' => 'role1'])->save();
161     Role::create(['id' => 'role2'])->save();
162
163     // Test cardinality of user roles.
164     $user = User::create([
165       'name' => 'role_test',
166       'mail' => 'test@example.com',
167       'roles' => ['role1', 'role2'],
168     ]);
169     $violations = $user->validate();
170     $this->assertEqual(count($violations), 0);
171
172     $user->roles[1]->target_id = 'unknown_role';
173     $violations = $user->validate();
174     $this->assertEqual(count($violations), 1);
175     $this->assertEqual($violations[0]->getPropertyPath(), 'roles.1.target_id');
176     $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%entity_type: %name) does not exist.', ['%entity_type' => 'user_role', '%name' => 'unknown_role']));
177   }
178
179   /**
180    * Verifies that a length violation exists for the given field.
181    *
182    * @param \Drupal\core\Entity\EntityInterface $entity
183    *   The entity object to validate.
184    * @param string $field_name
185    *   The field that violates the maximum length.
186    * @param int $length
187    *   Number of characters that was exceeded.
188    * @param int $count
189    *   (optional) The number of expected violations. Defaults to 1.
190    * @param int $expected_index
191    *   (optional) The index at which to expect the violation. Defaults to 0.
192    */
193   protected function assertLengthViolation(EntityInterface $entity, $field_name, $length, $count = 1, $expected_index = 0) {
194     $violations = $entity->validate();
195     $this->assertEqual(count($violations), $count, "Violation found when $field_name is too long.");
196     $this->assertEqual($violations[$expected_index]->getPropertyPath(), "$field_name.0.value");
197     $field_label = $entity->get($field_name)->getFieldDefinition()->getLabel();
198     $this->assertEqual($violations[$expected_index]->getMessage(), t('%name: may not be longer than @max characters.', ['%name' => $field_label, '@max' => $length]));
199   }
200
201   /**
202    * Verifies that a AllowedValues violation exists for the given field.
203    *
204    * @param \Drupal\core\Entity\EntityInterface $entity
205    *   The entity object to validate.
206    * @param string $field_name
207    *   The name of the field to verify.
208    */
209   protected function assertAllowedValuesViolation(EntityInterface $entity, $field_name) {
210     $violations = $entity->validate();
211     $this->assertEqual(count($violations), 1, "Allowed values violation for $field_name found.");
212     $this->assertEqual($violations[0]->getPropertyPath(), "$field_name.0.value");
213     $this->assertEqual($violations[0]->getMessage(), t('The value you selected is not a valid choice.'));
214   }
215
216 }