You are here

restws.test in RESTful Web Services 7.2

Same filename and directory in other branches
  1. 7 restws.test

RESTful web services tests.

File

restws.test
View source
<?php

/**
 * @file
 * RESTful web services tests.
 */
class RestWSTestCase extends DrupalWebTestCase {
  public static function getInfo() {
    return array(
      'name' => 'RESTful web services tests',
      'description' => 'Tests CRUD operations via the REST web service.',
      'group' => 'Services',
    );
  }
  public function setUp() {
    parent::setUp('restws');
  }

  /**
   * CRUD tests for nodes.
   */
  public function testCRUD() {

    // Test Read.
    $title = $this
      ->randomName(8);
    $node = $this
      ->drupalCreateNode(array(
      'title' => $title,
    ));
    $account = $this
      ->drupalCreateUser(array(
      'access resource node',
    ));
    $this
      ->drupalLogin($account);
    $result = $this
      ->httpRequest('node/' . $node->nid . '.json', 'GET', $account);
    $node_array = drupal_json_decode($result);
    $this
      ->assertEqual($node->title, $node_array['title'], 'Node title was received correctly.');
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');

    // Test Create.
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
    ));
    $title = $this
      ->randomName(8);
    $new_node = array(
      'body' => array(
        LANGUAGE_NONE => array(
          array(),
        ),
      ),
      'title' => $title,
      'type' => 'page',
      'author' => $account->uid,
    );
    $json = drupal_json_encode($new_node);
    $result = $this
      ->httpRequest('node', 'POST', $account, $json);
    $result_array = drupal_json_decode($result);
    $nid = $result_array['id'];
    $node = node_load($nid);
    $this
      ->assertEqual($title, $node->title, 'Node title in DB is equal to the new title.');
    $this
      ->assertResponse('201', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');

    // Test Update.
    $new_title = $this
      ->randomName(8);
    $json = drupal_json_encode(array(
      'title' => $new_title,
    ));
    $this
      ->httpRequest('node/' . $node->nid, 'PUT', $account, $json);

    // Clear the static cache, otherwise we won't see the update.
    $node = node_load($node->nid, NULL, TRUE);
    $this
      ->assertEqual($new_title, $node->title, 'Node title in DB is equal to the updated title.');
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');

    // Test delete.
    $this
      ->httpRequest('node/' . $node->nid, 'DELETE', $account);

    // Clear the static cache, otherwise we won't see the update.
    $node = node_load($node->nid, NULL, TRUE);
    $this
      ->assertFalse($node, 'Node is not in the DB anymore.');
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');
  }

  /**
   * Tests bad requests.
   */
  public function testBadRequests() {

    // Assure that nodes without types won't be created.
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
      'administer users',
    ));
    $title = $this
      ->randomName(8);
    $new_node = array(
      'body' => array(
        LANGUAGE_NONE => array(
          array(),
        ),
      ),
      'title' => $title,
    );
    $json = drupal_json_encode($new_node);
    $result = $this
      ->httpRequest('node', 'POST', $account, $json);
    $node = entity_load('node', FALSE, array(
      'title' => $title,
    ));
    $this
      ->assertEqual(count($node), 0, "Node wasn't created");
    $this
      ->assertResponse('406', 'Missing bundle: type');
  }

  /**
   * Tests access to restricted input formats.
   */
  public function testBadInputFormat() {
    module_enable(array(
      'php',
    ));

    // Reset the cache of valid permissions so that the PHP code format
    // permission exists.
    $this
      ->checkPermissions(array(), TRUE);

    // Assure that users can't create nodes with unauthorized input formats.
    $unprivileged_account = $this
      ->drupalCreateUser(array(
      'bypass node access',
      'access resource node',
    ));
    $title = $this
      ->randomName(8);
    $new_node = array(
      'body' => array(
        'value' => $this
          ->randomName(30),
        'format' => 'php_code',
      ),
      'title' => $title,
      'type' => 'page',
    );
    $json = drupal_json_encode($new_node);
    $result = $this
      ->httpRequest('node', 'POST', $unprivileged_account, $json);
    $this
      ->assertResponse('403');
    $this
      ->assertEqual($result, '403 Forbidden: Not authorized to set property body');
    $node = entity_load('node', FALSE, array(
      'title' => $title,
    ));
    $this
      ->assertEqual(count($node), 0, "Node with unauthorized input format wasn't created");

    // Check that the format is allowed if the permission is present.
    $privileged_account = $this
      ->drupalCreateUser(array(
      'bypass node access',
      'access resource node',
      'use text format php_code',
    ));
    $this
      ->httpRequest('node', 'POST', $privileged_account, $json);
    $this
      ->assertResponse('201');
    $node = entity_load('node', FALSE, array(
      'title' => $title,
    ));
    $this
      ->assertEqual(count($node), 1, "Node was created");
    $node = reset($node);
    $this
      ->assertEqual($node->body[LANGUAGE_NONE][0]['value'], $new_node['body']['value'], 'The new node body has the correct value');
    $this
      ->assertEqual($node->body[LANGUAGE_NONE][0]['format'], 'php_code', 'The new node has the correct format');

    // Check that users can't update nodes with unauthorized input formats.
    $node->body[LANGUAGE_NONE][0]['format'] = 'filtered_html';
    node_save($node);
    $new_body = $this
      ->randomName(30);
    $update = array(
      'body' => array(
        'value' => $new_body,
        'format' => 'php_code',
      ),
    );
    $json = drupal_json_encode($update);
    $result = $this
      ->httpRequest('node/1', 'PUT', $unprivileged_account, $json);
    $this
      ->assertResponse('403');
    $this
      ->assertEqual($result, '403 Forbidden: Not authorized to set property body');
    $node = node_load(1, NULL, TRUE);
    $this
      ->assertNotEqual($node->body[LANGUAGE_NONE][0]['value'], $new_body);
    $this
      ->assertEqual($node->body[LANGUAGE_NONE][0]['format'], 'filtered_html');

    // Check that the format is allowed if the permission is present.
    $this
      ->httpRequest('node/1', 'PUT', $privileged_account, $json);
    $this
      ->assertResponse('200');
    $node = node_load(1, NULL, TRUE);
    $this
      ->assertEqual($node->body[LANGUAGE_NONE][0]['value'], $new_body);
    $this
      ->assertEqual($node->body[LANGUAGE_NONE][0]['format'], 'php_code');
  }

  /**
   * Test field level access restrictions.
   *
   * @see restws_test_field_access()
   */
  public function testFieldAccess() {
    module_enable(array(
      'restws_test',
    ));

    // Add text field to nodes.
    $field_info = array(
      'field_name' => 'field_text',
      'type' => 'text',
      'entity_types' => array(
        'node',
      ),
    );
    field_create_field($field_info);
    $instance = array(
      'label' => 'Text Field',
      'field_name' => 'field_text',
      'entity_type' => 'node',
      'bundle' => 'page',
      'settings' => array(),
      'required' => FALSE,
    );
    field_create_instance($instance);

    // A user without the "administer users" permission should not be able to
    // create a node with the access protected field.
    $unprivileged_account = $this
      ->drupalCreateUser(array(
      'bypass node access',
      'access resource node',
    ));
    $title = $this
      ->randomName(8);
    $new_node = array(
      'title' => $title,
      'type' => 'page',
      'field_text' => 'test',
    );
    $json = drupal_json_encode($new_node);
    $this
      ->httpRequest('node', 'POST', $unprivileged_account, $json);
    $this
      ->assertResponse('403');
    $nodes = entity_load('node', FALSE, array(
      'title' => $title,
    ));
    $this
      ->assertEqual(count($nodes), 0, "Node with access protected field wasn't created");

    // Test again with the additional permission, this should work now.
    $privileged_account = $this
      ->drupalCreateUser(array(
      'bypass node access',
      'access resource node',
      'administer users',
    ));
    $this
      ->httpRequest('node', 'POST', $privileged_account, $json);
    $this
      ->assertResponse('201');
    $node = node_load(1, NULL, TRUE);
    $this
      ->assertEqual($node->field_text[LANGUAGE_NONE][0]['value'], 'test');

    // Update test: unpriviledged users should not be able to change the
    // protected field.
    $update = array(
      'field_text' => 'newvalue',
    );
    $json = drupal_json_encode($update);
    $result = $this
      ->httpRequest('node/1', 'PUT', $unprivileged_account, $json);
    $this
      ->assertResponse('403');
    $this
      ->assertEqual($result, '403 Forbidden: Not authorized to set property field_text');
    $node = node_load(1, NULL, TRUE);
    $this
      ->assertEqual($node->field_text[LANGUAGE_NONE][0]['value'], 'test');

    // Check that the update is allowed if the permission is present.
    $this
      ->httpRequest('node/1', 'PUT', $privileged_account, $json);
    $this
      ->assertResponse('200');
    $node = node_load(1, NULL, TRUE);
    $this
      ->assertEqual($node->field_text[LANGUAGE_NONE][0]['value'], 'newvalue');
  }

  /**
   * Test entity references with an array which contains id, entity type.
   */
  public function testResourceArray() {
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
    ));
    $this
      ->drupalLogin($account);
    $this
      ->createTerm("foo");
    $this
      ->createTerm("bar");

    // Test json create.
    $title = $this
      ->randomName(8);
    $new_node = array(
      'body' => array(
        LANGUAGE_NONE => array(
          array(),
        ),
      ),
      'type' => 'article',
      'title' => 'foo',
      'field_tags' => array(
        array(
          'id' => '2',
          'resource' => 'taxonomy_term',
        ),
        array(
          'id' => '1',
          'resource' => 'taxonomy_term',
        ),
      ),
      'author' => array(
        'id' => $account->uid,
        'resource' => 'user',
      ),
    );
    $json = drupal_json_encode($new_node);
    $result = $this
      ->httpRequest('node', 'POST', $account, $json);
    $result_array = drupal_json_decode($result);
    $nid = $result_array['id'];
    $node = node_load($nid);
    $this
      ->assertEqual($node->field_tags[LANGUAGE_NONE][0]['tid'], 2, 'Taxonomy term 1 was correctly added.');
    $this
      ->assertEqual($node->field_tags[LANGUAGE_NONE][1]['tid'], 1, 'Taxonomy term 2 was correctly added.');

    // Test XML update.
    $xml = '
      <node>
        <title>bar</title>
        <type>article</type>
        <author resource="user" id="' . $account->uid . '">' . restws_resource_uri('user', $account->uid) . '</author>
        <field_tags>
          <item resource="taxonomy_term" id="1">' . restws_resource_uri('taxonomy_term', 1) . '</item>
          <item resource="taxonomy_term" id="2">' . restws_resource_uri('taxonomy_term', 2) . '</item>
        </field_tags>
      </node>';
    $result = $this
      ->httpRequest('node/' . $nid, 'PUT', $account, $xml, 'xml');
    $node = node_load($nid, NULL, TRUE);
    $this
      ->assertEqual($node->field_tags[LANGUAGE_NONE][0]['tid'], 1, 'Taxonomy term 1 was correctly updated.');
    $this
      ->assertEqual($node->field_tags[LANGUAGE_NONE][1]['tid'], 2, 'Taxonomy term 2 was correctly updated.');

    // Test XML create.
    $result = $this
      ->httpRequest('node', 'POST', $account, $xml, 'xml');
    $xml_element = simplexml_load_string($result);
    $nid = $xml_element
      ->attributes()->id;
    $node = node_load((int) $nid, NULL, TRUE);
    $this
      ->assertEqual($node->field_tags[LANGUAGE_NONE][0]['tid'], 1, 'Taxonomy term 1 was correctly added.');
    $this
      ->assertEqual($node->field_tags[LANGUAGE_NONE][1]['tid'], 2, 'Taxonomy term 2 was correctly added.');
  }

  /**
   * Tests using the xml formatter.
   */
  public function testXmlFormatter() {

    // Test Read.
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
    ));
    $this
      ->drupalLogin($account);
    $title = $this
      ->randomName(8);
    $node = $this
      ->drupalCreateNode(array(
      'title' => $title,
    ));
    $result = $this
      ->drupalGet("node/{$node->nid}", array(), array(
      'Accept: application/xml',
    ));
    $this
      ->assertRaw("<title>{$title}</title>", 'XML has been generated.');

    // Test update.
    $new_title = 'foo';
    $result = $this
      ->httpRequest('node/' . $node->nid, 'PUT', $account, "<node><title>{$new_title}</title></node>", 'xml');

    // Clear the static cache, otherwise we won't see the update.
    $node = node_load($node->nid, NULL, TRUE);
    $this
      ->assertEqual($new_title, $node->title, 'Node title in DB is equal to the updated title.');
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/xml', 'HTTP content type is correct.');
  }

  /**
   * Test requests to non-existing resources and other errors.
   */
  public function testErrors() {

    // Read non-existing resource.
    $random_nid = rand(1, 1000);
    $result = $this
      ->httpRequest('node/' . $random_nid, 'GET');
    $this
      ->assertResponse('404', 'HTTP response code is correct.');

    // Update a node with an unknown property.
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
    ));
    $node = $this
      ->drupalCreateNode();
    $property_name = $this
      ->randomName(8);
    $json = drupal_json_encode(array(
      $property_name => $property_name,
    ));
    $result = $this
      ->httpRequest('node/' . $node->nid, 'PUT', $account, $json);
    $this
      ->assertEqual($result, "406 Not Acceptable: Unknown data property {$property_name}.", 'Response body is correct');
    $this
      ->assertResponse('406', 'HTTP response code is correct.');

    // Create a node with an unknown property.
    $title = $this
      ->randomName(8);
    $new_node = array(
      'body' => array(
        LANGUAGE_NONE => array(
          array(),
        ),
      ),
      'title' => $this
        ->randomName(8),
      'type' => 'page',
      'author' => $account->uid,
      $property_name => $property_name,
    );
    $json = drupal_json_encode($new_node);
    $result = $this
      ->httpRequest('node', 'POST', $account, $json);
    $this
      ->assertEqual($result, "406 Not Acceptable: Unknown data property {$property_name}.", 'Response body is correct');
    $this
      ->assertResponse('406', 'HTTP response code is correct.');

    // Simulate a CSRF attack without the required token.
    $new_title = 'HACKED!';
    $json = drupal_json_encode(array(
      'title' => $new_title,
    ));
    $this
      ->curlExec(array(
      CURLOPT_HTTPGET => FALSE,
      CURLOPT_POST => TRUE,
      CURLOPT_CUSTOMREQUEST => 'POST',
      CURLOPT_POSTFIELDS => $json,
      CURLOPT_URL => url('node/' . $node->nid, array(
        'absolute' => TRUE,
      )),
      CURLOPT_NOBODY => FALSE,
      CURLOPT_HTTPHEADER => array(
        'Content-Type: application/json',
      ),
    ));
    $this
      ->assertResponse(403);

    // Clear the static cache, otherwise we won't see the update.
    $node = node_load($node->nid, NULL, TRUE);
    $this
      ->assertNotEqual($node->title, $new_title, 'Node title was not updated in the database.');

    // Simulate a cache poisoning attack where JSON could get into the page
    // cache.
    // Grant node resource access to anonymous users.
    user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array(
      'access resource node',
    ));

    // Enable page caching.
    variable_set('cache', 1);

    // Reset cURL here to delete any stored request settings.
    unset($this->curlHandle);

    // Request the JSON representation of the node.
    $this
      ->drupalGet("node/{$node->nid}", array(), array(
      'Accept: application/json',
    ));
    $this
      ->assertUrl("node/{$node->nid}.json", array(), 'Requesting a resource with JSON Accept header redirects to the .json URL.');

    // Now request the HTML representation.
    $result = $this
      ->drupalGet("node/{$node->nid}");
    $content_type = $this
      ->drupalGetHeader('content-type');
    $this
      ->assertNotEqual($content_type, 'application/json', 'Content type header is not JSON after requesting HTML.');
    $this
      ->assertNull(drupal_json_decode($result), 'Response body is not JSON after requesting HTML.');
  }

  /**
   * Tests resource querying.
   */
  public function testQuerying() {
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
    ));
    $this
      ->drupalLogin($account);
    $this
      ->createTerm('foo');
    $nodes = array();
    for ($i = 0; $i < 5; $i++) {
      $title = "node{$i}";
      $node = array(
        'title' => $title,
        'type' => 'article',
      );

      // Add tags to the nodes 0 and 3.
      if ($i % 3 == 0) {
        $node['field_tags'][LANGUAGE_NONE][]['tid'] = 1;
      }

      // Set a body and the format to full_html for nodes 0 and 4.
      if ($i % 4 == 0) {
        $node['body'] = array(
          LANGUAGE_NONE => array(
            array(
              'value' => l('foo', 'node'),
              'format' => 'full_html',
            ),
          ),
        );
      }
      $nodes[$i] = $this
        ->drupalCreateNode($node);
    }

    // Retrieve a list of nodes with json sorted by the title descending.
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'sort' => 'title',
      'direction' => 'DESC',
    ));
    $result_nodes = drupal_json_decode($result);

    // Start by checking if the last node created is the first in the result.
    $i = 4;
    foreach ($result_nodes['list'] as $key => $node) {
      $this
        ->assertEqual($nodes[$i]->title, $node['title'], "Node title {$key} was received correctly.");
      $i--;
    }
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');

    // Retrieve a list of nodes with xml.
    $result = $this
      ->drupalGet('node', array(), array(
      'Accept: application/xml',
    ));
    $this
      ->assertRaw('<list>', 'XML has been generated.');
    for ($i = 0; $i < 5; $i++) {
      $this
        ->assertRaw("<title>node{$i}</title>", 'XML has been generated.');
    }

    // Query for a node with the title 'title1'.
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'title' => 'node1',
    ));
    $node = drupal_json_decode($result);
    $this
      ->assertEqual($node['list'][0]['title'], 'node1', 'Node title was received correctly.');

    // Query for nodes with the taxonomy term foo which has the tid 1.
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'field_tags' => '1',
    ));
    $nodes = drupal_json_decode($result);
    $this
      ->assertEqual($nodes['list'][0]['title'], 'node0', 'Right node title was received.');
    $this
      ->assertEqual($nodes['list'][0]['field_tags'][0]['id'], 1, 'Node has taxonomy term.');
    $this
      ->assertEqual($nodes['list'][1]['title'], 'node3', 'Right node title was received.');
    $this
      ->assertEqual($nodes['list'][1]['field_tags'][0]['id'], 1, 'Node has taxonomy term.');

    // Test paging and limiting.
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'limit' => 2,
      'page' => 0,
    ));
    $result_nodes = drupal_json_decode($result);
    $this
      ->assertTrue(count($result_nodes['list'] > 2), 'Only two elements where returned');
    $this
      ->assertTrue($result_nodes['self'] == url('node', array(
      'absolute' => TRUE,
      'query' => array(
        'limit' => 2,
        'page' => 0,
      ),
    )), 'Self link was generated');
    $this
      ->assertTrue($result_nodes['first'] == url('node', array(
      'absolute' => TRUE,
      'query' => array(
        'limit' => 2,
        'page' => 0,
      ),
    )), 'First link was generated');
    $this
      ->assertTrue($result_nodes['last'] == url('node', array(
      'absolute' => TRUE,
      'query' => array(
        'limit' => 2,
        'page' => 2,
      ),
    )), 'Last link was generated');
    $this
      ->assertTrue($result_nodes['next'] == url('node', array(
      'absolute' => TRUE,
      'query' => array(
        'limit' => 2,
        'page' => 1,
      ),
    )), 'Next link was generated');
    $this
      ->assertFalse(isset($result_nodes['prev']), 'Prev link was not generated');
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'limit' => 2,
      'page' => 2,
    ));
    $result_nodes = drupal_json_decode($result);
    $this
      ->assertFalse(isset($result_nodes['next']), 'Next link was not generated');
    $this
      ->assertTrue($result_nodes['prev'] == url('node', array(
      'absolute' => TRUE,
      'query' => array(
        'limit' => 2,
        'page' => 1,
      ),
    )), 'Prev link was generated');
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'limit' => 2,
      'page' => 5,
    ));
    $this
      ->assertResponse('404', 'HTTP response code is correct.');

    // Test meta control full.
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'full' => 0,
    ));
    $result_nodes = drupal_json_decode($result);
    foreach ($result_nodes['list'] as $node) {
      $this
        ->assertTrue($node['uri'] == restws_resource_uri('node', $node['id']), 'Rerence to node ' . $node['id'] . ' was received correctly.');
    }

    // Test field column queries.
    $result = $this
      ->httpRequest('node.json', 'GET', $account, array(
      'body[format]' => 'full_html',
    ));
    $result_nodes = drupal_json_decode($result);
    $this
      ->assertEqual($result_nodes['list'][0]['title'], 'node0', 'Right node title was received.');
    $this
      ->assertEqual($result_nodes['list'][0]['body']['format'], 'full_html', 'Node has body with full_html.');
    $this
      ->assertEqual($result_nodes['list'][1]['title'], 'node4', 'Right node title was received.');
    $this
      ->assertEqual($result_nodes['list'][1]['body']['format'], 'full_html', 'Node has body with full_html.');

    // Test SQL injection via order direction.
    $this
      ->httpRequest('node.json', 'GET', $account, array(
      'sort' => 'title',
      'direction' => 'ASC; DELETE FROM ' . $this->databasePrefix . 'node WHERE nid = 1; --',
    ));
    $node = node_load(1, NULL, TRUE);
    $this
      ->assertNotEqual($node, FALSE, 'Node has not been deleted through SQL injection.');
  }

  /**
   * Test that sensitive user data is hidden for the "access user profiles"
   * permission and unpublished nodes.
   */
  public function testPermissions() {

    // Test other user with "access user profiles" permission.
    $test_user = $this
      ->drupalCreateUser();
    $account = $this
      ->drupalCreateUser(array(
      'access resource user',
      'access user profiles',
    ));
    $result = $this
      ->httpRequest('user/' . $test_user->uid . '.json', 'GET', $account);
    $user_array = drupal_json_decode($result);
    $this
      ->assertEqual($test_user->name, $user_array['name'], 'User name was received correctly.');
    $this
      ->assertFalse(isset($user_array['mail']), 'User mail is not present in the response.');
    $this
      ->assertFalse(isset($user_array['roles']), 'User roles are not present in the response.');
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');

    // Test the own user - access to sensitive information should be allowed.
    $result = $this
      ->httpRequest('user/' . $account->uid . '.json', 'GET', $account);
    $user_array = drupal_json_decode($result);
    $this
      ->assertEqual($account->name, $user_array['name'], 'User name was received correctly.');
    $this
      ->assertEqual($account->mail, $user_array['mail'], 'User mail is present in the response.');
    $role_keys = array_keys($account->roles);
    $this
      ->assertEqual(sort($role_keys), sort($user_array['roles']), 'User roles are present in the response.');
    $this
      ->assertResponse('200', 'HTTP response code is correct.');
    $this
      ->assertEqual(curl_getinfo($this->curlHandle, CURLINFO_CONTENT_TYPE), 'application/json', 'HTTP content type is correct.');

    // Test node access with an unpublished node.
    $this
      ->drupalCreateNode(array(
      'title' => 'foo',
      'status' => 0,
    ));
    $this
      ->drupalLogout();
    $account = $this
      ->drupalCreateUser(array(
      'access resource node',
    ));
    $this
      ->drupalLogin($account);
    $result = $this
      ->httpRequest('node.json', 'GET', $account);
    $nodes = drupal_json_decode($result);

    // No node should be returned.
    $this
      ->assertEqual(count($nodes['list']), 0, 'Unpublished node was successfully hidden.');
    $this
      ->assertNoResponse(404, 'An empty collection should not cause a 404 response.');
  }

  /**
   * Test menu path resource setting.
   */
  public function testMenuPath() {
    $account = $this
      ->drupalCreateUser(array(
      'access content',
      'bypass node access',
      'access resource node',
    ));
    $this
      ->drupalLogin($account);
    $title = $this
      ->randomName(8);
    $node = $this
      ->drupalCreateNode(array(
      'title' => $title,
    ));
    module_enable(array(
      'restws_test',
    ), TRUE);
    foreach (array(
      'bar',
      'foo/bar',
      'foo/bar/node',
      'api-v1.2',
      'this/is/a/really/long/path',
    ) as $menu_path) {

      // Register the menu_path for this test.
      variable_set('restws_test_menu_path', $menu_path);
      variable_set('menu_rebuild_needed', TRUE);

      // Verify that the query uri is working.
      $result = $this
        ->drupalGet($menu_path . '.json');
      $this
        ->assertResponse(200);
      $result = drupal_json_decode($result);
      $this
        ->assertEqual($result['list'][0]['title'], $title);

      // Verify that the view uri is working.
      $result = $this
        ->drupalGet($menu_path . '/1.json');
      $this
        ->assertResponse(200);
      $result = drupal_json_decode($result);
      $this
        ->assertEqual($result['title'], $title);
    }
  }

  /**
   * Creates a term.
   */
  protected function createTerm($term_name) {
    $term = new stdClass();
    $term->name = $term_name;
    $term->vid = 1;
    taxonomy_term_save($term);
  }

  /**
   * Helper function to issue a HTTP request with simpletest's cURL.
   *
   * @param array $body
   *   Either the body for POST and PUT or additional URL parameters for GET.
   */
  protected function httpRequest($url, $method, $account = NULL, $body = NULL, $format = 'json') {
    if (isset($account)) {
      unset($this->curlHandle);
      $this
        ->drupalLogin($account);
    }
    if (in_array($method, array(
      'POST',
      'PUT',
      'DELETE',
    ))) {

      // GET the CSRF token first for writing requests.
      $token = $this
        ->drupalGet('restws/session/token');
    }
    switch ($method) {
      case 'GET':

        // Set query if there are addition GET parameters.
        $options = isset($body) ? array(
          'absolute' => TRUE,
          'query' => $body,
        ) : array(
          'absolute' => TRUE,
        );
        $curl_options = array(
          CURLOPT_HTTPGET => TRUE,
          CURLOPT_URL => url($url, $options),
          CURLOPT_NOBODY => FALSE,
        );
        break;
      case 'POST':
        $curl_options = array(
          CURLOPT_HTTPGET => FALSE,
          CURLOPT_POST => TRUE,
          CURLOPT_POSTFIELDS => $body,
          CURLOPT_URL => url($url, array(
            'absolute' => TRUE,
          )),
          CURLOPT_NOBODY => FALSE,
          CURLOPT_HTTPHEADER => array(
            'Content-Type: application/' . $format,
            'X-CSRF-Token: ' . $token,
          ),
        );
        break;
      case 'PUT':
        $curl_options = array(
          CURLOPT_HTTPGET => FALSE,
          CURLOPT_CUSTOMREQUEST => 'PUT',
          CURLOPT_POSTFIELDS => $body,
          CURLOPT_URL => url($url, array(
            'absolute' => TRUE,
          )),
          CURLOPT_NOBODY => FALSE,
          CURLOPT_HTTPHEADER => array(
            'Content-Type: application/' . $format,
            'X-CSRF-Token: ' . $token,
          ),
        );
        break;
      case 'DELETE':
        $curl_options = array(
          CURLOPT_HTTPGET => FALSE,
          CURLOPT_CUSTOMREQUEST => 'DELETE',
          CURLOPT_URL => url($url, array(
            'absolute' => TRUE,
          )),
          CURLOPT_NOBODY => FALSE,
          CURLOPT_HTTPHEADER => array(
            'X-CSRF-Token: ' . $token,
          ),
        );
        break;
    }
    $response = $this
      ->curlExec($curl_options);
    $headers = $this
      ->drupalGetHeaders();
    $headers = implode("\n", $headers);
    $this
      ->verbose($method . ' request to: ' . $url . '<hr />Code: ' . curl_getinfo($this->curlHandle, CURLINFO_HTTP_CODE) . '<hr />Response headers: ' . $headers . '<hr />Response body: ' . $response);
    return $response;
  }

}

Classes

Namesort descending Description
RestWSTestCase @file RESTful web services tests.