annotate core/modules/jsonapi/tests/src/Functional/TermTest.php @ 5:12f9dff5fda9 tip

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