EntityCrudHookTest.php 18.2 KB
Newer Older
1 2 3
<?php

/**
4
 * @file
5
 * Contains \Drupal\system\Tests\Entity\EntityCrudHookTest.
6 7
 */

8
namespace Drupal\system\Tests\Entity;
9

10
use Drupal\comment\Entity\Comment;
11
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
12
use Drupal\comment\Tests\CommentTestTrait;
13
use Drupal\Core\Database\Database;
14
use Drupal\Core\Language\LanguageInterface;
15
use Drupal\block\Entity\Block;
16
use Drupal\taxonomy\Entity\Term;
17
use Drupal\node\Entity\Node;
18
use Drupal\taxonomy\Entity\Vocabulary;
19
use Drupal\user\Entity\User;
20
use Drupal\file\Entity\File;
21

22
/**
23 24
 * Tests the invocation of hooks when creating, inserting, loading, updating or
 * deleting an entity.
25 26
 *
 * Tested hooks are:
27 28 29 30 31 32 33
 * - hook_entity_insert() and hook_ENTITY_TYPE_insert()
 * - hook_entity_load() and hook_ENTITY_TYPE_load()
 * - hook_entity_update() and hook_ENTITY_TYPE_update()
 * - hook_entity_predelete() and hook_ENTITY_TYPE_predelete()
 * - hook_entity_delete() and hook_ENTITY_TYPE_delete()
 *
 * These hooks are each tested for several entity types.
34 35
 *
 * @group Entity
36
 */
37
class EntityCrudHookTest extends EntityUnitTestBase {
38

39 40
  use CommentTestTrait;

41 42 43 44 45
  /**
   * Modules to enable.
   *
   * @var array
   */
46
  public static $modules = array('block', 'block_test', 'entity_crud_hook_test', 'file', 'taxonomy', 'node', 'comment');
47

48 49
  protected $ids = array();

50
  protected function setUp() {
51
    parent::setUp();
52

53
    $this->installSchema('user', array('users_data'));
54 55 56
    $this->installSchema('file', array('file_usage'));
    $this->installSchema('node', array('node_access'));
    $this->installSchema('comment', array('comment_entity_statistics'));
57
    $this->installConfig(['node', 'comment']);
58 59
  }

60
  /**
61
   * Checks the order of CRUD hook execution messages.
62
   *
63
   * entity_crud_hook_test.module implements all core entity CRUD hooks and
64
   * stores a message for each in $GLOBALS['entity_crud_hook_test'].
65 66 67
   *
   * @param $messages
   *   An array of plain-text messages in the order they should appear.
68
   */
69 70 71 72
  protected function assertHookMessageOrder($messages) {
    $positions = array();
    foreach ($messages as $message) {
      // Verify that each message is found and record its position.
73
      $position = array_search($message, $GLOBALS['entity_crud_hook_test']);
74 75 76
      if ($this->assertTrue($position !== FALSE, $message)) {
        $positions[] = $position;
      }
77
    }
78 79 80 81 82

    // Sort the positions and ensure they remain in the same order.
    $sorted = $positions;
    sort($sorted);
    $this->assertTrue($sorted == $positions, 'The hook messages appear in the correct order.');
83 84
  }

85 86 87 88 89
  /**
   * Tests hook invocations for CRUD operations on blocks.
   */
  public function testBlockHooks() {
    $entity = entity_create('block', array(
90
      'id' => 'stark_test_html',
91
      'plugin' => 'test_html',
92
      'theme' => 'stark',
93
    ));
94 95 96 97 98 99

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_block_create called',
      'entity_crud_hook_test_entity_create called for type block',
    ));

100
    $GLOBALS['entity_crud_hook_test'] = array();
101 102 103 104 105 106 107 108 109
    $entity->save();

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_block_presave called',
      'entity_crud_hook_test_entity_presave called for type block',
      'entity_crud_hook_test_block_insert called',
      'entity_crud_hook_test_entity_insert called for type block',
    ));

110
    $GLOBALS['entity_crud_hook_test'] = array();
111
    $entity = Block::load($entity->id());
112 113 114 115 116 117

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type block',
      'entity_crud_hook_test_block_load called',
    ));

118
    $GLOBALS['entity_crud_hook_test'] = array();
119 120 121 122 123 124 125 126 127 128
    $entity->label = 'New label';
    $entity->save();

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_block_presave called',
      'entity_crud_hook_test_entity_presave called for type block',
      'entity_crud_hook_test_block_update called',
      'entity_crud_hook_test_entity_update called for type block',
    ));

129
    $GLOBALS['entity_crud_hook_test'] = array();
