Update to Drupal 8.2.4. For more information, see https://www.drupal.org/project/drupal/releases/8.2.4

This commit is contained in:
Pantheon Automation 2016-12-07 12:19:38 -08:00 committed by Greg Anderson
parent 0a95b8440e
commit 8544b60b39
284 changed files with 12980 additions and 3199 deletions

View file

@ -0,0 +1,7 @@
name: 'Configuration test REST'
type: module
package: Testing
version: VERSION
core: 8.x
dependencies:
- config_test

View file

@ -0,0 +1,30 @@
<?php
/**
* @file
* Contains hook implementations for testing REST module.
*/
use Drupal\Core\Access\AccessResult;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Session\AccountInterface;
/**
* Implements hook_entity_type_alter().
*/
function config_test_rest_entity_type_alter(array &$entity_types) {
// Undo part of what config_test_entity_type_alter() did: remove this
// config_test_no_status entity type, because it uses the same entity class as
// the config_test entity type, which makes REST deserialization impossible.
unset($entity_types['config_test_no_status']);
}
/**
* Implements hook_ENTITY_TYPE_access().
*/
function config_test_rest_config_test_access(EntityInterface $entity, $operation, AccountInterface $account) {
// Add permission, so that EntityResourceTestBase's scenarios can test access
// being denied. By default, all access is always allowed for the config_test
// config entity.
return AccessResult::forbiddenIf(!$account->hasPermission('view config_test'))->cachePerPermissions();
}

View file

@ -0,0 +1,2 @@
view config_test:
title: 'View ConfigTest entities'

View file

@ -5,6 +5,11 @@
* Contains hook implementations for testing REST module.
*/
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Access\AccessResult;
/**
* Implements hook_rest_type_uri_alter().
*/
@ -22,3 +27,24 @@ function rest_test_rest_relation_uri_alter(&$uri, $context = array()) {
$uri = 'rest_test_relation';
}
}
/**
* Implements hook_entity_field_access().
*
* @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::setUp()
* @see \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase::testPost()
*/
function rest_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldItemListInterface $items = NULL) {
if ($field_definition->getName() === 'field_rest_test') {
switch ($operation) {
case 'view':
// Never ever allow this field to be viewed: this lets EntityResourceTestBase::testGet() test in a "vanilla" way.
return AccessResult::forbidden();
case 'edit':
return AccessResult::forbidden();
}
}
// No opinion.
return AccessResult::neutral();
}

View file

@ -0,0 +1,277 @@
langcode: en
status: true
dependencies:
config:
- node.type.article
module:
- node
- rest
- user
id: test_excluded_field_token_display
label: 'Test Excluded Field Token Display'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: serializer
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: true
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: false
ellipsis: false
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: string
settings:
link_to_entity: false
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: node
entity_field: title
plugin_id: field
nothing:
id: nothing
table: views
field: nothing
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: true
text: '{{ title }}'
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: false
plugin_id: custom
filters:
status:
value: '1'
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
type:
id: type
table: node_field_data
field: type
value:
article: article
entity_type: node
entity_field: type
plugin_id: bundle
sorts:
nid:
id: nid
table: node_field_data
field: nid
order: DESC
entity_type: node
entity_field: nid
plugin_id: standard
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- request_format
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }
rest_export_1:
display_plugin: rest_export
id: rest_export_1
display_title: 'REST export'
position: 1
display_options:
display_extenders: { }
path: rest/test/excluded-field-token
pager:
type: some
options:
items_per_page: 10
offset: 0
style:
type: serializer
options:
formats:
json: json
row:
type: data_field
options:
field_options:
title:
alias: ''
raw_output: false
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- request_format
- 'user.node_grants:view'
- user.permissions
tags: { }

View file

@ -0,0 +1,36 @@
<?php
namespace Drupal\Tests\rest\Functional;
use Drupal\Core\Url;
use Psr\Http\Message\ResponseInterface;
/**
* Trait for ResourceTestBase subclasses testing $auth=NULL, i.e. authless/anon.
*
* Characteristics:
* - When no authentication provider is being used, there also cannot be any
* particular error response for missing authentication, since by definition
* there is not any authentication.
* - For the same reason, there are no authentication edge cases to test.
* - Because no authentication is required, this is vulnerable to CSRF attacks
* by design. Hence a REST resource should probably only allow for anonymous
* for safe (GET/HEAD) HTTP methods, and only with extreme care should unsafe
* (POST/PATCH/DELETE) HTTP methods be allowed for a REST resource that allows
* anonymous access.
*/
trait AnonResourceTestTrait {
/**
* {@inheritdoc}
*/
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
throw new \LogicException('When testing for anonymous users, authentication cannot be missing.');
}
/**
* {@inheritdoc}
*/
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Drupal\Tests\rest\Functional;
use Drupal\Core\Url;
use Psr\Http\Message\ResponseInterface;
/**
* Trait for ResourceTestBase subclasses testing $auth=basic_auth.
*
* Characteristics:
* - Every request must send an Authorization header.
* - When accessing a URI that requires authentication without being
* authenticated, a 401 response must be sent.
* - Because every request must send an authorization, there is no danger of
* CSRF attacks.
*/
trait BasicAuthResourceTestTrait {
/**
* {@inheritdoc}
*/
protected function getAuthenticationRequestOptions($method) {
return [
'headers' => [
'Authorization' => 'Basic ' . base64_encode($this->account->name->value . ':' . $this->account->passRaw),
],
];
}
/**
* {@inheritdoc}
*/
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
$this->assertResourceErrorResponse(401, 'No authentication credentials provided.', $response);
}
/**
* {@inheritdoc}
*/
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {}
}

