blob: 9b2e61a73c9f5afc2955db592215a789fab06bb4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.openwhisk.core.cli.test
import akka.http.scaladsl.model.StatusCodes.NotFound
import akka.http.scaladsl.model.StatusCodes.OK
import akka.http.scaladsl.model.StatusCodes.BadRequest
import akka.http.scaladsl.model.StatusCodes.Conflict
import java.time.Instant
import java.time.Clock
import scala.language.postfixOps
import scala.concurrent.duration.DurationInt
import scala.util.Random
import org.junit.runner.RunWith
import org.scalatest.junit.JUnitRunner
import common.TestHelpers
import common.TestUtils
import common.TestUtils._
import common.WhiskProperties
import common.WskProps
import common.WskTestHelpers
import common.WskActorSystem
import common.rest.WskRestOperations
import spray.json.DefaultJsonProtocol._
import spray.json._
import org.apache.openwhisk.core.entity._
import org.apache.openwhisk.core.entity.size.SizeInt
import TestJsonArgs._
import org.apache.openwhisk.http.Messages
/**
* Tests for basic CLI usage. Some of these tests require a deployed backend.
*/
@RunWith(classOf[JUnitRunner])
class WskRestBasicUsageTests extends TestHelpers with WskTestHelpers with WskActorSystem {
implicit val wskprops = WskProps()
val wsk = new WskRestOperations
val defaultAction: Some[String] = Some(TestUtils.getTestActionFilename("hello.js"))
val usrAgentHeaderRegEx: String = """\bUser-Agent\b": \[\s+"OpenWhisk\-CLI/1.\d+.*"""
val requireAPIKeyAnnotation = WhiskProperties.getBooleanProperty("whisk.feature.requireApiKeyAnnotation", true);
behavior of "Wsk API basic usage"
it should "allow a 3 part Fully Qualified Name (FQN) without a leading '/'" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val guestNamespace = wsk.namespace.whois()
val packageName = "packageName3ptFQN"
val actionName = "actionName3ptFQN"
val triggerName = "triggerName3ptFQN"
val ruleName = "ruleName3ptFQN"
val fullQualifiedName = s"${guestNamespace}/${packageName}/${actionName}"
// Used for action and rule creation below
assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
pkg.create(packageName)
}
assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, _) =>
trigger.create(triggerName)
}
// Test action and rule creation where action name is 3 part FQN w/out leading slash
assetHelper.withCleaner(wsk.action, fullQualifiedName) { (action, _) =>
action.create(fullQualifiedName, defaultAction)
}
assetHelper.withCleaner(wsk.rule, ruleName) { (rule, _) =>
rule.create(ruleName, trigger = triggerName, action = fullQualifiedName)
}
val run = wsk.action.invoke(fullQualifiedName)
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe "success"
}
val action = wsk.action.get(fullQualifiedName)
action.getField("name") shouldBe actionName
action.getField("namespace") shouldBe s"${guestNamespace}/${packageName}"
}
behavior of "Wsk actions"
it should "reject creating entities with invalid names" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
val names = Seq(
("", NotFound.intValue),
(" ", BadRequest.intValue),
("hi+there", BadRequest.intValue),
("$hola", BadRequest.intValue),
("dora?", BadRequest.intValue),
("|dora|dora?", BadRequest.intValue))
names foreach {
case (name, ec) =>
assetHelper.withCleaner(wsk.action, name, confirmDelete = false) { (action, _) =>
action.create(name, defaultAction, expectedExitCode = ec)
}
}
}
it should "create, and get an action to verify parameter and annotation parsing" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "actionAnnotations"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, file, annotations = getValidJSONTestArgInput, parameters = getValidJSONTestArgInput)
}
val action = wsk.action.get(name)
action.getField("name") shouldBe name
val receivedParams = wsk.parseJsonString(action.stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(action.stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getValidJSONTestArgOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "create, and get an action to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "actionAnnotAndParamParsing"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
val argInput = Some(TestUtils.getTestActionFilename("validInput1.json"))
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, file, annotationFile = argInput, parameterFile = argInput)
}
val action = wsk.action.get(name)
action.getField("name") shouldBe name
val receivedParams = wsk.parseJsonString(action.stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(action.stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getJSONFileOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "create an action with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "actionEscapes"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, file, parameters = getEscapedJSONTestArgInput, annotations = getEscapedJSONTestArgInput)
}
val action = wsk.action.get(name)
action.getField("name") shouldBe name
val receivedParams = wsk.parseJsonString(action.stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(action.stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getEscapedJSONTestArgOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "invoke an action that exits during initialization and get appropriate error" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "abort init"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("initexit.js")))
}
withActivation(wsk.activation, wsk.action.invoke(name)) { activation =>
val response = activation.response
response.result.get.fields("error") shouldBe Messages.abnormalInitialization.toJson
response.status shouldBe ActivationResponse.messageForCode(ActivationResponse.DeveloperError)
}
}
it should "invoke an action that hangs during initialization and get appropriate error" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "hang init"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("initforever.js")), timeout = Some(3 seconds))
}
withActivation(wsk.activation, wsk.action.invoke(name)) { activation =>
val response = activation.response
response.result.get.fields("error") shouldBe Messages.timedoutActivation(3 seconds, true).toJson
response.status shouldBe ActivationResponse.messageForCode(ActivationResponse.DeveloperError)
}
}
it should "invoke an action that exits during run and get appropriate error" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "abort run"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("runexit.js")))
}
withActivation(wsk.activation, wsk.action.invoke(name)) { activation =>
val response = activation.response
response.result.get.fields("error") shouldBe Messages.abnormalRun.toJson
response.status shouldBe ActivationResponse.messageForCode(ActivationResponse.DeveloperError)
}
}
it should "ensure keys are not omitted from activation record" in withAssetCleaner(wskprops) {
val name = "activationRecordTest"
(wp, assetHelper) =>
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("argCheck.js")))
}
val run = wsk.action.invoke(name)
withActivation(wsk.activation, run) { activation =>
activation.start should be > Instant.EPOCH
activation.end should be > Instant.EPOCH
activation.response.status shouldBe ActivationResponse.messageForCode(ActivationResponse.Success)
activation.response.success shouldBe true
activation.response.result shouldBe Some(JsObject.empty)
activation.logs shouldBe Some(List.empty)
activation.annotations shouldBe defined
}
}
it should "write the action-path and the limits to the annotations" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "annotations"
val memoryLimit = 512 MB
val logLimit = 1 MB
val timeLimit = 60 seconds
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(
name,
Some(TestUtils.getTestActionFilename("helloAsync.js")),
memory = Some(memoryLimit),
timeout = Some(timeLimit),
logsize = Some(logLimit))
}
val run = wsk.action.invoke(name, Map("payload" -> "this is a test".toJson))
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe "success"
val annotations = activation.annotations.get
val limitsObj = JsObject(
"key" -> JsString("limits"),
"value" -> ActionLimits(TimeLimit(timeLimit), MemoryLimit(memoryLimit), LogLimit(logLimit)).toJson)
val path = annotations.find {
_.fields("key").convertTo[String] == "path"
}.get
path.fields("value").convertTo[String] should fullyMatch regex (s""".*/$name""")
annotations should contain(limitsObj)
}
}
it should "create, and invoke an action that utilizes an invalid docker container with appropriate error" in withAssetCleaner(
wskprops) {
val name = "invalidDockerContainer"
val containerName = s"bogus${Random.alphanumeric.take(16).mkString.toLowerCase}"
(wp, assetHelper) =>
assetHelper.withCleaner(wsk.action, name) {
// docker name is a randomly generate string
(action, _) =>
action.create(name, None, docker = Some(containerName))
}
val run = wsk.action.invoke(name)
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe ActivationResponse.messageForCode(ActivationResponse.DeveloperError)
activation.response.result.get
.fields("error") shouldBe s"Failed to pull container image '$containerName'.".toJson
activation.annotations shouldBe defined
val limits = activation.annotations.get.filter(_.fields("key").convertTo[String] == "limits")
withClue(limits) {
limits.length should be > 0
limits(0).fields("value") should not be JsNull
}
}
}
it should "invoke an action using npm openwhisk" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
val name = "hello npm openwhisk"
assetHelper.withCleaner(wsk.action, name, confirmDelete = false) { (action, _) =>
action.create(
name,
Some(TestUtils.getTestActionFilename("helloOpenwhiskPackage.js")),
annotations = Map(Annotations.ProvideApiKeyAnnotationName -> JsTrue))
}
val run = wsk.action.invoke(name, Map("ignore_certs" -> true.toJson, "name" -> name.toJson))
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe "success"
activation.response.result shouldBe Some(JsObject("delete" -> true.toJson))
activation.logs.get.mkString(" ") should include("action list has this many actions")
}
wsk.action.delete(name, expectedExitCode = NotFound.intValue)
}
it should "invoke an action receiving context properties excluding api key" in withAssetCleaner(wskprops) {
assume(requireAPIKeyAnnotation)
(wp, assetHelper) =>
val namespace = wsk.namespace.whois()
val name = "context"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("helloContext.js")))
}
val start = Instant.now(Clock.systemUTC()).toEpochMilli
val run = wsk.action.invoke(name)
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe "success"
val fields = activation.response.result.get.convertTo[Map[String, String]]
fields("api_host") shouldBe WhiskProperties.getApiHostForAction
fields.get("api_key") shouldBe empty
fields("namespace") shouldBe namespace
fields("action_name") shouldBe s"/$namespace/$name"
fields("action_version") should fullyMatch regex ("""\d+.\d+.\d+""")
fields("activation_id") shouldBe activation.activationId
fields("deadline").toLong should be >= start
}
}
it should "invoke an action receiving context properties including api key" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val namespace = wsk.namespace.whois()
val name = "context"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(
name,
Some(TestUtils.getTestActionFilename("helloContext.js")),
annotations = Map(Annotations.ProvideApiKeyAnnotationName -> JsTrue))
}
val start = Instant.now(Clock.systemUTC()).toEpochMilli
val run = wsk.action.invoke(name)
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe "success"
val fields = activation.response.result.get.convertTo[Map[String, String]]
fields("api_host") shouldBe WhiskProperties.getApiHostForAction
fields("api_key") shouldBe wskprops.authKey
fields("namespace") shouldBe namespace
fields("action_name") shouldBe s"/$namespace/$name"
fields("action_version") should fullyMatch regex ("""\d+.\d+.\d+""")
fields("activation_id") shouldBe activation.activationId
fields("deadline").toLong should be >= start
}
}
it should "invoke an action successfully with options --blocking and --result" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "invokeResult"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("echo.js")))
}
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
}
it should "invoke an action that returns a result by the deadline" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "deadline"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("helloDeadline.js")), timeout = Some(3 seconds))
}
val run = wsk.action.invoke(name)
withActivation(wsk.activation, run) { activation =>
activation.response.status shouldBe "success"
activation.response.result shouldBe Some(JsObject("timedout" -> true.toJson))
}
}
it should "invoke an action twice, where the first times out but the second does not and should succeed" in withAssetCleaner(
wskprops) {
// this test issues two activations: the first is forced to time out and not return a result by its deadline (ie it does not resolve
// its promise). The invoker should reclaim its container so that a second activation of the same action (which must happen within a
// short period of time (seconds, not minutes) is allocated a fresh container and hence runs as expected (vs. hitting in the container
// cache and reusing a bad container).
(wp, assetHelper) =>
val name = "timeout"
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, Some(TestUtils.getTestActionFilename("helloDeadline.js")), timeout = Some(3 seconds))
}
val start = Instant.now(Clock.systemUTC()).toEpochMilli
val hungRun = wsk.action.invoke(name, Map("forceHang" -> true.toJson))
withActivation(wsk.activation, hungRun) { activation =>
// the first action must fail with a timeout error
activation.response.status shouldBe ActivationResponse.messageForCode(ActivationResponse.DeveloperError)
activation.response.result shouldBe Some(
JsObject("error" -> Messages.timedoutActivation(3 seconds, false).toJson))
}
// run the action again, this time without forcing it to timeout
// it should succeed because it ran in a fresh container
val goodRun = wsk.action.invoke(name, Map("forceHang" -> false.toJson))
withActivation(wsk.activation, goodRun) { activation =>
// the first action must fail with a timeout error
activation.response.status shouldBe "success"
activation.response.result shouldBe Some(JsObject("timedout" -> true.toJson))
}
}
it should "ensure --web flags set the proper annotations" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
Seq("true", "faLse", "tRue", "nO", "yEs", "no", "raw", "NO", "Raw").foreach { flag =>
val webEnabled = flag.toLowerCase == "true" || flag.toLowerCase == "yes"
val rawEnabled = flag.toLowerCase == "raw"
val runtime = "nodejs:default"
val name = "webaction-" + flag
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(
name,
Some(TestUtils.getTestActionFilename("echo.js")),
web = Some(flag.toLowerCase),
kind = Some(runtime))
}
val action = wsk.action.get(name)
// first check if we got 'nodejs:*' in the exec value
action
.getFieldJsValue("annotations")
.convertTo[Seq[JsObject]]
.find(_.fields("key").convertTo[String] == "exec")
.map(_.fields("value"))
.map(exec => { exec.convertTo[String] should startWith("nodejs:") })
.getOrElse(fail())
// then we check the remaining annotations
val baseAnnotations = Parameters("web-export", JsBoolean(webEnabled || rawEnabled)) ++
Parameters("raw-http", JsBoolean(rawEnabled)) ++
Parameters("final", JsBoolean(webEnabled || rawEnabled))
val testAnnotations = if (requireAPIKeyAnnotation) {
baseAnnotations ++ Parameters(Annotations.ProvideApiKeyAnnotationName, JsFalse)
} else baseAnnotations
// we ignore the exec field here, since we already compared it above
action
.getFieldJsValue("annotations")
.convertTo[Set[JsObject]]
.filter(annotation => annotation.fields("key").convertTo[String] != "exec") shouldBe testAnnotations.toJsArray
.convertTo[Set[JsObject]]
}
}
it should "ensure action update creates an action with --web flag" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val runtime = "nodejs:default"
val name = "webaction"
val file = Some(TestUtils.getTestActionFilename("echo.js"))
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, file, web = Some("true"), update = true, kind = Some(runtime))
}
val baseAnnotations =
Parameters("web-export", JsTrue) ++
Parameters("raw-http", JsFalse) ++
Parameters("final", JsTrue)
val testAnnotations = if (requireAPIKeyAnnotation) {
baseAnnotations ++
Parameters(Annotations.ProvideApiKeyAnnotationName, JsFalse)
} else {
baseAnnotations
}
val action = wsk.action.get(name)
// first check if we got 'nodejs:*' in the exec value
action
.getFieldJsValue("annotations")
.convertTo[Seq[JsObject]]
.find(_.fields("key").convertTo[String] == "exec")
.map(_.fields("value"))
.map(exec => { exec.convertTo[String] should startWith("nodejs:") })
.getOrElse(fail())
// then we check the remaining annotations
// we ignore the exec field here, since we already compared it above
action
.getFieldJsValue("annotations")
.convertTo[Set[JsObject]]
.filter(annotation => annotation.fields("key").convertTo[String] != "exec") shouldBe testAnnotations.toJsArray
.convertTo[Set[JsObject]]
}
it should "invoke action while not encoding &, <, > characters" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
val name = "nonescape"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
val nonescape = "&<>"
val input = Map("payload" -> nonescape.toJson)
val output = JsObject("payload" -> JsString(s"hello, $nonescape!"))
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, file)
}
withActivation(wsk.activation, wsk.action.invoke(name, parameters = input)) { activation =>
activation.response.success shouldBe true
activation.response.result shouldBe Some(output)
activation.logs.toList.flatten.filter(_.contains(nonescape)).length shouldBe 1
}
}
behavior of "Wsk packages"
it should "create, and delete a package" in {
val name = "createDeletePackage"
wsk.pkg.create(name).statusCode shouldBe OK
wsk.pkg.delete(name).statusCode shouldBe OK
}
it should "create, and get a package to verify parameter and annotation parsing" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "packageAnnotAndParamParsing"
assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
pkg.create(name, annotations = getValidJSONTestArgInput, parameters = getValidJSONTestArgInput)
}
val pack = wsk.pkg.get(name)
val receivedParams = wsk.parseJsonString(pack.stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(pack.stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getValidJSONTestArgOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "create, and get a package to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "packageAnnotAndParamFileParsing"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
val argInput = Some(TestUtils.getTestActionFilename("validInput1.json"))
assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
pkg.create(name, annotationFile = argInput, parameterFile = argInput)
}
val stdout = wsk.pkg.get(name).stdout
val receivedParams = wsk.parseJsonString(stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getJSONFileOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "create a package with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "packageEscapses"
assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
pkg.create(name, parameters = getEscapedJSONTestArgInput, annotations = getEscapedJSONTestArgInput)
}
val pack = wsk.pkg.get(name)
val receivedParams = wsk.parseJsonString(pack.stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(pack.stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getEscapedJSONTestArgOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "report conformance error accessing action as package" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
val name = "aAsP"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
assetHelper.withCleaner(wsk.action, name) { (action, _) =>
action.create(name, file)
}
wsk.pkg.get(name, expectedExitCode = Conflict.intValue).stderr should include(Messages.conformanceMessage)
wsk.pkg.bind(name, "bogus", expectedExitCode = Conflict.intValue).stderr should include(
Messages.requestedBindingIsNotValid)
wsk.pkg.bind("bogus", "alsobogus", expectedExitCode = BadRequest.intValue).stderr should include(
Messages.bindingDoesNotExist)
}
behavior of "Wsk triggers"
it should "create, and get a trigger to verify parameter and annotation parsing" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "triggerAnnotAndParamParsing"
assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
trigger.create(name, annotations = getValidJSONTestArgInput, parameters = getValidJSONTestArgInput)
}
val stdout = wsk.trigger.get(name).stdout
val receivedParams = wsk.parseJsonString(stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getValidJSONTestArgOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "create, and get a trigger to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "triggerAnnotAndParamFileParsing"
val file = Some(TestUtils.getTestActionFilename("hello.js"))
val argInput = Some(TestUtils.getTestActionFilename("validInput1.json"))
assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
trigger.create(name, annotationFile = argInput, parameterFile = argInput)
}
val stdout = wsk.trigger.get(name).stdout
val receivedParams = wsk.parseJsonString(stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getJSONFileOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "create a trigger with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) {
(wp, assetHelper) =>
val name = "triggerEscapes"
assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
trigger.create(name, parameters = getEscapedJSONTestArgInput, annotations = getEscapedJSONTestArgInput)
}
val stdout = wsk.trigger.get(name).stdout
val receivedParams = wsk.parseJsonString(stdout).fields("parameters").convertTo[JsArray].elements
val receivedAnnots = wsk.parseJsonString(stdout).fields("annotations").convertTo[JsArray].elements
val escapedJSONArr = getEscapedJSONTestArgOutput.convertTo[JsArray].elements
for (expectedItem <- escapedJSONArr) {
receivedParams should contain(expectedItem)
receivedAnnots should contain(expectedItem)
}
}
it should "not create a trigger when feed fails to initialize" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
assetHelper.withCleaner(wsk.trigger, "badfeed", confirmDelete = false) { (trigger, name) =>
trigger.create(name, feed = Some(s"bogus"), expectedExitCode = ANY_ERROR_EXIT).exitCode should equal(NOT_FOUND)
trigger.get(name, expectedExitCode = NotFound.intValue)
trigger.create(name, feed = Some(s"bogus/feed"), expectedExitCode = ANY_ERROR_EXIT).exitCode should equal(
NOT_FOUND)
trigger.get(name, expectedExitCode = NotFound.intValue)
}
}
it should "invoke a feed action with the correct lifecyle event when creating, retrieving and deleting a feed trigger" in withAssetCleaner(
wskprops) { (wp, assetHelper) =>
val actionName = "echo"
val triggerName = "feedTest"
assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
action.create(actionName, Some(TestUtils.getTestActionFilename("echo.js")))
}
try {
wsk.trigger.create(triggerName, feed = Some(actionName)).statusCode shouldBe OK
wsk.trigger.get(triggerName).statusCode shouldBe OK
} finally {
wsk.trigger.delete(triggerName).statusCode shouldBe OK
}
}
}