Commit 8f53fb14 authored by catch's avatar catch

Issue #2824576 by Wim Leers: Delete old REST test coverage:...

Issue #2824576 by Wim Leers: Delete old REST test coverage: (Read|Create|Update|Delete)Test, deprecate RESTTestBase
parent 9e0ae4b0
......@@ -7,7 +7,7 @@
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
/**
* Tests that entities can be denormalized from HAL.
* Tests HAL denormalization edge cases for EntityResource.
*
* @group hal
*/
......@@ -110,98 +110,4 @@ public function testMarkFieldForDeletion() {
$this->assertEqual($entity->field_test_text->count(), 0);
}
/**
* Test that non-reference fields can be denormalized.
*/
public function testBasicFieldDenormalization() {
$data = array(
'_links' => array(
'type' => array(
'href' => Url::fromUri('base:rest/type/entity_test/entity_test', array('absolute' => TRUE))->toString(),
),
),
'uuid' => array(
array(
'value' => 'e5c9fb96-3acf-4a8d-9417-23de1b6c3311',
),
),
'field_test_text' => array(
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
),
'field_test_translatable_text' => array(
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
array(
'value' => $this->randomMachineName(),
'format' => 'filtered_html',
),
array(
'value' => $this->randomMachineName(),
'format' => 'filtered_html',
'lang' => 'de',
),
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
'lang' => 'de',
),
),
);
$expected_value_default = array(
array (
'value' => $data['field_test_translatable_text'][0]['value'],
'format' => 'full_html',
),
array (
'value' => $data['field_test_translatable_text'][1]['value'],
'format' => 'filtered_html',
),
);
$expected_value_de = array(
array (
'value' => $data['field_test_translatable_text'][2]['value'],
'format' => 'filtered_html',
),
array (
'value' => $data['field_test_translatable_text'][3]['value'],
'format' => 'full_html',
),
);
$denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format);
$this->assertEqual($data['uuid'], $denormalized->get('uuid')->getValue(), 'A preset value (e.g. UUID) is overridden by incoming data.');
$this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue(), 'A basic text field is denormalized.');
$this->assertEqual($expected_value_default, $denormalized->get('field_test_translatable_text')->getValue(), 'Values in the default language are properly handled for a translatable field.');
$this->assertEqual($expected_value_de, $denormalized->getTranslation('de')->get('field_test_translatable_text')->getValue(), 'Values in a translation language are properly handled for a translatable field.');
}
/**
* Verifies that the denormalized entity is correct in the PATCH context.
*/
public function testPatchDenormalization() {
$data = array(
'_links' => array(
'type' => array(
'href' => Url::fromUri('base:rest/type/entity_test/entity_test', array('absolute' => TRUE))->toString(),
),
),
'field_test_text' => array(
array(
'value' => $this->randomMachineName(),
'format' => 'full_html',
),
),
);
$denormalized = $this->serializer->denormalize($data, $this->entityClass, $this->format, array('request_method' => 'patch'));
// Check that the one field got populated as expected.
$this->assertEqual($data['field_test_text'], $denormalized->get('field_test_text')->getValue());
// Check the custom property that contains the list of fields to merge.
$this->assertEqual($denormalized->_restSubmittedFields, ['field_test_text']);
}
}
<?php
namespace Drupal\Tests\hal\Kernel;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\comment\Entity\Comment;
use Drupal\node\Entity\Node;
use Drupal\user\Entity\User;
use Drupal\node\Entity\NodeType;
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
/**
* Tests that nodes and terms are correctly normalized and denormalized.
*
* @group hal
*/
class EntityNormalizeTest extends NormalizerTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = array('node', 'taxonomy', 'comment');
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
\Drupal::service('router.builder')->rebuild();
$this->installSchema('system', array('sequences'));
$this->installSchema('comment', array('comment_entity_statistics'));
$this->installEntitySchema('taxonomy_term');
$this->installConfig(['node', 'comment']);
}
/**
* Tests the normalization of nodes.
*/
public function testNode() {
$node_type = NodeType::create(['type' => 'example_type']);
$node_type->save();
$user = User::create(['name' => $this->randomMachineName()]);
$user->save();
// Add comment type.
$this->container->get('entity.manager')->getStorage('comment_type')->create(array(
'id' => 'comment',
'label' => 'comment',
'target_entity_type_id' => 'node',
))->save();
$this->addDefaultCommentField('node', 'example_type');
$node = Node::create([
'title' => $this->randomMachineName(),
'uid' => $user->id(),
'type' => $node_type->id(),
'status' => NODE_PUBLISHED,
'promote' => 1,
'sticky' => 0,
'body' => [
'value' => $this->randomMachineName(),
'format' => $this->randomMachineName()
],
'revision_log' => $this->randomString(),
]);
$node->save();
$original_values = $node->toArray();
$normalized = $this->serializer->normalize($node, $this->format);
/** @var \Drupal\node\NodeInterface $denormalized_node */
$denormalized_node = $this->serializer->denormalize($normalized, 'Drupal\node\Entity\Node', $this->format);
$this->assertEqual($original_values, $denormalized_node->toArray(), 'Node values are restored after normalizing and denormalizing.');
}
/**
* Tests the normalization of terms.
*/
public function testTerm() {
$vocabulary = Vocabulary::create(['vid' => 'example_vocabulary']);
$vocabulary->save();
$account = User::create(['name' => $this->randomMachineName()]);
$account->save();
// @todo Until https://www.drupal.org/node/2327935 is fixed, if no parent is
// set, the test fails because target_id => 0 is reserialized to NULL.
$term_parent = Term::create([
'name' => $this->randomMachineName(),
'vid' => $vocabulary->id(),
]);
$term_parent->save();
$term = Term::create([
'name' => $this->randomMachineName(),
'vid' => $vocabulary->id(),
'description' => array(
'value' => $this->randomMachineName(),
'format' => $this->randomMachineName(),
),
'parent' => $term_parent->id(),
]);
$term->save();
$original_values = $term->toArray();
$normalized = $this->serializer->normalize($term, $this->format, ['account' => $account]);
/** @var \Drupal\taxonomy\TermInterface $denormalized_term */
$denormalized_term = $this->serializer->denormalize($normalized, 'Drupal\taxonomy\Entity\Term', $this->format, ['account' => $account]);
$this->assertEqual($original_values, $denormalized_term->toArray(), 'Term values are restored after normalizing and denormalizing.');
}
/**
* Tests the normalization of comments.
*/
public function testComment() {
$node_type = NodeType::create(['type' => 'example_type']);
$node_type->save();
$account = User::create(['name' => $this->randomMachineName()]);
$account->save();
// Add comment type.
$this->container->get('entity.manager')->getStorage('comment_type')->create(array(
'id' => 'comment',
'label' => 'comment',
'target_entity_type_id' => 'node',
))->save();
$this->addDefaultCommentField('node', 'example_type');
$node = Node::create([
'title' => $this->randomMachineName(),
'uid' => $account->id(),
'type' => $node_type->id(),
'status' => NODE_PUBLISHED,
'promote' => 1,
'sticky' => 0,
'body' => [[
'value' => $this->randomMachineName(),
'format' => $this->randomMachineName()
]],
]);
$node->save();
$parent_comment = Comment::create(array(
'uid' => $account->id(),
'subject' => $this->randomMachineName(),
'comment_body' => [
'value' => $this->randomMachineName(),
'format' => NULL,
],
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
));
$parent_comment->save();
$comment = Comment::create(array(
'uid' => $account->id(),
'subject' => $this->randomMachineName(),
'comment_body' => [
'value' => $this->randomMachineName(),
'format' => NULL,
],
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'pid' => $parent_comment->id(),
'mail' => 'dries@drupal.org',
'homepage' => 'http://buytaert.net',
));
$comment->save();
$original_values = $comment->toArray();
// Hostname will always be denied view access.
// No value will exist for name as this is only for anonymous users.
unset($original_values['hostname'], $original_values['name']);
$normalized = $this->serializer->normalize($comment, $this->format, ['account' => $account]);
// Assert that the hostname field does not appear at all in the normalized
// data.
$this->assertFalse(array_key_exists('hostname', $normalized), 'Hostname was not found in normalized comment data.');
/** @var \Drupal\comment\CommentInterface $denormalized_comment */
$denormalized_comment = $this->serializer->denormalize($normalized, 'Drupal\comment\Entity\Comment', $this->format, ['account' => $account]);
// Before comparing, unset values that are expected to differ.
$denormalized_comment_values = $denormalized_comment->toArray();
unset($denormalized_comment_values['hostname'], $denormalized_comment_values['name']);
$this->assertEqual($original_values, $denormalized_comment_values, 'The expected comment values are restored after normalizing and denormalizing.');
}
}
......@@ -7,7 +7,7 @@
use Drupal\entity_test\Entity\EntityTest;
/**
* Tests that entities can be normalized in HAL.
* Tests HAL normalization edge cases for EntityResource.
*
* @group hal
*/
......
<?php
namespace Drupal\rest\Tests;
use Drupal\Core\Url;
/**
* Tests authentication provider restrictions.
*
* @group rest
*/
class AuthTest extends RESTTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = array('basic_auth', 'hal', 'rest', 'entity_test');
/**
* Tests reading from an authenticated resource.
*/
public function testRead() {
$entity_type = 'entity_test';
// Enable a test resource through GET method and basic HTTP authentication.
$this->enableService('entity:' . $entity_type, 'GET', NULL, array('basic_auth'));
// Create an entity programmatically.
$entity = $this->entityCreate($entity_type);
$entity->save();
// Try to read the resource as an anonymous user, which should not work.
$this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
$this->assertResponse('401', 'HTTP response code is 401 when the request is not authenticated and the user is anonymous.');
$this->assertRaw(json_encode(['message' => 'A fatal error occurred: No authentication credentials provided.']));
// Ensure that cURL settings/headers aren't carried over to next request.
unset($this->curlHandle);
// Create a user account that has the required permissions to read
// resources via the REST API, but the request is authenticated
// with session cookies.
$permissions = $this->entityPermissions($entity_type, 'view');
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
// Try to read the resource with session cookie authentication, which is
// not enabled and should not work.
$this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
$this->assertResponse('403', 'HTTP response code is 403 when the request was authenticated by the wrong authentication provider.');
// Ensure that cURL settings/headers aren't carried over to next request.
unset($this->curlHandle);
// Now read it with the Basic authentication which is enabled and should
// work.
$this->basicAuthGet($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), $account->getUsername(), $account->pass_raw);
$this->assertResponse('200', 'HTTP response code is 200 for successfully authenticated requests.');
$this->curlClose();
}
/**
* Performs a HTTP request with Basic authentication.
*
* We do not use \Drupal\simpletest\WebTestBase::drupalGet because we need to
* set curl settings for basic authentication.
*
* @param \Drupal\Core\Url $url
* A Url object.
* @param string $username
* The user name to authenticate with.
* @param string $password
* The password.
* @param string $mime_type
* The MIME type for the Accept header.
*
* @return string
* Curl output.
*/
protected function basicAuthGet(Url $url, $username, $password, $mime_type = NULL) {
if (!isset($mime_type)) {
$mime_type = $this->defaultMimeType;
}
$out = $this->curlExec(
array(
CURLOPT_HTTPGET => TRUE,
CURLOPT_URL => $url->setAbsolute()->toString(),
CURLOPT_NOBODY => FALSE,
CURLOPT_HTTPAUTH => CURLAUTH_BASIC,
CURLOPT_USERPWD => $username . ':' . $password,
CURLOPT_HTTPHEADER => array('Accept: ' . $mime_type),
)
);
$this->verbose('GET request to: ' . $url->toString() .
'<hr />' . $out);
return $out;
}
}
This diff is collapsed.
<?php
namespace Drupal\rest\Tests;
use Drupal\Core\Url;
/**
* Tests the CSRF protection.
*
* @group rest
*/
class CsrfTest extends RESTTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = array('hal', 'rest', 'entity_test', 'basic_auth');
/**
* A testing user account.
*
* @var \Drupal\user\Entity\User
*/
protected $account;
/**
* The serialized entity.
*
* @var string
*/
protected $serialized;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->enableService('entity:' . $this->testEntityType, 'POST', 'hal_json', array('basic_auth', 'cookie'));
// Create a user account that has the required permissions to create
// resources via the REST API.
$permissions = $this->entityPermissions($this->testEntityType, 'create');
$this->account = $this->drupalCreateUser($permissions);
// Serialize an entity to a string to use in the content body of the POST
// request.
$serializer = $this->container->get('serializer');
$entity_values = $this->entityValues($this->testEntityType);
$entity = $this->container->get('entity_type.manager')
->getStorage($this->testEntityType)
->create($entity_values);
$this->serialized = $serializer->serialize($entity, $this->defaultFormat);
}
/**
* Tests that CSRF check is not triggered for Basic Auth requests.
*/
public function testBasicAuth() {
$curl_options = $this->getCurlOptions();
$curl_options[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
$curl_options[CURLOPT_USERPWD] = $this->account->getUsername() . ':' . $this->account->pass_raw;
$this->curlExec($curl_options);
$this->assertResponse(201);
// Ensure that the entity was created.
$loaded_entity = $this->loadEntityFromLocationHeader($this->drupalGetHeader('location'));
$this->assertTrue($loaded_entity, 'An entity was created in the database');
}
/**
* Tests that CSRF check is triggered for Cookie Auth requests.
*
* @deprecated as of Drupal 8.2.x, will be removed before Drupal 9.0.0. Use
* \Drupal\Tests\system\Functional\CsrfRequestHeaderTest::testRouteAccess
* instead.
*/
public function testCookieAuth() {
$this->drupalLogin($this->account);
$curl_options = $this->getCurlOptions();
// Try to create an entity without the CSRF token.
// Note: this will fail with PHP 5.6 when always_populate_raw_post_data is
// set to something other than -1. See https://www.drupal.org/node/2456025.
$this->curlExec($curl_options);
$this->assertResponse(403);
// Ensure that the entity was not created.
$storage = $this->container->get('entity_type.manager')
->getStorage($this->testEntityType);
$storage->resetCache();
$this->assertFalse($storage->loadMultiple(), 'No entity has been created in the database.');
// Create an entity with the CSRF token.
$token = $this->drupalGet('rest/session/token');
$curl_options[CURLOPT_HTTPHEADER][] = "X-CSRF-Token: $token";
$this->curlExec($curl_options);
$this->assertResponse(201);
// Ensure that the entity was created.
$loaded_entity = $this->loadEntityFromLocationHeader($this->drupalGetHeader('location'));
$this->assertTrue($loaded_entity, 'An entity was created in the database');
}
/**
* Gets the cURL options to create an entity with POST.
*
* @return array
* The array of cURL options.
*/
protected function getCurlOptions() {
return array(
CURLOPT_HTTPGET => FALSE,
CURLOPT_POST => TRUE,
CURLOPT_POSTFIELDS => $this->serialized,
CURLOPT_URL => Url::fromRoute('rest.entity.' . $this->testEntityType . '.POST')->setAbsolute()->toString(),
CURLOPT_NOBODY => FALSE,
CURLOPT_HTTPHEADER => array(
"Content-Type: {$this->defaultMimeType}",
),
);
}
}
<?php
namespace Drupal\rest\Tests;
use Drupal\Core\Url;
/**
* Tests the deletion of resources.
*
* @group rest
*/
class DeleteTest extends RESTTestBase {
/**
* Modules to install.
*
* @var array
*/
public static $modules = array('hal', 'rest', 'entity_test', 'node');
/**
* Tests several valid and invalid delete requests on all entity types.
*/
public function testDelete() {
// Define the entity types we want to test.
// @todo expand this test to at least users once their access
// controllers are implemented.
$entity_types = array('entity_test', 'node');
foreach ($entity_types as $entity_type) {
$this->enableService('entity:' . $entity_type, 'DELETE');
// Create a user account that has the required permissions to delete
// resources via the REST API.
$permissions = $this->entityPermissions($entity_type, 'delete');
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
// Create an entity programmatically.
$entity = $this->entityCreate($entity_type);
$entity->save();
// Try first to delete over REST API without the CSRF token.
$url = $entity->toUrl()->setRouteParameter('_format', $this->defaultFormat);
$this->httpRequest($url, 'DELETE', NULL, 'application/hal+json', FALSE);
$this->assertResponse(403);
$this->assertRaw('X-CSRF-Token request header is missing');
// Then try with an invalid CSRF token.
$this->httpRequest($url, 'DELETE', NULL, 'application/hal+json', 'invalid-csrf-token');
$this->assertResponse(403);
$this->assertRaw('X-CSRF-Token request header is invalid');
// Delete it over the REST API.
$response = $this->httpRequest($url, 'DELETE');
$this->assertResponse(204);
// Clear the static cache with entity_load(), otherwise we won't see the
// update.
$storage = $this->container->get('entity_type.manager')
->getStorage($entity_type);
$storage->resetCache([$entity->id()]);
$entity = $storage->load($entity->id());
$this->assertFalse($entity, $entity_type . ' entity is not in the DB anymore.');
$this->assertResponse('204', 'HTTP response code is correct.');
$this->assertEqual($response, '', 'Response body is empty.');
// Try to delete an entity that does not exist.
$response = $this->httpRequest(Url::fromRoute('entity.' . $entity_type . '.canonical', [$entity_type => 9999]), 'DELETE');
$this->assertResponse(404);
$this->assertText('The requested page could not be found.');
// Try to delete an entity without proper permissions.
$this->drupalLogout();
// Re-save entity to the database.
$entity = $this->entityCreate($entity_type);
$entity->save();
$this->httpRequest($entity->urlInfo(), 'DELETE');
$this->assertResponse(403);
$storage->resetCache([$entity->id()]);
$this->assertNotIdentical(FALSE, $storage->load($entity->id()),
'The ' . $entity_type . ' entity is still in the database.');
}
// Try to delete a resource which is not REST API enabled.
$this->enableService(FALSE);
$account = $this->drupalCreateUser();
$this->drupalLogin($account);
$this->httpRequest($account->urlInfo(), 'DELETE');
$user_storage = $this->container->get('entity.manager')->getStorage('user');
$user_storage->resetCache(array($account->id()));
$user = $user_storage->load($account->id());
$this->assertEqual($account->id(), $user->id(), 'User still exists in the database.');
$this->assertResponse(405);
}
}
<?php
namespace Drupal\rest\Tests;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
/**
* Tests special cases for node entities.
*
* @group rest
*/
class NodeTest extends RESTTestBase {
/**
* Modules to install.
*
* Ensure that the node resource works with comment module enabled.
*
* @var array
*/
public static $modules = array('hal', 'rest', 'comment', 'node');
/**
* Enables node specific REST API configuration and authentication.
*
* @param string $method
* The HTTP method to be tested.
* @param string $operation
* The operation, one of 'view', 'create', 'update' or 'delete'.
*/
protected function enableNodeConfiguration($method, $operation) {
$this->enableService('entity:node', $method);
$permissions = $this->entityPermissions('node', $operation);
$account = $this->drupalCreateUser($permissions);
$this->drupalLogin($account);
}
/**
* Serializes and attempts to create a node via a REST "post" http request.
*
* @param array $data
*/
protected function postNode($data) {
// Enable node creation via POST.
$this->enableNodeConfiguration('POST', 'create');
$this->enableService('entity:node', 'POST', 'json');
// Create a JSON version of a simple node with the title.
$serialized = $this->container->get('serializer')->serialize($data, 'json');
// Post to the REST service to create the node.
$this->httpRequest('/entity/node', 'POST', $serialized, 'application/json');
}
/**
* Tests the title on a newly created node.
*
* @param array $data
* @return \Drupal\node\Entity\Node
*/
protected function assertNodeTitleMatch($data) {
/** @var \Drupal\node\Entity\Node $node */
// Load the newly created node.
$node = Node::load(1);