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

rabbah 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 a8addf6  Extend system test suite (#3950)
a8addf6 is described below

commit a8addf629d56b789d0c1ce503cf6d691e6ecbf72
Author: Martin Gencur <mgen...@redhat.com>
AuthorDate: Thu Oct 25 20:18:16 2018 +0200

    Extend system test suite (#3950)
---
 .../src/main/resources/apiv1swagger.json           |  6 +++
 tests/dat/actions/argsPrint.js                     |  8 ++++
 tests/dat/actions/runexception.js                  |  6 +++
 tests/src/test/scala/common/WskCliOperations.scala | 10 ++++-
 tests/src/test/scala/common/WskOperations.scala    |  1 +
 .../test/scala/common/rest/WskRestOperations.scala | 37 ++++++++++++----
 .../test/scala/system/basic/WskActionTests.scala   | 37 +++++++++++++++-
 .../whisk/core/cli/test/WskEntitlementTests.scala  | 34 +++++++++++++++
 .../core/cli/test/WskRestBasicUsageTests.scala     |  1 +
 .../core/controller/test/ActivationsApiTests.scala | 51 ++++++++++++++++++++++
 .../core/controller/test/PackagesApiTests.scala    |  9 ++++
 11 files changed, 189 insertions(+), 11 deletions(-)

diff --git a/core/controller/src/main/resources/apiv1swagger.json 
b/core/controller/src/main/resources/apiv1swagger.json
index 989c5fa..8f3c332 100644
--- a/core/controller/src/main/resources/apiv1swagger.json
+++ b/core/controller/src/main/resources/apiv1swagger.json
@@ -1280,6 +1280,9 @@
                     "401": {
                         "$ref": "#/responses/UnauthorizedRequest"
                     },
+                    "403": {
+                        "$ref": "#/responses/UnauthorizedRequest"
+                    },
                     "500": {
                         "$ref": "#/responses/ServerError"
                     }
@@ -1323,6 +1326,9 @@
                     "401": {
                         "$ref": "#/responses/UnauthorizedRequest"
                     },
+                    "403": {
+                        "$ref": "#/responses/UnauthorizedRequest"
+                    },
                     "404": {
                         "$ref": "#/responses/ItemNotFound"
                     },
diff --git a/tests/dat/actions/argsPrint.js b/tests/dat/actions/argsPrint.js
new file mode 100644
index 0000000..6bd35ef
--- /dev/null
+++ b/tests/dat/actions/argsPrint.js
@@ -0,0 +1,8 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+// license agreements; and to You under the Apache License, Version 2.0.
+
+function main(params) {
+    var param1 = params.param1 || '';
+    var param2 = params.param2 || '';
+    return {param1: param1, param2: param2};
+}
diff --git a/tests/dat/actions/runexception.js 
b/tests/dat/actions/runexception.js
new file mode 100644
index 0000000..4e15f9e
--- /dev/null
+++ b/tests/dat/actions/runexception.js
@@ -0,0 +1,6 @@
+// Licensed to the Apache Software Foundation (ASF) under one or more 
contributor
+// license agreements; and to You under the Apache License, Version 2.0.
+
+function main() {
+    throw "Extraordinary exception"
+}
diff --git a/tests/src/test/scala/common/WskCliOperations.scala 
b/tests/src/test/scala/common/WskCliOperations.scala
index 459dc59..259ca9d 100644
--- a/tests/src/test/scala/common/WskCliOperations.scala
+++ b/tests/src/test/scala/common/WskCliOperations.scala
@@ -480,12 +480,14 @@ class CliActivationOperations(val wsk: RunCliCmd) extends 
ActivationOperations w
    * @param filter (optional) if define, must be a simple entity name
    * @param limit (optional) the maximum number of activation to return
    * @param since (optional) only the activations since this timestamp are 
included
+   * @param skip (optional) the number of activations to skip
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
   def list(filter: Option[String] = None,
            limit: Option[Int] = None,
            since: Option[Instant] = None,
+           skip: Option[Int] = None,
            expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): 
RunResult = {
     val params = Seq(noun, "list", "--auth", wp.authKey) ++ { filter map { 
Seq(_) } getOrElse Seq.empty } ++ {
       limit map { l =>
@@ -495,6 +497,10 @@ class CliActivationOperations(val wsk: RunCliCmd) extends 
ActivationOperations w
       since map { i =>
         Seq("--since", i.toEpochMilli.toString)
       } getOrElse Seq.empty
+    } ++ {
+      skip map { i =>
+        Seq("--skip", i.toString)
+      } getOrElse Seq.empty
     }
     wsk.cli(wp.overrides ++ params, expectedExitCode)
   }
@@ -606,6 +612,7 @@ class CliActivationOperations(val wsk: RunCliCmd) extends 
ActivationOperations w
    * @param entity the name of the entity to filter from activation list
    * @param limit the maximum number of entities to list (if entity name is 
not unique use Some(0))
    * @param since (optional) only the activations since this timestamp are 
included
+   * @param skip (optional) the number of activations to skip
    * @param retries the maximum retries (total timeout is retries + 1 seconds)
    * @return activation ids found, caller must check length of sequence
    */
@@ -613,11 +620,12 @@ class CliActivationOperations(val wsk: RunCliCmd) extends 
ActivationOperations w
                        entity: Option[String],
                        limit: Option[Int] = None,
                        since: Option[Instant] = None,
+                       skip: Option[Int] = Some(0),
                        retries: Int = 10,
                        pollPeriod: Duration = 1.second)(implicit wp: 
WskProps): Seq[String] = {
     Try {
       retry({
-        val result = ids(list(filter = entity, limit = limit, since = since))
+        val result = ids(list(filter = entity, limit = limit, since = since, 
skip = skip))
         if (result.length >= N) result else throw PartialResult(result)
       }, retries, waitBeforeRetry = Some(pollPeriod))
     } match {
diff --git a/tests/src/test/scala/common/WskOperations.scala 
b/tests/src/test/scala/common/WskOperations.scala
index 9b012c7..5fb020e 100644
--- a/tests/src/test/scala/common/WskOperations.scala
+++ b/tests/src/test/scala/common/WskOperations.scala
@@ -314,6 +314,7 @@ trait ActivationOperations {
               entity: Option[String],
               limit: Option[Int] = None,
               since: Option[Instant] = None,
+              skip: Option[Int] = None,
               retries: Int,
               pollPeriod: Duration = 1.second)(implicit wp: WskProps): 
Seq[String]
 
diff --git a/tests/src/test/scala/common/rest/WskRestOperations.scala 
b/tests/src/test/scala/common/rest/WskRestOperations.scala
index 94023d7..66f6ef2 100644
--- a/tests/src/test/scala/common/rest/WskRestOperations.scala
+++ b/tests/src/test/scala/common/rest/WskRestOperations.scala
@@ -26,7 +26,6 @@ import org.apache.commons.io.{FileUtils, FilenameUtils}
 import org.scalatest.Matchers
 import org.scalatest.concurrent.ScalaFutures
 import org.scalatest.time.Span.convertDurationToSpan
-
 import scala.collection.immutable.Seq
 import scala.concurrent.duration.Duration
 import scala.concurrent.duration.DurationInt
@@ -40,7 +39,7 @@ import akka.http.scaladsl.model.StatusCodes.OK
 import akka.http.scaladsl.model.HttpRequest
 import akka.http.scaladsl.model.HttpMethod
 import akka.http.scaladsl.model.HttpResponse
-import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials, 
HttpCredentials, OAuth2BearerToken}
+import akka.http.scaladsl.model.headers.{Authorization, BasicHttpCredentials, 
OAuth2BearerToken}
 import akka.http.scaladsl.model.HttpEntity
 import akka.http.scaladsl.model.ContentTypes
 import akka.http.scaladsl.Http
@@ -76,6 +75,7 @@ import akka.actor.ActorSystem
 import akka.util.ByteString
 import pureconfig.loadConfigOrThrow
 import whisk.common.Https.HttpsConfig
+import whisk.common.AkkaLogging
 
 class AcceptAllHostNameVerifier extends HostnameVerifier {
   override def verify(s: String, sslSession: SSLSession): Boolean = true
@@ -644,10 +644,12 @@ class RestActivationOperations(implicit val actorSystem: 
ActorSystem)
   def listActivation(filter: Option[String] = None,
                      limit: Option[Int] = None,
                      since: Option[Instant] = None,
+                     skip: Option[Int] = None,
                      docs: Boolean = true,
                      expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: 
WskProps): RestResult = {
     val entityPath = Path(s"${basePath}/namespaces/${wp.namespace}/$noun")
-    val paramMap = Map("skip" -> "0", "docs" -> docs.toString) ++
+    val paramMap = Map("docs" -> docs.toString) ++
+      skip.map(s => Map("skip" -> s.toString)).getOrElse(Map.empty) ++
       limit.map(l => Map("limit" -> l.toString)).getOrElse(Map.empty) ++
       filter.map(f => Map("name" -> f.toString)).getOrElse(Map.empty) ++
       since.map(s => Map("since" -> 
s.toEpochMilli.toString)).getOrElse(Map.empty)
@@ -706,6 +708,7 @@ class RestActivationOperations(implicit val actorSystem: 
ActorSystem)
    * @param entity the name of the entity to filter from activation list
    * @param limit the maximum number of entities to list (if entity name is 
not unique use Some(0))
    * @param since (optional) only the activations since this timestamp are 
included
+   * @param skip (optional) the number of activations to skip
    * @param retries the maximum retries (total timeout is retries + 1 seconds)
    * @return activation ids found, caller must check length of sequence
    */
@@ -713,11 +716,13 @@ class RestActivationOperations(implicit val actorSystem: 
ActorSystem)
                        entity: Option[String],
                        limit: Option[Int] = Some(30),
                        since: Option[Instant] = None,
+                       skip: Option[Int] = Some(0),
                        retries: Int = 10,
                        pollPeriod: Duration = 1.second)(implicit wp: 
WskProps): Seq[String] = {
     Try {
       retry({
-        val result = idsActivation(listActivation(filter = entity, limit = 
limit, since = since, docs = false))
+        val result =
+          idsActivation(listActivation(filter = entity, limit = limit, since = 
since, skip = skip, docs = false))
         if (result.length >= N) result else throw PartialResult(result)
       }, retries, waitBeforeRetry = Some(pollPeriod))
     } match {
@@ -732,13 +737,23 @@ class RestActivationOperations(implicit val actorSystem: 
ActorSystem)
                    fieldFilter: Option[String] = None,
                    last: Option[Boolean] = None,
                    summary: Option[Boolean] = None)(implicit wp: WskProps): 
RestResult = {
-    val rr = activationId match {
+    val actId = activationId match {
+      case Some(id) => activationId
+      case None =>
+        last match {
+          case Some(true) => {
+            val activations = pollFor(N = 1, entity = None, limit = Some(1))
+            require(activations.size <= 1)
+            if (activations.isEmpty) None else Some(activations.head)
+          }
+          case _ => None
+        }
+    }
+    val rr = actId match {
       case Some(id) =>
         val resp = requestEntity(GET, getNamePath(wp.namespace, noun, id))
         new RestResult(resp.status, getRespData(resp))
-
-      case None =>
-        new RestResult(NotFound)
+      case None => new RestResult(NotFound)
     }
     validateStatusCode(expectedExitCode, rr.statusCode.intValue)
     rr
@@ -1141,6 +1156,7 @@ trait RunRestCmd extends Matchers with ScalaFutures with 
SwaggerValidator {
   val maxOpenRequest = 1024
   val basePath = Path("/api/v1")
   val systemNamespace = "whisk.system"
+  val logger = new AkkaLogging(actorSystem.log)
 
   implicit val config = PatienceConfig(100 seconds, 15 milliseconds)
   implicit val actorSystem: ActorSystem
@@ -1192,6 +1208,9 @@ trait RunRestCmd extends Matchers with ScalaFutures with 
SwaggerValidator {
         body.map(b => HttpEntity.Strict(ContentTypes.`application/json`, 
ByteString(b))).getOrElse(HttpEntity.Empty))
     val response = Http().singleRequest(request, connectionContext).flatMap { 
_.toStrict(toStrictTimeout) }.futureValue
 
+    logger.debug(this, s"Request: $request")
+    logger.debug(this, s"Response: $response")
+
     val validationErrors = validateRequestAndResponse(request, response)
     if (validationErrors.nonEmpty) {
       fail(
@@ -1201,7 +1220,7 @@ trait RunRestCmd extends Matchers with ScalaFutures with 
SwaggerValidator {
     response
   }
 
-  private def getHttpCredentials(wp: WskProps): HttpCredentials = {
+  private def getHttpCredentials(wp: WskProps) = {
     if (wp.authKey.contains(":")) {
       val authKey = wp.authKey.split(":")
       new BasicHttpCredentials(authKey(0), authKey(1))
diff --git a/tests/src/test/scala/system/basic/WskActionTests.scala 
b/tests/src/test/scala/system/basic/WskActionTests.scala
index 6db4b22..7830da7 100644
--- a/tests/src/test/scala/system/basic/WskActionTests.scala
+++ b/tests/src/test/scala/system/basic/WskActionTests.scala
@@ -32,7 +32,7 @@ import spray.json.DefaultJsonProtocol._
 class WskActionTests extends TestHelpers with WskTestHelpers with JsHelpers 
with WskActorSystem {
 
   implicit val wskprops = WskProps()
-  val wsk: WskOperations = new WskRestOperations
+  val wsk = new WskRestOperations
 
   val testString = "this is a test"
   val testResult = JsObject("count" -> testString.split(" ").length.toJson)
@@ -75,6 +75,21 @@ class WskActionTests extends TestHelpers with WskTestHelpers 
with JsHelpers with
     }
   }
 
+  it should "invoke an action that throws an uncaught exception and returns 
correct status code" in withAssetCleaner(
+    wskprops) { (wp, assetHelper) =>
+    val name = "throwExceptionAction"
+    assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+      action.create(name, 
Some(TestUtils.getTestActionFilename("runexception.js")))
+    }
+
+    withActivation(wsk.activation, wsk.action.invoke(name)) { activation =>
+      val response = activation.response
+      activation.response.status shouldBe "action developer error"
+      activation.response.result shouldBe Some(
+        JsObject("error" -> "An error has occurred: Extraordinary 
exception".toJson))
+    }
+  }
+
   it should "pass parameters bound on creation-time to the action" in 
withAssetCleaner(wskprops) { (wp, assetHelper) =>
     val name = "printParams"
     val params = Map("param1" -> "test1", "param2" -> "test2")
@@ -214,6 +229,26 @@ class WskActionTests extends TestHelpers with 
WskTestHelpers with JsHelpers with
     }
   }
 
+  it should "update an action with different language and check preserving 
params" in withAssetCleaner(wskprops) {
+    (wp, assetHelper) =>
+      val name = "updatedAction"
+
+      assetHelper.withCleaner(wsk.action, name, false) { (action, _) =>
+        wsk.action.create(
+          name,
+          Some(TestUtils.getTestActionFilename("hello.js")),
+          parameters = Map("name" -> testString.toJson)) //unused in the first 
function
+      }
+
+      wsk.action.create(name, 
Some(TestUtils.getTestActionFilename("hello.py")), update = true)
+
+      val run = wsk.action.invoke(name)
+      withActivation(wsk.activation, run) { activation =>
+        activation.response.status shouldBe "success"
+        activation.logs.get.mkString(" ") should include(s"Hello $testString")
+      }
+  }
+
   it should "fail to invoke an action with an empty file" in 
withAssetCleaner(wskprops) { (wp, assetHelper) =>
     val name = "empty"
     assetHelper.withCleaner(wsk.action, name) { (action, _) =>
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskEntitlementTests.scala 
b/tests/src/test/scala/whisk/core/cli/test/WskEntitlementTests.scala
index 0e689c8..64a7793 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskEntitlementTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskEntitlementTests.scala
@@ -160,6 +160,40 @@ abstract class WskEntitlementTests extends TestHelpers 
with WskTestHelpers with
     }
   }
 
+  it should "list shared packages when package is turned into public" in 
withAssetCleaner(guestWskProps) {
+    (wp, assetHelper) =>
+      assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+        pkg.create(samplePackage)(wp)
+      }
+
+      retry {
+        val packageList = 
wsk.pkg.list(Some(s"/$guestNamespace"))(defaultWskProps)
+        verifyPackageNotSharedList(packageList, guestNamespace, samplePackage)
+      }
+
+      wsk.pkg.create(samplePackage, update = true, shared = Some(true))(wp)
+
+      retry {
+        val packageList = 
wsk.pkg.list(Some(s"/$guestNamespace"))(defaultWskProps)
+        verifyPackageSharedList(packageList, guestNamespace, samplePackage)
+      }
+  }
+
+  //TODO: convert to API-level test under whisk.core.controller once 
issues/3959 is resolved
+  it should "reject getting package from invalid namespace" in 
withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+    val invalidNamespace = "whisk.systsdf"
+    wsk.pkg.get(s"/${invalidNamespace}/utils", expectedExitCode = 
forbiddenCode)(wp).stderr should include(
+      "not authorized")
+  }
+
+  //TODO: convert to API-level test under whisk.core.controller once 
issues/3959 is resolved
+  it should "reject getting invalid package from valid namespace" in 
withAssetCleaner(guestWskProps) {
+    (wp, assetHelper) =>
+      val invalidPackage = "utilssss"
+      wsk.pkg.get(s"/whisk.system/${invalidPackage}", expectedExitCode = 
forbiddenCode)(wp).stderr should include(
+        "not authorized")
+  }
+
   def verifyPackageSharedList(packageList: RunResult, namespace: String, 
packageName: String): Unit = {
     val fullyQualifiedPackageName = s"/$namespace/$packageName"
     withClue(s"Packagelist is: ${packageList.stdout}; Packagename is: 
$fullyQualifiedPackageName")(
diff --git 
a/tests/src/test/scala/whisk/core/cli/test/WskRestBasicUsageTests.scala 
b/tests/src/test/scala/whisk/core/cli/test/WskRestBasicUsageTests.scala
index 7fd950a..097ed25 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskRestBasicUsageTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskRestBasicUsageTests.scala
@@ -342,6 +342,7 @@ class WskRestBasicUsageTests extends TestHelpers with 
WskTestHelpers with WskAct
       }
       val args = Map("hello" -> "Robert".toJson)
       val run = wsk.action.invoke(name, args, blocking = true, result = true)
+      //--result takes precedence over --blocking
       run.stdout.parseJson shouldBe args.toJson
   }
 
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 6810ef2..7d5bf85 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
@@ -468,6 +468,57 @@ class ActivationsApiTests extends ControllerTestCommon 
with WhiskActivationsApi
     }
   }
 
+  it should "skip activations and return correct ones" in {
+    implicit val tid = transid()
+    val activations: Seq[WhiskActivation] = (1 to 3).map { i =>
+      //make sure the time is different for each activation
+      val time = Instant.now.plusMillis(i)
+      WhiskActivation(namespace, aname(), creds.subject, 
ActivationId.generate(), start = time, end = time)
+    }.toList
+
+    try {
+      activations.foreach(storeActivation(_, context))
+      waitOnListActivationsInNamespace(namespace, activations.size, context)
+
+      Get(s"$collectionPath?skip=1") ~> Route.seal(routes(creds)) ~> check {
+        status should be(OK)
+        val resultActivationIds = 
responseAs[List[JsObject]].map(_.fields("name"))
+        val expectedActivationIds = 
activations.map(_.toJson.fields("name")).reverse.drop(1)
+        resultActivationIds should be(expectedActivationIds)
+      }
+    } finally {
+      activations.foreach(a => 
deleteActivation(ActivationId(a.docid.asString), context))
+      waitOnListActivationsInNamespace(namespace, 0, context)
+    }
+  }
+
+  it should "return last activation" in {
+    implicit val tid = transid()
+    val activations = (1 to 3).map { i =>
+      //make sure the time is different for each activation
+      val time = Instant.now.plusMillis(i)
+      WhiskActivation(namespace, aname(), creds.subject, 
ActivationId.generate(), start = time, end = time)
+    }.toList
+
+    try {
+      activations.foreach(storeActivation(_, context))
+      waitOnListActivationsInNamespace(namespace, activations.size, context)
+
+      Get(s"$collectionPath?limit=1") ~> Route.seal(routes(creds)) ~> check {
+        status should be(OK)
+        val activationsJson = activations.map(_.toJson)
+        withClue(s"Original activations: ${activationsJson}") {
+          val respNames = responseAs[List[JsObject]].map(_.fields("name"))
+          val expectNames = activationsJson.map(_.fields("name")).drop(2)
+          respNames should be(expectNames)
+        }
+      }
+    } finally {
+      activations.foreach(a => 
deleteActivation(ActivationId(a.docid.asString), context))
+      waitOnListActivationsInNamespace(namespace, 0, context)
+    }
+  }
+
   //// GET /activations/id
   it should "get activation by id" in {
     implicit val tid = transid()
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 88ad062..1be2065 100644
--- a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
@@ -797,6 +797,15 @@ class PackagesApiTests extends ControllerTestCommon with 
WhiskPackagesApi {
     }
   }
 
+  it should "return empty list for invalid namespace" in {
+    implicit val tid = transid()
+    val path = s"/whisk.systsdf/${collection.path}"
+    Get(path) ~> Route.seal(routes(creds)) ~> check {
+      status should be(OK)
+      responseAs[List[JsObject]] should be(List.empty)
+    }
+  }
+
   it should "reject bind to non-package" in {
     implicit val tid = transid()
     val action = WhiskAction(namespace, aname(), jsDefault("??"))

Reply via email to