This is an automated email from the ASF dual-hosted git repository.

csantanapr pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new 8364759  Terminate route for invalid list skip values (#3615)
8364759 is described below

commit 8364759dec7f32a34695b6bfc631cf800ec30ce4
Author: James Dubee <jwdu...@us.ibm.com>
AuthorDate: Wed May 9 18:38:34 2018 -0400

    Terminate route for invalid list skip values (#3615)
---
 .../src/main/scala/whisk/http/ErrorResponse.scala  |  7 +++-
 .../main/scala/whisk/core/controller/Actions.scala | 32 +++++++++------
 .../scala/whisk/core/controller/Activations.scala  | 15 ++++---
 .../scala/whisk/core/controller/Packages.scala     | 46 ++++++++++++----------
 .../scala/whisk/core/controller/RestAPIs.scala     | 16 +++++++-
 .../main/scala/whisk/core/controller/Rules.scala   | 30 ++++++++------
 .../scala/whisk/core/controller/Triggers.scala     | 31 +++++++++------
 .../scala/whisk/core/entitlement/Collection.scala  |  5 ++-
 .../core/controller/test/ActionsApiTests.scala     | 33 ++++++++++++++++
 .../core/controller/test/ActivationsApiTests.scala | 35 +++++++++++++++-
 .../core/controller/test/PackagesApiTests.scala    | 33 ++++++++++++++++
 .../whisk/core/controller/test/RulesApiTests.scala | 33 ++++++++++++++++
 .../core/controller/test/TriggersApiTests.scala    | 33 ++++++++++++++++
 13 files changed, 281 insertions(+), 68 deletions(-)

diff --git a/common/scala/src/main/scala/whisk/http/ErrorResponse.scala 
b/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
index 40685c0..5a31855 100644
--- a/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
+++ b/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
@@ -151,9 +151,12 @@ object Messages {
   }
 
   def listLimitOutOfRange(collection: String, value: Int, max: Int) = {
-    s"The value $value is not in the range of 0 to $max for $collection."
+    s"The value '$value' is not in the range of 0 to $max for $collection."
   }
-  def listLimitIsNotAString = s"The API expects the 'limit' value to be an 
integer but the given value is not."
+  def listSkipOutOfRange(collection: String, value: Int) = {
+    s"The value '$value' is not greater than or equal to 0 for $collection."
+  }
+  def argumentNotInteger(collection: String, value: String) = s"The value 
'$value' is not an integer for $collection."
 
   def truncateLogs(limit: ByteSize) = {
     s"Logs were truncated because the total bytes size exceeds the limit of 
${limit.toBytes} bytes."
diff --git a/core/controller/src/main/scala/whisk/core/controller/Actions.scala 
b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
index fd5f943..25be8fa 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Actions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
@@ -34,7 +34,7 @@ import spray.json._
 import spray.json.DefaultJsonProtocol._
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
-import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.controller.RestApiCommons.{ListLimit, ListSkip}
 import whisk.core.controller.actions.PostActionActivation
 import whisk.core.database.CacheChangeNotification
 import whisk.core.database.NoDocumentException
@@ -339,20 +339,22 @@ trait WhiskActionsApi extends WhiskCollectionAPI with 
PostActionActivation with
    * - 500 Internal Server Error
    */
   override def list(user: Identity, namespace: EntityPath)(implicit transid: 
TransactionId) = {
-    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
-      (skip, limit, count) =>
-        if (!count) {
-          listEntities {
-            WhiskAction.listCollectionInNamespace(entityStore, namespace, 
skip, limit.n, includeDocs = false) map {
-              list =>
-                list.fold((js) => js, (as) => 
as.map(WhiskAction.serdes.write(_)))
-            }
-          }
-        } else {
-          countEntities {
-            WhiskAction.countCollectionInNamespace(entityStore, namespace, 
skip)
+    parameter(
+      'skip.as[ListSkip] ? ListSkip(collection.defaultListSkip),
+      'limit.as[ListLimit] ? ListLimit(collection.defaultListLimit),
+      'count ? false) { (skip, limit, count) =>
+      if (!count) {
+        listEntities {
+          WhiskAction.listCollectionInNamespace(entityStore, namespace, 
skip.n, limit.n, includeDocs = false) map {
+            list =>
+              list.fold((js) => js, (as) => 
as.map(WhiskAction.serdes.write(_)))
           }
         }
+      } else {
+        countEntities {
+          WhiskAction.countCollectionInNamespace(entityStore, namespace, 
skip.n)
+        }
+      }
     }
   }
 
@@ -697,6 +699,10 @@ trait WhiskActionsApi extends WhiskCollectionAPI with 
PostActionActivation with
 
   /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
   private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
+
+  /** Custom unmarshaller for query parameters "skip" for "list" operations. */
+  private implicit val stringToListSkip: Unmarshaller[String, ListSkip] = 
RestApiCommons.stringToListSkip(collection)
+
 }
 
 private case class TooManyActionsInSequence() extends IllegalArgumentException
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/Activations.scala 
b/core/controller/src/main/scala/whisk/core/controller/Activations.scala
index ff31e4f..4a141fc 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Activations.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Activations.scala
@@ -30,7 +30,7 @@ import akka.http.scaladsl.unmarshalling._
 import spray.json.DefaultJsonProtocol.RootJsObjectFormat
 import whisk.common.TransactionId
 import whisk.core.containerpool.logging.LogStore
-import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.controller.RestApiCommons.{ListLimit, ListSkip}
 import whisk.core.database.StaleParameter
 import whisk.core.entitlement.Privilege.READ
 import whisk.core.entitlement.{Collection, Privilege, Resource}
@@ -64,6 +64,10 @@ object WhiskActivationsApi {
   private implicit val stringToListLimit: Unmarshaller[String, ListLimit] =
     RestApiCommons.stringToListLimit(Collection(Collection.ACTIVATIONS))
 
+  /** Custom unmarshaller for query parameters "skip" for "list" operations. */
+  private implicit val stringToListSkip: Unmarshaller[String, ListSkip] =
+    RestApiCommons.stringToListSkip(Collection(Collection.ACTIVATIONS))
+
 }
 
 /** A trait implementing the activations API. */
@@ -143,9 +147,10 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
     import WhiskActivationsApi.stringToRestrictedEntityPath
     import WhiskActivationsApi.stringToInstantDeserializer
     import WhiskActivationsApi.stringToListLimit
+    import WhiskActivationsApi.stringToListSkip
 
     parameter(
-      'skip ? 0,
+      'skip.as[ListSkip] ? ListSkip(collection.defaultListSkip),
       'limit.as[ListLimit] ? ListLimit(collection.defaultListLimit),
       'count ? false,
       'docs ? false,
@@ -157,7 +162,7 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
           WhiskActivation.countCollectionInNamespace(
             activationStore,
             name.flatten.map(p => namespace.addPath(p)).getOrElse(namespace),
-            skip,
+            skip.n,
             since,
             upto,
             StaleParameter.UpdateAfter,
@@ -172,7 +177,7 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
               activationStore,
               namespace,
               action,
-              skip,
+              skip.n,
               limit.n,
               docs,
               since,
@@ -182,7 +187,7 @@ trait WhiskActivationsApi extends Directives with 
AuthenticatedRouteProvider wit
             WhiskActivation.listCollectionInNamespace(
               activationStore,
               namespace,
-              skip,
+              skip.n,
               limit.n,
               docs,
               since,
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/Packages.scala 
b/core/controller/src/main/scala/whisk/core/controller/Packages.scala
index 77000a8..509cc76 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Packages.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Packages.scala
@@ -26,7 +26,7 @@ import akka.http.scaladsl.server.{RequestContext, RouteResult}
 import akka.http.scaladsl.unmarshalling.Unmarshaller
 
 import whisk.common.TransactionId
-import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.controller.RestApiCommons.{ListLimit, ListSkip}
 import whisk.core.database.{CacheChangeNotification, 
DocumentTypeMismatchException, NoDocumentException}
 import whisk.core.entitlement._
 import whisk.core.entity._
@@ -168,26 +168,28 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with 
ReferencedEntities {
    * - 500 Internal Server Error
    */
   override def list(user: Identity, namespace: EntityPath)(implicit transid: 
TransactionId) = {
-    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
-      (skip, limit, count) =>
-        val viewName = if (user.namespace.toPath == namespace) 
WhiskPackage.view else WhiskPackage.publicPackagesView
-        if (!count) {
-          listEntities {
-            WhiskPackage
-              .listCollectionInNamespace(
-                entityStore,
-                namespace,
-                skip,
-                limit.n,
-                includeDocs = false,
-                viewName = viewName)
-              .map(_.fold((js) => js, (ps) => 
ps.map(WhiskPackage.serdes.write(_))))
-          }
-        } else {
-          countEntities {
-            WhiskPackage.countCollectionInNamespace(entityStore, namespace, 
skip, viewName = viewName)
-          }
+    parameter(
+      'skip.as[ListSkip] ? ListSkip(collection.defaultListSkip),
+      'limit.as[ListLimit] ? ListLimit(collection.defaultListLimit),
+      'count ? false) { (skip, limit, count) =>
+      val viewName = if (user.namespace.toPath == namespace) WhiskPackage.view 
else WhiskPackage.publicPackagesView
+      if (!count) {
+        listEntities {
+          WhiskPackage
+            .listCollectionInNamespace(
+              entityStore,
+              namespace,
+              skip.n,
+              limit.n,
+              includeDocs = false,
+              viewName = viewName)
+            .map(_.fold((js) => js, (ps) => 
ps.map(WhiskPackage.serdes.write(_))))
+        }
+      } else {
+        countEntities {
+          WhiskPackage.countCollectionInNamespace(entityStore, namespace, 
skip.n, viewName = viewName)
         }
+      }
     }
   }
 
@@ -330,4 +332,8 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with 
ReferencedEntities {
 
   /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
   private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
+
+  /** Custom unmarshaller for query parameters "skip" for "list" operations. */
+  private implicit val stringToListSkip: Unmarshaller[String, ListSkip] = 
RestApiCommons.stringToListSkip(collection)
+
 }
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala 
b/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
index bc1e0b5..9791fa8 100644
--- a/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
@@ -126,7 +126,21 @@ protected[controller] object RestApiCommons {
         case Success(n) =>
           throw new IllegalArgumentException(
             Messages.listLimitOutOfRange(collection.path, n, 
Collection.MAX_LIST_LIMIT))
-        case Failure(t) => throw new 
IllegalArgumentException(Messages.listLimitIsNotAString)
+        case Failure(t) => throw new 
IllegalArgumentException(Messages.argumentNotInteger(collection.path, value))
+      }
+    }
+  }
+
+  /** Custom unmarshaller for query parameters "skip" for "list" operations. */
+  case class ListSkip(n: Int)
+
+  def stringToListSkip(collection: Collection): Unmarshaller[String, ListSkip] 
= {
+    Unmarshaller.strict[String, ListSkip] { value =>
+      Try { value.toInt } match {
+        case Success(n) if (n >= 0) => ListSkip(n)
+        case Success(n) =>
+          throw new 
IllegalArgumentException(Messages.listSkipOutOfRange(collection.path, n))
+        case Failure(t) => throw new 
IllegalArgumentException(Messages.argumentNotInteger(collection.path, value))
       }
     }
   }
diff --git a/core/controller/src/main/scala/whisk/core/controller/Rules.scala 
b/core/controller/src/main/scala/whisk/core/controller/Rules.scala
index dc511be..5e99ff9 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Rules.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Rules.scala
@@ -24,7 +24,7 @@ import akka.http.scaladsl.server.StandardRoute
 import akka.http.scaladsl.unmarshalling.Unmarshaller
 import spray.json.DeserializationException
 import whisk.common.TransactionId
-import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.controller.RestApiCommons.{ListLimit, ListSkip}
 import whisk.core.database.{CacheChangeNotification, 
DocumentConflictException, NoDocumentException}
 import whisk.core.entitlement.{Collection, Privilege, ReferencedEntities}
 import whisk.core.entity._
@@ -253,19 +253,21 @@ trait WhiskRulesApi extends WhiskCollectionAPI with 
ReferencedEntities {
    * - 500 Internal Server Error
    */
   override def list(user: Identity, namespace: EntityPath)(implicit transid: 
TransactionId) = {
-    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
-      (skip, limit, count) =>
-        if (!count) {
-          listEntities {
-            WhiskRule.listCollectionInNamespace(entityStore, namespace, skip, 
limit.n, includeDocs = true) map { list =>
-              list.fold((js) => js, (rls) => 
rls.map(WhiskRule.serdes.write(_)))
-            }
-          }
-        } else {
-          countEntities {
-            WhiskRule.countCollectionInNamespace(entityStore, namespace, skip)
+    parameter(
+      'skip.as[ListSkip] ? ListSkip(collection.defaultListSkip),
+      'limit.as[ListLimit] ? ListLimit(collection.defaultListLimit),
+      'count ? false) { (skip, limit, count) =>
+      if (!count) {
+        listEntities {
+          WhiskRule.listCollectionInNamespace(entityStore, namespace, skip.n, 
limit.n, includeDocs = true) map { list =>
+            list.fold((js) => js, (rls) => rls.map(WhiskRule.serdes.write(_)))
           }
         }
+      } else {
+        countEntities {
+          WhiskRule.countCollectionInNamespace(entityStore, namespace, skip.n)
+        }
+      }
     }
   }
 
@@ -428,6 +430,10 @@ trait WhiskRulesApi extends WhiskCollectionAPI with 
ReferencedEntities {
 
   /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
   private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
+
+  /** Custom unmarshaller for query parameters "skip" for "list" operations. */
+  private implicit val stringToListSkip: Unmarshaller[String, ListSkip] = 
RestApiCommons.stringToListSkip(collection)
+
 }
 
 private case class IgnoredRuleActivation(noop: Boolean) extends Throwable
diff --git 
a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala 
b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
index 760ae04..b7f0438 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
@@ -38,7 +38,7 @@ import com.typesafe.sslconfig.akka.AkkaSSLConfig
 import pureconfig.loadConfigOrThrow
 import spray.json._
 import whisk.common.{Https, TransactionId}
-import whisk.core.controller.RestApiCommons.ListLimit
+import whisk.core.controller.RestApiCommons.{ListLimit, ListSkip}
 import whisk.core.database.CacheChangeNotification
 import whisk.core.entitlement.Collection
 import whisk.core.entity._
@@ -227,20 +227,22 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
    * - 500 Internal Server Error
    */
   override def list(user: Identity, namespace: EntityPath)(implicit transid: 
TransactionId) = {
-    parameter('skip ? 0, 'limit.as[ListLimit] ? 
ListLimit(collection.defaultListLimit), 'count ? false) {
-      (skip, limit, count) =>
-        if (!count) {
-          listEntities {
-            WhiskTrigger.listCollectionInNamespace(entityStore, namespace, 
skip, limit.n, includeDocs = false) map {
-              list =>
-                list.fold((js) => js, (ts) => 
ts.map(WhiskTrigger.serdes.write(_)))
-            }
-          }
-        } else {
-          countEntities {
-            WhiskTrigger.countCollectionInNamespace(entityStore, namespace, 
skip)
+    parameter(
+      'skip.as[ListSkip] ? ListSkip(collection.defaultListSkip),
+      'limit.as[ListLimit] ? ListLimit(collection.defaultListLimit),
+      'count ? false) { (skip, limit, count) =>
+      if (!count) {
+        listEntities {
+          WhiskTrigger.listCollectionInNamespace(entityStore, namespace, 
skip.n, limit.n, includeDocs = false) map {
+            list =>
+              list.fold((js) => js, (ts) => 
ts.map(WhiskTrigger.serdes.write(_)))
           }
         }
+      } else {
+        countEntities {
+          WhiskTrigger.countCollectionInNamespace(entityStore, namespace, 
skip.n)
+        }
+      }
     }
   }
 
@@ -424,4 +426,7 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
   /** Custom unmarshaller for query parameters "limit" for "list" operations. 
*/
   private implicit val stringToListLimit: Unmarshaller[String, ListLimit] = 
RestApiCommons.stringToListLimit(collection)
 
+  /** Custom unmarshaller for query parameters "skip" for "list" operations. */
+  private implicit val stringToListSkip: Unmarshaller[String, ListSkip] = 
RestApiCommons.stringToListSkip(collection)
+
 }
diff --git 
a/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala 
b/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
index b5063f2..5f65876 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
@@ -45,9 +45,11 @@ import whisk.core.entity.types.EntityStore
  * @param path the name of the collection (the resource path in URI and the 
view name in the datastore)
  * @param activate the privilege for an activate (may be ACTIVATE or REJECT 
for example)
  * @param listLimit the default limit on number of entities returned from a 
collection on a list operation
+ * @param skipLimit the default skip on number of entities returned from a 
collection on a list operation
  */
 protected[core] case class Collection protected (val path: String,
-                                                 val defaultListLimit: Int = 
Collection.DEFAULT_LIST_LIMIT) {
+                                                 val defaultListLimit: Int = 
Collection.DEFAULT_LIST_LIMIT,
+                                                 val defaultListSkip: Int = 
Collection.DEFAULT_SKIP_LIMIT) {
   override def toString = path
 
   /** Determines the right to request for the resources and context. */
@@ -110,6 +112,7 @@ protected[core] object Collection {
   /** Number of records allowed per query. */
   protected[core] val DEFAULT_LIST_LIMIT = 30
   protected[core] val MAX_LIST_LIMIT = 200
+  protected[core] val DEFAULT_SKIP_LIMIT = 0
 
   protected[core] val ACTIONS = WhiskAction.collectionName
   protected[core] val TRIGGERS = WhiskTrigger.collectionName
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
index 380c5f0..e20936a 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
@@ -100,6 +100,39 @@ class ActionsApiTests extends ControllerTestCommon with 
WhiskActionsApi {
     }
   }
 
+  it should "reject list when limit is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?limit=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.ACTIONS, notAnInteger)
+      }
+    }
+  }
+
+  it should "reject list when skip is negative" in {
+    implicit val tid = transid()
+    val negativeSkip = -1
+    val response = Get(s"$collectionPath?skip=$negativeSkip") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.listSkipOutOfRange(Collection.ACTIONS, negativeSkip)
+      }
+    }
+  }
+
+  it should "reject list when skip is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?skip=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.ACTIONS, notAnInteger)
+      }
+    }
+  }
+
   // ?docs disabled
   ignore should "list action by default namespace with full docs" in {
     implicit val tid = transid()
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
index 1e4e684..b9a8915 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
@@ -385,7 +385,7 @@ class ActivationsApiTests extends ControllerTestCommon with 
WhiskActivationsApi
     }
   }
 
-  it should "reject activation list when limit is greater than maximum allowed 
value" in {
+  it should "reject list when limit is greater than maximum allowed value" in {
     implicit val tid = transid()
     val exceededMaxLimit = Collection.MAX_LIST_LIMIT + 1
     val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> 
Route.seal(routes(creds)) ~> check {
@@ -396,6 +396,39 @@ class ActivationsApiTests extends ControllerTestCommon 
with WhiskActivationsApi
     }
   }
 
+  it should "reject list when limit is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?limit=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.ACTIVATIONS, notAnInteger)
+      }
+    }
+  }
+
+  it should "reject list when skip is negative" in {
+    implicit val tid = transid()
+    val negativeSkip = -1
+    val response = Get(s"$collectionPath?skip=$negativeSkip") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.listSkipOutOfRange(Collection.ACTIVATIONS, negativeSkip)
+      }
+    }
+  }
+
+  it should "reject list when skip is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?skip=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.ACTIVATIONS, notAnInteger)
+      }
+    }
+  }
+
   it should "reject get activation by namespace and action name when action 
