annotate core/modules/jsonapi/tests/src/Functional/TermTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@18 1 <?php
Chris@18 2
Chris@18 3 namespace Drupal\Tests\jsonapi\Functional;
Chris@18 4
Chris@18 5 use Drupal\Component\Serialization\Json;
Chris@18 6 use Drupal\Component\Utility\NestedArray;
Chris@18 7 use Drupal\Core\Cache\Cache;
Chris@18 8 use Drupal\Core\Entity\EntityInterface;
Chris@18 9 use Drupal\Core\Url;
Chris@18 10 use Drupal\taxonomy\Entity\Term;
Chris@18 11 use Drupal\taxonomy\Entity\Vocabulary;
Chris@18 12 use Drupal\Tests\jsonapi\Traits\CommonCollectionFilterAccessTestPatternsTrait;
Chris@18 13 use GuzzleHttp\RequestOptions;
Chris@18 14
Chris@18 15 /**
Chris@18 16 * JSON:API integration test for the "Term" content entity type.
Chris@18 17 *
Chris@18 18 * @group jsonapi
Chris@18 19 */
Chris@18 20 class TermTest extends ResourceTestBase {
Chris@18 21
Chris@18 22 use CommonCollectionFilterAccessTestPatternsTrait;
Chris@18 23
Chris@18 24 /**
Chris@18 25 * {@inheritdoc}
Chris@18 26 */
Chris@18 27 public static $modules = ['taxonomy', 'path'];
Chris@18 28
Chris@18 29 /**
Chris@18 30 * {@inheritdoc}
Chris@18 31 */
Chris@18 32 protected static $entityTypeId = 'taxonomy_term';
Chris@18 33
Chris@18 34 /**
Chris@18 35 * {@inheritdoc}
Chris@18 36 */
Chris@18 37 protected static $resourceTypeName = 'taxonomy_term--camelids';
Chris@18 38
Chris@18 39 /**
Chris@18 40 * {@inheritdoc}
Chris@18 41 */
Chris@18 42 protected static $patchProtectedFieldNames = [
Chris@18 43 'changed' => NULL,
Chris@18 44 ];
Chris@18 45
Chris@18 46 /**
Chris@18 47 * {@inheritdoc}
Chris@18 48 *
Chris@18 49 * @var \Drupal\taxonomy\TermInterface
Chris@18 50 */
Chris@18 51 protected $entity;
Chris@18 52
Chris@18 53 /**
Chris@18 54 * {@inheritdoc}
Chris@18 55 */
Chris@18 56 protected function setUpAuthorization($method) {
Chris@18 57 switch ($method) {
Chris@18 58 case 'GET':
Chris@18 59 $this->grantPermissionsToTestedRole(['access content']);
Chris@18 60 break;
Chris@18 61
Chris@18 62 case 'POST':
Chris@18 63 $this->grantPermissionsToTestedRole(['create terms in camelids']);
Chris@18 64 break;
Chris@18 65
Chris@18 66 case 'PATCH':
Chris@18 67 // Grant the 'create url aliases' permission to test the case when
Chris@18 68 // the path field is accessible, see
Chris@18 69 // \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase
Chris@18 70 // for a negative test.
Chris@18 71 $this->grantPermissionsToTestedRole(['edit terms in camelids', 'create url aliases']);
Chris@18 72 break;
Chris@18 73
Chris@18 74 case 'DELETE':
Chris@18 75 $this->grantPermissionsToTestedRole(['delete terms in camelids']);
Chris@18 76 break;
Chris@18 77 }
Chris@18 78 }
Chris@18 79
Chris@18 80 /**
Chris@18 81 * {@inheritdoc}
Chris@18 82 */
Chris@18 83 protected function createEntity() {
Chris@18 84 $vocabulary = Vocabulary::load('camelids');
Chris@18 85 if (!$vocabulary) {
Chris@18 86 // Create a "Camelids" vocabulary.
Chris@18 87 $vocabulary = Vocabulary::create([
Chris@18 88 'name' => 'Camelids',
Chris@18 89 'vid' => 'camelids',
Chris@18 90 ]);
Chris@18 91 $vocabulary->save();
Chris@18 92 }
Chris@18 93
Chris@18 94 // Create a "Llama" taxonomy term.
Chris@18 95 $term = Term::create(['vid' => $vocabulary->id()])
Chris@18 96 ->setName('Llama')
Chris@18 97 ->setDescription("It is a little known fact that llamas cannot count higher than seven.")
Chris@18 98 ->setChangedTime(123456789)
Chris@18 99 ->set('path', '/llama');
Chris@18 100 $term->save();
Chris@18 101
Chris@18 102 return $term;
Chris@18 103 }
Chris@18 104
Chris@18 105 /**
Chris@18 106 * {@inheritdoc}
Chris@18 107 */
Chris@18 108 protected function getExpectedDocument() {
Chris@18 109 $self_url = Url::fromUri('base:/jsonapi/taxonomy_term/camelids/' . $this->entity->uuid())->setAbsolute()->toString(TRUE)->getGeneratedUrl();
Chris@18 110
Chris@18 111 // We test with multiple parent terms, and combinations thereof.
Chris@18 112 // @see ::createEntity()
Chris@18 113 // @see ::testGetIndividual()
Chris@18 114 // @see ::testGetIndividualTermWithParent()
Chris@18 115 // @see ::providerTestGetIndividualTermWithParent()
Chris@18 116 $parent_term_ids = [];
Chris@18 117 for ($i = 0; $i < $this->entity->get('parent')->count(); $i++) {
Chris@18 118 $parent_term_ids[$i] = (int) $this->entity->get('parent')[$i]->target_id;
Chris@18 119 }
Chris@18 120
Chris@18 121 $expected_parent_normalization = FALSE;
Chris@18 122 switch ($parent_term_ids) {
Chris@18 123 case [0]:
Chris@18 124 $expected_parent_normalization = [
Chris@18 125 'data' => [
Chris@18 126 [
Chris@18 127 'id' => 'virtual',
Chris@18 128 'type' => 'taxonomy_term--camelids',
Chris@18 129 'meta' => [
Chris@18 130 'links' => [
Chris@18 131 'help' => [
Chris@18 132 'href' => 'https://www.drupal.org/docs/8/modules/json-api/core-concepts#virtual',
Chris@18 133 'meta' => [
Chris@18 134 'about' => "Usage and meaning of the 'virtual' resource identifier.",
Chris@18 135 ],
Chris@18 136 ],
Chris@18 137 ],
Chris@18 138 ],
Chris@18 139 ],
Chris@18 140 ],
Chris@18 141 'links' => [
Chris@18 142 'related' => ['href' => $self_url . '/parent'],
Chris@18 143 'self' => ['href' => $self_url . '/relationships/parent'],
Chris@18 144 ],
Chris@18 145 ];
Chris@18 146 break;
Chris@18 147
Chris@18 148 case [2]:
Chris@18 149 $expected_parent_normalization = [
Chris@18 150 'data' => [
Chris@18 151 [
Chris@18 152 'id' => Term::load(2)->uuid(),
Chris@18 153 'type' => 'taxonomy_term--camelids',
Chris@18 154 ],
Chris@18 155 ],
Chris@18 156 'links' => [
Chris@18 157 'related' => ['href' => $self_url . '/parent'],
Chris@18 158 'self' => ['href' => $self_url . '/relationships/parent'],
Chris@18 159 ],
Chris@18 160 ];
Chris@18 161 break;
Chris@18 162
Chris@18 163 case [0, 2]:
Chris@18 164 $expected_parent_normalization = [
Chris@18 165 'data' => [
Chris@18 166 [
Chris@18 167 'id' => 'virtual',
Chris@18 168 'type' => 'taxonomy_term--camelids',
Chris@18 169 'meta' => [
Chris@18 170 'links' => [
Chris@18 171 'help' => [
Chris@18 172 'href' => 'https://www.drupal.org/docs/8/modules/json-api/core-concepts#virtual',
Chris@18 173 'meta' => [
Chris@18 174 'about' => "Usage and meaning of the 'virtual' resource identifier.",
Chris@18 175 ],
Chris@18 176 ],
Chris@18 177 ],
Chris@18 178 ],
Chris@18 179 ],
Chris@18 180 [
Chris@18 181 'id' => Term::load(2)->uuid(),
Chris@18 182 'type' => 'taxonomy_term--camelids',
Chris@18 183 ],
Chris@18 184 ],
Chris@18 185 'links' => [
Chris@18 186 'related' => ['href' => $self_url . '/parent'],
Chris@18 187 'self' => ['href' => $self_url . '/relationships/parent'],
Chris@18 188 ],
Chris@18 189 ];
Chris@18 190 break;
Chris@18 191
Chris@18 192 case [3, 2]:
Chris@18 193 $expected_parent_normalization = [
Chris@18 194 'data' => [
Chris@18 195 [
Chris@18 196 'id' => Term::load(3)->uuid(),
Chris@18 197 'type' => 'taxonomy_term--camelids',
Chris@18 198 ],
Chris@18 199 [
Chris@18 200 'id' => Term::load(2)->uuid(),
Chris@18 201 'type' => 'taxonomy_term--camelids',
Chris@18 202 ],
Chris@18 203 ],
Chris@18 204 'links' => [
Chris@18 205 'related' => ['href' => $self_url . '/parent'],
Chris@18 206 'self' => ['href' => $self_url . '/relationships/parent'],
Chris@18 207 ],
Chris@18 208 ];
Chris@18 209 break;
Chris@18 210 }
Chris@18 211
Chris@18 212 return [
Chris@18 213 'jsonapi' => [
Chris@18 214 'meta' => [
Chris@18 215 'links' => [
Chris@18 216 'self' => ['href' => 'http://jsonapi.org/format/1.0/'],
Chris@18 217 ],
Chris@18 218 ],
Chris@18 219 'version' => '1.0',
Chris@18 220 ],
Chris@18 221 'links' => [
Chris@18 222 'self' => ['href' => $self_url],
Chris@18 223 ],
Chris@18 224 'data' => [
Chris@18 225 'id' => $this->entity->uuid(),
Chris@18 226 'type' => 'taxonomy_term--camelids',
Chris@18 227 'links' => [
Chris@18 228 'self' => ['href' => $self_url],
Chris@18 229 ],
Chris@18 230 'attributes' => [
Chris@18 231 'changed' => (new \DateTime())->setTimestamp($this->entity->getChangedTime())->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339),
Chris@18 232 'default_langcode' => TRUE,
Chris@18 233 'description' => [
Chris@18 234 'value' => 'It is a little known fact that llamas cannot count higher than seven.',
Chris@18 235 'format' => NULL,
Chris@18 236 'processed' => "<p>It is a little known fact that llamas cannot count higher than seven.</p>\n",
Chris@18 237 ],
Chris@18 238 'langcode' => 'en',
Chris@18 239 'name' => 'Llama',
Chris@18 240 'path' => [
Chris@18 241 'alias' => '/llama',
Chris@18 242 'pid' => 1,
Chris@18 243 'langcode' => 'en',
Chris@18 244 ],
Chris@18 245 'weight' => 0,
Chris@18 246 'drupal_internal__tid' => 1,
Chris@18 247 'status' => TRUE,
Chris@18 248 'drupal_internal__revision_id' => 1,
Chris@18 249 'revision_created' => (new \DateTime())->setTimestamp($this->entity->getRevisionCreationTime())->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339),
Chris@18 250 'revision_log_message' => NULL,
Chris@18 251 // @todo Attempt to remove this in https://www.drupal.org/project/drupal/issues/2933518.
Chris@18 252 'revision_translation_affected' => TRUE,
Chris@18 253 ],
Chris@18 254 'relationships' => [
Chris@18 255 'parent' => $expected_parent_normalization,
Chris@18 256 'vid' => [
Chris@18 257 'data' => [
Chris@18 258 'id' => Vocabulary::load('camelids')->uuid(),
Chris@18 259 'type' => 'taxonomy_vocabulary--taxonomy_vocabulary',
Chris@18 260 ],
Chris@18 261 'links' => [
Chris@18 262 'related' => ['href' => $self_url . '/vid'],
Chris@18 263 'self' => ['href' => $self_url . '/relationships/vid'],
Chris@18 264 ],
Chris@18 265 ],
Chris@18 266 'revision_user' => [
Chris@18 267 'data' => NULL,
Chris@18 268 'links' => [
Chris@18 269 'related' => [
Chris@18 270 'href' => $self_url . '/revision_user',
Chris@18 271 ],
Chris@18 272 'self' => [
Chris@18 273 'href' => $self_url . '/relationships/revision_user',
Chris@18 274 ],
Chris@18 275 ],
Chris@18 276 ],
Chris@18 277 ],
Chris@18 278 ],
Chris@18 279 ];
Chris@18 280 }
Chris@18 281
Chris@18 282 /**
Chris@18 283 * {@inheritdoc}
Chris@18 284 */
Chris@18 285 protected function getExpectedGetRelationshipDocumentData($relationship_field_name, EntityInterface $entity = NULL) {
Chris@18 286 $data = parent::getExpectedGetRelationshipDocumentData($relationship_field_name, $entity);
Chris@18 287 if ($relationship_field_name === 'parent') {
Chris@18 288 $data = [
Chris@18 289 0 => [
Chris@18 290 'id' => 'virtual',
Chris@18 291 'type' => 'taxonomy_term--camelids',
Chris@18 292 'meta' => [
Chris@18 293 'links' => [
Chris@18 294 'help' => [
Chris@18 295 'href' => 'https://www.drupal.org/docs/8/modules/json-api/core-concepts#virtual',
Chris@18 296 'meta' => [
Chris@18 297 'about' => "Usage and meaning of the 'virtual' resource identifier.",
Chris@18 298 ],
Chris@18 299 ],
Chris@18 300 ],
Chris@18 301 ],
Chris@18 302 ],
Chris@18 303 ];
Chris@18 304 }
Chris@18 305 return $data;
Chris@18 306 }
Chris@18 307
Chris@18 308 /**
Chris@18 309 * {@inheritdoc}
Chris@18 310 */
Chris@18 311 protected function getPostDocument() {
Chris@18 312 return [
Chris@18 313 'data' => [
Chris@18 314 'type' => 'taxonomy_term--camelids',
Chris@18 315 'attributes' => [
Chris@18 316 'name' => 'Dramallama',
Chris@18 317 'description' => [
Chris@18 318 'value' => 'Dramallamas are the coolest camelids.',
Chris@18 319 'format' => NULL,
Chris@18 320 ],
Chris@18 321 ],
Chris@18 322 ],
Chris@18 323 ];
Chris@18 324 }
Chris@18 325
Chris@18 326 /**
Chris@18 327 * {@inheritdoc}
Chris@18 328 */
Chris@18 329 protected function getExpectedUnauthorizedAccessMessage($method) {
Chris@18 330 switch ($method) {
Chris@18 331 case 'GET':
Chris@18 332 return "The 'access content' permission is required and the taxonomy term must be published.";
Chris@18 333
Chris@18 334 case 'POST':
Chris@18 335 return "The following permissions are required: 'create terms in camelids' OR 'administer taxonomy'.";
Chris@18 336
Chris@18 337 case 'PATCH':
Chris@18 338 return "The following permissions are required: 'edit terms in camelids' OR 'administer taxonomy'.";
Chris@18 339
Chris@18 340 case 'DELETE':
Chris@18 341 return "The following permissions are required: 'delete terms in camelids' OR 'administer taxonomy'.";
Chris@18 342
Chris@18 343 default:
Chris@18 344 return parent::getExpectedUnauthorizedAccessMessage($method);
Chris@18 345 }
Chris@18 346 }
Chris@18 347
Chris@18 348 /**
Chris@18 349 * {@inheritdoc}
Chris@18 350 */
Chris@18 351 protected function getExpectedUnauthorizedAccessCacheability() {
Chris@18 352 $cacheability = parent::getExpectedUnauthorizedAccessCacheability();
Chris@18 353 $cacheability->addCacheableDependency($this->entity);
Chris@18 354 return $cacheability;
Chris@18 355 }
Chris@18 356
Chris@18 357 /**
Chris@18 358 * Tests PATCHing a term's path.
Chris@18 359 *
Chris@18 360 * For a negative test, see the similar test coverage for Node.
Chris@18 361 *
Chris@18 362 * @see \Drupal\Tests\jsonapi\Functional\NodeTest::testPatchPath()
Chris@18 363 * @see \Drupal\Tests\rest\Functional\EntityResource\Node\NodeResourceTestBase::testPatchPath()
Chris@18 364 */
Chris@18 365 public function testPatchPath() {
Chris@18 366 $this->setUpAuthorization('GET');
Chris@18 367 $this->setUpAuthorization('PATCH');
Chris@18 368 $this->config('jsonapi.settings')->set('read_only', FALSE)->save(TRUE);
Chris@18 369
Chris@18 370 // @todo Remove line below in favor of commented line in https://www.drupal.org/project/jsonapi/issues/2878463.
Chris@18 371 $url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $this->entity->uuid()]);
Chris@18 372 /* $url = $this->entity->toUrl('jsonapi'); */
Chris@18 373 $request_options = [];
Chris@18 374 $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
Chris@18 375 $request_options[RequestOptions::HEADERS]['Content-Type'] = 'application/vnd.api+json';
Chris@18 376 $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
Chris@18 377
Chris@18 378 // GET term's current normalization.
Chris@18 379 $response = $this->request('GET', $url, $request_options);
Chris@18 380 $normalization = Json::decode((string) $response->getBody());
Chris@18 381
Chris@18 382 // Change term's path alias.
Chris@18 383 $normalization['data']['attributes']['path']['alias'] .= 's-rule-the-world';
Chris@18 384
Chris@18 385 // Create term PATCH request.
Chris@18 386 $request_options[RequestOptions::BODY] = Json::encode($normalization);
Chris@18 387
Chris@18 388 // PATCH request: 200.
Chris@18 389 $response = $this->request('PATCH', $url, $request_options);
Chris@18 390 $this->assertResourceResponse(200, FALSE, $response);
Chris@18 391 $updated_normalization = Json::decode((string) $response->getBody());
Chris@18 392 $this->assertSame($normalization['data']['attributes']['path']['alias'], $updated_normalization['data']['attributes']['path']['alias']);
Chris@18 393 }
Chris@18 394
Chris@18 395 /**
Chris@18 396 * {@inheritdoc}
Chris@18 397 */
Chris@18 398 protected function getExpectedCacheTags(array $sparse_fieldset = NULL) {
Chris@18 399 $tags = parent::getExpectedCacheTags($sparse_fieldset);
Chris@18 400 if ($sparse_fieldset === NULL || in_array('description', $sparse_fieldset)) {
Chris@18 401 $tags = Cache::mergeTags($tags, ['config:filter.format.plain_text', 'config:filter.settings']);
Chris@18 402 }
Chris@18 403 return $tags;
Chris@18 404 }
Chris@18 405
Chris@18 406 /**
Chris@18 407 * {@inheritdoc}
Chris@18 408 */
Chris@18 409 protected function getExpectedCacheContexts(array $sparse_fieldset = NULL) {
Chris@18 410 $contexts = parent::getExpectedCacheContexts($sparse_fieldset);
Chris@18 411 if ($sparse_fieldset === NULL || in_array('description', $sparse_fieldset)) {
Chris@18 412 $contexts = Cache::mergeContexts($contexts, ['languages:language_interface', 'theme']);
Chris@18 413 }
Chris@18 414 return $contexts;
Chris@18 415 }
Chris@18 416
Chris@18 417 /**
Chris@18 418 * Tests GETting a term with a parent term other than the default <root> (0).
Chris@18 419 *
Chris@18 420 * @see ::getExpectedNormalizedEntity()
Chris@18 421 *
Chris@18 422 * @dataProvider providerTestGetIndividualTermWithParent
Chris@18 423 */
Chris@18 424 public function testGetIndividualTermWithParent(array $parent_term_ids) {
Chris@18 425 // Create all possible parent terms.
Chris@18 426 Term::create(['vid' => Vocabulary::load('camelids')->id()])
Chris@18 427 ->setName('Lamoids')
Chris@18 428 ->save();
Chris@18 429 Term::create(['vid' => Vocabulary::load('camelids')->id()])
Chris@18 430 ->setName('Wimoids')
Chris@18 431 ->save();
Chris@18 432
Chris@18 433 // Modify the entity under test to use the provided parent terms.
Chris@18 434 $this->entity->set('parent', $parent_term_ids)->save();
Chris@18 435
Chris@18 436 // @todo Remove line below in favor of commented line in https://www.drupal.org/project/jsonapi/issues/2878463.
Chris@18 437 $url = Url::fromRoute(sprintf('jsonapi.%s.individual', static::$resourceTypeName), ['entity' => $this->entity->uuid()]);
Chris@18 438 /* $url = $this->entity->toUrl('jsonapi'); */
Chris@18 439 $request_options = [];
Chris@18 440 $request_options[RequestOptions::HEADERS]['Accept'] = 'application/vnd.api+json';
Chris@18 441 $request_options = NestedArray::mergeDeep($request_options, $this->getAuthenticationRequestOptions());
Chris@18 442 $this->setUpAuthorization('GET');
Chris@18 443 $response = $this->request('GET', $url, $request_options);
Chris@18 444 $this->assertSameDocument($this->getExpectedDocument(), Json::decode($response->getBody()));
Chris@18 445 }
Chris@18 446
Chris@18 447 /**
Chris@18 448 * Data provider for ::testGetIndividualTermWithParent().
Chris@18 449 */
Chris@18 450 public function providerTestGetIndividualTermWithParent() {
Chris@18 451 return [
Chris@18 452 'root parent: [0] (= no parent)' => [
Chris@18 453 [0],
Chris@18 454 ],
Chris@18 455 'non-root parent: [2]' => [
Chris@18 456 [2],
Chris@18 457 ],
Chris@18 458 'multiple parents: [0,2] (root + non-root parent)' => [
Chris@18 459 [0, 2],
Chris@18 460 ],
Chris@18 461 'multiple parents: [3,2] (both non-root parents)' => [
Chris@18 462 [3, 2],
Chris@18 463 ],
Chris@18 464 ];
Chris@18 465 }
Chris@18 466
Chris@18 467 /**
Chris@18 468 * {@inheritdoc}
Chris@18 469 */
Chris@18 470 public function testRelated() {
Chris@18 471 $this->markTestSkipped('Remove this in https://www.drupal.org/project/jsonapi/issues/2940339');
Chris@18 472 }
Chris@18 473
Chris@18 474 /**
Chris@18 475 * {@inheritdoc}
Chris@18 476 */
Chris@18 477 public function testCollectionFilterAccess() {
Chris@18 478 $this->doTestCollectionFilterAccessBasedOnPermissions('name', 'access content');
Chris@18 479 }
Chris@18 480
Chris@18 481 }