From 24915867a7369fe1055dbd67197c00a4a23490f3 Mon Sep 17 00:00:00 2001
From: Kyle Browning <kylebrowning@me.com>
Date: Wed, 20 Apr 2011 10:57:20 -0700
Subject: [PATCH] Index resource Fixes

---
 resources/comment_resource.inc                     |   79 +++++++++++--
 resources/file_resource.inc                        |   70 ++++++++++-
 resources/node_resource.inc                        |   30 +----
 resources/taxonomy_resource.inc                    |  134 ++++++++++++++++++++
 resources/user_resource.inc                        |   31 ++----
 services.module                                    |   51 ++++++++
 tests/functional/ServicesResourceCommentTests.test |   49 +++++++
 .../functional/ServicesResourceTaxonomyTests.test  |   93 ++++++++++++++
 tests/functional/ServicesResourceUserTests.test    |    2 +-
 tests/services.test                                |   12 ++
 10 files changed, 492 insertions(+), 59 deletions(-)

diff --git a/resources/comment_resource.inc b/resources/comment_resource.inc
index 949d033..e28f725 100644
--- a/resources/comment_resource.inc
+++ b/resources/comment_resource.inc
@@ -85,6 +85,39 @@ function _comment_resource_definition() {
           ),
         ),
       ),
