Upgraded drupal core with security updates
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Entity / EntityCrudHookTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Entity;
4
5 use Drupal\comment\Entity\Comment;
6 use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
7 use Drupal\comment\Tests\CommentTestTrait;
8 use Drupal\Core\Database\Database;
9 use Drupal\Core\Language\LanguageInterface;
10 use Drupal\block\Entity\Block;
11 use Drupal\entity_test\Entity\EntityTest;
12 use Drupal\node\Entity\NodeType;
13 use Drupal\taxonomy\Entity\Term;
14 use Drupal\node\Entity\Node;
15 use Drupal\taxonomy\Entity\Vocabulary;
16 use Drupal\user\Entity\User;
17 use Drupal\file\Entity\File;
18
19 /**
20  * Tests the invocation of hooks when creating, inserting, loading, updating or
21  * deleting an entity.
22  *
23  * Tested hooks are:
24  * - hook_entity_insert() and hook_ENTITY_TYPE_insert()
25  * - hook_entity_load() and hook_ENTITY_TYPE_load()
26  * - hook_entity_update() and hook_ENTITY_TYPE_update()
27  * - hook_entity_predelete() and hook_ENTITY_TYPE_predelete()
28  * - hook_entity_delete() and hook_ENTITY_TYPE_delete()
29  *
30  * These hooks are each tested for several entity types.
31  *
32  * @group Entity
33  */
34 class EntityCrudHookTest extends EntityKernelTestBase {
35
36   use CommentTestTrait;
37
38   /**
39    * Modules to enable.
40    *
41    * @var array
42    */
43   public static $modules = ['block', 'block_test', 'entity_crud_hook_test', 'file', 'taxonomy', 'node', 'comment'];
44
45   protected $ids = [];
46
47   protected function setUp() {
48     parent::setUp();
49
50     $this->installSchema('user', ['users_data']);
51     $this->installSchema('file', ['file_usage']);
52     $this->installSchema('node', ['node_access']);
53     $this->installSchema('comment', ['comment_entity_statistics']);
54     $this->installConfig(['node', 'comment']);
55   }
56
57   /**
58    * Checks the order of CRUD hook execution messages.
59    *
60    * entity_crud_hook_test.module implements all core entity CRUD hooks and
61    * stores a message for each in $GLOBALS['entity_crud_hook_test'].
62    *
63    * @param $messages
64    *   An array of plain-text messages in the order they should appear.
65    */
66   protected function assertHookMessageOrder($messages) {
67     $positions = [];
68     foreach ($messages as $message) {
69       // Verify that each message is found and record its position.
70       $position = array_search($message, $GLOBALS['entity_crud_hook_test']);
71       if ($this->assertTrue($position !== FALSE, $message)) {
72         $positions[] = $position;
73       }
74     }
75
76     // Sort the positions and ensure they remain in the same order.
77     $sorted = $positions;
78     sort($sorted);
79     $this->assertTrue($sorted == $positions, 'The hook messages appear in the correct order.');
80   }
81
82   /**
83    * Tests hook invocations for CRUD operations on blocks.
84    */
85   public function testBlockHooks() {
86     $entity = Block::create([
87       'id' => 'stark_test_html',
88       'plugin' => 'test_html',
89       'theme' => 'stark',
90     ]);
91
92     $this->assertHookMessageOrder([
93       'entity_crud_hook_test_block_create called',
94       'entity_crud_hook_test_entity_create called for type block',
95     ]);
96
97     $GLOBALS['entity_crud_hook_test'] = [];
98     $entity->save();
99
100     $this->assertHookMessageOrder([
101       'entity_crud_hook_test_block_presave called',
102       'entity_crud_hook_test_entity_presave called for type block',
103       'entity_crud_hook_test_block_insert called',
104       'entity_crud_hook_test_entity_insert called for type block',
105     ]);
106
107     $GLOBALS['entity_crud_hook_test'] = [];
108     $entity = Block::load($entity->id());
109
110     $this->assertHookMessageOrder([
111       'entity_crud_hook_test_entity_load called for type block',
112       'entity_crud_hook_test_block_load called',
113     ]);
114
115     $GLOBALS['entity_crud_hook_test'] = [];
116     $entity->label = 'New label';
117     $entity->save();
118
119     $this->assertHookMessageOrder([
120       'entity_crud_hook_test_block_presave called',
121       'entity_crud_hook_test_entity_presave called for type block',
122       'entity_crud_hook_test_block_update called',
123       'entity_crud_hook_test_entity_update called for type block',
124     ]);
125
126     $GLOBALS['entity_crud_hook_test'] = [];
127     $entity->delete();
128
129     $this->assertHookMessageOrder([
130       'entity_crud_hook_test_block_predelete called',
131       'entity_crud_hook_test_entity_predelete called for type block',
132       'entity_crud_hook_test_block_delete called',
133       'entity_crud_hook_test_entity_delete called for type block',
134     ]);
135   }
136
137   /**
138    * Tests hook invocations for CRUD operations on comments.
139    */
140   public function testCommentHooks() {
141     $account = $this->createUser();
142     NodeType::create([
143       'type' => 'article',
144       'name' => 'Article',
145     ])->save();
146     $this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN);
147
148     $node = Node::create([
149       'uid' => $account->id(),
150       'type' => 'article',
151       'title' => 'Test node',
152       'status' => 1,
153       'promote' => 0,
154       'sticky' => 0,
155       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
156       'created' => REQUEST_TIME,
157       'changed' => REQUEST_TIME,
158     ]);
159     $node->save();
160     $nid = $node->id();
161     $GLOBALS['entity_crud_hook_test'] = [];
162
163     $comment = Comment::create([
164       'cid' => NULL,
165       'pid' => 0,
166       'entity_id' => $nid,
167       'entity_type' => 'node',
168       'field_name' => 'comment',
169       'uid' => $account->id(),
170       'subject' => 'Test comment',
171       'created' => REQUEST_TIME,
172       'changed' => REQUEST_TIME,
173       'status' => 1,
174       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
175     ]);
176
177     $this->assertHookMessageOrder([
178       'entity_crud_hook_test_comment_create called',
179       'entity_crud_hook_test_entity_create called for type comment',
180     ]);
181
182     $GLOBALS['entity_crud_hook_test'] = [];
183     $comment->save();
184
185     $this->assertHookMessageOrder([
186       'entity_crud_hook_test_comment_presave called',
187       'entity_crud_hook_test_entity_presave called for type comment',
188       'entity_crud_hook_test_comment_insert called',
189       'entity_crud_hook_test_entity_insert called for type comment',
190     ]);
191
192     $GLOBALS['entity_crud_hook_test'] = [];
193     $comment = Comment::load($comment->id());
194
195     $this->assertHookMessageOrder([
196       'entity_crud_hook_test_entity_load called for type comment',
197       'entity_crud_hook_test_comment_load called',
198     ]);
199
200     $GLOBALS['entity_crud_hook_test'] = [];
201     $comment->setSubject('New subject');
202     $comment->save();
203
204     $this->assertHookMessageOrder([
205       'entity_crud_hook_test_comment_presave called',
206       'entity_crud_hook_test_entity_presave called for type comment',
207       'entity_crud_hook_test_comment_update called',
208       'entity_crud_hook_test_entity_update called for type comment',
209     ]);
210
211     $GLOBALS['entity_crud_hook_test'] = [];
212     $comment->delete();
213
214     $this->assertHookMessageOrder([
215       'entity_crud_hook_test_comment_predelete called',
216       'entity_crud_hook_test_entity_predelete called for type comment',
217       'entity_crud_hook_test_comment_delete called',
218       'entity_crud_hook_test_entity_delete called for type comment',
219     ]);
220   }
221
222   /**
223    * Tests hook invocations for CRUD operations on files.
224    */
225   public function testFileHooks() {
226     $this->installEntitySchema('file');
227
228     $url = 'public://entity_crud_hook_test.file';
229     file_put_contents($url, 'Test test test');
230     $file = File::create([
231       'fid' => NULL,
232       'uid' => 1,
233       'filename' => 'entity_crud_hook_test.file',
234       'uri' => $url,
235       'filemime' => 'text/plain',
236       'filesize' => filesize($url),
237       'status' => 1,
238       'created' => REQUEST_TIME,
239       'changed' => REQUEST_TIME,
240     ]);
241
242     $this->assertHookMessageOrder([
243       'entity_crud_hook_test_file_create called',
244       'entity_crud_hook_test_entity_create called for type file',
245     ]);
246
247     $GLOBALS['entity_crud_hook_test'] = [];
248     $file->save();
249
250     $this->assertHookMessageOrder([
251       'entity_crud_hook_test_file_presave called',
252       'entity_crud_hook_test_entity_presave called for type file',
253       'entity_crud_hook_test_file_insert called',
254       'entity_crud_hook_test_entity_insert called for type file',
255     ]);
256
257     $GLOBALS['entity_crud_hook_test'] = [];
258     $file = File::load($file->id());
259
260     $this->assertHookMessageOrder([
261       'entity_crud_hook_test_entity_load called for type file',
262       'entity_crud_hook_test_file_load called',
263     ]);
264
265     $GLOBALS['entity_crud_hook_test'] = [];
266     $file->setFilename('new.entity_crud_hook_test.file');
267     $file->save();
268
269     $this->assertHookMessageOrder([
270       'entity_crud_hook_test_file_presave called',
271       'entity_crud_hook_test_entity_presave called for type file',
272       'entity_crud_hook_test_file_update called',
273       'entity_crud_hook_test_entity_update called for type file',
274     ]);
275
276     $GLOBALS['entity_crud_hook_test'] = [];
277     $file->delete();
278
279     $this->assertHookMessageOrder([
280       'entity_crud_hook_test_file_predelete called',
281       'entity_crud_hook_test_entity_predelete called for type file',
282       'entity_crud_hook_test_file_delete called',
283       'entity_crud_hook_test_entity_delete called for type file',
284     ]);
285   }
286
287   /**
288    * Tests hook invocations for CRUD operations on nodes.
289    */
290   public function testNodeHooks() {
291     $account = $this->createUser();
292
293     $node = Node::create([
294       'uid' => $account->id(),
295       'type' => 'article',
296       'title' => 'Test node',
297       'status' => 1,
298       'promote' => 0,
299       'sticky' => 0,
300       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
301       'created' => REQUEST_TIME,
302       'changed' => REQUEST_TIME,
303     ]);
304
305     $this->assertHookMessageOrder([
306       'entity_crud_hook_test_node_create called',
307       'entity_crud_hook_test_entity_create called for type node',
308     ]);
309
310     $GLOBALS['entity_crud_hook_test'] = [];
311     $node->save();
312
313     $this->assertHookMessageOrder([
314       'entity_crud_hook_test_node_presave called',
315       'entity_crud_hook_test_entity_presave called for type node',
316       'entity_crud_hook_test_node_insert called',
317       'entity_crud_hook_test_entity_insert called for type node',
318     ]);
319
320     $GLOBALS['entity_crud_hook_test'] = [];
321     $node = Node::load($node->id());
322
323     $this->assertHookMessageOrder([
324       'entity_crud_hook_test_entity_load called for type node',
325       'entity_crud_hook_test_node_load called',
326     ]);
327
328     $GLOBALS['entity_crud_hook_test'] = [];
329     $node->title = 'New title';
330     $node->save();
331
332     $this->assertHookMessageOrder([
333       'entity_crud_hook_test_node_presave called',
334       'entity_crud_hook_test_entity_presave called for type node',
335       'entity_crud_hook_test_node_update called',
336       'entity_crud_hook_test_entity_update called for type node',
337     ]);
338
339     $GLOBALS['entity_crud_hook_test'] = [];
340     $node->delete();
341
342     $this->assertHookMessageOrder([
343       'entity_crud_hook_test_node_predelete called',
344       'entity_crud_hook_test_entity_predelete called for type node',
345       'entity_crud_hook_test_node_delete called',
346       'entity_crud_hook_test_entity_delete called for type node',
347     ]);
348   }
349
350   /**
351    * Tests hook invocations for CRUD operations on taxonomy terms.
352    */
353   public function testTaxonomyTermHooks() {
354     $this->installEntitySchema('taxonomy_term');
355
356     $vocabulary = Vocabulary::create([
357       'name' => 'Test vocabulary',
358       'vid' => 'test',
359       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
360       'description' => NULL,
361       'module' => 'entity_crud_hook_test',
362     ]);
363     $vocabulary->save();
364     $GLOBALS['entity_crud_hook_test'] = [];
365
366     $term = Term::create([
367       'vid' => $vocabulary->id(),
368       'name' => 'Test term',
369       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
370       'description' => NULL,
371       'format' => 1,
372     ]);
373
374     $this->assertHookMessageOrder([
375       'entity_crud_hook_test_taxonomy_term_create called',
376       'entity_crud_hook_test_entity_create called for type taxonomy_term',
377     ]);
378
379     $GLOBALS['entity_crud_hook_test'] = [];
380     $term->save();
381
382     $this->assertHookMessageOrder([
383       'entity_crud_hook_test_taxonomy_term_presave called',
384       'entity_crud_hook_test_entity_presave called for type taxonomy_term',
385       'entity_crud_hook_test_taxonomy_term_insert called',
386       'entity_crud_hook_test_entity_insert called for type taxonomy_term',
387     ]);
388
389     $GLOBALS['entity_crud_hook_test'] = [];
390     $term = Term::load($term->id());
391
392     $this->assertHookMessageOrder([
393       'entity_crud_hook_test_entity_load called for type taxonomy_term',
394       'entity_crud_hook_test_taxonomy_term_load called',
395     ]);
396
397     $GLOBALS['entity_crud_hook_test'] = [];
398     $term->setName('New name');
399     $term->save();
400
401     $this->assertHookMessageOrder([
402       'entity_crud_hook_test_taxonomy_term_presave called',
403       'entity_crud_hook_test_entity_presave called for type taxonomy_term',
404       'entity_crud_hook_test_taxonomy_term_update called',
405       'entity_crud_hook_test_entity_update called for type taxonomy_term',
406     ]);
407
408     $GLOBALS['entity_crud_hook_test'] = [];
409     $term->delete();
410
411     $this->assertHookMessageOrder([
412       'entity_crud_hook_test_taxonomy_term_predelete called',
413       'entity_crud_hook_test_entity_predelete called for type taxonomy_term',
414       'entity_crud_hook_test_taxonomy_term_delete called',
415       'entity_crud_hook_test_entity_delete called for type taxonomy_term',
416     ]);
417   }
418
419   /**
420    * Tests hook invocations for CRUD operations on taxonomy vocabularies.
421    */
422   public function testTaxonomyVocabularyHooks() {
423     $this->installEntitySchema('taxonomy_term');
424
425     $vocabulary = Vocabulary::create([
426       'name' => 'Test vocabulary',
427       'vid' => 'test',
428       'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
429       'description' => NULL,
430       'module' => 'entity_crud_hook_test',
431     ]);
432
433     $this->assertHookMessageOrder([
434       'entity_crud_hook_test_taxonomy_vocabulary_create called',
435       'entity_crud_hook_test_entity_create called for type taxonomy_vocabulary',
436     ]);
437
438     $GLOBALS['entity_crud_hook_test'] = [];
439     $vocabulary->save();
440
441     $this->assertHookMessageOrder([
442       'entity_crud_hook_test_taxonomy_vocabulary_presave called',
443       'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
444       'entity_crud_hook_test_taxonomy_vocabulary_insert called',
445       'entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary',
446     ]);
447
448     $GLOBALS['entity_crud_hook_test'] = [];
449     $vocabulary = Vocabulary::load($vocabulary->id());
450
451     $this->assertHookMessageOrder([
452       'entity_crud_hook_test_entity_load called for type taxonomy_vocabulary',
453       'entity_crud_hook_test_taxonomy_vocabulary_load called',
454     ]);
455
456     $GLOBALS['entity_crud_hook_test'] = [];
457     $vocabulary->set('name', 'New name');
458     $vocabulary->save();
459
460     $this->assertHookMessageOrder([
461       'entity_crud_hook_test_taxonomy_vocabulary_presave called',
462       'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
463       'entity_crud_hook_test_taxonomy_vocabulary_update called',
464       'entity_crud_hook_test_entity_update called for type taxonomy_vocabulary',
465     ]);
466
467     $GLOBALS['entity_crud_hook_test'] = [];
468     $vocabulary->delete();
469
470     $this->assertHookMessageOrder([
471       'entity_crud_hook_test_taxonomy_vocabulary_predelete called',
472       'entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary',
473       'entity_crud_hook_test_taxonomy_vocabulary_delete called',
474       'entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary',
475     ]);
476   }
477
478   /**
479    * Tests hook invocations for CRUD operations on users.
480    */
481   public function testUserHooks() {
482     $account = User::create([
483       'name' => 'Test user',
484       'mail' => 'test@example.com',
485       'created' => REQUEST_TIME,
486       'status' => 1,
487       'language' => 'en',
488     ]);
489
490     $this->assertHookMessageOrder([
491       'entity_crud_hook_test_user_create called',
492       'entity_crud_hook_test_entity_create called for type user',
493     ]);
494
495     $GLOBALS['entity_crud_hook_test'] = [];
496     $account->save();
497
498     $this->assertHookMessageOrder([
499       'entity_crud_hook_test_user_presave called',
500       'entity_crud_hook_test_entity_presave called for type user',
501       'entity_crud_hook_test_user_insert called',
502       'entity_crud_hook_test_entity_insert called for type user',
503     ]);
504
505     $GLOBALS['entity_crud_hook_test'] = [];
506     User::load($account->id());
507
508     $this->assertHookMessageOrder([
509       'entity_crud_hook_test_entity_load called for type user',
510       'entity_crud_hook_test_user_load called',
511     ]);
512
513     $GLOBALS['entity_crud_hook_test'] = [];
514     $account->name = 'New name';
515     $account->save();
516
517     $this->assertHookMessageOrder([
518       'entity_crud_hook_test_user_presave called',
519       'entity_crud_hook_test_entity_presave called for type user',
520       'entity_crud_hook_test_user_update called',
521       'entity_crud_hook_test_entity_update called for type user',
522     ]);
523
524     $GLOBALS['entity_crud_hook_test'] = [];
525     user_delete($account->id());
526
527     $this->assertHookMessageOrder([
528       'entity_crud_hook_test_user_predelete called',
529       'entity_crud_hook_test_entity_predelete called for type user',
530       'entity_crud_hook_test_user_delete called',
531       'entity_crud_hook_test_entity_delete called for type user',
532     ]);
533   }
534
535   /**
536    * Tests rollback from failed entity save.
537    */
538   public function testEntityRollback() {
539     // Create a block.
540     try {
541       EntityTest::create(['name' => 'fail_insert'])->save();
542       $this->fail('Expected exception has not been thrown.');
543     }
544     catch (\Exception $e) {
545       $this->pass('Expected exception has been thrown.');
546     }
547
548     if (Database::getConnection()->supportsTransactions()) {
549       // Check that the block does not exist in the database.
550       $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
551       $this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.');
552     }
553     else {
554       // Check that the block exists in the database.
555       $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
556       $this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.');
557     }
558   }
559
560 }