name is not a valid name" in {
     implicit val tid = transid()
     Get(s"$collectionPath?name=0%20") ~> Route.seal(routes(creds)) ~> check {
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
index 706ca9c..9e48eed 100644
--- a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
@@ -162,6 +162,39 @@ class PackagesApiTests extends ControllerTestCommon with 
WhiskPackagesApi {
     }
   }
 
+  it should "reject list when limit is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?limit=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.PACKAGES, notAnInteger)
+      }
+    }
+  }
+
+  it should "reject list when skip is negative" in {
+    implicit val tid = transid()
+    val negativeSkip = -1
+    val response = Get(s"$collectionPath?skip=$negativeSkip") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.listSkipOutOfRange(Collection.PACKAGES, negativeSkip)
+      }
+    }
+  }
+
+  it should "reject list when skip is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?skip=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.PACKAGES, notAnInteger)
+      }
+    }
+  }
+
   ignore should "list all public packages excluding bindings" in {
     implicit val tid = transid()
     // create packages and package bindings, set some public and confirm API 
lists only public packages
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
index 78cea3c..f5e7449 100644
--- a/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
@@ -104,6 +104,39 @@ class RulesApiTests extends ControllerTestCommon with 
WhiskRulesApi {
     }
   }
 
+  it should "reject list when limit is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?limit=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.RULES, notAnInteger)
+      }
+    }
+  }
+
+  it should "reject list when skip is negative" in {
+    implicit val tid = transid()
+    val negativeSkip = -1
+    val response = Get(s"$collectionPath?skip=$negativeSkip") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.listSkipOutOfRange(Collection.RULES, negativeSkip)
+      }
+    }
+  }
+
+  it should "reject list when skip is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?skip=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.RULES, notAnInteger)
+      }
+    }
+  }
+
   //?docs disabled
   ignore should "list rules by default namespace with full docs" in {
     implicit val tid = transid()
diff --git 
a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala 
b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
index 9f79540..aceae91 100644
--- a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
@@ -108,6 +108,39 @@ class TriggersApiTests extends ControllerTestCommon with 
WhiskTriggersApi {
     }
   }
 
+  it should "reject list when limit is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?limit=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.TRIGGERS, notAnInteger)
+      }
+    }
+  }
+
+  it should "reject list when skip is negative" in {
+    implicit val tid = transid()
+    val negativeSkip = -1
+    val response = Get(s"$collectionPath?skip=$negativeSkip") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.listSkipOutOfRange(Collection.TRIGGERS, negativeSkip)
+      }
+    }
+  }
+
+  it should "reject list when skip is not an integer" in {
+    implicit val tid = transid()
+    val notAnInteger = "string"
+    val response = Get(s"$collectionPath?skip=$notAnInteger") ~> 
Route.seal(routes(creds)) ~> check {
+      status should be(BadRequest)
+      responseAs[String] should include {
+        Messages.argumentNotInteger(Collection.TRIGGERS, notAnInteger)
+      }
+    }
+  }
+
   // ?docs disabled
   ignore should "list triggers by default namespace with full docs" in {
     implicit val tid = transid()

-- 
To stop receiving notification emails like this one, please contact
csantan...@apache.org.

Reply via email to