rabbah closed pull request #3950: Extend system testsuite
URL: https://github.com/apache/incubator-openwhisk/pull/3950
This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:
As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):
diff --git a/core/controller/src/main/resources/apiv1swagger.json
b/core/controller/src/main/resources/apiv1swagger.json
index 989c5fa843..8f3c332f96 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 0000000000..6bd35efc20
--- /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 0000000000..4e15f9ea10
--- /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 459dc59272..259ca9d7b2 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 9b012c709f..5fb020eab7 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 94023d7f95..66f6ef200c 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 6db4b220f8..7830da7fc8 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 0e689c8e6f..64a77937c1 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 7fd950ad0c..097ed25a01 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 6810ef29e1..7d5bf8555a 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 88ad062738..1be2065f8e 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("??"))
----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
For queries about this service, please contact Infrastructure at:
[email protected]
With regards,
Apache Git Services