View file

@ -0,0 +1,129 @@
<?php
namespace Drupal\Tests\rest\Functional;
use Drupal\Core\Url;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\ResponseInterface;
/**
* Trait for ResourceTestBase subclasses testing $auth=cookie.
*
* Characteristics:
* - After performing a valid "log in" request, the server responds with a 2xx
* status code and a 'Set-Cookie' response header. This cookie is what
* continues to identify the user in subsequent requests.
* - When accessing a URI that requires authentication without being
* authenticated, a standard 403 response must be sent.
* - Because of the reliance on cookies, and the fact that user agents send
* cookies with every request, this is vulnerable to CSRF attacks. To mitigate
* this, the response for the "log in" request contains a CSRF token that must
* be sent with every unsafe (POST/PATCH/DELETE) HTTP request.
*/
trait CookieResourceTestTrait {
/**
* The session cookie.
*
* @see ::initAuthentication
*
* @var string
*/
protected $sessionCookie;
/**
* The CSRF token.
*
* @see ::initAuthentication
*
* @var string
*/
protected $csrfToken;
/**
* The logout token.
*
* @see ::initAuthentication
*
* @var string
*/
protected $logoutToken;
/**
* {@inheritdoc}
*/
protected function initAuthentication() {
// @todo Remove hardcoded use of the 'json' format, and use static::$format
// + static::$mimeType instead in https://www.drupal.org/node/2820888.
$user_login_url = Url::fromRoute('user.login.http')
->setRouteParameter('_format', 'json');
$request_body = [
'name' => $this->account->name->value,
'pass' => $this->account->passRaw,
];
$request_options[RequestOptions::BODY] = $this->serializer->encode($request_body, 'json');
$request_options[RequestOptions::HEADERS]['Accept'] = 'application/json';
$response = $this->request('POST', $user_login_url, $request_options);
// Parse and store the session cookie.
$this->sessionCookie = explode(';', $response->getHeader('Set-Cookie')[0], 2)[0];
// Parse and store the CSRF token and logout token.
$data = $this->serializer->decode((string)$response->getBody(), static::$format);
$this->csrfToken = $data['csrf_token'];
$this->logoutToken = $data['logout_token'];
}
/**
* {@inheritdoc}
*/
protected function getAuthenticationRequestOptions($method) {
$request_options[RequestOptions::HEADERS]['Cookie'] = $this->sessionCookie;
// @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
if (!in_array($method, ['HEAD', 'GET', 'OPTIONS', 'TRACE'])) {
$request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken;
}
return $request_options;
}
/**
* {@inheritdoc}
*/
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
$this->assertResourceErrorResponse(403, '', $response);
}
/**
* {@inheritdoc}
*/
protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options) {
// X-CSRF-Token request header is unnecessary for safe and side effect-free
// HTTP methods. No need for additional assertions.
// @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
if (in_array($method, ['HEAD', 'GET', 'OPTIONS', 'TRACE'])) {
return;
}
unset($request_options[RequestOptions::HEADERS]['X-CSRF-Token']);
// DX: 403 when missing X-CSRF-Token request header.
$response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is missing', $response);
$request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = 'this-is-not-the-token-you-are-looking-for';
// DX: 403 when invalid X-CSRF-Token request header.
$response = $this->request($method, $url, $request_options);
$this->assertResourceErrorResponse(403, 'X-CSRF-Token request header is invalid', $response);
$request_options[RequestOptions::HEADERS]['X-CSRF-Token'] = $this->csrfToken;
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class BlockJsonAnonTest extends BlockResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class BlockJsonBasicAuthTest extends BlockResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class BlockJsonCookieTest extends BlockResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,130 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Block;
use Drupal\block\Entity\Block;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class BlockResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['block'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'block';
/**
* @var \Drupal\block\BlockInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->entity->setVisibilityConfig('user_role', [])->save();
break;
case 'POST':
$this->grantPermissionsToTestedRole(['administer blocks']);
break;
case 'PATCH':
$this->grantPermissionsToTestedRole(['administer blocks']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$block = Block::create([
'plugin' => 'llama_block',
'region' => 'header',
'id' => 'llama',
'theme' => 'classy',
]);
// All blocks can be viewed by the anonymous user by default. An interesting
// side effect of this is that any anonymous user is also able to read the
// corresponding block config entity via REST, even if an authentication
// provider is configured for the block config entity REST resource! In
// other words: Block entities do not distinguish between 'view' as in
// "render on a page" and 'view' as in "read the configuration".
// This prevents that.
// @todo Fix this in https://www.drupal.org/node/2820315.
$block->setVisibilityConfig('user_role', [
'id' => 'user_role',
'roles' => ['non-existing-role' => 'non-existing-role'],
'negate' => FALSE,
'context_mapping' => [
'user' => '@user.current_user_context:current_user',
],
]);
$block->save();
return $block;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$normalization = [
'uuid' => $this->entity->uuid(),
'id' => 'llama',
'weight' => NULL,
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [
'theme' => [
'classy',
],
],
'theme' => 'classy',
'region' => 'header',
'provider' => NULL,
'plugin' => 'llama_block',
'settings' => [
'id' => 'broken',
'label' => '',
'provider' => 'core',
'label_display' => 'visible',
],
'visibility' => [],
];
return $normalization;
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheContexts() {
// @see ::createEntity()
return [];
}
/**
* {@inheritdoc}
*/
protected function getExpectedCacheTags() {
// Because the 'user.permissions' cache context is missing, the cache tag
// for the anonymous user role is never added automatically.
return array_filter(parent::getExpectedCacheTags(), function ($tag) {
return $tag !== 'config:user.role.anonymous';
});
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class CommentJsonAnonTest extends CommentResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*
* Anononymous users cannot edit their own comments.
*
* @see \Drupal\comment\CommentAccessControlHandler::checkAccess
*
* Therefore we grant them the 'administer comments' permission for the
* purpose of this test.
*
* @see ::setUpAuthorization
*/
protected static $patchProtectedFieldNames = [
'pid',
'entity_id',
'changed',
'thread',
'entity_type',
'field_name',
];
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class CommentJsonBasicAuthTest extends CommentResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class CommentJsonCookieTest extends CommentResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,309 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Comment;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Entity\CommentType;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
use GuzzleHttp\RequestOptions;
abstract class CommentResourceTestBase extends EntityResourceTestBase {
use CommentTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['comment', 'entity_test'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'comment';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'pid',
'entity_id',
'uid',
'name',
'homepage',
'created',
'changed',
'status',
'thread',
'entity_type',
'field_name',
];
/**
* @var \Drupal\comment\CommentInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access comments', 'view test entity']);
break;
case 'POST':
$this->grantPermissionsToTestedRole(['post comments']);
break;
case 'PATCH':
// Anononymous users are not ever allowed to edit their own comments. To
// be able to test PATCHing comments as the anonymous user, the more
// permissive 'administer comments' permission must be granted.
// @see \Drupal\comment\CommentAccessControlHandler::checkAccess
if (static::$auth) {
$this->grantPermissionsToTestedRole(['edit own comments']);
}
else {
$this->grantPermissionsToTestedRole(['administer comments']);
}
break;
case 'DELETE':
$this->grantPermissionsToTestedRole(['administer comments']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "bar" bundle for the "entity_test" entity type and create.
$bundle = 'bar';
entity_test_create_bundle($bundle, NULL, 'entity_test');
// Create a comment field on this bundle.
$this->addDefaultCommentField('entity_test', 'bar', 'comment');
// Create a "Camelids" test entity that the comment will be assigned to.
$commented_entity = EntityTest::create(array(
'name' => 'Camelids',
'type' => 'bar',
));
$commented_entity->save();
// Create a "Llama" comment.
$comment = Comment::create([
'comment_body' => [
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
'format' => 'plain_text',
],
'entity_id' => $commented_entity->id(),
'entity_type' => 'entity_test',
'field_name' => 'comment',
]);
$comment->setSubject('Llama')
->setOwnerId(static::$auth ? $this->account->id() : 0)
->setPublished(TRUE)
->setCreatedTime(123456789)
->setChangedTime(123456789);
$comment->save();
return $comment;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$author = User::load($this->entity->getOwnerId());
return [
'cid' => [
['value' => 1],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'langcode' => [
[
'value' => 'en',
],
],
'comment_type' => [
[
'target_id' => 'comment',
'target_type' => 'comment_type',
'target_uuid' => CommentType::load('comment')->uuid(),
],
],
'subject' => [
[
'value' => 'Llama',
],
],
'status' => [
[
'value' => 1,
],
],
'created' => [
[
'value' => '123456789',
],
],
'changed' => [
[
'value' => '123456789',
],
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'uid' => [
[
'target_id' => $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'pid' => [],
'entity_type' => [
[
'value' => 'entity_test',
],
],
'entity_id' => [
[
'target_id' => '1',
'target_type' => 'entity_test',
'target_uuid' => EntityTest::load(1)->uuid(),
'url' => base_path() . 'entity_test/1',
],
],
'field_name' => [
[
'value' => 'comment',
],
],
'name' => [],
'homepage' => [],
'thread' => [
[
'value' => '01/',
],
],
'comment_body' => [
[
'value' => 'The name "llama" was adopted by European settlers from native Peruvians.',
'format' => 'plain_text',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'comment_type' => [
[
'target_id' => 'comment',
],
],
'entity_type' => [
[
'value' => 'entity_test',
],
],
'entity_id' => [
[
'target_id' => EntityTest::load(1)->id(),
],
],
'field_name' => [
[
'value' => 'comment',
],
],
'subject' => [
[
'value' => 'Dramallama',
],
],
'comment_body' => [
[
'value' => 'Llamas are awesome.',
'format' => 'plain_text',
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPatchEntity() {
return array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE, 'entity_id' => TRUE, 'field_name' => TRUE]);
}
/**
* Tests POSTing a comment without critical base fields.
*
* testPost() is testing with the most minimal normalization possible: the one
* returned by ::getNormalizedPostEntity().
*
* But Comment entities have some very special edge cases:
* - base fields that are not marked as required in
* \Drupal\comment\Entity\Comment::baseFieldDefinitions() yet in fact are
* required.
* - base fields that are marked as required, but yet can still result in
* validation errors other than "missing required field".
*/
public function testPostDxWithoutCriticalBaseFields() {
$this->initAuthentication();
$this->provisionEntityResource();
$this->setUpAuthorization('POST');
$url = $this->getPostUrl()->setOption('query', ['_format' => static::$format]);
$request_options = [];
$request_options[RequestOptions::HEADERS]['Accept'] = static::$mimeType;
$request_options[RequestOptions::HEADERS]['Content-Type'] = static::$mimeType;
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('POST'));
// DX: 422 when missing 'entity_type' field.
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_type' => TRUE]), static::$format);
$response = $this->request('POST', $url, $request_options);
// @todo Uncomment, remove next line in https://www.drupal.org/node/2820364.
$this->assertResourceErrorResponse(500, 'A fatal error occurred: Internal Server Error', $response);
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
// DX: 422 when missing 'entity_id' field.
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['entity_id' => TRUE]), static::$format);
// @todo Remove the try/catch in favor of the two commented lines in
// https://www.drupal.org/node/2820364.
try {
$response = $this->request('POST', $url, $request_options);
// This happens on DrupalCI.
//$this->assertSame(500, $response->getStatusCode());
}
catch (\Exception $e) {
// This happens on Wim's local machine.
//$this->assertSame("Error: Call to a member function get() on null\nDrupal\\comment\\Plugin\\Validation\\Constraint\\CommentNameConstraintValidator->getAnonymousContactDetailsSetting()() (Line: 96)\n", $e->getMessage());
}
//$response = $this->request('POST', $url, $request_options);
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nentity_type: This value should not be null.\n", $response);
// DX: 422 when missing 'entity_type' field.
$request_options[RequestOptions::BODY] = $this->serializer->encode(array_diff_key($this->getNormalizedPostEntity(), ['field_name' => TRUE]), static::$format);
$response = $this->request('POST', $url, $request_options);
// @todo Uncomment, remove next line in https://www.drupal.org/node/2820364.
$this->assertResourceErrorResponse(500, 'A fatal error occurred: Field is unknown.', $response);
//$this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nfield_name: This value should not be null.\n", $response);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class ConfigTestJsonAnonTest extends ConfigTestResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class ConfigTestJsonBasicAuthTest extends ConfigTestResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class ConfigTestJsonCookieTest extends ConfigTestResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,73 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\ConfigTest;
use Drupal\config_test\Entity\ConfigTest;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class ConfigTestResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['config_test', 'config_test_rest'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'config_test';
/**
* @var \Drupal\config_test\ConfigTestInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['view config_test']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$config_test = ConfigTest::create([
'id' => 'llama',
'label' => 'Llama',
]);
$config_test->save();
return $config_test;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$normalization = [
'uuid' => $this->entity->uuid(),
'id' => 'llama',
'weight' => 0,
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [],
'label' => 'Llama',
'style' => NULL,
'size' => NULL,
'size_value' => NULL,
'protected_property' => NULL,
];
return $normalization;
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class EntityTestJsonAnonTest extends EntityTestResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class EntityTestJsonBasicAuthTest extends EntityTestResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class EntityTestJsonCookieTest extends EntityTestResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,127 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\EntityTest;
use Drupal\entity_test\Entity\EntityTest;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
abstract class EntityTestResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'entity_test';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [];
/**
* @var \Drupal\entity_test\Entity\EntityTest
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['view test entity']);
break;
case 'POST':
$this->grantPermissionsToTestedRole(['create entity_test entity_test_with_bundle entities']);
break;
case 'PATCH':
case 'DELETE':
$this->grantPermissionsToTestedRole(['administer entity_test content']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$entity_test = EntityTest::create([
'name' => 'Llama',
'type' => 'entity_test',
]);
$entity_test->setOwnerId(0);
$entity_test->save();
return $entity_test;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$author = User::load(0);
$normalization = [
'uuid' => [
[
'value' => $this->entity->uuid()
]
],
'id' => [
[
'value' => '1',
],
],
'langcode' => [
[
'value' => 'en',
],
],
'type' => [
[
'value' => 'entity_test',
]
],
'name' => [
[
'value' => 'Llama',
]
],
'created' => [
[
'value' => $this->entity->get('created')->value,
]
],
'user_id' => [
[
'target_id' => $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => $author->toUrl()->toString(),
]
],
'field_test_text' => [],
];
return $normalization;
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'type' => 'entity_test',
'name' => [
[
'value' => 'Dramallama',
],
],
];
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Node;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class NodeJsonAnonTest extends NodeResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Node;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class NodeJsonBasicAuthTest extends NodeResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Node;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class NodeJsonCookieTest extends NodeResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,196 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Node;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
abstract class NodeResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'node';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'uid',
'created',
'changed',
'promote',
'sticky',
'revision_timestamp',
'revision_uid',
];
/**
* @var \Drupal\node\NodeInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access content']);
break;
case 'POST':
$this->grantPermissionsToTestedRole(['access content', 'create camelids content']);
break;
case 'PATCH':
$this->grantPermissionsToTestedRole(['access content', 'edit any camelids content']);
break;
case 'DELETE':
$this->grantPermissionsToTestedRole(['access content', 'delete any camelids content']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
if (!NodeType::load('camelids')) {
// Create a "Camelids" node type.
NodeType::create([
'name' => 'Camelids',
'type' => 'camelids',
])->save();
}
// Create a "Llama" node.
$node = Node::create(['type' => 'camelids']);
$node->setTitle('Llama')
->setOwnerId(static::$auth ? $this->account->id() : 0)
->setPublished(TRUE)
->setCreatedTime(123456789)
->setChangedTime(123456789)
->setRevisionCreationTime(123456789)
->save();
return $node;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
$author = User::load($this->entity->getOwnerId());
return [
'nid' => [
['value' => 1],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'vid' => [
['value' => 1],
],
'langcode' => [
[
'value' => 'en',
],
],
'type' => [
[
'target_id' => 'camelids',
'target_type' => 'node_type',
'target_uuid' => NodeType::load('camelids')->uuid(),
],
],
'title' => [
[
'value' => 'Llama',
],
],
'status' => [
[
'value' => 1,
],
],
'created' => [
[
'value' => '123456789',
],
],
'changed' => [
[
'value' => '123456789',
],
],
'promote' => [
[
'value' => 1,
],
],
'sticky' => [
[
'value' => '0',
],
],
'revision_timestamp' => [
[
'value' => '123456789',
],
],
'revision_translation_affected' => [
[
'value' => TRUE,
],
],
'default_langcode' => [
[
'value' => TRUE,
],
],
'uid' => [
[
'target_id' => $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'revision_uid' => [
[
'target_id' => $author->id(),
'target_type' => 'user',
'target_uuid' => $author->uuid(),
'url' => base_path() . 'user/' . $author->id(),
],
],
'revision_log' => [
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'type' => [
[
'target_id' => 'camelids',
],
],
'title' => [
[
'value' => 'Dramallama',
],
],
];
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Role;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class RoleJsonAnonTest extends RoleResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,48 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Role;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Psr\Http\Message\ResponseInterface;
/**
* @group rest
*/
class RoleJsonBasicAuthTest extends RoleResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
/**
* {@inheritdoc}
*/
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
$this->assertSame(401, $response->getStatusCode());
$this->assertSame('{"message":"A fatal error occurred: No authentication credentials provided."}', (string) $response->getBody());
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Role;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class RoleJsonCookieTest extends RoleResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Role;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\Role;
abstract class RoleResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'user_role';
/**
* @var \Drupal\user\RoleInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer permissions']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$role = Role::create([
'id' => 'llama',
'name' => $this->randomString(),
]);
$role->save();
return $role;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'uuid' => $this->entity->uuid(),
'weight' => 2,
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [],
'id' => 'llama',
'label' => NULL,
'is_admin' => NULL,
'permissions' => [],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Term;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class TermJsonAnonTest extends TermResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Term;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class TermJsonBasicAuthTest extends TermResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Term;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class TermJsonCookieTest extends TermResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,140 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Term;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class TermResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'taxonomy_term';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed',
];
/**
* @var \Drupal\taxonomy\TermInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access content']);
break;
case 'POST':
case 'PATCH':
case 'DELETE':
// @todo Update once https://www.drupal.org/node/2824408 lands.
$this->grantPermissionsToTestedRole(['administer taxonomy']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$vocabulary = Vocabulary::load('camelids');
if (!$vocabulary) {
// Create a "Camelids" vocabulary.
$vocabulary = Vocabulary::create([
'name' => 'Camelids',
'vid' => 'camelids',
]);
$vocabulary->save();
}
// Create a "Llama" taxonomy term.
$term = Term::create(['vid' => $vocabulary->id()])
->setName('Llama')
->setChangedTime(123456789);
$term->save();
return $term;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'tid' => [
['value' => 1],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'vid' => [
[
'target_id' => 'camelids',
'target_type' => 'taxonomy_vocabulary',
'target_uuid' => Vocabulary::load('camelids')->uuid(),
],
],
'name' => [
['value' => 'Llama'],
],
'description' => [
[
'value' => NULL,
'format' => NULL,
],
],
'parent' => [],
'weight' => [
['value' => 0],
],
'langcode' => [
[
'value' => 'en',
],
],
'changed' => [
[
'value' => '123456789',
],
],
'default_langcode' => [
[
'value' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'vid' => [
[
'target_id' => 'camelids',
],
],
'name' => [
[
'value' => 'Dramallama',
],
],
];
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\User;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class UserJsonAnonTest extends UserResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\User;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class UserJsonBasicAuthTest extends UserResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\User;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class UserJsonCookieTest extends UserResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,232 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\User;
use Drupal\Core\Url;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
use Drupal\user\Entity\User;
use GuzzleHttp\RequestOptions;
abstract class UserResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['user'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'user';
/**
* {@inheritdoc}
*/
protected static $patchProtectedFieldNames = [
'changed',
];
/**
* @var \Drupal\user\UserInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected static $labelFieldName = 'name';
/**
* {@inheritdoc}
*/
protected static $firstCreatedEntityId = 4;
/**
* {@inheritdoc}
*/
protected static $secondCreatedEntityId = 5;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
switch ($method) {
case 'GET':
$this->grantPermissionsToTestedRole(['access user profiles']);
break;
case 'POST':
case 'PATCH':
case 'DELETE':
$this->grantPermissionsToTestedRole(['administer users']);
break;
}
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
// Create a "Llama" user.
$user = User::create(['created' => 123456789]);
$user->setUsername('Llama')
->setChangedTime(123456789)
->activate()
->save();
return $user;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'uid' => [
['value' => '3'],
],
'uuid' => [
['value' => $this->entity->uuid()],
],
'langcode' => [
[
'value' => 'en',
],
],
'name' => [
[
'value' => 'Llama',
],
],
'created' => [
[
'value' => '123456789',
],
],
'changed' => [
[
'value' => '123456789',
],
],
'default_langcode' => [
[
'value' => TRUE,
],
],
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
return [
'name' => [
[
'value' => 'Dramallama ' . $this->randomMachineName(),
],
],
];
}
/**
* Tests PATCHing security-sensitive base fields of the logged in account.
*/
public function testPatchDxForSecuritySensitiveBaseFields() {
// The anonymous user is never allowed to modify itself.
if (!static::$auth) {
$this->markTestSkipped();
}
$this->initAuthentication();
$this->provisionEntityResource();
$this->setUpAuthorization('PATCH');
/** @var \Drupal\user\UserInterface $user */
$user = static::$auth ? $this->account : User::load(0);
$original_normalization = array_diff_key($this->serializer->normalize($user, static::$format), ['changed' => TRUE]);
// Since this test must be performed by the user that is being modified,
// we cannot use $this->getUrl().
$url = $user->toUrl()->setOption('query', ['_format' => static::$format]);
$request_options = [
RequestOptions::HEADERS => ['Content-Type' => static::$mimeType],
];
$request_options = array_merge_recursive($request_options, $this->getAuthenticationRequestOptions('PATCH'));
// Test case 1: changing email.
$normalization = $original_normalization;
$normalization['mail'] = [['value' => 'new-email@example.com']];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 422 when changing email without providing the password.
$response = $this->request('PATCH', $url, $request_options);
// @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813755 lands.
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Email</em>.\n", $response);
$this->assertSame(422, $response->getStatusCode());
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
$this->assertSame($this->serializer->encode(['message' => "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Email</em>.\n"], static::$format), (string) $response->getBody());
$normalization['pass'] = [['existing' => 'wrong']];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 422 when changing email while providing a wrong password.
$response = $this->request('PATCH', $url, $request_options);
// @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813755 lands.
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Email</em>.\n", $response);
$this->assertSame(422, $response->getStatusCode());
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
$this->assertSame($this->serializer->encode(['message' => "Unprocessable Entity: validation failed.\nmail: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Email</em>.\n"], static::$format), (string) $response->getBody());
$normalization['pass'] = [['existing' => $this->account->passRaw]];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// 200 for well-formed request.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
// Test case 2: changing password.
$normalization = $original_normalization;
$new_password = $this->randomString();
$normalization['pass'] = [['value' => $new_password]];
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// DX: 422 when changing password without providing the current password.
$response = $this->request('PATCH', $url, $request_options);
// @todo use this commented line instead of the 3 lines thereafter once https://www.drupal.org/node/2813755 lands.
// $this->assertResourceErrorResponse(422, "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Password</em>.\n", $response);
$this->assertSame(422, $response->getStatusCode());
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
$this->assertSame($this->serializer->encode(['message' => "Unprocessable Entity: validation failed.\npass: Your current password is missing or incorrect; it's required to change the <em class=\"placeholder\">Password</em>.\n"], static::$format), (string) $response->getBody());
$normalization['pass'][0]['existing'] = $this->account->pass_raw;
$request_options[RequestOptions::BODY] = $this->serializer->encode($normalization, static::$format);
// 200 for well-formed request.
$response = $this->request('PATCH', $url, $request_options);
$this->assertResourceResponse(200, FALSE, $response);
// Verify that we can log in with the new password.
$request_body = [
'name' => $user->getAccountName(),
'pass' => $new_password,
];
$request_options = [
RequestOptions::HEADERS => [],
RequestOptions::BODY => $this->serializer->encode($request_body, 'json'),
];
$response = $this->httpClient->request('POST', Url::fromRoute('user.login.http')->setRouteParameter('_format', 'json')->toString(), $request_options);
$this->assertSame(200, $response->getStatusCode());
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Vocabulary;
use Drupal\Tests\rest\Functional\AnonResourceTestTrait;
/**
* @group rest
*/
class VocabularyJsonAnonTest extends VocabularyResourceTestBase {
use AnonResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* Disable the GET test coverage due to bug in taxonomy module.
* @todo Fix in https://www.drupal.org/node/2805281: remove this override.
*/
public function testGet() {
$this->markTestSkipped();
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Vocabulary;
use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
/**
* @group rest
*/
class VocabularyJsonBasicAuthTest extends VocabularyResourceTestBase {
use BasicAuthResourceTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['basic_auth'];
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'basic_auth';
// @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
use JsonBasicAuthWorkaroundFor2805281Trait {
JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Vocabulary;
use Drupal\Tests\rest\Functional\CookieResourceTestTrait;
/**
* @group rest
*/
class VocabularyJsonCookieTest extends VocabularyResourceTestBase {
use CookieResourceTestTrait;
/**
* {@inheritdoc}
*/
protected static $format = 'json';
/**
* {@inheritdoc}
*/
protected static $mimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* {@inheritdoc}
*/
protected static $auth = 'cookie';
}

View file

@ -0,0 +1,69 @@
<?php
namespace Drupal\Tests\rest\Functional\EntityResource\Vocabulary;
use Drupal\taxonomy\Entity\Vocabulary;
use Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase;
abstract class VocabularyResourceTestBase extends EntityResourceTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['taxonomy'];
/**
* {@inheritdoc}
*/
protected static $entityTypeId = 'taxonomy_vocabulary';
/**
* @var \Drupal\taxonomy\VocabularyInterface
*/
protected $entity;
/**
* {@inheritdoc}
*/
protected function setUpAuthorization($method) {
$this->grantPermissionsToTestedRole(['administer taxonomy']);
}
/**
* {@inheritdoc}
*/
protected function createEntity() {
$vocabulary = Vocabulary::create([
'name' => 'Llama',
'vid' => 'llama',
]);
$vocabulary->save();
return $vocabulary;
}
/**
* {@inheritdoc}
*/
protected function getExpectedNormalizedEntity() {
return [
'uuid' => $this->entity->uuid(),
'vid' => 'llama',
'langcode' => 'en',
'status' => TRUE,
'dependencies' => [],
'name' => 'Llama',
'description' => NULL,
'hierarchy' => 0,
'weight' => 0,
];
}
/**
* {@inheritdoc}
*/
protected function getNormalizedPostEntity() {
// @todo Update in https://www.drupal.org/node/2300677.
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Drupal\Tests\rest\Functional;
use Psr\Http\Message\ResponseInterface;
trait JsonBasicAuthWorkaroundFor2805281Trait {
/**
* {@inheritdoc}
*
* Note that strange 'A fatal error occurred: ' prefix, that should not exist.
*
* @todo Fix in https://www.drupal.org/node/2805281: remove this trait.
*/
protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
$this->assertSame(401, $response->getStatusCode());
$this->assertSame([static::$expectedErrorMimeType], $response->getHeader('Content-Type'));
// Note that strange 'A fatal error occurred: ' prefix, that should not
// exist.
// @todo Fix in https://www.drupal.org/node/2805281.
$this->assertSame('{"message":"A fatal error occurred: No authentication credentials provided."}', (string) $response->getBody());
}
}

View file

@ -0,0 +1,349 @@
<?php
namespace Drupal\Tests\rest\Functional;
use Drupal\Core\Url;
use Drupal\rest\RestResourceConfigInterface;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\Entity\Role;
use Drupal\user\RoleInterface;
use GuzzleHttp\RequestOptions;
use Psr\Http\Message\ResponseInterface;
/**
* Subclass this for every REST resource, every format and every auth provider.
*
* For more guidance see
* \Drupal\Tests\rest\Functional\EntityResource\EntityResourceTestBase
* which has recommendations for testing the
* \Drupal\rest\Plugin\rest\resource\EntityResource REST resource for every
* format and every auth provider. It's a special case (because that single REST
* resource generates supports not just one thing, but many things  multiple
* entity types), but the same principles apply.
*/
abstract class ResourceTestBase extends BrowserTestBase {
/**
* The format to use in this test.
*
* A format is the combination of a certain normalizer and a certain
* serializer.
*
* @see https://www.drupal.org/developing/api/8/serialization
*
* (The default is 'json' because that doesn't depend on any module.)
*
* @var string
*/
protected static $format = 'json';
/**
* The MIME type that corresponds to $format.
*
* (Sadly this cannot be computed automatically yet.)
*
* @var string
*/
protected static $mimeType = 'application/json';
/**
* The expected MIME type in case of 4xx error responses.
*
* (Can be different, when $mimeType for example encodes a particular
* normalization, such as 'application/hal+json': its error response MIME
* type is 'application/json'.)
*
* @var string
*/
protected static $expectedErrorMimeType = 'application/json';
/**
* The authentication mechanism to use in this test.
*
* (The default is 'cookie' because that doesn't depend on any module.)
*
* @var string
*/
protected static $auth = FALSE;
/**
* The account to use for authentication, if any.
*
* @var null|\Drupal\Core\Session\AccountInterface
*/
protected $account = NULL;
/**
* The REST resource config entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $resourceConfigStorage;
/**
* The serializer service.
*
* @var \Symfony\Component\Serializer\Serializer
*/
protected $serializer;
/**
* Modules to install.
*
* @var array
*/
public static $modules = ['rest'];
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
// Ensure the anonymous user role has no permissions at all.
$user_role = Role::load(RoleInterface::ANONYMOUS_ID);
foreach ($user_role->getPermissions() as $permission) {
$user_role->revokePermission($permission);
}
$user_role->save();
assert('[] === $user_role->getPermissions()', 'The anonymous user role has no permissions at all.');
if (static::$auth !== FALSE) {
// Ensure the authenticated user role has no permissions at all.
$user_role = Role::load(RoleInterface::AUTHENTICATED_ID);
foreach ($user_role->getPermissions() as $permission) {
$user_role->revokePermission($permission);
}
$user_role->save();
assert('[] === $user_role->getPermissions()', 'The authenticated user role has no permissions at all.');
// Create an account.
$this->account = $this->createUser();
}
else {
// Otherwise, also create an account, so that any test involving User
// entities will have the same user IDs regardless of authentication.
$this->createUser();
}
$this->resourceConfigStorage = $this->container->get('entity_type.manager')->getStorage('rest_resource_config');
// Ensure there's a clean slate: delete all REST resource config entities.
$this->resourceConfigStorage->delete($this->resourceConfigStorage->loadMultiple());
}
/**
* Provisions a REST resource.
*
* @param string $resource_type
* The resource type (REST resource plugin ID).
* @param string[] $formats
* The allowed formats for this resource.
* @param string[] $authentication
* The allowed authentication providers for this resource.
*/
protected function provisionResource($resource_type, $formats = [], $authentication = []) {
$this->resourceConfigStorage->create([
'id' => $resource_type,
'granularity' => RestResourceConfigInterface::RESOURCE_GRANULARITY,
'configuration' => [
'methods' => ['GET', 'POST', 'PATCH', 'DELETE'],
'formats' => $formats,
'authentication' => $authentication,
]
])->save();
// @todo Remove this in https://www.drupal.org/node/2815845.
drupal_flush_all_caches();
}
/**
* Sets up the necessary authorization.
*
* In case of a test verifying publicly accessible REST resources: grant
* permissions to the anonymous user role.
*
* In case of a test verifying behavior when using a particular authentication
* provider: create a user with a particular set of permissions.
*
* Because of the $method parameter, it's possible to first set up
* authentication for only GET, then add POST, et cetera. This then also
* allows for verifying a 403 in case of missing authorization.
*
* @param string $method
* The HTTP method for which to set up authentication.
*
* @see ::grantPermissionsToAnonymousRole()
* @see ::grantPermissionsToAuthenticatedRole()
*/
abstract protected function setUpAuthorization($method);
/**
* Verifies the error response in case of missing authentication.
*/
abstract protected function assertResponseWhenMissingAuthentication(ResponseInterface $response);
/**
* Asserts normalization-specific edge cases.
*
* (Should be called before sending a well-formed request.)
*
* @see \GuzzleHttp\ClientInterface::request()
*
* @param string $method
* HTTP method.
* @param \Drupal\Core\Url $url
* URL to request.
* @param array $request_options
* Request options to apply.
*/
abstract protected function assertNormalizationEdgeCases($method, Url $url, array $request_options);
/**
* Asserts authentication provider-specific edge cases.
*
* (Should be called before sending a well-formed request.)
*
* @see \GuzzleHttp\ClientInterface::request()
*
* @param string $method
* HTTP method.
* @param \Drupal\Core\Url $url
* URL to request.
* @param array $request_options
* Request options to apply.
*/
abstract protected function assertAuthenticationEdgeCases($method, Url $url, array $request_options);
/**
* Initializes authentication.
*
* E.g. for cookie authentication, we first need to get a cookie.
*/
protected function initAuthentication() {}
/**
* Returns Guzzle request options for authentication.
*
* @param string $method
* The HTTP method for this authenticated request.
*
* @return array
* Guzzle request options to use for authentication.
*
* @see \GuzzleHttp\ClientInterface::request()
*/
protected function getAuthenticationRequestOptions($method) {
return [];
}
/**
* Grants permissions to the anonymous role.
*
* @param string[] $permissions
* Permissions to grant.
*/
protected function grantPermissionsToAnonymousRole(array $permissions) {
$this->grantPermissions(Role::load(RoleInterface::ANONYMOUS_ID), $permissions);
}
/**
* Grants permissions to the authenticated role.
*
* @param string[] $permissions
* Permissions to grant.
*/
protected function grantPermissionsToAuthenticatedRole(array $permissions) {
$this->grantPermissions(Role::load(RoleInterface::AUTHENTICATED_ID), $permissions);
}
/**
* Grants permissions to the tested role: anonymous or authenticated.
*
* @param string[] $permissions
* Permissions to grant.
*
* @see ::grantPermissionsToAuthenticatedRole()
* @see ::grantPermissionsToAnonymousRole()
*/
protected function grantPermissionsToTestedRole(array $permissions) {
if (static::$auth) {
$this->grantPermissionsToAuthenticatedRole($permissions);
}
else {
$this->grantPermissionsToAnonymousRole($permissions);
}
}
/**
* Performs a HTTP request. Wraps the Guzzle HTTP client.
*
* Why wrap the Guzzle HTTP client? Because we want to keep the actual test
* code as simple as possible, and hence not require them to specify the
* 'http_errors = FALSE' request option, nor do we want them to have to
* convert Drupal Url objects to strings.
*
* @see \GuzzleHttp\ClientInterface::request()
*
* @param string $method
* HTTP method.
* @param \Drupal\Core\Url $url
* URL to request.
* @param array $request_options
* Request options to apply.
*
* @return \Psr\Http\Message\ResponseInterface
*/
protected function request($method, Url $url, array $request_options) {
$request_options[RequestOptions::HTTP_ERRORS] = FALSE;
return $this->httpClient->request($method, $url->toString(), $request_options);
}
/**
* Asserts that a resource response has the given status code and body.
*
* (Also asserts that the expected error MIME type is present, but this is
* defined globally for the test via static::$expectedErrorMimeType, because
* all error responses should use the same MIME type.)
*
* @param int $expected_status_code
* The expected response status.
* @param string|false $expected_body
* The expected response body. FALSE in case this should not be asserted.
* @param \Psr\Http\Message\ResponseInterface $response
* The response to assert.
*/
protected function assertResourceResponse($expected_status_code, $expected_body, ResponseInterface $response) {
$this->assertSame($expected_status_code, $response->getStatusCode());
if ($expected_status_code < 400) {
$this->assertSame([static::$mimeType], $response->getHeader('Content-Type'));
}
else {
$this->assertSame([static::$expectedErrorMimeType], $response->getHeader('Content-Type'));
}
if ($expected_body !== FALSE) {
$this->assertSame($expected_body, (string) $response->getBody());
}
}
/**
* Asserts that a resource error response has the given message.
*
* (Also asserts that the expected error MIME type is present, but this is
* defined globally for the test via static::$expectedErrorMimeType, because
* all error responses should use the same MIME type.)
*
* @param int $expected_status_code
* The expected response status.
* @param string $expected_message
* The expected error message.
* @param \Psr\Http\Message\ResponseInterface $response
* The error response to assert.
*/
protected function assertResourceErrorResponse($expected_status_code, $expected_message, ResponseInterface $response) {
// @todo Fix this in https://www.drupal.org/node/2813755.
$encode_options = ['json_encode_options' => JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT];
$expected_body = $this->serializer->encode(['message' => $expected_message], static::$format, $encode_options);
$this->assertResourceResponse($expected_status_code, $expected_body, $response);
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace Drupal\Tests\rest\Unit\Entity;
use Drupal\rest\Entity\RestResourceConfig;
use Drupal\rest\RestResourceConfigInterface;
use Drupal\Tests\UnitTestCase;
/**
* @coversDefaultClass \Drupal\rest\Entity\RestResourceConfig
*
* @group rest
*/
class RestResourceConfigTest extends UnitTestCase {
/**
* Asserts that rest methods are normalized to upper case.
*
* This also tests that no exceptions are thrown during that method so that
* alternate methods such as OPTIONS and PUT are supported.
*/
public function testNormalizeRestMethod() {
$expected = ['GET', 'PUT', 'POST', 'PATCH', 'DELETE', 'OPTIONS', 'FOO'];
$methods = ['get', 'put', 'post', 'patch', 'delete', 'options', 'foo'];
$configuration = [];
foreach ($methods as $method) {
$configuration[$method] = [
'supported_auth' => ['cookie'],
'supported_formats' => ['json'],
];
}
$entity = new RestResourceConfig([
'plugin_id' => 'entity:entity_test',
'granularity' => RestResourceConfigInterface::METHOD_GRANULARITY,
'configuration' => $configuration,
], 'rest_resource_config');
$this->assertArrayEquals($expected, $entity->getMethods());
}
}