130 131 132 133 134 135 136 137 138 139
    $entity->delete();

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_block_predelete called',
      'entity_crud_hook_test_entity_predelete called for type block',
      'entity_crud_hook_test_block_delete called',
      'entity_crud_hook_test_entity_delete called for type block',
    ));
  }

140
  /**
141
   * Tests hook invocations for CRUD operations on comments.
142 143
   */
  public function testCommentHooks() {
144
    $account = $this->createUser();
145 146 147 148
    entity_create('node_type', array(
      'type' => 'article',
      'name' => 'Article',
    ))->save();
149
    $this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN);
150

151
    $node = entity_create('node', array(
152
      'uid' => $account->id(),
153 154 155 156 157
      'type' => 'article',
      'title' => 'Test node',
      'status' => 1,
      'promote' => 0,
      'sticky' => 0,
158
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
159 160
      'created' => REQUEST_TIME,
      'changed' => REQUEST_TIME,
161 162
    ));
    $node->save();
163
    $nid = $node->id();
164
    $GLOBALS['entity_crud_hook_test'] = array();
165

166
    $comment = Comment::create(array(
167 168
      'cid' => NULL,
      'pid' => 0,
169 170 171
      'entity_id' => $nid,
      'entity_type' => 'node',
      'field_name' => 'comment',
172
      'uid' => $account->id(),
173 174 175 176
      'subject' => 'Test comment',
      'created' => REQUEST_TIME,
      'changed' => REQUEST_TIME,
      'status' => 1,
177
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
178
    ));
179

180 181 182 183 184
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_comment_create called',
      'entity_crud_hook_test_entity_create called for type comment',
    ));

185
    $GLOBALS['entity_crud_hook_test'] = array();
186
    $comment->save();
187

188 189 190 191 192 193
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_comment_presave called',
      'entity_crud_hook_test_entity_presave called for type comment',
      'entity_crud_hook_test_comment_insert called',
      'entity_crud_hook_test_entity_insert called for type comment',
    ));
194

195
    $GLOBALS['entity_crud_hook_test'] = array();
196
    $comment = Comment::load($comment->id());
197

198 199 200 201
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type comment',
      'entity_crud_hook_test_comment_load called',
    ));
202

203
    $GLOBALS['entity_crud_hook_test'] = array();
204
    $comment->setSubject('New subject');
205
    $comment->save();
206

207 208 209 210 211 212
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_comment_presave called',
      'entity_crud_hook_test_entity_presave called for type comment',
      'entity_crud_hook_test_comment_update called',
      'entity_crud_hook_test_entity_update called for type comment',
    ));
213

214
    $GLOBALS['entity_crud_hook_test'] = array();
215
    $comment->delete();
216

217 218 219 220 221 222
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_comment_predelete called',
      'entity_crud_hook_test_entity_predelete called for type comment',
      'entity_crud_hook_test_comment_delete called',
      'entity_crud_hook_test_entity_delete called for type comment',
    ));
223 224 225
  }

  /**
226
   * Tests hook invocations for CRUD operations on files.
227 228
   */
  public function testFileHooks() {
229 230
    $this->installEntitySchema('file');

231 232
    $url = 'public://entity_crud_hook_test.file';
    file_put_contents($url, 'Test test test');
233
    $file = entity_create('file', array(
234 235 236 237 238 239 240
      'fid' => NULL,
      'uid' => 1,
      'filename' => 'entity_crud_hook_test.file',
      'uri' => $url,
      'filemime' => 'text/plain',
      'filesize' => filesize($url),
      'status' => 1,
241 242
      'created' => REQUEST_TIME,
      'changed' => REQUEST_TIME,
243
    ));
244 245 246 247 248 249

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_file_create called',
      'entity_crud_hook_test_entity_create called for type file',
    ));

250
    $GLOBALS['entity_crud_hook_test'] = array();
251
    $file->save();
252

253 254 255 256 257 258
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_file_presave called',
      'entity_crud_hook_test_entity_presave called for type file',
      'entity_crud_hook_test_file_insert called',
      'entity_crud_hook_test_entity_insert called for type file',
    ));
259

260
    $GLOBALS['entity_crud_hook_test'] = array();
261
    $file = File::load($file->id());
262

263 264 265 266
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type file',
      'entity_crud_hook_test_file_load called',
    ));
267

268
    $GLOBALS['entity_crud_hook_test'] = array();
269
    $file->setFilename('new.entity_crud_hook_test.file');
270
    $file->save();
271

272 273 274 275 276 277
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_file_presave called',
      'entity_crud_hook_test_entity_presave called for type file',
      'entity_crud_hook_test_file_update called',
      'entity_crud_hook_test_entity_update called for type file',
    ));
278

279
    $GLOBALS['entity_crud_hook_test'] = array();
280
    $file->delete();
281