+      'index' => array(
+        'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/comment_resource'),
+        'callback' => '_comment_resource_index',
+        'args' => array(
+          array(
+            'name' => 'page',
+            'optional' => TRUE,
+            'type' => 'int',
+            'description' => 'The zero-based index of the page to get, defaults to 0.',
+            'default value' => 0,
+            'source' => array('param' => 'page'),
+          ),
+          array(
+            'name' => 'fields',
+            'optional' => TRUE,
+            'type' => 'string',
+            'description' => 'The fields to get.',
+            'default value' => '*',
+            'source' => array('param' => 'fields'),
+          ),
+          array(
+            'name' => 'parameters',
+            'optional' => TRUE,
+            'type' => 'array',
+            'description' => 'Parameters',
+            'default value' => NULL,
+            'source' => array('param' => 'parameters'),
+          ),
+        ),
+        'access callback' => '_comment_resource_access',
+        'access arguments' => array('view'),
+        'access arguments append' => TRUE,
+      ),
       'actions' => array(
         'countAll' => array(
           'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/comment_resource'),
@@ -236,6 +269,42 @@ function _comment_resource_delete($cid) {
   cache_clear_all();
   return TRUE;
 }
+/**
+ * Return an array of optionally paged cids baed on a set of criteria.
+ *
+ * An example request might look like
+ *
+ * http://domain/endpoint/comment?fields=cid,nid&parameters[nid]=7&parameters[uid]=2
+ *
+ * This would return an array of objects with only cid and nid defined, where
+ * nid = 7 and uid = 1.
+ *
+ * @param $page
+ *   Page number of results to return (in pages of 20).
+ * @param $fields
+ *   The fields you want returned.
+ * @param $parameters
+ *   An array of fields and values used to build a sql WHERE clause indicating
+ *   what items should be deleted.
+ * @return
+ *   An array of comment objects.
+ *
+ * @see _node_resource_index() for more notes
+ **/
+function _comment_resource_index($page, $fields, $parameters) {
+  $comment_select = db_select('comment', 't')
+    ->orderBy('created', 'DESC');
+
+  services_resource_build_index_query($comment_select, $page, $fields, $parameters);
+
+  if (!user_access('administer comments')) {
+    $comment_select->condition('status', COMMENT_PUBLISHED);
+  }
+
+  $results = $comment_select->execute();
+
+  return services_resource_build_index_list($results, 'comment', 'cid');
+}
 
 /**
  * Returns the number of comments on a given node id.
@@ -278,19 +347,13 @@ function _comment_resource_access($op = 'view', $args = array()) {
   else {
     $comment = comment_load($args[0]);
   }
-
-  // If the submitted comment does not contain a nid, then return an error.
-  if (!isset($comment->nid)) {
-    return services_error(t("No node specified."));
-  }
-
+  if(isset($comment->nid))
   $node = node_load($comment->nid);
 
   switch ($op) {
     case 'view':
       // Check if the user has access to comments
-      // and that the node has comments enabled.
-      return $comment->status && user_access('access comments');
+      return user_access('access comments');
     case 'create':
     case 'delete':
     case 'edit':
diff --git a/resources/file_resource.inc b/resources/file_resource.inc
index a3580ed..ab71727 100644
--- a/resources/file_resource.inc
+++ b/resources/file_resource.inc
@@ -13,7 +13,6 @@
 function _file_resource_definition() {
   return array(
     'file' => array(
-
       'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/file_resource'),
       'create' => array(
         'help' => 'Creates a file',
@@ -31,7 +30,6 @@ function _file_resource_definition() {
           ),
         ),
       ),
-
       'retrieve' => array(
         'help' => 'Retrieves a file',
         'callback' => '_file_resource_retrieve',
@@ -56,7 +54,6 @@ function _file_resource_definition() {
           ),
         ),
       ),
-
       'delete' => array(
         'help' => 'Deletes a file',
         'callback' => '_file_resource_delete',
@@ -73,6 +70,39 @@ function _file_resource_definition() {
           ),
         ),
       ),
+      'index' => array(
+        'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/file_resource'),
+        'callback' => '_file_resource_index',
+        'args' => array(
+          array(
+            'name' => 'page',
+            'optional' => TRUE,
+            'type' => 'int',
+            'description' => 'The zero-based index of the page to get, defaults to 0.',
+            'default value' => 0,
+            'source' => array('param' => 'page'),
+          ),
+          array(
+            'name' => 'fields',
+            'optional' => TRUE,
+            'type' => 'string',
+            'description' => 'The fields to get.',
+            'default value' => '*',
+            'source' => array('param' => 'fields'),
+          ),
+          array(
+            'name' => 'parameters',
+            'optional' => TRUE,
+            'type' => 'array',
+            'description' => 'Parameters',
+            'default value' => NULL,
+            'source' => array('param' => 'parameters'),
+          ),
+        ),
+        'access callback' => '_file_resource_access',
+        'access arguments' => array('view'),
+        'access arguments append' => TRUE,
+      ),
     ),
   );
 }
@@ -171,6 +201,40 @@ function _file_resource_delete($fid) {
 }
 
 /**
+ * Return an array of optionally paged fids baed on a set of criteria.
+ *
+ * An example request might look like
+ *
+ * http://domain/endpoint/file?fields=fid,filename&parameters[fid]=7&parameters[uid]=1
+ *
+ * This would return an array of objects with only fid and filename defined, where
+ * fid = 7 and uid = 1.
+ *
+ * @param $page
+ *   Page number of results to return (in pages of 20).
+ * @param $fields
+ *   The fields you want returned.
+ * @param $parameters
+ *   An array of fields and values used to build a sql WHERE clause indicating
+ *   what items should be deleted.
+ * @return
+ *   An array of file objects.
+ *
+ * @see _node_resource_index() for more notes
+ **/
+function _file_resource_index($page, $fields, $parameters) {
+  $file_select = db_select('files', 't')
+    ->orderBy('timestamp', 'DESC');
+
+  services_resource_build_index_query($file_select, $page, $fields, $parameters);
+
+  $results = $file_select->execute();
+
+  // Put together array of matching files to return.
+  return services_resource_build_index_list($results, 'file', 'fid');
+}
+
+/**
  * Access check callback for file controllers.
  */
 function _file_resource_access($op = 'view', $args = array()) {
diff --git a/resources/node_resource.inc b/resources/node_resource.inc
index 5f7872f..4c7cd14 100644
--- a/resources/node_resource.inc
+++ b/resources/node_resource.inc
@@ -335,39 +335,21 @@ function _node_resource_delete($nid) {
  *       for index requests more straightforward?
  */
 function _node_resource_index($page, $fields, $parameters) {
-  $node_select = db_select('node', 'n')
+  module_load_include('inc', 'services', 'services.module');
+  $node_select = db_select('node', 't')
     ->addTag('node_access')
     ->orderBy('sticky', 'DESC')
-    ->orderBy('created', 'DESC')
-    ->range($page * 20, 20);
+    ->orderBy('created', 'DESC');
 
-  if ($fields == '*') {
-    $node_select->fields('n');
-  }
-  else {
-    $fields = explode(',', $fields);
-    $node_select->fields('n', $fields);
-  }
+  services_resource_build_index_query($node_select, $page, $fields, $parameters);
 
   if (!user_access('administer nodes')) {
     $node_select->condition('status', 1);
   }
 
-  foreach ($parameters as $parameter => $parameter_value) {
-    $node_select->condition($parameter, $parameter_value, '=');
-  }
-
-  $result = $node_select->execute();
-
-  $nodes = array();
-  foreach ($result as $node) {
-    if ($uri = services_resource_uri(array('node', $node->nid))) {
-      $node->uri = $uri;
-    }
-    $nodes[] = $node;
-  }
+  $results = $node_select->execute();
 
-  return $nodes;
+  return services_resource_build_index_list($results, 'node', 'nid');
 }
 
 /**
diff --git a/resources/taxonomy_resource.inc b/resources/taxonomy_resource.inc
index 39c8e55..1fd98f8 100644
--- a/resources/taxonomy_resource.inc
+++ b/resources/taxonomy_resource.inc
@@ -76,6 +76,37 @@ function _taxonomy_resource_definition() {
         'access arguments append' => TRUE,
         'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/taxonomy_resource'),
       ),
+      'index' => array(
+        'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/taxonomy_resource'),
+        'callback' => '_taxonomy_term_resource_index',
+        'args' => array(
+          array(
+            'name' => 'page',
+            'optional' => TRUE,
+            'type' => 'int',
+            'description' => 'The zero-based index of the page to get, defaults to 0.',
+            'default value' => 0,
+            'source' => array('param' => 'page'),
+          ),
+          array(
+            'name' => 'fields',
+            'optional' => TRUE,
+            'type' => 'string',
+            'description' => 'The fields to get.',
+            'default value' => '*',
+            'source' => array('param' => 'fields'),
+          ),
+          array(
+            'name' => 'parameters',
+            'optional' => TRUE,
+            'type' => 'array',
+            'description' => 'Parameters',
+            'default value' => NULL,
+            'source' => array('param' => 'parameters'),
+          ),
+        ),
+        'access arguments' => array('access content'),
+      ),
       'actions' => array(
         'selectNodes' => array(
           'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/taxonomy_resource'),
@@ -182,6 +213,37 @@ function _taxonomy_resource_definition() {
         'access arguments' => array('administer taxonomy'),
         'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/taxonomy_resource'),
       ),
+      'index' => array(
+        'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/taxonomy_resource'),
+        'callback' => '_taxonomy_vocabulary_resource_index',
+        'args' => array(
+          array(
+            'name' => 'page',
+            'optional' => TRUE,
+            'type' => 'int',
+            'description' => 'The zero-based index of the page to get, defaults to 0.',
+            'default value' => 0,
+            'source' => array('param' => 'page'),
+          ),
+          array(
+            'name' => 'fields',
+            'optional' => TRUE,
+            'type' => 'string',
+            'description' => 'The fields to get.',
+            'default value' => '*',
+            'source' => array('param' => 'fields'),
+          ),
+          array(
+            'name' => 'parameters',
+            'optional' => TRUE,
+            'type' => 'array',
+            'description' => 'Parameters',
+            'default value' => NULL,
+            'source' => array('param' => 'parameters'),
+          ),
+        ),
+        'access arguments' => array('access content'),
+      ),
       'actions' => array(
         'getTree' => array(
           'file' => array('type' => 'inc', 'module' => 'services', 'name' => 'resources/taxonomy_resource'),
@@ -445,3 +507,75 @@ function _taxonomy_resource_delete_access($tid) {
   }
   return user_access('delete terms in ' . $term->vid) || user_access('administer taxonomy');
 }
+
+/**
+ * Return an array of optionally paged tids baed on a set of criteria.
+ *
+ * An example request might look like
+ *
+ * http://domain/endpoint/taxonomy_term?fields=tid,name&parameters[tid]=7&parameters[vid]=1
+ *
+ * This would return an array of objects with only tid and name defined, where
+ * tid = 7 and vid = 1.
+ *
+ * @param $page
+ *   Page number of results to return (in pages of 20).
+ * @param $fields
+ *   The fields you want returned.
+ * @param $parameters
+ *   An array of fields and values used to build a sql WHERE clause indicating
+ *   what items should be returned.
+ * @return
+ *   An array of term objects.
+ *
+ * @see _node_resource_index() for more notes
+ **/
+function _taxonomy_term_resource_index($page, $fields, $parameters) {
+  $taxonomy_select = db_select('taxonomy_term_data', 't')
+    ->orderBy('vid', 'DESC')
+    ->orderBy('weight', 'DESC')
+    ->orderBy('name', 'DESC');
+
+  services_resource_build_index_query($taxonomy_select, $page, $fields, $parameters);
+
+  $results = $taxonomy_select->execute();
+
+  return services_resource_build_index_list($results, 'taxonomy_term', 'tid');
+}
+
+/**
+ * Return an array of optionally paged vids baed on a set of criteria.
+ *
+ * An example request might look like
+ *
+ * http://domain/endpoint/taxonomy_vocabulary?fields=vid,name&parameters[vid]=2
+ *
+ * This would return an array of objects with only vid and name defined, where
+ * vid = 2.
+ *
+ * @param $page
+ *   Page number of results to return (in pages of 20).
+ * @param $fields
+ *   The fields you want returned.
+ * @param $parameters
+ *   An array of fields and values used to build a sql WHERE clause indicating
+ *   what items should be returned.
+ * @return
+ *   An array of vocabulary objects.
+ *
+ * @todo
+ *   Support node types as parameters.
+ *
+ * @see _node_resource_index() for more notes
+ **/
+function _taxonomy_vocabulary_resource_index($page, $fields, $parameters) {
+  $taxonomy_select = db_select('taxonomy_vocabulary', 't')
+    ->orderBy('weight', 'DESC')
+    ->orderBy('name', 'DESC');
+
+  services_resource_build_index_query($taxonomy_select, $page, $fields, $parameters);
+
+  $results = $taxonomy_select->execute();
+
+  return services_resource_build_index_list($results, 'taxonomy_vocabulary', 'vid');
+}
\ No newline at end of file
diff --git a/resources/user_resource.inc b/resources/user_resource.inc
index 0067cb4..c81e0c8 100644
--- a/resources/user_resource.inc
+++ b/resources/user_resource.inc
@@ -400,29 +400,14 @@ function _user_resource_logout() {
  * @see _node_resource_index() for more notes
  */
 function _user_resource_index($page, $fields, $parameters) {
-  $user_select = db_select('users', 'u');
-  if ($fields == '*') {
-    $user_select->fields('u');
-  }
-  else {
-    $fields = explode(',', $fields);
-    $user_select->fields('u', $fields);
-  }
-  foreach ($parameters as $key => $value) {
-    $user_select->condition('u.' . $key, $value, '=');
-  }
-  $user_select->orderBy('created', 'DESC');
-  $user_select->range($page * 20, 20);
-
-  $result = $user_select->execute();
-  $users = array();
-  foreach ($result as $user) {
-    if ($uri = services_resource_uri(array('user', $user->uid))) {
-      $user->uri = $uri;
-    }
-    $users[] = $user;
-  }
-  return $users;
+  $user_select = db_select('users', 't')
+    ->orderBy('created', 'DESC');
+
+  services_resource_build_index_query($user_select, $page, $fields, $parameters);
+
+  $results = $user_select->execute();
+
+  return services_resource_build_index_list($results, 'user', 'uid');
 }
 
 /**
diff --git a/services.module b/services.module
index b725b2b..9fac565 100644
--- a/services.module
+++ b/services.module
@@ -447,3 +447,54 @@ function _services_resource_controller_as_procedure($resource, $name, $controlle
 
   return $method;
 }
+
+/**
+ * Helper function to build index queries.
+ *
+ * @param $query
+ *   Object database query object.
+ * @param $page
+ *   Integer page number we are requesting.
+ * @param $fields
+ *   Array fields to return.
+ * @param $parameter
+ *   Array parameters to add to the index query.
+ */
+function services_resource_build_index_query($query, $page, $fields, $parameter = array()) {
+  $page_size = variable_get('services_index_page_size', 20);
+  $query->range($page * $page_size, $page_size);
+  if ($fields == '*') {
+    $query->fields('t');
+  }
+  else {
+    $fields = explode(',', $fields);
+    $query->fields('t', $explode(',', $fields));
+  }
+  if(isset($parameters) && is_array($parameters))
+  foreach ($parameters as $parameter => $parameter_value) {
+    $query->condition($parameter, $parameter_value, '=');
+  }
+}
+
+/**
+ * Helper function to build a list of items satisfying the index query.
+ *
+ * @param $results
+ *   Object database query results object.
+ * @param $type
+ *   String type of index that is being processed.
+ * @param $field
+ *   String field to use for looking up uri.
+ */
+function services_resource_build_index_list($results, $type, $field) {
+  // Put together array of matching items to return.
+  $items = array();
+  foreach ($results as $result) {
+    if ($uri = services_resource_uri(array($type, $result->{$field}))) {
+      $result->uri = $uri;
+    }
+    $items[] = $result;
+  }
+
+  return $items;
+}
\ No newline at end of file
diff --git a/tests/functional/ServicesResourceCommentTests.test b/tests/functional/ServicesResourceCommentTests.test
index e642126..a0904b6 100644
--- a/tests/functional/ServicesResourceCommentTests.test
+++ b/tests/functional/ServicesResourceCommentTests.test
@@ -43,7 +43,56 @@ class ServicesResourceCommentTests extends ServicesWebTestCase {
      'group'       => t('Services'),
    );
   }
+  public function testCommentIndex() {
+    // Create and log in our privileged user.
+    $this->privilegedUser = $this->drupalCreateUser(array(
+      'administer services',
+    ));
+    $this->drupalLogin($this->privilegedUser);
+
 
+    // Create a set of comments. The comment resource returns 20 comments at a time,
+    // so we create two pages and a half worth.
+    $comments = array();
+    $count = 50;
+    $node = $this->drupalCreateNode();
+    $nid = $node->nid;
+    for ($i = 0; $i < $count; $i++) {
+      $comment = (object)$this->getCommentValues($nid);
+      $comment->created = REQUEST_TIME + $i;
+      comment_save($comment);
+      $comments[$comment->cid] = $comment;
+    }
+
+    // Get the content.
+    $page_count = ceil(count($comments) / 20);
+    $retrieved_comments = array();
+    for ($page = 0; $page < $page_count; $page++) {
+      $responseArray = $this->servicesGet($this->endpoint->path . '/comment', array('page' => $page));
+      $this->assertTrue(count($responseArray['body']) <= 20, t('Correct number of items returned'));
+
+      // Store the returned comment IDs.
+      foreach ($responseArray['body'] as $comment) {
+        if (isset($retrieved_comments[$comment->cid])) {
+          $this->fail(t('Duplicate comment @cid returned.', array('@cid' => $comment->cid)));
+        }
+        $retrieved_comments[$comment->cid] = TRUE;
+
+        $this->assertTrue($comments[$comment->cid]->subject == $comment->subject, t('Successfully received Comment info'), 'CommentResource: Index');
+      }
+    }
+    // We should have got all the comments.
+    $expected_cids = array_keys($comments);
+    sort($expected_cids);
+    $retrieved_cids = array_keys($retrieved_comments);
+    sort($retrieved_cids);
+    
+    $this->assertEqual($expected_cids, $retrieved_cids, t('Retrieved all comments'));
+
+    // The n+1 page should be empty.
+    $responseArray = $this->servicesGet($this->endpoint->path . '/comment', array('page' => $page_count + 1));
+    $this->assertEqual(count($responseArray['body']), 0, t('The n+1 page is empty'));
+  }
   /**
    * Test create comment.
    */
diff --git a/tests/functional/ServicesResourceTaxonomyTests.test b/tests/functional/ServicesResourceTaxonomyTests.test
index 23a1d48..1147225 100644
--- a/tests/functional/ServicesResourceTaxonomyTests.test
+++ b/tests/functional/ServicesResourceTaxonomyTests.test
@@ -39,6 +39,99 @@ class ServicesResourceTaxonomyTests extends ServicesWebtestCase {
      'group'       => t('Services'),
    );
   }
+    public function testTaxonomyVocabularyIndex() {
+    // Create and log in our privileged user.
+    $this->privilegedUser = $this->drupalCreateUser(array(
+      'administer services',
+    ));
+    $this->drupalLogin($this->privilegedUser);
+
+
+    // Create a set of taxonomy vocabularys. The taxonomy resource returns 20 vocabularys at a time,
+    // so we create two pages and a half worth.
+    $vocabularys = array();
+    $count = 50;
+    for ($i = 0; $i < $count; $i++) {
+      $vocabulary = $this->createVocabulary();
+      $vocabularys[$vocabulary['vid']] = $vocabulary;
+    }
+    $vocabulary = taxonomy_vocabulary_load(1);
+    $vocabularys[1] = (array)$vocabulary;
+    // Get the content.
+    $page_count = ceil(count($vocabularys) / 20);
+    $retrieved_terms = array();
+    for ($page = 0; $page < $page_count; $page++) {
+      $responseArray = $this->servicesGet($this->endpoint->path . '/taxonomy_vocabulary', array('page' => $page));
+      $this->assertTrue(count($responseArray['body']) <= 20, t('Correct number of items returned'));
+
+      // Store the returned comment IDs.
+      foreach ($responseArray['body'] as $vocabulary) {
+        if (isset($retrieved_vocabularys[$vocabulary->vid])) {
+          $this->fail(t('Duplicate vocabulary @vid returned.', array('@vid' => $vocabulary->tid)));
+        }
+        $retrieved_vocabularys[$vocabulary->vid] = TRUE;
+
+        $this->assertTrue($vocabularys[$vocabulary->vid]['name'] == $vocabulary->name, t('Successfully received vocabulary Name info'), 'TaxonomyVocabularyResource: Index');
+      }
+    }
+    // We should have got all the comments.
+    $expected_vids = array_keys($vocabularys);
+    sort($expected_vids);
+    $retrieved_vids = array_keys($retrieved_vocabularys);
+    sort($retrieved_vids);
+    $this->assertEqual($expected_vids, $retrieved_vids, t('Retrieved all vocabularys'));
+
+    // The n+1 page should be empty.
+    $responseArray = $this->servicesGet($this->endpoint->path . '/taxonomy_vocabulary', array('page' => $page_count + 1));
+    $this->assertEqual(count($responseArray['body']), 0, t('The n+1 page is empty'));
+  }
+  public function testTaxonomyTermIndex() {
+    // Create and log in our privileged user.
+    $this->privilegedUser = $this->drupalCreateUser(array(
+      'administer services',
+    ));
+    $this->drupalLogin($this->privilegedUser);
+
+
+    // Create a set of taxonomy terms. The taxonomy resource returns 20 terms at a time,
+    // so we create two pages and a half worth.
+    $terms = array();
+    $count = 50;
+    $vocabulary = $this->createVocabulary();
+    for ($i = 0; $i < $count; $i++) {
+      $term = $this->createTerm($vocabulary['vid']);
+      $terms[$term['tid']] = $term;
+    }
+    
+    // Get the content.
+    $page_count = ceil(count($terms) / 20);
+    $retrieved_terms = array();
+    for ($page = 0; $page < $page_count; $page++) {
+      $responseArray = $this->servicesGet($this->endpoint->path . '/taxonomy_term', array('page' => $page));
+      $this->assertTrue(count($responseArray['body']) <= 20, t('Correct number of items returned'));
+
+      // Store the returned comment IDs.
+      foreach ($responseArray['body'] as $term) {
+        if (isset($retrieved_terms[$term->tid])) {
+          $this->fail(t('Duplicate term @tid returned.', array('@tid' => $term->tid)));
+        }
+        $retrieved_terms[$term->tid] = TRUE;
+
+        $this->assertTrue($terms[$term->tid]['name'] == $term->name, t('Successfully received Term Name info'), 'TaxonomyTermResource: Index');
+      }
+    }
+    // We should have got all the comments.
+    $expected_tids = array_keys($terms);
+    sort($expected_tids);
+    $retrieved_tids = array_keys($retrieved_terms);
+    sort($retrieved_tids);
+    $this->assertEqual($expected_tids, $retrieved_tids, t('Retrieved all terms'));
+
+    // The n+1 page should be empty.
+    $responseArray = $this->servicesGet($this->endpoint->path . '/taxonomy_term', array('page' => $page_count + 1));
+    $this->assertEqual(count($responseArray['body']), 0, t('The n+1 page is empty'));
+    taxonomy_vocabulary_delete($vocabulary['vid']);
+  }
 
   /**
    * Test taxonomy vocabulary create method.
diff --git a/tests/functional/ServicesResourceUserTests.test b/tests/functional/ServicesResourceUserTests.test
index c17d2cb..7a63648 100644
--- a/tests/functional/ServicesResourceUserTests.test
+++ b/tests/functional/ServicesResourceUserTests.test
@@ -59,7 +59,7 @@ class ServicesResourceUsertests extends ServicesWebtestCase {
 
     $response = $this->servicesPost($this->endpoint->path . '/user', array('account' => $user));
     $account = $response['body'];
-    $this->assertTrue(!empty($account->uid), t('User has been create successfully.'), 'UserResource: Create');
+    $this->assertTrue(!empty($account['uid']), t('User has been create successfully.'), 'UserResource: Create');
 
     // Load user.
     $user_load = user_load($account['uid']);
diff --git a/tests/services.test b/tests/services.test
index fbec159..9b32c6b 100644
--- a/tests/services.test
+++ b/tests/services.test
@@ -233,6 +233,9 @@ class ServicesWebTestCase extends DrupalWebTestCase {
           'delete' => array(
             'enabled' => 1,
           ),
+          'index' => array(
+            'enabled' => 1,
+          ),
         ),
         'actions' => array(
           'selectNodes' => array(
@@ -255,6 +258,9 @@ class ServicesWebTestCase extends DrupalWebTestCase {
           'delete' => array(
             'enabled' => 1,
           ),
+          'index' => array(
+            'enabled' => 1,
+          ),
         ),
         'actions' => array(
           'getTree' => array(
@@ -305,6 +311,9 @@ class ServicesWebTestCase extends DrupalWebTestCase {
           'delete' => array(
             'enabled' => 1,
           ),
+          'index' => array(
+            'enabled' => 1,
+          )
         ),
         'actions' => array(
           'countAll' => array(
@@ -327,6 +336,9 @@ class ServicesWebTestCase extends DrupalWebTestCase {
           'delete' => array(
             'enabled' => 1,
           ),
+          'index' => array(
+            'enabled' => 1,
+          ),
         ),
         'actions' => array(
           'nodeFiles' => array(
-- 
1.7.3.4