282 283 284 285 286 287
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_file_predelete called',
      'entity_crud_hook_test_entity_predelete called for type file',
      'entity_crud_hook_test_file_delete called',
      'entity_crud_hook_test_entity_delete called for type file',
    ));
288 289 290
  }

  /**
291
   * Tests hook invocations for CRUD operations on nodes.
292 293
   */
  public function testNodeHooks() {
294 295
    $account = $this->createUser();

296
    $node = entity_create('node', array(
297
      'uid' => $account->id(),
298 299 300 301 302
      'type' => 'article',
      'title' => 'Test node',
      'status' => 1,
      'promote' => 0,
      'sticky' => 0,
303
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
304 305
      'created' => REQUEST_TIME,
      'changed' => REQUEST_TIME,
306
    ));
307 308 309 310 311 312

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_node_create called',
      'entity_crud_hook_test_entity_create called for type node',
    ));

313
    $GLOBALS['entity_crud_hook_test'] = array();
314
    $node->save();
315

316 317 318 319 320 321
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_node_presave called',
      'entity_crud_hook_test_entity_presave called for type node',
      'entity_crud_hook_test_node_insert called',
      'entity_crud_hook_test_entity_insert called for type node',
    ));
322

323
    $GLOBALS['entity_crud_hook_test'] = array();
324
    $node = Node::load($node->id());
325

326 327 328 329
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type node',
      'entity_crud_hook_test_node_load called',
    ));
330

331
    $GLOBALS['entity_crud_hook_test'] = array();
332
    $node->title = 'New title';
333
    $node->save();
334

335 336 337 338 339 340
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_node_presave called',
      'entity_crud_hook_test_entity_presave called for type node',
      'entity_crud_hook_test_node_update called',
      'entity_crud_hook_test_entity_update called for type node',
    ));
341

342
    $GLOBALS['entity_crud_hook_test'] = array();
343
    $node->delete();
344

345 346 347 348 349 350
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_node_predelete called',
      'entity_crud_hook_test_entity_predelete called for type node',
      'entity_crud_hook_test_node_delete called',
      'entity_crud_hook_test_entity_delete called for type node',
    ));
351 352 353
  }

  /**
354
   * Tests hook invocations for CRUD operations on taxonomy terms.
355 356
   */
  public function testTaxonomyTermHooks() {
357
    $this->installEntitySchema('taxonomy_term');
358

359
    $vocabulary = entity_create('taxonomy_vocabulary', array(
360
      'name' => 'Test vocabulary',
361
      'vid' => 'test',
362
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
363 364
      'description' => NULL,
      'module' => 'entity_crud_hook_test',
365
    ));
366
    $vocabulary->save();
367
    $GLOBALS['entity_crud_hook_test'] = array();
368

369
    $term = entity_create('taxonomy_term', array(
370
      'vid' => $vocabulary->id(),
371
      'name' => 'Test term',
372
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
373 374
      'description' => NULL,
      'format' => 1,
375
    ));
376 377 378 379 380 381

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_term_create called',
      'entity_crud_hook_test_entity_create called for type taxonomy_term',
    ));

382
    $GLOBALS['entity_crud_hook_test'] = array();
383
    $term->save();
384

385 386 387 388 389 390
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_term_presave called',
      'entity_crud_hook_test_entity_presave called for type taxonomy_term',
      'entity_crud_hook_test_taxonomy_term_insert called',
      'entity_crud_hook_test_entity_insert called for type taxonomy_term',
    ));
391

392
    $GLOBALS['entity_crud_hook_test'] = array();
393
    $term = Term::load($term->id());
394

395 396 397 398
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type taxonomy_term',
      'entity_crud_hook_test_taxonomy_term_load called',
    ));
399

400
    $GLOBALS['entity_crud_hook_test'] = array();
401
    $term->setName('New name');
402
    $term->save();
403

404 405 406 407 408 409
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_term_presave called',
      'entity_crud_hook_test_entity_presave called for type taxonomy_term',
      'entity_crud_hook_test_taxonomy_term_update called',
      'entity_crud_hook_test_entity_update called for type taxonomy_term',
    ));
410

411
    $GLOBALS['entity_crud_hook_test'] = array();
412
    $term->delete();
413

414 415 416 417 418 419
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_term_predelete called',
      'entity_crud_hook_test_entity_predelete called for type taxonomy_term',
      'entity_crud_hook_test_taxonomy_term_delete called',
      'entity_crud_hook_test_entity_delete called for type taxonomy_term',
    ));
420 421 422
  }

  /**
423
   * Tests hook invocations for CRUD operations on taxonomy vocabularies.
424 425
   */
  public function testTaxonomyVocabularyHooks() {
426
    $this->installEntitySchema('taxonomy_term');
427

428
    $vocabulary = entity_create('taxonomy_vocabulary', array(
429
      'name' => 'Test vocabulary',
430
      'vid' => 'test',
431
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
432 433
      'description' => NULL,
      'module' => 'entity_crud_hook_test',
434
    ));
435 436 437 438 439 440

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_vocabulary_create called',
      'entity_crud_hook_test_entity_create called for type taxonomy_vocabulary',
    ));

441
    $GLOBALS['entity_crud_hook_test'] = array();
442
    $vocabulary->save();
443

444 445 446 447 448 449
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_vocabulary_presave called',
      'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
      'entity_crud_hook_test_taxonomy_vocabulary_insert called',
      'entity_crud_hook_test_entity_insert called for type taxonomy_vocabulary',
    ));
450

451
    $GLOBALS['entity_crud_hook_test'] = array();
452
    $vocabulary = Vocabulary::load($vocabulary->id());
453

454 455 456 457
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type taxonomy_vocabulary',
      'entity_crud_hook_test_taxonomy_vocabulary_load called',
    ));
458

459
    $GLOBALS['entity_crud_hook_test'] = array();
460
    $vocabulary->set('name', 'New name');
461
    $vocabulary->save();
462

463 464 465 466 467 468
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_vocabulary_presave called',
      'entity_crud_hook_test_entity_presave called for type taxonomy_vocabulary',
      'entity_crud_hook_test_taxonomy_vocabulary_update called',
      'entity_crud_hook_test_entity_update called for type taxonomy_vocabulary',
    ));
469

470
    $GLOBALS['entity_crud_hook_test'] = array();
471
    $vocabulary->delete();
472

473 474 475 476 477 478
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_taxonomy_vocabulary_predelete called',
      'entity_crud_hook_test_entity_predelete called for type taxonomy_vocabulary',
      'entity_crud_hook_test_taxonomy_vocabulary_delete called',
      'entity_crud_hook_test_entity_delete called for type taxonomy_vocabulary',
    ));
479 480 481
  }

  /**
482
   * Tests hook invocations for CRUD operations on users.
483 484
   */
  public function testUserHooks() {
485
    $account = entity_create('user', array(
486 487 488 489 490
      'name' => 'Test user',
      'mail' => 'test@example.com',
      'created' => REQUEST_TIME,
      'status' => 1,
      'language' => 'en',
491
    ));
492 493 494 495 496 497

    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_user_create called',
      'entity_crud_hook_test_entity_create called for type user',
    ));

498
    $GLOBALS['entity_crud_hook_test'] = array();
499
    $account->save();
500

501 502 503 504 505 506
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_user_presave called',
      'entity_crud_hook_test_entity_presave called for type user',
      'entity_crud_hook_test_user_insert called',
      'entity_crud_hook_test_entity_insert called for type user',
    ));
507

508
    $GLOBALS['entity_crud_hook_test'] = array();
509
    User::load($account->id());
510

511 512 513 514
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_entity_load called for type user',
      'entity_crud_hook_test_user_load called',
    ));
515

516
    $GLOBALS['entity_crud_hook_test'] = array();
517 518
    $account->name = 'New name';
    $account->save();
519

520 521 522 523 524 525
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_user_presave called',
      'entity_crud_hook_test_entity_presave called for type user',
      'entity_crud_hook_test_user_update called',
      'entity_crud_hook_test_entity_update called for type user',
    ));
526

527
    $GLOBALS['entity_crud_hook_test'] = array();
528
    user_delete($account->id());
529

530 531 532 533 534 535
    $this->assertHookMessageOrder(array(
      'entity_crud_hook_test_user_predelete called',
      'entity_crud_hook_test_entity_predelete called for type user',
      'entity_crud_hook_test_user_delete called',
      'entity_crud_hook_test_entity_delete called for type user',
    ));
536
  }
537 538

  /**
539
   * Tests rollback from failed entity save.
540
   */
541
  function testEntityRollback() {
542 543
    // Create a block.
    try {
544
      entity_create('entity_test', array('name' => 'fail_insert'))->save();
545 546 547 548 549 550 551 552
      $this->fail('Expected exception has not been thrown.');
    }
    catch (\Exception $e) {
      $this->pass('Expected exception has been thrown.');
    }

    if (Database::getConnection()->supportsTransactions()) {
      // Check that the block does not exist in the database.
553
      $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
554 555 556 557
      $this->assertTrue(empty($ids), 'Transactions supported, and entity not found in database.');
    }
    else {
      // Check that the block exists in the database.
558
      $ids = \Drupal::entityQuery('entity_test')->condition('name', 'fail_insert')->execute();
559 560 561
      $this->assertFalse(empty($ids), 'Transactions not supported, and entity found in database.');
    }
  }
562
}