You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by cs...@apache.org on 2017/09/18 14:55:39 UTC
[incubator-openwhisk-cli] 11/16: Apply standard scala formatting.
(#2650)
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-cli.git
commit 5e39d4bbaba4be93b027afc2d145395dd05257cf
Author: Markus Thömmes <ma...@me.com>
AuthorDate: Wed Sep 6 20:53:48 2017 +0200
Apply standard scala formatting. (#2650)
Formats all .scala files according to `scalafmt`'s (opinionated) style.
Adds Travis checks for correctly formatted code.
---
.../apigw/healthtests/ApiGwEndToEndTests.scala | 227 +-
.../test/scala/system/basic/WskActionTests.scala | 539 ++--
.../test/scala/system/basic/WskBasicTests.scala | 1628 +++++-----
.../test/scala/system/basic/WskConsoleTests.scala | 84 +-
.../test/scala/system/basic/WskPackageTests.scala | 169 +-
.../src/test/scala/system/basic/WskRuleTests.scala | 706 ++---
.../src/test/scala/system/basic/WskSdkTests.scala | 152 +-
.../test/scala/system/basic/WskSequenceTests.scala | 933 +++---
.../scala/whisk/core/admin/WskAdminTests.scala | 275 +-
.../actions/test/ApiGwRoutemgmtActionTests.scala | 781 +++--
.../scala/whisk/core/cli/test/ApiGwTests.scala | 1839 ++++++------
.../core/cli/test/WskActionSequenceTests.scala | 81 +-
.../whisk/core/cli/test/WskBasicUsageTests.scala | 3140 ++++++++++----------
.../scala/whisk/core/cli/test/WskConfigTests.scala | 448 +--
.../whisk/core/cli/test/WskEntitlementTests.scala | 557 ++--
.../whisk/core/cli/test/WskWebActionsTests.scala | 530 ++--
16 files changed, 6217 insertions(+), 5872 deletions(-)
diff --git a/tests/src/test/scala/apigw/healthtests/ApiGwEndToEndTests.scala b/tests/src/test/scala/apigw/healthtests/ApiGwEndToEndTests.scala
index 332a5ca..4c95bf5 100644
--- a/tests/src/test/scala/apigw/healthtests/ApiGwEndToEndTests.scala
+++ b/tests/src/test/scala/apigw/healthtests/ApiGwEndToEndTests.scala
@@ -53,118 +53,119 @@ class ApiGwEndToEndTests
with WskTestHelpers
with BeforeAndAfterAll {
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val clinamespace = wsk.namespace.whois()
-
- // Custom CLI properties file
- val cliWskPropsFile = File.createTempFile("wskprops", ".tmp")
-
- /*
- * Create a CLI properties file for use by the tests
- */
- override def beforeAll() = {
- cliWskPropsFile.deleteOnExit()
- val wskprops = WskProps(token = "SOME TOKEN")
- wskprops.writeFile(cliWskPropsFile)
- println(s"wsk temporary props file created here: ${cliWskPropsFile.getCanonicalPath()}")
- }
-
- behavior of "Wsk api"
-
- it should s"create an API and successfully invoke that API" in {
- val testName = "APIGW_HEALTHTEST1"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_echo"
- val urlqueryparam = "name"
- val urlqueryvalue = testName
-
- try {
- println("cli namespace: " + clinamespace)
-
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo-web-http.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, annotations = Map("web-export" -> true.toJson))
-
- // Create the API
- var rr = wsk.api.create(
- basepath = Some(testbasepath),
- relpath = Some(testrelpath),
- operation = Some(testurlop),
- action = Some(actionName),
- apiname = Some(testapiname),
- responsetype = Some("http"),
- cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
- rr.stdout should include("ok: created API")
- val apiurl = rr.stdout.split("\n")(1)
- println(s"apiurl: '$apiurl'")
-
- // Validate the API was successfully created
- // List result will look like:
- // ok: APIs
- // Action Verb API Name URL
- // /_//whisk.system/utils/echo get APIGW_HEALTHTEST1 API Name http://172.17.0.1:9001/api/ab9082cd-ea8e-465a-8a65-b491725cc4ef/APIGW_HEALTHTEST1_bp/path
- rr = wsk.api.list(
- basepathOrApiName = Some(testbasepath),
- relpath = Some(testrelpath),
- operation = Some(testurlop),
- cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"$actionName\\s+$testurlop\\s+$testapiname\\s+")
- rr.stdout should include(testbasepath + testrelpath)
-
- // Recreate the API using a JSON swagger file
- rr = wsk.api.get(
- basepathOrApiName = Some(testbasepath),
- cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
- val swaggerfile = File.createTempFile("api", ".json")
- swaggerfile.deleteOnExit()
- val bw = new BufferedWriter(new FileWriter(swaggerfile))
- bw.write(rr.stdout)
- bw.close()
-
- // Delete API to that it can be recreated again using the generated swagger file
- val deleteApiResult = wsk.api.delete(
- basepathOrApiName = testbasepath,
- expectedExitCode = DONTCARE_EXIT,
- cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
-
- // Create the API again, but use the swagger file this time
- rr = wsk.api.create(
- swagger = Some(swaggerfile.getAbsolutePath()),
- cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
- rr.stdout should include("ok: created API")
- val swaggerapiurl = rr.stdout.split("\n")(1)
- println(s"Returned api url: '${swaggerapiurl}'")
-
- // Call the API URL and validate the results
- val start = java.lang.System.currentTimeMillis
- val apiToInvoke = s"$swaggerapiurl?$urlqueryparam=$urlqueryvalue&guid=$start"
- println(s"Invoking: '${apiToInvoke}'")
- val response = whisk.utils.retry({
- val response = RestAssured.given().config(sslconfig).get(s"$apiToInvoke")
- println("URL invocation response status: " + response.statusCode)
- response.statusCode should be(200)
- response
- }, 6, Some(2.second))
- val end = java.lang.System.currentTimeMillis
- val elapsed = end - start
- println("Elapsed time (milliseconds) for a successful response: " + elapsed)
- val responseString = response.body.asString
- println("URL invocation response: " + responseString)
- responseString.parseJson.asJsObject.fields(urlqueryparam).convertTo[String] should be(urlqueryvalue)
-
- } finally {
- println("Deleting action: " + actionName)
- val finallydeleteActionResult = wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- println("Deleting API: " + testbasepath)
- val finallydeleteApiResult = wsk.api.delete(
- basepathOrApiName = testbasepath,
- expectedExitCode = DONTCARE_EXIT,
- cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
- }
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val clinamespace = wsk.namespace.whois()
+
+ // Custom CLI properties file
+ val cliWskPropsFile = File.createTempFile("wskprops", ".tmp")
+
+ /*
+ * Create a CLI properties file for use by the tests
+ */
+ override def beforeAll() = {
+ cliWskPropsFile.deleteOnExit()
+ val wskprops = WskProps(token = "SOME TOKEN")
+ wskprops.writeFile(cliWskPropsFile)
+ println(s"wsk temporary props file created here: ${cliWskPropsFile.getCanonicalPath()}")
+ }
+
+ behavior of "Wsk api"
+
+ it should s"create an API and successfully invoke that API" in {
+ val testName = "APIGW_HEALTHTEST1"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_echo"
+ val urlqueryparam = "name"
+ val urlqueryvalue = testName
+
+ try {
+ println("cli namespace: " + clinamespace)
+
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo-web-http.js")
+ wsk.action.create(
+ name = actionName,
+ artifact = Some(file),
+ expectedExitCode = SUCCESS_EXIT,
+ annotations = Map("web-export" -> true.toJson))
+
+ // Create the API
+ var rr = wsk.api.create(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ responsetype = Some("http"),
+ cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
+ rr.stdout should include("ok: created API")
+ val apiurl = rr.stdout.split("\n")(1)
+ println(s"apiurl: '$apiurl'")
+
+ // Validate the API was successfully created
+ // List result will look like:
+ // ok: APIs
+ // Action Verb API Name URL
+ // /_//whisk.system/utils/echo get APIGW_HEALTHTEST1 API Name http://172.17.0.1:9001/api/ab9082cd-ea8e-465a-8a65-b491725cc4ef/APIGW_HEALTHTEST1_bp/path
+ rr = wsk.api.list(
+ basepathOrApiName = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"$actionName\\s+$testurlop\\s+$testapiname\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+
+ // Recreate the API using a JSON swagger file
+ rr = wsk.api.get(basepathOrApiName = Some(testbasepath), cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
+ val swaggerfile = File.createTempFile("api", ".json")
+ swaggerfile.deleteOnExit()
+ val bw = new BufferedWriter(new FileWriter(swaggerfile))
+ bw.write(rr.stdout)
+ bw.close()
+
+ // Delete API to that it can be recreated again using the generated swagger file
+ val deleteApiResult = wsk.api.delete(
+ basepathOrApiName = testbasepath,
+ expectedExitCode = DONTCARE_EXIT,
+ cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
+
+ // Create the API again, but use the swagger file this time
+ rr = wsk.api
+ .create(swagger = Some(swaggerfile.getAbsolutePath()), cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
+ rr.stdout should include("ok: created API")
+ val swaggerapiurl = rr.stdout.split("\n")(1)
+ println(s"Returned api url: '${swaggerapiurl}'")
+
+ // Call the API URL and validate the results
+ val start = java.lang.System.currentTimeMillis
+ val apiToInvoke = s"$swaggerapiurl?$urlqueryparam=$urlqueryvalue&guid=$start"
+ println(s"Invoking: '${apiToInvoke}'")
+ val response = whisk.utils.retry({
+ val response = RestAssured.given().config(sslconfig).get(s"$apiToInvoke")
+ println("URL invocation response status: " + response.statusCode)
+ response.statusCode should be(200)
+ response
+ }, 6, Some(2.second))
+ val end = java.lang.System.currentTimeMillis
+ val elapsed = end - start
+ println("Elapsed time (milliseconds) for a successful response: " + elapsed)
+ val responseString = response.body.asString
+ println("URL invocation response: " + responseString)
+ responseString.parseJson.asJsObject.fields(urlqueryparam).convertTo[String] should be(urlqueryvalue)
+
+ } finally {
+ println("Deleting action: " + actionName)
+ val finallydeleteActionResult = wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ println("Deleting API: " + testbasepath)
+ val finallydeleteApiResult = wsk.api.delete(
+ basepathOrApiName = testbasepath,
+ expectedExitCode = DONTCARE_EXIT,
+ cliCfgFile = Some(cliWskPropsFile.getCanonicalPath()))
}
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskActionTests.scala b/tests/src/test/scala/system/basic/WskActionTests.scala
index 06778a3..fb65f2c 100644
--- a/tests/src/test/scala/system/basic/WskActionTests.scala
+++ b/tests/src/test/scala/system/basic/WskActionTests.scala
@@ -34,327 +34,274 @@ import spray.json.JsObject
import spray.json.pimpAny
@RunWith(classOf[JUnitRunner])
-class WskActionTests
- extends TestHelpers
- with WskTestHelpers
- with JsHelpers {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
-
- val testString = "this is a test"
- val testResult = JsObject("count" -> testString.split(" ").length.toJson)
- val guestNamespace = wskprops.namespace
-
- behavior of "Whisk actions"
-
- it should "invoke an action returning a promise" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "hello promise"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("helloPromise.js")))
- }
-
- val run = wsk.action.invoke(name)
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.status shouldBe "success"
- activation.response.result shouldBe Some(JsObject("done" -> true.toJson))
- activation.logs.get.mkString(" ") shouldBe empty
- }
+class WskActionTests extends TestHelpers with WskTestHelpers with JsHelpers {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+
+ val testString = "this is a test"
+ val testResult = JsObject("count" -> testString.split(" ").length.toJson)
+ val guestNamespace = wskprops.namespace
+
+ behavior of "Whisk actions"
+
+ it should "invoke an action returning a promise" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "hello promise"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("helloPromise.js")))
+ }
+
+ val run = wsk.action.invoke(name)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "success"
+ activation.response.result shouldBe Some(JsObject("done" -> true.toJson))
+ activation.logs.get.mkString(" ") shouldBe empty
+ }
+ }
+
+ it should "invoke an action with a space in the name" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "hello Async"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("helloAsync.js")))
+ }
+
+ val run = wsk.action.invoke(name, Map("payload" -> testString.toJson))
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "success"
+ activation.response.result shouldBe Some(testResult)
+ activation.logs.get.mkString(" ") should include(testString)
+ }
+ }
+
+ 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")
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(
+ name,
+ Some(TestUtils.getTestActionFilename("printParams.js")),
+ parameters = params.mapValues(_.toJson))
+ }
+
+ val invokeParams = Map("payload" -> testString)
+ val run = wsk.action.invoke(name, invokeParams.mapValues(_.toJson))
+ withActivation(wsk.activation, run) { activation =>
+ val logs = activation.logs.get.mkString(" ")
+
+ (params ++ invokeParams).foreach {
+ case (key, value) =>
+ logs should include(s"params.$key: $value")
+ }
+ }
+ }
+
+ it should "copy an action and invoke it successfully" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "copied"
+ val packageName = "samples"
+ val actionName = "wordcount"
+ val fullQualifiedName = s"/$guestNamespace/$packageName/$actionName"
+
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, shared = Some(true))
}
- it should "invoke an action with a space in the name" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "hello Async"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("helloAsync.js")))
- }
-
- val run = wsk.action.invoke(name, Map("payload" -> testString.toJson))
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.status shouldBe "success"
- activation.response.result shouldBe Some(testResult)
- activation.logs.get.mkString(" ") should include(testString)
- }
+ assetHelper.withCleaner(wsk.action, fullQualifiedName) {
+ val file = Some(TestUtils.getTestActionFilename("wc.js"))
+ (action, _) =>
+ action.create(fullQualifiedName, file)
}
- 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")
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(
- name,
- Some(TestCLIUtils.getTestActionFilename("printParams.js")),
- parameters = params.mapValues(_.toJson))
- }
-
- val invokeParams = Map("payload" -> testString)
- val run = wsk.action.invoke(name, invokeParams.mapValues(_.toJson))
- withActivation(wsk.activation, run) {
- activation =>
- val logs = activation.logs.get.mkString(" ")
-
- (params ++ invokeParams).foreach {
- case (key, value) =>
- logs should include(s"params.$key: $value")
- }
- }
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(fullQualifiedName), Some("copy"))
}
- it should "copy an action and invoke it successfully" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "copied"
- val packageName = "samples"
- val actionName = "wordcount"
- val fullQualifiedName = s"/$guestNamespace/$packageName/$actionName"
-
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, _) => pkg.create(packageName, shared = Some(true))
- }
-
- assetHelper.withCleaner(wsk.action, fullQualifiedName) {
- val file = Some(TestCLIUtils.getTestActionFilename("wc.js"))
- (action, _) => action.create(fullQualifiedName, file)
- }
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(fullQualifiedName), Some("copy"))
- }
-
- val run = wsk.action.invoke(name, Map("payload" -> testString.toJson))
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.status shouldBe "success"
- activation.response.result shouldBe Some(testResult)
- activation.logs.get.mkString(" ") should include(testString)
- }
+ val run = wsk.action.invoke(name, Map("payload" -> testString.toJson))
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "success"
+ activation.response.result shouldBe Some(testResult)
+ activation.logs.get.mkString(" ") should include(testString)
+ }
+ }
+
+ it should "copy an action and ensure exec, parameters, and annotations copied" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val origActionName = "origAction"
+ val copiedActionName = "copiedAction"
+ val params = Map("a" -> "A".toJson)
+ val annots = Map("b" -> "B".toJson)
+
+ assetHelper.withCleaner(wsk.action, origActionName) {
+ val file = Some(TestUtils.getTestActionFilename("wc.js"))
+ (action, _) =>
+ action.create(origActionName, file, parameters = params, annotations = annots)
+ }
+
+ assetHelper.withCleaner(wsk.action, copiedActionName) { (action, _) =>
+ action.create(copiedActionName, Some(origActionName), Some("copy"))
+ }
+
+ val copiedAction = getJSONFromCLIResponse(wsk.action.get(copiedActionName).stdout)
+ val origAction = getJSONFromCLIResponse(wsk.action.get(copiedActionName).stdout)
+
+ copiedAction.fields("annotations") shouldBe origAction.fields("annotations")
+ copiedAction.fields("parameters") shouldBe origAction.fields("parameters")
+ copiedAction.fields("exec") shouldBe origAction.fields("exec")
+ copiedAction.fields("version") shouldBe JsString("0.0.1")
+ }
+
+ it should "add new parameters and annotations while copying an action" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val origName = "origAction"
+ val copiedName = "copiedAction"
+ val origParams = Map("origParam1" -> "origParamValue1".toJson, "origParam2" -> 999.toJson)
+ val copiedParams = Map("copiedParam1" -> "copiedParamValue1".toJson, "copiedParam2" -> 123.toJson)
+ val origAnnots = Map("origAnnot1" -> "origAnnotValue1".toJson, "origAnnot2" -> true.toJson)
+ val copiedAnnots = Map("copiedAnnot1" -> "copiedAnnotValue1".toJson, "copiedAnnot2" -> false.toJson)
+ val resParams = Seq(
+ JsObject("key" -> JsString("copiedParam1"), "value" -> JsString("copiedParamValue1")),
+ JsObject("key" -> JsString("copiedParam2"), "value" -> JsNumber(123)),
+ JsObject("key" -> JsString("origParam1"), "value" -> JsString("origParamValue1")),
+ JsObject("key" -> JsString("origParam2"), "value" -> JsNumber(999)))
+ val resAnnots = Seq(
+ JsObject("key" -> JsString("origAnnot1"), "value" -> JsString("origAnnotValue1")),
+ JsObject("key" -> JsString("copiedAnnot2"), "value" -> JsBoolean(false)),
+ JsObject("key" -> JsString("copiedAnnot1"), "value" -> JsString("copiedAnnotValue1")),
+ JsObject("key" -> JsString("origAnnot2"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
+
+ assetHelper.withCleaner(wsk.action, origName) {
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ (action, _) =>
+ action.create(origName, file, parameters = origParams, annotations = origAnnots)
+ }
+
+ assetHelper.withCleaner(wsk.action, copiedName) { (action, _) =>
+ action.create(copiedName, Some(origName), Some("copy"), parameters = copiedParams, annotations = copiedAnnots)
+ }
+
+ val copiedAction = getJSONFromCLIResponse(wsk.action.get(copiedName).stdout)
+
+ // CLI does not guarantee order of annotations and parameters so do a diff to compare the values
+ copiedAction.fields("parameters").convertTo[Seq[JsObject]] diff resParams shouldBe List()
+ copiedAction.fields("annotations").convertTo[Seq[JsObject]] diff resAnnots shouldBe List()
+ }
+
+ it should "recreate and invoke a new action with different code" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "recreatedAction"
+ assetHelper.withCleaner(wsk.action, name, false) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("wc.js")))
}
- it should "copy an action and ensure exec, parameters, and annotations copied" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val origActionName = "origAction"
- val copiedActionName = "copiedAction"
- val params = Map("a" -> "A".toJson)
- val annots = Map("b" -> "B".toJson)
-
- assetHelper.withCleaner(wsk.action, origActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("wc.js"))
- (action, _) => action.create(origActionName, file, parameters = params, annotations = annots)
- }
-
- assetHelper.withCleaner(wsk.action, copiedActionName) {
- (action, _) => action.create(copiedActionName, Some(origActionName), Some("copy"))
- }
-
- val copiedAction = getJSONFromCLIResponse(wsk.action.get(copiedActionName).stdout)
- val origAction = getJSONFromCLIResponse(wsk.action.get(copiedActionName).stdout)
-
- copiedAction.fields("annotations") shouldBe origAction.fields("annotations")
- copiedAction.fields("parameters") shouldBe origAction.fields("parameters")
- copiedAction.fields("exec") shouldBe origAction.fields("exec")
- copiedAction.fields("version") shouldBe JsString("0.0.1")
+ val run1 = wsk.action.invoke(name, Map("payload" -> testString.toJson))
+ withActivation(wsk.activation, run1) { activation =>
+ activation.response.status shouldBe "success"
+ activation.logs.get.mkString(" ") should include(s"The message '$testString' has")
}
- it should "add new parameters and annotations while copying an action" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val origName = "origAction"
- val copiedName = "copiedAction"
- val origParams = Map("origParam1" -> "origParamValue1".toJson, "origParam2" -> 999.toJson)
- val copiedParams = Map("copiedParam1" -> "copiedParamValue1".toJson, "copiedParam2" -> 123.toJson)
- val origAnnots = Map("origAnnot1" -> "origAnnotValue1".toJson, "origAnnot2" -> true.toJson)
- val copiedAnnots = Map("copiedAnnot1" -> "copiedAnnotValue1".toJson, "copiedAnnot2" -> false.toJson)
- val resParams = Seq(
- JsObject(
- "key" -> JsString("copiedParam1"),
- "value" -> JsString("copiedParamValue1")
- ),
- JsObject(
- "key" -> JsString("copiedParam2"),
- "value" -> JsNumber(123)
- ),
- JsObject(
- "key" -> JsString("origParam1"),
- "value" -> JsString("origParamValue1")
- ),
- JsObject(
- "key" -> JsString("origParam2"),
- "value" -> JsNumber(999)
- )
- )
- val resAnnots = Seq(
- JsObject(
- "key" -> JsString("origAnnot1"),
- "value" -> JsString("origAnnotValue1")
- ),
- JsObject(
- "key" -> JsString("copiedAnnot2"),
- "value" -> JsBoolean(false)
- ),
- JsObject(
- "key" -> JsString("copiedAnnot1"),
- "value" -> JsString("copiedAnnotValue1")
- ),
- JsObject(
- "key" -> JsString("origAnnot2"),
- "value" -> JsBoolean(true)
- ),
- JsObject(
- "key" -> JsString("exec"),
- "value" -> JsString("nodejs:6")
- )
- )
-
- assetHelper.withCleaner(wsk.action, origName) {
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
- (action, _) => action.create(origName, file, parameters = origParams, annotations = origAnnots)
- }
-
- assetHelper.withCleaner(wsk.action, copiedName) {
- (action, _) => action.create(copiedName, Some(origName), Some("copy"), parameters = copiedParams, annotations = copiedAnnots)
- }
-
- val copiedAction = getJSONFromCLIResponse(wsk.action.get(copiedName).stdout)
-
- // CLI does not guarantee order of annotations and parameters so do a diff to compare the values
- copiedAction.fields("parameters").convertTo[Seq[JsObject]] diff resParams shouldBe List()
- copiedAction.fields("annotations").convertTo[Seq[JsObject]] diff resAnnots shouldBe List()
+ wsk.action.delete(name)
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("hello.js")))
}
- it should "recreate and invoke a new action with different code" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "recreatedAction"
- assetHelper.withCleaner(wsk.action, name, false) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("wc.js")))
- }
-
- val run1 = wsk.action.invoke(name, Map("payload" -> testString.toJson))
- withActivation(wsk.activation, run1) {
- activation =>
- activation.response.status shouldBe "success"
- activation.logs.get.mkString(" ") should include(s"The message '$testString' has")
- }
-
- wsk.action.delete(name)
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("hello.js")))
- }
-
- val run2 = wsk.action.invoke(name, Map("payload" -> testString.toJson))
- withActivation(wsk.activation, run2) {
- activation =>
- activation.response.status shouldBe "success"
- activation.logs.get.mkString(" ") should include(s"hello, $testString")
- }
+ val run2 = wsk.action.invoke(name, Map("payload" -> testString.toJson))
+ withActivation(wsk.activation, run2) { 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, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("empty.js")))
- }
- val run = wsk.action.invoke(name)
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.status shouldBe "action developer error"
- activation.response.result shouldBe Some(JsObject("error" -> "Missing main/no code to execute.".toJson))
- }
+ 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, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("empty.js")))
+ }
+ val run = wsk.action.invoke(name)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "action developer error"
+ activation.response.result shouldBe Some(JsObject("error" -> "Missing main/no code to execute.".toJson))
}
+ }
- it should "create an action with an empty file" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "empty"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("empty.js")))
- }
- val rr = wsk.action.get(name)
- wsk.parseJsonString(rr.stdout).getFieldPath("exec", "code") shouldBe Some(JsString(""))
+ it should "create an action with an empty file" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "empty"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("empty.js")))
}
+ val rr = wsk.action.get(name)
+ wsk.parseJsonString(rr.stdout).getFieldPath("exec", "code") shouldBe Some(JsString(""))
+ }
- it should "blocking invoke of nested blocking actions" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "nestedBlockingAction"
- val child = "wc"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("wcbin.js")))
- }
- assetHelper.withCleaner(wsk.action, child) {
- (action, _) => action.create(child, Some(TestCLIUtils.getTestActionFilename("wc.js")))
- }
-
- val run = wsk.action.invoke(name, Map("payload" -> testString.toJson), blocking = true)
- val activation = wsk.parseJsonString(run.stdout).convertTo[ActivationResult]
-
- withClue(s"check failed for activation: $activation") {
- val wordCount = testString.split(" ").length
- activation.response.result.get shouldBe JsObject("binaryCount" -> s"${wordCount.toBinaryString} (base 2)".toJson)
- }
+ it should "blocking invoke of nested blocking actions" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "nestedBlockingAction"
+ val child = "wc"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("wcbin.js")))
+ }
+ assetHelper.withCleaner(wsk.action, child) { (action, _) =>
+ action.create(child, Some(TestUtils.getTestActionFilename("wc.js")))
}
- it should "blocking invoke an asynchronous action" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "helloAsync"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("helloAsync.js")))
- }
-
- val run = wsk.action.invoke(name, Map("payload" -> testString.toJson), blocking = true)
- val activation = wsk.parseJsonString(run.stdout).convertTo[ActivationResult]
-
- withClue(s"check failed for activation: $activation") {
- activation.response.status shouldBe "success"
- activation.response.result shouldBe Some(testResult)
- activation.logs shouldBe Some(List())
- }
+ val run = wsk.action.invoke(name, Map("payload" -> testString.toJson), blocking = true)
+ val activation = wsk.parseJsonString(run.stdout).convertTo[ActivationResult]
+
+ withClue(s"check failed for activation: $activation") {
+ val wordCount = testString.split(" ").length
+ activation.response.result.get shouldBe JsObject("binaryCount" -> s"${wordCount.toBinaryString} (base 2)".toJson)
+ }
+ }
+
+ it should "blocking invoke an asynchronous action" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "helloAsync"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("helloAsync.js")))
+ }
+
+ val run = wsk.action.invoke(name, Map("payload" -> testString.toJson), blocking = true)
+ val activation = wsk.parseJsonString(run.stdout).convertTo[ActivationResult]
+
+ withClue(s"check failed for activation: $activation") {
+ activation.response.status shouldBe "success"
+ activation.response.result shouldBe Some(testResult)
+ activation.logs shouldBe Some(List())
+ }
+ }
+
+ it should "reject an invoke with the wrong parameters set" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val fullQualifiedName = s"/$guestNamespace/samples/helloWorld"
+ val payload = "bob"
+ val rr = wsk.cli(
+ Seq("action", "invoke", fullQualifiedName, payload) ++ wskprops.overrides,
+ expectedExitCode = TestUtils.ERROR_EXIT)
+ rr.stderr should include("Run 'wsk --help' for usage.")
+ rr.stderr should include(s"error: Invalid argument(s): $payload")
+ }
+
+ it should "not be able to use 'ping' in an action" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "ping"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("ping.js")))
}
- it should "reject an invoke with the wrong parameters set" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val fullQualifiedName = s"/$guestNamespace/samples/helloWorld"
- val payload = "bob"
- val rr = wsk.cli(Seq("action", "invoke", fullQualifiedName, payload) ++ wskprops.overrides,
- expectedExitCode = TestUtils.ERROR_EXIT)
- rr.stderr should include("Run 'wsk --help' for usage.")
- rr.stderr should include(s"error: Invalid argument(s): $payload")
+ val run = wsk.action.invoke(name, Map("payload" -> "google.com".toJson))
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(
+ JsObject("stderr" -> "ping: icmp open socket: Operation not permitted\n".toJson, "stdout" -> "".toJson))
}
+ }
- it should "not be able to use 'ping' in an action" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "ping"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("ping.js")))
- }
-
- val run = wsk.action.invoke(name, Map("payload" -> "google.com".toJson))
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.result shouldBe Some(JsObject(
- "stderr" -> "ping: icmp open socket: Operation not permitted\n".toJson,
- "stdout" -> "".toJson))
- }
+ ignore should "support UTF-8 as input and output format" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "utf8Test"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("hello.js")))
}
- ignore should "support UTF-8 as input and output format" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "utf8Test"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("hello.js")))
- }
-
- val utf8 = "«ταБЬℓσö»: 1<2 & 4+1>³, now 20%€§$ off!"
- val run = wsk.action.invoke(name, Map("payload" -> utf8.toJson))
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.status shouldBe "success"
- activation.logs.get.mkString(" ") should include(s"hello $utf8")
- }
+ val utf8 = "«ταБЬℓσö»: 1<2 & 4+1>³, now 20%€§$ off!"
+ val run = wsk.action.invoke(name, Map("payload" -> utf8.toJson))
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "success"
+ activation.logs.get.mkString(" ") should include(s"hello $utf8")
}
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskBasicTests.scala b/tests/src/test/scala/system/basic/WskBasicTests.scala
index d83cf6f..4ee00cb 100644
--- a/tests/src/test/scala/system/basic/WskBasicTests.scala
+++ b/tests/src/test/scala/system/basic/WskBasicTests.scala
@@ -37,823 +37,831 @@ import spray.json.DefaultJsonProtocol._
import spray.json.pimpAny
@RunWith(classOf[JUnitRunner])
-class WskBasicTests
- extends TestHelpers
- with WskTestHelpers {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val defaultAction = Some(TestCLIUtils.getTestActionFilename("hello.js"))
-
- behavior of "Wsk CLI"
-
- it should "reject creating duplicate entity" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "testDuplicateCreate"
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, name, confirmDelete = false) {
- (action, _) => action.create(name, defaultAction, expectedExitCode = CONFLICT)
- }
+class WskBasicTests extends TestHelpers with WskTestHelpers {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val defaultAction = Some(TestUtils.getTestActionFilename("hello.js"))
+
+ behavior of "Wsk CLI"
+
+ it should "reject creating duplicate entity" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "testDuplicateCreate"
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, name, confirmDelete = false) { (action, _) =>
+ action.create(name, defaultAction, expectedExitCode = CONFLICT)
+ }
+ }
+
+ it should "reject deleting entity in wrong collection" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "testCrossDelete"
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+ wsk.action.delete(name, expectedExitCode = CONFLICT)
+ }
+
+ it should "reject unauthenticated access" in {
+ implicit val wskprops = WskProps("xxx") // shadow properties
+ val errormsg = "The supplied authentication is invalid"
+ wsk.namespace.list(expectedExitCode = UNAUTHORIZED).stderr should include(errormsg)
+ wsk.namespace.get(expectedExitCode = UNAUTHORIZED).stderr should include(errormsg)
+ }
+
+ behavior of "Wsk Package CLI"
+
+ it should "create, update, get and list a package" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "testPackage"
+ val params = Map("a" -> "A".toJson)
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name, parameters = params, shared = Some(true))
+ pkg.create(name, update = true)
+ }
+ val stdout = wsk.pkg.get(name).stdout
+ stdout should include regex (""""key": "a"""")
+ stdout should include regex (""""value": "A"""")
+ stdout should include regex (""""publish": true""")
+ stdout should include regex (""""version": "0.0.2"""")
+ wsk.pkg.list().stdout should include(name)
+ }
+
+ it should "create, and get a package summary" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val packageName = "packageName"
+ val actionName = "actionName"
+ val packageAnnots = Map(
+ "description" -> JsString("Package description"),
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+ val actionAnnots = Map(
+ "description" -> JsString("Action description"),
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, annotations = packageAnnots)
+ }
+
+ wsk.action.create(packageName + "/" + actionName, defaultAction, annotations = actionAnnots)
+ val stdout = wsk.pkg.get(packageName, summary = true).stdout
+ val ns = wsk.namespace.whois()
+ wsk.action.delete(packageName + "/" + actionName)
+
+ stdout should include regex (s"(?i)package /$ns/$packageName: Package description\\s*\\(parameters: paramName1, paramName2\\)")
+ stdout should include regex (s"(?i)action /$ns/$packageName/$actionName: Action description\\s*\\(parameters: paramName1, paramName2\\)")
+ }
+
+ it should "create a package with a name that contains spaces" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "package with spaces"
+
+ val res = assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name)
+ }
+
+ res.stdout should include(s"ok: created package $name")
+ }
+
+ it should "create a package, and get its individual fields" in withAssetCleaner(wskprops) {
+ val name = "packageFields"
+ val paramInput = Map("payload" -> "test".toJson)
+ val successMsg = s"ok: got package $name, displaying field"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name, parameters = paramInput)
+ }
+
+ val expectedParam = JsObject("payload" -> JsString("test"))
+ val ns = wsk.namespace.whois()
+
+ wsk.pkg
+ .get(name, fieldFilter = Some("namespace"))
+ .stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
+ wsk.pkg.get(name, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
+ wsk.pkg.get(name, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
+ wsk.pkg.get(name, fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
+ wsk.pkg.get(name, fieldFilter = Some("binding")).stdout should include regex (s"""\\{\\}""")
+ wsk.pkg.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include(
+ "error: Invalid field filter 'invalid'.")
+ }
+
+ it should "reject creation of duplication packages" in withAssetCleaner(wskprops) {
+ val name = "dupePackage"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name)
+ }
+
+ val stderr = wsk.pkg.create(name, expectedExitCode = CONFLICT).stderr
+ stderr should include regex (s"""Unable to create package '$name': resource already exists \\(code \\d+\\)""")
+ }
+
+ it should "reject delete of package that does not exist" in {
+ val name = "nonexistentPackage"
+ val stderr = wsk.pkg.delete(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to delete package '$name'. The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject get of package that does not exist" in {
+ val name = "nonexistentPackage"
+ val stderr = wsk.pkg.get(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get package '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ behavior of "Wsk Action CLI"
+
+ it should "create the same action twice with different cases" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, "TWICE") { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.action, "twice") { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ }
+
+ it should "create, update, get and list an action" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "createAndUpdate"
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ val params = Map("a" -> "A".toJson)
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, parameters = params)
+ action.create(name, None, parameters = Map("b" -> "B".toJson), update = true)
+ }
+
+ val stdout = wsk.action.get(name).stdout
+ stdout should not include regex(""""key": "a"""")
+ stdout should not include regex(""""value": "A"""")
+ stdout should include regex (""""key": "b""")
+ stdout should include regex (""""value": "B"""")
+ stdout should include regex (""""publish": false""")
+ stdout should include regex (""""version": "0.0.2"""")
+ wsk.action.list().stdout should include(name)
+ }
+
+ it should "reject create of an action that already exists" in withAssetCleaner(wskprops) {
+ val name = "dupeAction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file)
+ }
+
+ val stderr = wsk.action.create(name, file, expectedExitCode = CONFLICT).stderr
+ stderr should include regex (s"""Unable to create action '$name': resource already exists \\(code \\d+\\)""")
+ }
+
+ it should "reject delete of action that does not exist" in {
+ val name = "nonexistentAction"
+ val stderr = wsk.action.delete(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to delete action '$name'. The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject invocation of action that does not exist" in {
+ val name = "nonexistentAction"
+ val stderr = wsk.action.invoke(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to invoke action '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject get of an action that does not exist" in {
+ val name = "nonexistentAction"
+ val stderr = wsk.action.get(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get action '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "create, and invoke an action that utilizes a docker container" in withAssetCleaner(wskprops) {
+ val name = "dockerContainer"
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, name) {
+ // this docker image will be need to be pulled from dockerhub and hence has to be published there first
+ (action, _) =>
+ action.create(name, None, docker = Some("openwhisk/example"))
+ }
+
+ val args = Map("payload" -> "test".toJson)
+ val run = wsk.action.invoke(name, args)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(
+ JsObject("args" -> args.toJson, "msg" -> "Hello from arbitrary C program!".toJson))
+ }
+ }
+
+ it should "create, and invoke an action that utilizes dockerskeleton with native zip" in withAssetCleaner(wskprops) {
+ val name = "dockerContainerWithZip"
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, name) {
+ // this docker image will be need to be pulled from dockerhub and hence has to be published there first
+ (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("blackbox.zip")), kind = Some("native"))
+ }
+
+ val run = wsk.action.invoke(name, Map())
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(JsObject("msg" -> "hello zip".toJson))
+ activation.logs shouldBe defined
+ val logs = activation.logs.get.toString
+ logs should include("This is an example zip used with the docker skeleton action.")
+ logs should not include ("XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX")
+ }
+ }
+
+ it should "create, and invoke an action using a parameter file" in withAssetCleaner(wskprops) {
+ val name = "paramFileAction"
+ val file = Some(TestUtils.getTestActionFilename("argCheck.js"))
+ val argInput = Some(TestUtils.getTestActionFilename("validInput2.json"))
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file)
+ }
+
+ val expectedOutput = JsObject("payload" -> JsString("test"))
+ val run = wsk.action.invoke(name, parameterFile = argInput)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(expectedOutput)
+ }
+ }
+
+ it should "create an action, and get its individual fields" in withAssetCleaner(wskprops) {
+ val name = "actionFields"
+ val paramInput = Map("payload" -> "test".toJson)
+ val successMsg = s"ok: got action $name, displaying field"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, defaultAction, parameters = paramInput)
+ }
+
+ val expectedParam = JsObject("payload" -> JsString("test"))
+ val ns = wsk.namespace.whois()
+
+ wsk.action.get(name, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
+ wsk.action.get(name, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
+ wsk.action.get(name, fieldFilter = Some("exec")).stdout should include(s"""$successMsg""")
+ wsk.action
+ .get(name, fieldFilter = Some("exec"))
+ .stdout should include regex (s"""$successMsg exec\n\\{\\s+"kind":\\s+"nodejs:6",\\s+"code":\\s+"\\/\\*\\*[\\\\r]*\\\\n \\* Hello, world.[\\\\r]*\\\\n \\*\\/[\\\\r]*\\\\nfunction main\\(params\\) \\{[\\\\r]*\\\\n greeting \\= 'hello, ' \\+ params.payload \\+ '!'[\\\\r]*\\\\n console.log\\(greeting\\);[\\\\r]*\\\\n return \\{payload: greeting\\}[\\\\r]*\\\\n\\}""")
+ wsk.action
+ .get(name, fieldFilter = Some("parameters"))
+ .stdout should include regex (s"""$successMsg parameters\n\\[\\s+\\{\\s+"key":\\s+"payload",\\s+"value":\\s+"test"\\s+\\}\\s+\\]""")
+ wsk.action
+ .get(name, fieldFilter = Some("annotations"))
+ .stdout should include regex (s"""$successMsg annotations\n\\[\\s+\\{\\s+"key":\\s+"exec",\\s+"value":\\s+"nodejs:6"\\s+\\}\\s+\\]""")
+ wsk.action
+ .get(name, fieldFilter = Some("limits"))
+ .stdout should include regex (s"""$successMsg limits\n\\{\\s+"timeout":\\s+60000,\\s+"memory":\\s+256,\\s+"logs":\\s+10\\s+\\}""")
+ wsk.action
+ .get(name, fieldFilter = Some("namespace"))
+ .stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
+ wsk.action.get(name, fieldFilter = Some("invalid"), expectedExitCode = MISUSE_EXIT).stderr should include(
+ "error: Invalid field filter 'invalid'.")
+ wsk.action.get(name, fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
+ }
+
+ /**
+ * Tests creating an action from a malformed js file. This should fail in
+ * some way - preferably when trying to create the action. If not, then
+ * surely when it runs there should be some indication in the logs. Don't
+ * think this is true currently.
+ */
+ it should "create and invoke action with malformed js resulting in activation error" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "MALFORMED"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("malformed.js")))
+ }
+
+ val run = wsk.action.invoke(name, Map("payload" -> "whatever".toJson))
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.status shouldBe "action developer error"
+ // representing nodejs giving an error when given malformed.js
+ activation.response.result.get.toString should include("ReferenceError")
+ }
+ }
+
+ it should "create and invoke a blocking action resulting in an application error response" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val name = "applicationError"
+ val strErrInput = Map("error" -> "Error message".toJson)
+ val numErrInput = Map("error" -> 502.toJson)
+ val boolErrInput = Map("error" -> true.toJson)
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("echo.js")))
+ }
+
+ Seq(strErrInput, numErrInput, boolErrInput) foreach { input =>
+ getJSONFromCLIResponse(
+ wsk.action.invoke(name, parameters = input, blocking = true, expectedExitCode = 246).stderr)
+ .fields("response")
+ .asJsObject
+ .fields("result")
+ .asJsObject shouldBe input.toJson.asJsObject
+
+ wsk.action
+ .invoke(name, parameters = input, blocking = true, result = true, expectedExitCode = 246)
+ .stderr
+ .parseJson
+ .asJsObject shouldBe input.toJson.asJsObject
+ }
+ }
+
+ it should "create and invoke a blocking action resulting in an failed promise" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "errorResponseObject"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("asyncError.js")))
+ }
+
+ val stderr = wsk.action.invoke(name, blocking = true, expectedExitCode = 246).stderr
+ ActivationResult.serdes.read(removeCLIHeader(stderr).parseJson).response.result shouldBe Some {
+ JsObject("error" -> JsObject("msg" -> "failed activation on purpose".toJson))
+ }
+ }
+
+ it should "invoke a blocking action and get only the result" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "basicInvoke"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("wc.js")))
+ }
+
+ wsk.action
+ .invoke(name, Map("payload" -> "one two three".toJson), result = true)
+ .stdout should include regex (""""count": 3""")
+ }
+
+ it should "create, and get an action summary" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "actionName"
+ val annots = Map(
+ "description" -> JsString("Action description"),
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, defaultAction, annotations = annots)
+ }
+
+ val stdout = wsk.action.get(name, summary = true).stdout
+ val ns = wsk.namespace.whois()
+
+ stdout should include regex (s"(?i)action /$ns/$name: Action description\\s*\\(parameters: paramName1, paramName2\\)")
+ }
+
+ it should "create an action with a name that contains spaces" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "action with spaces"
+
+ val res = assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, defaultAction)
+ }
+
+ res.stdout should include(s"ok: created action $name")
+ }
+
+ it should "create an action, and invoke an action that returns an empty JSON object" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "emptyJSONAction"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("emptyJSONResult.js")))
+ }
+
+ val stdout = wsk.action.invoke(name, result = true).stdout
+ stdout.parseJson.asJsObject shouldBe JsObject()
+ }
+
+ it should "create, and invoke an action that times out to ensure the proper response is received" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val name = "sleepAction"
+ val params = Map("payload" -> "100000".toJson)
+ val allowedActionDuration = 120 seconds
+ val res = assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("timeout.js")), timeout = Some(allowedActionDuration))
+ action.invoke(name, parameters = params, result = true, expectedExitCode = ACCEPTED)
}
- it should "reject deleting entity in wrong collection" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "testCrossDelete"
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name)
- }
- wsk.action.delete(name, expectedExitCode = CONFLICT)
+ res.stderr should include("""but the request has not yet finished""")
+ }
+
+ it should "create, and get docker action get ensure exec code is omitted" in withAssetCleaner(wskprops) {
+ val name = "dockerContainer"
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, None, docker = Some("fake-container"))
+ }
+
+ wsk.action.get(name).stdout should not include (""""code"""")
+ }
+
+ behavior of "Wsk Trigger CLI"
+
+ it should "create, update, get, fire and list trigger" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "listTriggers"
+ val params = Map("a" -> "A".toJson)
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name, parameters = params)
+ trigger.create(name, update = true)
+ }
+ val stdout = wsk.trigger.get(name).stdout
+ stdout should include regex (""""key": "a"""")
+ stdout should include regex (""""value": "A"""")
+ stdout should include regex (""""publish": false""")
+ stdout should include regex (""""version": "0.0.2"""")
+
+ val dynamicParams = Map("t" -> "T".toJson)
+ val run = wsk.trigger.fire(name, dynamicParams)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(dynamicParams.toJson)
+ activation.duration shouldBe 0L // shouldn't exist but CLI generates it
+ activation.end shouldBe Instant.EPOCH // shouldn't exist but CLI generates it
}
- it should "reject unauthenticated access" in {
- implicit val wskprops = WskProps("xxx") // shadow properties
- val errormsg = "The supplied authentication is invalid"
- wsk.namespace.list(expectedExitCode = UNAUTHORIZED).
- stderr should include(errormsg)
- wsk.namespace.get(expectedExitCode = UNAUTHORIZED).
- stderr should include(errormsg)
+ val runWithNoParams = wsk.trigger.fire(name, Map())
+ withActivation(wsk.activation, runWithNoParams) { activation =>
+ activation.response.result shouldBe Some(JsObject())
+ activation.duration shouldBe 0L // shouldn't exist but CLI generates it
+ activation.end shouldBe Instant.EPOCH // shouldn't exist but CLI generates it
}
- behavior of "Wsk Package CLI"
-
- it should "create, update, get and list a package" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "testPackage"
- val params = Map("a" -> "A".toJson)
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) =>
- pkg.create(name, parameters = params, shared = Some(true))
- pkg.create(name, update = true)
- }
- val stdout = wsk.pkg.get(name).stdout
- stdout should include regex (""""key": "a"""")
- stdout should include regex (""""value": "A"""")
- stdout should include regex (""""publish": true""")
- stdout should include regex (""""version": "0.0.2"""")
- wsk.pkg.list().stdout should include(name)
+ wsk.trigger.list().stdout should include(name)
+ }
+
+ it should "create, and get a trigger summary" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "triggerName"
+ val annots = Map(
+ "description" -> JsString("Trigger description"),
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name, annotations = annots)
}
- it should "create, and get a package summary" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val packageName = "packageName"
- val actionName = "actionName"
- val packageAnnots = Map(
- "description" -> JsString("Package description"),
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
- val actionAnnots = Map(
- "description" -> JsString("Action description"),
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
-
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, _) => pkg.create(packageName, annotations = packageAnnots)
- }
-
- wsk.action.create(packageName + "/" + actionName, defaultAction, annotations = actionAnnots)
- val stdout = wsk.pkg.get(packageName, summary = true).stdout
- val ns = wsk.namespace.whois()
- wsk.action.delete(packageName + "/" + actionName)
-
- stdout should include regex (s"(?i)package /$ns/$packageName: Package description\\s*\\(parameters: paramName1, paramName2\\)")
- stdout should include regex (s"(?i)action /$ns/$packageName/$actionName: Action description\\s*\\(parameters: paramName1, paramName2\\)")
- }
-
- it should "create a package with a name that contains spaces" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "package with spaces"
-
- val res = assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) => pkg.create(name)
- }
-
- res.stdout should include(s"ok: created package $name")
- }
-
- it should "create a package, and get its individual fields" in withAssetCleaner(wskprops) {
- val name = "packageFields"
- val paramInput = Map("payload" -> "test".toJson)
- val successMsg = s"ok: got package $name, displaying field"
-
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) => pkg.create(name, parameters = paramInput)
- }
-
- val expectedParam = JsObject("payload" -> JsString("test"))
- val ns = wsk.namespace.whois()
-
- wsk.pkg.get(name, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
- wsk.pkg.get(name, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
- wsk.pkg.get(name, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
- wsk.pkg.get(name, fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
- wsk.pkg.get(name, fieldFilter = Some("binding")).stdout should include regex (s"""\\{\\}""")
- wsk.pkg.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include("error: Invalid field filter 'invalid'.")
- }
-
- it should "reject creation of duplication packages" in withAssetCleaner(wskprops) {
- val name = "dupePackage"
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) => pkg.create(name)
- }
-
- val stderr = wsk.pkg.create(name, expectedExitCode = CONFLICT).stderr
- stderr should include regex (s"""Unable to create package '$name': resource already exists \\(code \\d+\\)""")
- }
-
- it should "reject delete of package that does not exist" in {
- val name = "nonexistentPackage"
- val stderr = wsk.pkg.delete(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to delete package '$name'. The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject get of package that does not exist" in {
- val name = "nonexistentPackage"
- val stderr = wsk.pkg.get(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get package '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- behavior of "Wsk Action CLI"
-
- it should "create the same action twice with different cases" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.action, "TWICE") { (action, name) => action.create(name, defaultAction) }
- assetHelper.withCleaner(wsk.action, "twice") { (action, name) => action.create(name, defaultAction) }
- }
-
- it should "create, update, get and list an action" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "createAndUpdate"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- val params = Map("a" -> "A".toJson)
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, parameters = params)
- action.create(name, None, parameters = Map("b" -> "B".toJson), update = true)
- }
-
- val stdout = wsk.action.get(name).stdout
- stdout should not include regex(""""key": "a"""")
- stdout should not include regex(""""value": "A"""")
- stdout should include regex (""""key": "b""")
- stdout should include regex (""""value": "B"""")
- stdout should include regex (""""publish": false""")
- stdout should include regex (""""version": "0.0.2"""")
- wsk.action.list().stdout should include(name)
- }
-
- it should "reject create of an action that already exists" in withAssetCleaner(wskprops) {
- val name = "dupeAction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file)
- }
-
- val stderr = wsk.action.create(name, file, expectedExitCode = CONFLICT).stderr
- stderr should include regex (s"""Unable to create action '$name': resource already exists \\(code \\d+\\)""")
- }
-
- it should "reject delete of action that does not exist" in {
- val name = "nonexistentAction"
- val stderr = wsk.action.delete(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to delete action '$name'. The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject invocation of action that does not exist" in {
- val name = "nonexistentAction"
- val stderr = wsk.action.invoke(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to invoke action '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject get of an action that does not exist" in {
- val name = "nonexistentAction"
- val stderr = wsk.action.get(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get action '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "create, and invoke an action that utilizes a docker container" in withAssetCleaner(wskprops) {
- val name = "dockerContainer"
- (wp, assetHelper) => assetHelper.withCleaner(wsk.action, name) {
- // this docker image will be need to be pulled from dockerhub and hence has to be published there first
- (action, _) => action.create(name, None, docker = Some("openwhisk/example"))
- }
-
- val args = Map("payload" -> "test".toJson)
- val run = wsk.action.invoke(name, args)
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.result shouldBe Some(JsObject(
- "args" -> args.toJson,
- "msg" -> "Hello from arbitrary C program!".toJson))
- }
- }
-
- it should "create, and invoke an action that utilizes dockerskeleton with native zip" in withAssetCleaner(wskprops) {
- val name = "dockerContainerWithZip"
- (wp, assetHelper) => assetHelper.withCleaner(wsk.action, name) {
- // this docker image will be need to be pulled from dockerhub and hence has to be published there first
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("blackbox.zip")), kind = Some("native"))
- }
-
- val run = wsk.action.invoke(name, Map())
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.result shouldBe Some(JsObject(
- "msg" -> "hello zip".toJson))
- activation.logs shouldBe defined
- val logs = activation.logs.get.toString
- logs should include("This is an example zip used with the docker skeleton action.")
- logs should not include ("XXX_THE_END_OF_A_WHISK_ACTIVATION_XXX")
- }
- }
-
- it should "create, and invoke an action using a parameter file" in withAssetCleaner(wskprops) {
- val name = "paramFileAction"
- val file = Some(TestCLIUtils.getTestActionFilename("argCheck.js"))
- val argInput = Some(TestCLIUtils.getTestActionFilename("validInput2.json"))
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file)
- }
-
- val expectedOutput = JsObject("payload" -> JsString("test"))
- val run = wsk.action.invoke(name, parameterFile = argInput)
- withActivation(wsk.activation, run) {
- activation => activation.response.result shouldBe Some(expectedOutput)
- }
- }
-
- it should "create an action, and get its individual fields" in withAssetCleaner(wskprops) {
- val name = "actionFields"
- val paramInput = Map("payload" -> "test".toJson)
- val successMsg = s"ok: got action $name, displaying field"
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, defaultAction, parameters = paramInput)
- }
-
- val expectedParam = JsObject("payload" -> JsString("test"))
- val ns = wsk.namespace.whois()
-
- wsk.action.get(name, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
- wsk.action.get(name, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
- wsk.action.get(name, fieldFilter = Some("exec")).stdout should include(s"""$successMsg""")
- wsk.action.get(name, fieldFilter = Some("exec")).stdout should include regex (s"""$successMsg exec\n\\{\\s+"kind":\\s+"nodejs:6",\\s+"code":\\s+"\\/\\*\\*[\\\\r]*\\\\n \\* Hello, world.[\\\\r]*\\\\n \\*\\/[\\\\r]*\\\\nfunction main\\(params\\) \\{[\\\\r]*\\\\n greeting \\= 'hello, ' \\+ params.payload \\+ '!'[\\\\r]*\\\\n console.log\\(greeting\\);[\\\\r]*\\\\n return \\{payload: greeting\\}[\\\\r]*\\\\n\\}""")
- wsk.action.get(name, fieldFilter = Some("parameters")).stdout should include regex (s"""$successMsg parameters\n\\[\\s+\\{\\s+"key":\\s+"payload",\\s+"value":\\s+"test"\\s+\\}\\s+\\]""")
- wsk.action.get(name, fieldFilter = Some("annotations")).stdout should include regex (s"""$successMsg annotations\n\\[\\s+\\{\\s+"key":\\s+"exec",\\s+"value":\\s+"nodejs:6"\\s+\\}\\s+\\]""")
- wsk.action.get(name, fieldFilter = Some("limits")).stdout should include regex (s"""$successMsg limits\n\\{\\s+"timeout":\\s+60000,\\s+"memory":\\s+256,\\s+"logs":\\s+10\\s+\\}""")
- wsk.action.get(name, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
- wsk.action.get(name, fieldFilter = Some("invalid"), expectedExitCode = MISUSE_EXIT).stderr should include("error: Invalid field filter 'invalid'.")
- wsk.action.get(name, fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
- }
-
- /**
- * Tests creating an action from a malformed js file. This should fail in
- * some way - preferably when trying to create the action. If not, then
- * surely when it runs there should be some indication in the logs. Don't
- * think this is true currently.
- */
- it should "create and invoke action with malformed js resulting in activation error" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "MALFORMED"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("malformed.js")))
- }
-
- val run = wsk.action.invoke(name, Map("payload" -> "whatever".toJson))
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.status shouldBe "action developer error"
- // representing nodejs giving an error when given malformed.js
- activation.response.result.get.toString should include("ReferenceError")
- }
- }
-
- it should "create and invoke a blocking action resulting in an application error response" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "applicationError"
- val strErrInput = Map("error" -> "Error message".toJson)
- val numErrInput = Map("error" -> 502.toJson)
- val boolErrInput = Map("error" -> true.toJson)
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("echo.js")))
- }
-
- Seq(strErrInput, numErrInput, boolErrInput) foreach { input =>
- getJSONFromCLIResponse(wsk.action.invoke(name, parameters = input, blocking = true, expectedExitCode = 246).stderr).
- fields("response").asJsObject.fields("result").asJsObject shouldBe input.toJson.asJsObject
-
- wsk.action.invoke(name, parameters = input, blocking = true, result = true, expectedExitCode = 246).
- stderr.parseJson.asJsObject shouldBe input.toJson.asJsObject
- }
- }
-
- it should "create and invoke a blocking action resulting in an failed promise" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "errorResponseObject"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("asyncError.js")))
- }
-
- val stderr = wsk.action.invoke(name, blocking = true, expectedExitCode = 246).stderr
- ActivationResult.serdes.read(removeCLIHeader(stderr).parseJson).response.result shouldBe Some {
- JsObject("error" -> JsObject("msg" -> "failed activation on purpose".toJson))
- }
- }
-
- it should "invoke a blocking action and get only the result" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "basicInvoke"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("wc.js")))
- }
-
- wsk.action.invoke(name, Map("payload" -> "one two three".toJson), result = true)
- .stdout should include regex (""""count": 3""")
- }
-
- it should "create, and get an action summary" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "actionName"
- val annots = Map(
- "description" -> JsString("Action description"),
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, defaultAction, annotations = annots)
- }
-
- val stdout = wsk.action.get(name, summary = true).stdout
- val ns = wsk.namespace.whois()
-
- stdout should include regex (s"(?i)action /$ns/$name: Action description\\s*\\(parameters: paramName1, paramName2\\)")
- }
-
- it should "create an action with a name that contains spaces" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "action with spaces"
-
- val res = assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, defaultAction)
- }
-
- res.stdout should include(s"ok: created action $name")
- }
-
- it should "create an action, and invoke an action that returns an empty JSON object" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "emptyJSONAction"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, Some(TestCLIUtils.getTestActionFilename("emptyJSONResult.js")))
- }
-
- val stdout = wsk.action.invoke(name, result = true).stdout
- stdout.parseJson.asJsObject shouldBe JsObject()
- }
-
- it should "create, and invoke an action that times out to ensure the proper response is received" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "sleepAction"
- val params = Map("payload" -> "100000".toJson)
- val allowedActionDuration = 120 seconds
- val res = assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, Some(TestCLIUtils.getTestActionFilename("timeout.js")),
- timeout = Some(allowedActionDuration))
- action.invoke(name, parameters = params, result = true, expectedExitCode = ACCEPTED)
- }
-
- res.stderr should include("""but the request has not yet finished""")
- }
-
- it should "create, and get docker action get ensure exec code is omitted" in withAssetCleaner(wskprops) {
- val name = "dockerContainer"
- (wp, assetHelper) => assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, None, docker = Some("fake-container"))
- }
+ val stdout = wsk.trigger.get(name, summary = true).stdout
+ val ns = wsk.namespace.whois()
- wsk.action.get(name).stdout should not include (""""code"""")
- }
-
- behavior of "Wsk Trigger CLI"
-
- it should "create, update, get, fire and list trigger" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "listTriggers"
- val params = Map("a" -> "A".toJson)
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) =>
- trigger.create(name, parameters = params)
- trigger.create(name, update = true)
- }
- val stdout = wsk.trigger.get(name).stdout
- stdout should include regex (""""key": "a"""")
- stdout should include regex (""""value": "A"""")
- stdout should include regex (""""publish": false""")
- stdout should include regex (""""version": "0.0.2"""")
-
- val dynamicParams = Map("t" -> "T".toJson)
- val run = wsk.trigger.fire(name, dynamicParams)
- withActivation(wsk.activation, run) {
- activation =>
- activation.response.result shouldBe Some(dynamicParams.toJson)
- activation.duration shouldBe 0L // shouldn't exist but CLI generates it
- activation.end shouldBe Instant.EPOCH // shouldn't exist but CLI generates it
- }
-
- val runWithNoParams = wsk.trigger.fire(name, Map())
- withActivation(wsk.activation, runWithNoParams) {
- activation =>
- activation.response.result shouldBe Some(JsObject())
- activation.duration shouldBe 0L // shouldn't exist but CLI generates it
- activation.end shouldBe Instant.EPOCH // shouldn't exist but CLI generates it
- }
-
- wsk.trigger.list().stdout should include(name)
- }
-
- it should "create, and get a trigger summary" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "triggerName"
- val annots = Map(
- "description" -> JsString("Trigger description"),
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
-
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) =>
- trigger.create(name, annotations = annots)
- }
-
- val stdout = wsk.trigger.get(name, summary = true).stdout
- val ns = wsk.namespace.whois()
-
- stdout should include regex (s"trigger /$ns/$name: Trigger description\\s*\\(parameters: paramName1, paramName2\\)")
- }
-
- it should "create a trigger with a name that contains spaces" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "trigger with spaces"
-
- val res = assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name)
- }
-
- res.stdout should include regex (s"ok: created trigger $name")
- }
-
- it should "create, and fire a trigger using a parameter file" in withAssetCleaner(wskprops) {
- val name = "paramFileTrigger"
- val file = Some(TestCLIUtils.getTestActionFilename("argCheck.js"))
- val argInput = Some(TestCLIUtils.getTestActionFilename("validInput2.json"))
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name)
- }
-
- val expectedOutput = JsObject("payload" -> JsString("test"))
- val run = wsk.trigger.fire(name, parameterFile = argInput)
- withActivation(wsk.activation, run) {
- activation => activation.response.result shouldBe Some(expectedOutput)
- }
- }
-
- it should "create a trigger, and get its individual fields" in withAssetCleaner(wskprops) {
- val name = "triggerFields"
- val paramInput = Map("payload" -> "test".toJson)
- val successMsg = s"ok: got trigger $name, displaying field"
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name, parameters = paramInput)
- }
-
- val expectedParam = JsObject("payload" -> JsString("test"))
- val ns = wsk.namespace.whois()
-
- wsk.trigger.get(name, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
- wsk.trigger.get(name, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
- wsk.trigger.get(name, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
- wsk.trigger.get(name, fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
- wsk.trigger.get(name, fieldFilter = Some("annotations")).stdout should include(s"""$successMsg annotations\n[]""")
- wsk.trigger.get(name, fieldFilter = Some("parameters")).stdout should include regex (s"""$successMsg parameters\n\\[\\s+\\{\\s+"key":\\s+"payload",\\s+"value":\\s+"test"\\s+\\}\\s+\\]""")
- wsk.trigger.get(name, fieldFilter = Some("limits")).stdout should include(s"""$successMsg limits\n{}""")
- wsk.trigger.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include("error: Invalid field filter 'invalid'.")
- }
-
- it should "create, and fire a trigger to ensure result is empty" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "emptyResultTrigger"
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name)
- }
-
- val run = wsk.trigger.fire(name)
- withActivation(wsk.activation, run) {
- activation => activation.response.result shouldBe Some(JsObject())
- }
- }
-
- it should "reject creation of duplicate triggers" in withAssetCleaner(wskprops) {
- val name = "dupeTrigger"
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) => trigger.create(name)
- }
-
- val stderr = wsk.trigger.create(name, expectedExitCode = CONFLICT).stderr
- stderr should include regex (s"""Unable to create trigger '$name': resource already exists \\(code \\d+\\)""")
- }
-
- it should "reject delete of trigger that does not exist" in {
- val name = "nonexistentTrigger"
- val stderr = wsk.trigger.delete(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get trigger '$name'. The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject get of trigger that does not exist" in {
- val name = "nonexistentTrigger"
- val stderr = wsk.trigger.get(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get trigger '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject firing of a trigger that does not exist" in {
- val name = "nonexistentTrigger"
- val stderr = wsk.trigger.fire(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to fire trigger '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- behavior of "Wsk Rule CLI"
-
- it should "create rule, get rule, update rule and list rule" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = "listRules"
- val triggerName = "listRulesTrigger"
- val actionName = "listRulesAction"
-
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) =>
- rule.create(name, trigger = triggerName, action = actionName)
- }
-
- // finally, we perform the update, and expect success this time
- wsk.rule.create(ruleName, trigger = triggerName, action = actionName, update = true)
-
- val stdout = wsk.rule.get(ruleName).stdout
- stdout should include(ruleName)
- stdout should include(triggerName)
- stdout should include(actionName)
- stdout should include regex (""""version": "0.0.2"""")
- wsk.rule.list().stdout should include(ruleName)
- }
-
- it should "create rule, get rule, ensure rule is enabled by default" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = "enabledRule"
- val triggerName = "enabledRuleTrigger"
- val actionName = "enabledRuleAction"
-
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
- }
-
- val stdout = wsk.rule.get(ruleName).stdout
- stdout should include regex (""""status":\s*"active"""")
- }
-
- it should "display a rule summary when --summary flag is used with 'wsk rule get'" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = "mySummaryRule"
- val triggerName = "summaryRuleTrigger"
- val actionName = "summaryRuleAction"
-
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- assetHelper.withCleaner(wsk.rule, ruleName, confirmDelete = false) {
- (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
- }
-
- // Summary namespace should match one of the allowable namespaces (typically 'guest')
- val ns = wsk.namespace.whois()
- val stdout = wsk.rule.get(ruleName, summary = true).stdout
-
- stdout should include regex (s"(?i)rule /$ns/$ruleName\\s*\\(status: active\\)")
- }
-
- it should "create a rule, and get its individual fields" in withAssetCleaner(wskprops) {
- val ruleName = "ruleFields"
- val triggerName = "ruleTriggerFields"
- val actionName = "ruleActionFields"
- val paramInput = Map("payload" -> "test".toJson)
- val successMsg = s"ok: got rule $ruleName, displaying field"
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
- }
-
- val ns = wsk.namespace.whois()
- wsk.rule.get(ruleName, fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
- wsk.rule.get(ruleName, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$ruleName"""")
- wsk.rule.get(ruleName, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"\n""")
- wsk.rule.get(ruleName, fieldFilter = Some("status")).stdout should include(s"""$successMsg status\n"active"""")
- val trigger = wsk.rule.get(ruleName, fieldFilter = Some("trigger")).stdout
- trigger should include regex (s"""$successMsg trigger\n""")
- trigger should include(triggerName)
- trigger should not include (actionName)
- val action = wsk.rule.get(ruleName, fieldFilter = Some("action")).stdout
- action should include regex (s"""$successMsg action\n""")
- action should include(actionName)
- action should not include (triggerName)
- }
-
- it should "reject creation of duplicate rules" in withAssetCleaner(wskprops) {
- val ruleName = "dupeRule"
- val triggerName = "triggerName"
- val actionName = "actionName"
-
- (wp, assetHelper) => assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) => rule.create(name, trigger = triggerName, action = actionName)
- }
-
- val stderr = wsk.rule.create(ruleName, trigger = triggerName, action = actionName, expectedExitCode = CONFLICT).stderr
- stderr should include regex (s"""Unable to create rule '$ruleName': resource already exists \\(code \\d+\\)""")
- }
-
- it should "reject delete of rule that does not exist" in {
- val name = "nonexistentRule"
- val stderr = wsk.rule.delete(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to delete rule '$name'. The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject enable of rule that does not exist" in {
- val name = "nonexistentRule"
- val stderr = wsk.rule.enable(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to enable rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject disable of rule that does not exist" in {
- val name = "nonexistentRule"
- val stderr = wsk.rule.disable(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to disable rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject status of rule that does not exist" in {
- val name = "nonexistentRule"
- val stderr = wsk.rule.state(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get status of rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject get of rule that does not exist" in {
- val name = "nonexistentRule"
- val stderr = wsk.rule.get(name, expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- behavior of "Wsk Namespace CLI"
-
- it should "return a list of exactly one namespace" in {
- val lines = wsk.namespace.list().stdout.lines.toSeq
- lines should have size 2
- lines.head shouldBe "namespaces"
- lines(1).trim should not be empty
- }
-
- it should "list entities in default namespace" in {
- // use a fresh wsk props instance that is guaranteed to use
- // the default namespace
- wsk.namespace.get(expectedExitCode = SUCCESS_EXIT)(WskProps()).
- stdout should include("default")
- }
-
- it should "not list entities with an invalid namespace" in {
- val namespace = "fakeNamespace"
- val stderr = wsk.namespace.get(Some(s"/${namespace}"), expectedExitCode = FORBIDDEN).stderr
-
- stderr should include(s"Unable to obtain the list of entities for namespace '${namespace}'")
- }
-
- behavior of "Wsk Activation CLI"
-
- it should "create a trigger, and fire a trigger to get its individual fields from an activation" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "activationFields"
-
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) =>
- trigger.create(name)
- }
-
- val ns = s""""${wsk.namespace.whois()}""""
- val run = wsk.trigger.fire(name)
- withActivation(wsk.activation, run) {
- activation =>
- val successMsg = s"ok: got activation ${activation.activationId}, displaying field"
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("namespace")).stdout should include regex (s"""(?i)$successMsg namespace\n$ns""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("subject")).stdout should include regex (s"""(?i)$successMsg subject\n""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("activationid")).stdout should include(s"""$successMsg activationid\n"${activation.activationId}""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("start")).stdout should include regex (s"""$successMsg start\n\\d""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("end")).stdout should include regex (s"""$successMsg end\n\\d""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("duration")).stdout should include regex (s"""$successMsg duration\n\\d""")
- wsk.activation.get(Some(activation.activationId), fieldFilter = Some("annotations")).stdout should include(s"""$successMsg annotations\n[]""")
- }
- }
-
- it should "reject get of activation that does not exist" in {
- val name = "0" * 32
- val stderr = wsk.activation.get(Some(name), expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get activation '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject logs of activation that does not exist" in {
- val name = "0" * 32
- val stderr = wsk.activation.logs(Some(name), expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get logs for activation '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject result of activation that does not exist" in {
- val name = "0" * 32
- val stderr = wsk.activation.result(Some(name), expectedExitCode = NOT_FOUND).stderr
- stderr should include regex (s"""Unable to get result for activation '$name': The requested resource does not exist. \\(code \\d+\\)""")
- }
-
- it should "reject activation request when using activation ID with --last Flag" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val auth: Seq[String] = Seq("--auth", wskprops.authKey)
-
- val lastId = "dummyActivationId"
- val tooManyArgsMsg = s"${lastId}. An activation ID is required."
- val invalidField = s"Invalid field filter '${lastId}'."
-
- val invalidCmd = Seq(
- (Seq("activation", "get", s"$lastId", "publish", "--last"), tooManyArgsMsg),
- (Seq("activation", "get", s"$lastId", "--last"), invalidField),
- (Seq("activation", "logs", s"$lastId", "--last"), tooManyArgsMsg),
- (Seq("activation", "result", s"$lastId", "--last"), tooManyArgsMsg))
-
- invalidCmd foreach {
- case (cmd, err) =>
- val stderr = wsk.cli(cmd ++ wskprops.overrides ++ auth, expectedExitCode = ERROR_EXIT).stderr
- stderr should include(err)
- }
- }
+ stdout should include regex (s"trigger /$ns/$name: Trigger description\\s*\\(parameters: paramName1, paramName2\\)")
+ }
+
+ it should "create a trigger with a name that contains spaces" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "trigger with spaces"
+
+ val res = assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+
+ res.stdout should include regex (s"ok: created trigger $name")
+ }
+
+ it should "create, and fire a trigger using a parameter file" in withAssetCleaner(wskprops) {
+ val name = "paramFileTrigger"
+ val file = Some(TestUtils.getTestActionFilename("argCheck.js"))
+ val argInput = Some(TestUtils.getTestActionFilename("validInput2.json"))
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+
+ val expectedOutput = JsObject("payload" -> JsString("test"))
+ val run = wsk.trigger.fire(name, parameterFile = argInput)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(expectedOutput)
+ }
+ }
+
+ it should "create a trigger, and get its individual fields" in withAssetCleaner(wskprops) {
+ val name = "triggerFields"
+ val paramInput = Map("payload" -> "test".toJson)
+ val successMsg = s"ok: got trigger $name, displaying field"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name, parameters = paramInput)
+ }
+
+ val expectedParam = JsObject("payload" -> JsString("test"))
+ val ns = wsk.namespace.whois()
+
+ wsk.trigger
+ .get(name, fieldFilter = Some("namespace"))
+ .stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
+ wsk.trigger.get(name, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$name"""")
+ wsk.trigger.get(name, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"""")
+ wsk.trigger.get(name, fieldFilter = Some("publish")).stdout should include(s"""$successMsg publish\nfalse""")
+ wsk.trigger.get(name, fieldFilter = Some("annotations")).stdout should include(s"""$successMsg annotations\n[]""")
+ wsk.trigger
+ .get(name, fieldFilter = Some("parameters"))
+ .stdout should include regex (s"""$successMsg parameters\n\\[\\s+\\{\\s+"key":\\s+"payload",\\s+"value":\\s+"test"\\s+\\}\\s+\\]""")
+ wsk.trigger.get(name, fieldFilter = Some("limits")).stdout should include(s"""$successMsg limits\n{}""")
+ wsk.trigger.get(name, fieldFilter = Some("invalid"), expectedExitCode = ERROR_EXIT).stderr should include(
+ "error: Invalid field filter 'invalid'.")
+ }
+
+ it should "create, and fire a trigger to ensure result is empty" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "emptyResultTrigger"
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+
+ val run = wsk.trigger.fire(name)
+ withActivation(wsk.activation, run) { activation =>
+ activation.response.result shouldBe Some(JsObject())
+ }
+ }
+
+ it should "reject creation of duplicate triggers" in withAssetCleaner(wskprops) {
+ val name = "dupeTrigger"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+
+ val stderr = wsk.trigger.create(name, expectedExitCode = CONFLICT).stderr
+ stderr should include regex (s"""Unable to create trigger '$name': resource already exists \\(code \\d+\\)""")
+ }
+
+ it should "reject delete of trigger that does not exist" in {
+ val name = "nonexistentTrigger"
+ val stderr = wsk.trigger.delete(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get trigger '$name'. The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject get of trigger that does not exist" in {
+ val name = "nonexistentTrigger"
+ val stderr = wsk.trigger.get(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get trigger '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject firing of a trigger that does not exist" in {
+ val name = "nonexistentTrigger"
+ val stderr = wsk.trigger.fire(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to fire trigger '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ behavior of "Wsk Rule CLI"
+
+ it should "create rule, get rule, update rule and list rule" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val ruleName = "listRules"
+ val triggerName = "listRulesTrigger"
+ val actionName = "listRulesAction"
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+
+ // finally, we perform the update, and expect success this time
+ wsk.rule.create(ruleName, trigger = triggerName, action = actionName, update = true)
+
+ val stdout = wsk.rule.get(ruleName).stdout
+ stdout should include(ruleName)
+ stdout should include(triggerName)
+ stdout should include(actionName)
+ stdout should include regex (""""version": "0.0.2"""")
+ wsk.rule.list().stdout should include(ruleName)
+ }
+
+ it should "create rule, get rule, ensure rule is enabled by default" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = "enabledRule"
+ val triggerName = "enabledRuleTrigger"
+ val actionName = "enabledRuleAction"
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+
+ val stdout = wsk.rule.get(ruleName).stdout
+ stdout should include regex (""""status":\s*"active"""")
+ }
+
+ it should "display a rule summary when --summary flag is used with 'wsk rule get'" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = "mySummaryRule"
+ val triggerName = "summaryRuleTrigger"
+ val actionName = "summaryRuleAction"
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName, confirmDelete = false) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+
+ // Summary namespace should match one of the allowable namespaces (typically 'guest')
+ val ns = wsk.namespace.whois()
+ val stdout = wsk.rule.get(ruleName, summary = true).stdout
+
+ stdout should include regex (s"(?i)rule /$ns/$ruleName\\s*\\(status: active\\)")
+ }
+
+ it should "create a rule, and get its individual fields" in withAssetCleaner(wskprops) {
+ val ruleName = "ruleFields"
+ val triggerName = "ruleTriggerFields"
+ val actionName = "ruleActionFields"
+ val paramInput = Map("payload" -> "test".toJson)
+ val successMsg = s"ok: got rule $ruleName, displaying field"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+
+ val ns = wsk.namespace.whois()
+ wsk.rule
+ .get(ruleName, fieldFilter = Some("namespace"))
+ .stdout should include regex (s"""(?i)$successMsg namespace\n"$ns"""")
+ wsk.rule.get(ruleName, fieldFilter = Some("name")).stdout should include(s"""$successMsg name\n"$ruleName"""")
+ wsk.rule.get(ruleName, fieldFilter = Some("version")).stdout should include(s"""$successMsg version\n"0.0.1"\n""")
+ wsk.rule.get(ruleName, fieldFilter = Some("status")).stdout should include(s"""$successMsg status\n"active"""")
+ val trigger = wsk.rule.get(ruleName, fieldFilter = Some("trigger")).stdout
+ trigger should include regex (s"""$successMsg trigger\n""")
+ trigger should include(triggerName)
+ trigger should not include (actionName)
+ val action = wsk.rule.get(ruleName, fieldFilter = Some("action")).stdout
+ action should include regex (s"""$successMsg action\n""")
+ action should include(actionName)
+ action should not include (triggerName)
+ }
+
+ it should "reject creation of duplicate rules" in withAssetCleaner(wskprops) {
+ val ruleName = "dupeRule"
+ val triggerName = "triggerName"
+ val actionName = "actionName"
+
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+
+ val stderr =
+ wsk.rule.create(ruleName, trigger = triggerName, action = actionName, expectedExitCode = CONFLICT).stderr
+ stderr should include regex (s"""Unable to create rule '$ruleName': resource already exists \\(code \\d+\\)""")
+ }
+
+ it should "reject delete of rule that does not exist" in {
+ val name = "nonexistentRule"
+ val stderr = wsk.rule.delete(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to delete rule '$name'. The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject enable of rule that does not exist" in {
+ val name = "nonexistentRule"
+ val stderr = wsk.rule.enable(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to enable rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject disable of rule that does not exist" in {
+ val name = "nonexistentRule"
+ val stderr = wsk.rule.disable(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to disable rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject status of rule that does not exist" in {
+ val name = "nonexistentRule"
+ val stderr = wsk.rule.state(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get status of rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject get of rule that does not exist" in {
+ val name = "nonexistentRule"
+ val stderr = wsk.rule.get(name, expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get rule '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ behavior of "Wsk Namespace CLI"
+
+ it should "return a list of exactly one namespace" in {
+ val lines = wsk.namespace.list().stdout.lines.toSeq
+ lines should have size 2
+ lines.head shouldBe "namespaces"
+ lines(1).trim should not be empty
+ }
+
+ it should "list entities in default namespace" in {
+ // use a fresh wsk props instance that is guaranteed to use
+ // the default namespace
+ wsk.namespace.get(expectedExitCode = SUCCESS_EXIT)(WskProps()).stdout should include("default")
+ }
+
+ it should "not list entities with an invalid namespace" in {
+ val namespace = "fakeNamespace"
+ val stderr = wsk.namespace.get(Some(s"/${namespace}"), expectedExitCode = FORBIDDEN).stderr
+
+ stderr should include(s"Unable to obtain the list of entities for namespace '${namespace}'")
+ }
+
+ behavior of "Wsk Activation CLI"
+
+ it should "create a trigger, and fire a trigger to get its individual fields from an activation" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val name = "activationFields"
+
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+
+ val ns = s""""${wsk.namespace.whois()}""""
+ val run = wsk.trigger.fire(name)
+ withActivation(wsk.activation, run) { activation =>
+ val successMsg = s"ok: got activation ${activation.activationId}, displaying field"
+ wsk.activation
+ .get(Some(activation.activationId), fieldFilter = Some("namespace"))
+ .stdout should include regex (s"""(?i)$successMsg namespace\n$ns""")
+ wsk.activation.get(Some(activation.activationId), fieldFilter = Some("name")).stdout should include(
+ s"""$successMsg name\n"$name"""")
+ wsk.activation.get(Some(activation.activationId), fieldFilter = Some("version")).stdout should include(
+ s"""$successMsg version\n"0.0.1"""")
+ wsk.activation.get(Some(activation.activationId), fieldFilter = Some("publish")).stdout should include(
+ s"""$successMsg publish\nfalse""")
+ wsk.activation
+ .get(Some(activation.activationId), fieldFilter = Some("subject"))
+ .stdout should include regex (s"""(?i)$successMsg subject\n""")
+ wsk.activation.get(Some(activation.activationId), fieldFilter = Some("activationid")).stdout should include(
+ s"""$successMsg activationid\n"${activation.activationId}""")
+ wsk.activation
+ .get(Some(activation.activationId), fieldFilter = Some("start"))
+ .stdout should include regex (s"""$successMsg start\n\\d""")
+ wsk.activation
+ .get(Some(activation.activationId), fieldFilter = Some("end"))
+ .stdout should include regex (s"""$successMsg end\n\\d""")
+ wsk.activation
+ .get(Some(activation.activationId), fieldFilter = Some("duration"))
+ .stdout should include regex (s"""$successMsg duration\n\\d""")
+ wsk.activation.get(Some(activation.activationId), fieldFilter = Some("annotations")).stdout should include(
+ s"""$successMsg annotations\n[]""")
+ }
+ }
+
+ it should "reject get of activation that does not exist" in {
+ val name = "0" * 32
+ val stderr = wsk.activation.get(Some(name), expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get activation '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject logs of activation that does not exist" in {
+ val name = "0" * 32
+ val stderr = wsk.activation.logs(Some(name), expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get logs for activation '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject result of activation that does not exist" in {
+ val name = "0" * 32
+ val stderr = wsk.activation.result(Some(name), expectedExitCode = NOT_FOUND).stderr
+ stderr should include regex (s"""Unable to get result for activation '$name': The requested resource does not exist. \\(code \\d+\\)""")
+ }
+
+ it should "reject activation request when using activation ID with --last Flag" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val auth: Seq[String] = Seq("--auth", wskprops.authKey)
+
+ val lastId = "dummyActivationId"
+ val tooManyArgsMsg = s"${lastId}. An activation ID is required."
+ val invalidField = s"Invalid field filter '${lastId}'."
+
+ val invalidCmd = Seq(
+ (Seq("activation", "get", s"$lastId", "publish", "--last"), tooManyArgsMsg),
+ (Seq("activation", "get", s"$lastId", "--last"), invalidField),
+ (Seq("activation", "logs", s"$lastId", "--last"), tooManyArgsMsg),
+ (Seq("activation", "result", s"$lastId", "--last"), tooManyArgsMsg))
+
+ invalidCmd foreach {
+ case (cmd, err) =>
+ val stderr = wsk.cli(cmd ++ wskprops.overrides ++ auth, expectedExitCode = ERROR_EXIT).stderr
+ stderr should include(err)
+ }
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskConsoleTests.scala b/tests/src/test/scala/system/basic/WskConsoleTests.scala
index 1848733..c2191fa 100644
--- a/tests/src/test/scala/system/basic/WskConsoleTests.scala
+++ b/tests/src/test/scala/system/basic/WskConsoleTests.scala
@@ -41,59 +41,53 @@ import spray.json.pimpAny
* Tests of the text console
*/
@RunWith(classOf[JUnitRunner])
-class WskConsoleTests
- extends TestHelpers
- with WskTestHelpers {
+class WskConsoleTests extends TestHelpers with WskTestHelpers {
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val guestNamespace = wskprops.namespace
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val guestNamespace = wskprops.namespace
- behavior of "Wsk Activation Console"
+ behavior of "Wsk Activation Console"
- it should "show an activation log message for hello world" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val packageName = "samples"
- val actionName = "helloWorld"
- val fullActionName = s"/$guestNamespace/$packageName/$actionName"
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, _) => pkg.create(packageName, shared = Some(true))
- }
+ it should "show an activation log message for hello world" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val packageName = "samples"
+ val actionName = "helloWorld"
+ val fullActionName = s"/$guestNamespace/$packageName/$actionName"
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, shared = Some(true))
+ }
- assetHelper.withCleaner(wsk.action, fullActionName) {
- (action, _) => action.create(fullActionName, Some(TestCLIUtils.getTestActionFilename("hello.js")))
- }
+ assetHelper.withCleaner(wsk.action, fullActionName) { (action, _) =>
+ action.create(fullActionName, Some(TestUtils.getTestActionFilename("hello.js")))
+ }
- val duration = Some(30 seconds)
- val payload = new String("from the console!".getBytes, "UTF-8")
- val run = wsk.action.invoke(fullActionName, Map("payload" -> payload.toJson))
- withActivation(wsk.activation, run, totalWait = duration.get) {
- activation =>
- val console = wsk.activation.console(10 seconds, since = duration)
- println(console.stdout)
- console.stdout should include(payload)
- }
+ val duration = Some(30 seconds)
+ val payload = new String("from the console!".getBytes, "UTF-8")
+ val run = wsk.action.invoke(fullActionName, Map("payload" -> payload.toJson))
+ withActivation(wsk.activation, run, totalWait = duration.get) { activation =>
+ val console = wsk.activation.console(10 seconds, since = duration)
+ println(console.stdout)
+ console.stdout should include(payload)
}
+ }
- it should "show repeated activations" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "countdown"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("countdown.js")))
- }
+ it should "show repeated activations" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "countdown"
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("countdown.js")))
+ }
- val start = Instant.now(Clock.systemUTC())
- val run = wsk.action.invoke(name, Map("n" -> 3.toJson))
- withActivation(wsk.activation, run) {
- activation =>
- val activations = wsk.activation.pollFor(N = 4, Some(name), since = Some(start), retries = 80).length
- withClue(s"expected activations:") {
- activations should be(4)
- }
- val duration = Duration(Instant.now(Clock.systemUTC()).toEpochMilli - start.toEpochMilli, MILLISECONDS)
- val console = wsk.activation.console(10 seconds, since = Some(duration))
- console.stdout should include("Happy New Year")
- }
+ val start = Instant.now(Clock.systemUTC())
+ val run = wsk.action.invoke(name, Map("n" -> 3.toJson))
+ withActivation(wsk.activation, run) { activation =>
+ val activations = wsk.activation.pollFor(N = 4, Some(name), since = Some(start), retries = 80).length
+ withClue(s"expected activations:") {
+ activations should be(4)
+ }
+ val duration = Duration(Instant.now(Clock.systemUTC()).toEpochMilli - start.toEpochMilli, MILLISECONDS)
+ val console = wsk.activation.console(10 seconds, since = Some(duration))
+ console.stdout should include("Happy New Year")
}
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskPackageTests.scala b/tests/src/test/scala/system/basic/WskPackageTests.scala
index 88618f0..1fae059 100644
--- a/tests/src/test/scala/system/basic/WskPackageTests.scala
+++ b/tests/src/test/scala/system/basic/WskPackageTests.scala
@@ -34,110 +34,103 @@ import common.TestHelpers
import common.WskProps
@RunWith(classOf[JUnitRunner])
-class WskPackageTests
- extends TestHelpers
- with WskTestHelpers {
+class WskPackageTests extends TestHelpers with WskTestHelpers {
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val LOG_DELAY = 80 seconds
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val LOG_DELAY = 80 seconds
- behavior of "Wsk Package"
+ behavior of "Wsk Package"
- it should "allow creation and deletion of a package" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "simplepackage"
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) => pkg.create(name, Map())
- }
+ it should "allow creation and deletion of a package" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "simplepackage"
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name, Map())
}
+ }
- val params1 = Map("p1" -> "v1".toJson, "p2" -> "".toJson)
- val params2 = Map("p1" -> "v1".toJson, "p2" -> "v2".toJson, "p3" -> "v3".toJson)
+ val params1 = Map("p1" -> "v1".toJson, "p2" -> "".toJson)
+ val params2 = Map("p1" -> "v1".toJson, "p2" -> "v2".toJson, "p3" -> "v3".toJson)
- it should "allow creation of a package with parameters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "simplepackagewithparams"
- assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
- pkg.create(name, params1)
- }
+ it should "allow creation of a package with parameters" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "simplepackagewithparams"
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name, params1)
}
+ }
- it should "allow updating a package" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "simplepackagetoupdate"
- assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
- pkg.create(name, params1)
- pkg.create(name, params2, update = true)
- }
+ it should "allow updating a package" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "simplepackagetoupdate"
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name, params1)
+ pkg.create(name, params2, update = true)
}
+ }
- it should "allow binding of a package" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "simplepackagetobind"
- val bindName = "simplebind"
- assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
- pkg.create(name, params1)
- }
- assetHelper.withCleaner(wsk.pkg, bindName) { (pkg, _) =>
- pkg.bind(name, bindName, params2)
- }
+ it should "allow binding of a package" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "simplepackagetobind"
+ val bindName = "simplebind"
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name, params1)
}
+ assetHelper.withCleaner(wsk.pkg, bindName) { (pkg, _) =>
+ pkg.bind(name, bindName, params2)
+ }
+ }
- it should "perform package binds so parameters are inherited" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val packageName = "package1"
- val bindName = "package2"
- val actionName = "print"
- val packageActionName = packageName + "/" + actionName
- val bindActionName = bindName + "/" + actionName
- val packageParams = Map("key1a" -> "value1a".toJson, "key1b" -> "value1b".toJson)
- val bindParams = Map("key2a" -> "value2a".toJson, "key1b" -> "value2b".toJson)
- val actionParams = Map("key0" -> "value0".toJson)
- val file = TestCLIUtils.getTestActionFilename("printParams.js")
- assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
- pkg.create(packageName, packageParams)
- }
- assetHelper.withCleaner(wsk.action, packageActionName) { (action, _) =>
- action.create(packageActionName, Some(file), parameters = actionParams)
- }
- assetHelper.withCleaner(wsk.pkg, bindName) { (pkg, _) =>
- pkg.bind(packageName, bindName, bindParams)
- }
+ it should "perform package binds so parameters are inherited" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val packageName = "package1"
+ val bindName = "package2"
+ val actionName = "print"
+ val packageActionName = packageName + "/" + actionName
+ val bindActionName = bindName + "/" + actionName
+ val packageParams = Map("key1a" -> "value1a".toJson, "key1b" -> "value1b".toJson)
+ val bindParams = Map("key2a" -> "value2a".toJson, "key1b" -> "value2b".toJson)
+ val actionParams = Map("key0" -> "value0".toJson)
+ val file = TestUtils.getTestActionFilename("printParams.js")
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, packageParams)
+ }
+ assetHelper.withCleaner(wsk.action, packageActionName) { (action, _) =>
+ action.create(packageActionName, Some(file), parameters = actionParams)
+ }
+ assetHelper.withCleaner(wsk.pkg, bindName) { (pkg, _) =>
+ pkg.bind(packageName, bindName, bindParams)
+ }
- // Check that the description of packages and actions includes all the inherited parameters.
- val packageDescription = wsk.pkg.get(packageName).stdout
- val bindDescription = wsk.pkg.get(bindName).stdout
- val packageActionDescription = wsk.action.get(packageActionName).stdout
- val bindActionDescription = wsk.action.get(bindActionName).stdout
- checkForParameters(packageDescription, packageParams)
- checkForParameters(bindDescription, packageParams, bindParams)
- checkForParameters(packageActionDescription, packageParams, actionParams)
- checkForParameters(bindActionDescription, packageParams, bindParams, actionParams)
+ // Check that the description of packages and actions includes all the inherited parameters.
+ val packageDescription = wsk.pkg.get(packageName).stdout
+ val bindDescription = wsk.pkg.get(bindName).stdout
+ val packageActionDescription = wsk.action.get(packageActionName).stdout
+ val bindActionDescription = wsk.action.get(bindActionName).stdout
+ checkForParameters(packageDescription, packageParams)
+ checkForParameters(bindDescription, packageParams, bindParams)
+ checkForParameters(packageActionDescription, packageParams, actionParams)
+ checkForParameters(bindActionDescription, packageParams, bindParams, actionParams)
- // Check that inherited parameters are passed to the action.
- val now = new Date().toString()
- val run = wsk.action.invoke(bindActionName, Map("payload" -> now.toJson))
- withActivation(wsk.activation, run, totalWait = LOG_DELAY) {
- _.logs.get.mkString(" ") should include regex (
- String.format(".*key0: value0.*key1a: value1a.*key1b: value2b.*key2a: value2a.*payload: %s", now))
- }
+ // Check that inherited parameters are passed to the action.
+ val now = new Date().toString()
+ val run = wsk.action.invoke(bindActionName, Map("payload" -> now.toJson))
+ withActivation(wsk.activation, run, totalWait = LOG_DELAY) {
+ _.logs.get.mkString(" ") should include regex (String
+ .format(".*key0: value0.*key1a: value1a.*key1b: value2b.*key2a: value2a.*payload: %s", now))
}
+ }
- /**
- * Check that a description of an item includes the specified parameters.
- * Parameters keys in later parameter maps override earlier ones.
- */
- def checkForParameters(itemDescription: String, paramSets: Map[String, JsValue]*) {
- // Merge and the parameters handling overrides.
- val merged = HashMap.empty[String, JsValue]
- paramSets.foreach { merged ++= _ }
- val flatDescription = itemDescription.replace("\n", "").replace("\r", "")
- merged.foreach {
- case (key: String, value: JsValue) =>
- val toFind = s""""key": "${key}",.*"value": ${value.toString}"""
- flatDescription should include regex toFind
- }
+ /**
+ * Check that a description of an item includes the specified parameters.
+ * Parameters keys in later parameter maps override earlier ones.
+ */
+ def checkForParameters(itemDescription: String, paramSets: Map[String, JsValue]*) {
+ // Merge and the parameters handling overrides.
+ val merged = HashMap.empty[String, JsValue]
+ paramSets.foreach { merged ++= _ }
+ val flatDescription = itemDescription.replace("\n", "").replace("\r", "")
+ merged.foreach {
+ case (key: String, value: JsValue) =>
+ val toFind = s""""key": "${key}",.*"value": ${value.toString}"""
+ flatDescription should include regex toFind
}
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskRuleTests.scala b/tests/src/test/scala/system/basic/WskRuleTests.scala
index ae8768d..adc95a7 100644
--- a/tests/src/test/scala/system/basic/WskRuleTests.scala
+++ b/tests/src/test/scala/system/basic/WskRuleTests.scala
@@ -29,364 +29,394 @@ import spray.json.DefaultJsonProtocol._
import java.time.Instant
@RunWith(classOf[JUnitRunner])
-class WskRuleTests
- extends TestHelpers
- with WskTestHelpers {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val defaultAction = TestCLIUtils.getTestActionFilename("wc.js")
- val secondAction = TestCLIUtils.getTestActionFilename("hello.js")
- val testString = "this is a test"
- val testResult = JsObject("count" -> testString.split(" ").length.toJson)
-
- /**
- * Invoker clock skew can sometimes make it appear as if an action was invoked
- * _before_ the trigger was fired. The "fudge factor" below allows the test to look
- * for action activations that occur starting at most this amount of time before
- * the trigger was fired.
- */
- val activationTimeSkewFactorMs = 500
-
- /**
- * Sets up trigger -> rule -> action triplets. Deduplicates triggers and rules
- * and links it all up.
- *
- * @param rules Tuple3s containing
- * (rule, trigger, (action name for created action, action name for the rule binding, actionFile))
- * where the action name for the created action is allowed to differ from that used by the rule binding
- * for cases that reference actions in a package binding.
- */
- def ruleSetup(rules: Seq[(String, String, (String, String, String))], assetHelper: AssetCleaner) = {
- val triggers = rules.map(_._2).distinct
- val actions = rules.map(_._3).distinct
-
- triggers.foreach { trigger =>
- assetHelper.withCleaner(wsk.trigger, trigger) {
- (trigger, name) => trigger.create(name)
- }
- }
+class WskRuleTests extends TestHelpers with WskTestHelpers {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val defaultAction = TestCLIUtils.getTestActionFilename("wc.js")
+ val secondAction = TestCLIUtils.getTestActionFilename("hello.js")
+ val testString = "this is a test"
+ val testResult = JsObject("count" -> testString.split(" ").length.toJson)
+
+ /**
+ * Invoker clock skew can sometimes make it appear as if an action was invoked
+ * _before_ the trigger was fired. The "fudge factor" below allows the test to look
+ * for action activations that occur starting at most this amount of time before
+ * the trigger was fired.
+ */
+ val activationTimeSkewFactorMs = 500
+
+ /**
+ * Sets up trigger -> rule -> action triplets. Deduplicates triggers and rules
+ * and links it all up.
+ *
+ * @param rules Tuple3s containing
+ * (rule, trigger, (action name for created action, action name for the rule binding, actionFile))
+ * where the action name for the created action is allowed to differ from that used by the rule binding
+ * for cases that reference actions in a package binding.
+ */
+ def ruleSetup(rules: Seq[(String, String, (String, String, String))], assetHelper: AssetCleaner) = {
+ val triggers = rules.map(_._2).distinct
+ val actions = rules.map(_._3).distinct
+
+ triggers.foreach { trigger =>
+ assetHelper.withCleaner(wsk.trigger, trigger) { (trigger, name) =>
+ trigger.create(name)
+ }
+ }
- actions.foreach {
- case (actionName, _, file) =>
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, Some(file))
- }
+ actions.foreach {
+ case (actionName, _, file) =>
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, Some(file))
}
+ }
- rules.foreach {
- case (ruleName, triggerName, action) =>
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) => rule.create(name, triggerName, action._2)
- }
+ rules.foreach {
+ case (ruleName, triggerName, action) =>
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, triggerName, action._2)
}
}
+ }
+
+ /**
+ * Append the current timestamp in ms
+ */
+ def withTimestamp(text: String) = s"${text}-${System.currentTimeMillis}"
+
+ behavior of "Whisk rules"
+
+ it should "invoke the action attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val ruleName = withTimestamp("r1to1")
+ val triggerName = withTimestamp("t1to1")
+ val actionName = withTimestamp("a1 to 1") // spaces in name intended for greater test coverage
+
+ ruleSetup(Seq((ruleName, triggerName, (actionName, actionName, defaultAction))), assetHelper)
+
+ val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+
+ withActivation(wsk.activation, run) { triggerActivation =>
+ triggerActivation.cause shouldBe None
+
+ withActivationsFromEntity(
+ wsk.activation,
+ ruleName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.cause shouldBe Some(triggerActivation.activationId)
+ }
+
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activationList =>
+ activationList.head.response.result shouldBe Some(testResult)
+ activationList.head.cause shouldBe None
+ }
+ }
+ }
+
+ it should "invoke the action from a package attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val ruleName = withTimestamp("pr1to1")
+ val triggerName = withTimestamp("pt1to1")
+ val pkgName = withTimestamp("rule pkg") // spaces in name intended to test uri path encoding
+ val actionName = withTimestamp("a1 to 1")
+ val pkgActionName = s"$pkgName/$actionName"
+
+ assetHelper.withCleaner(wsk.pkg, pkgName) { (pkg, name) =>
+ pkg.create(name)
+ }
- /**
- * Append the current timestamp in ms
- */
- def withTimestamp(text: String) = s"${text}-${System.currentTimeMillis}"
-
- behavior of "Whisk rules"
-
- it should "invoke the action attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("r1to1")
- val triggerName = withTimestamp("t1to1")
- val actionName = withTimestamp("a1 to 1") // spaces in name intended for greater test coverage
-
- ruleSetup(Seq(
- (ruleName, triggerName, (actionName, actionName, defaultAction))),
- assetHelper)
+ ruleSetup(Seq((ruleName, triggerName, (pkgActionName, pkgActionName, defaultAction))), assetHelper)
- val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+ val now = Instant.now
+ val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
- withActivation(wsk.activation, run) {
- triggerActivation =>
- triggerActivation.cause shouldBe None
+ withActivation(wsk.activation, run) { triggerActivation =>
+ triggerActivation.cause shouldBe None
- withActivationsFromEntity(wsk.activation, ruleName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.cause shouldBe Some(triggerActivation.activationId)
- }
+ withActivationsFromEntity(
+ wsk.activation,
+ ruleName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.cause shouldBe Some(triggerActivation.activationId)
+ }
- withActivationsFromEntity(wsk.activation, actionName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activationList =>
- activationList.head.response.result shouldBe Some(testResult)
- activationList.head.cause shouldBe None
- }
- }
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
}
-
- it should "invoke the action from a package attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("pr1to1")
- val triggerName = withTimestamp("pt1to1")
- val pkgName = withTimestamp("rule pkg") // spaces in name intended to test uri path encoding
- val actionName = withTimestamp("a1 to 1")
- val pkgActionName = s"$pkgName/$actionName"
-
- assetHelper.withCleaner(wsk.pkg, pkgName) {
- (pkg, name) => pkg.create(name)
- }
-
- ruleSetup(Seq(
- (ruleName, triggerName, (pkgActionName, pkgActionName, defaultAction))),
- assetHelper)
-
- val now = Instant.now
- val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
-
- withActivation(wsk.activation, run) {
- triggerActivation =>
- triggerActivation.cause shouldBe None
-
- withActivationsFromEntity(wsk.activation, ruleName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.cause shouldBe Some(triggerActivation.activationId)
- }
-
- withActivationsFromEntity(wsk.activation, actionName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.response.result shouldBe Some(testResult)
- }
- }
- }
-
- it should "invoke the action from a package binding attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("pr1to1")
- val triggerName = withTimestamp("pt1to1")
- val pkgName = withTimestamp("rule pkg") // spaces in name intended to test uri path encoding
- val pkgBindingName = withTimestamp("rule pkg binding")
- val actionName = withTimestamp("a1 to 1")
- val pkgActionName = s"$pkgName/$actionName"
-
- assetHelper.withCleaner(wsk.pkg, pkgName) {
- (pkg, name) => pkg.create(name)
- }
-
- assetHelper.withCleaner(wsk.pkg, pkgBindingName) {
- (pkg, name) => pkg.bind(pkgName, pkgBindingName)
- }
-
- ruleSetup(Seq(
- (ruleName, triggerName, (pkgActionName, s"$pkgBindingName/$actionName", defaultAction))),
- assetHelper)
-
- val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
-
- withActivation(wsk.activation, run) {
- triggerActivation =>
- triggerActivation.cause shouldBe None
-
- withActivationsFromEntity(wsk.activation, ruleName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.cause shouldBe Some(triggerActivation.activationId)
- }
-
- withActivationsFromEntity(wsk.activation, actionName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.response.result shouldBe Some(testResult)
- }
- }
+ }
+
+ it should "invoke the action from a package binding attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val ruleName = withTimestamp("pr1to1")
+ val triggerName = withTimestamp("pt1to1")
+ val pkgName = withTimestamp("rule pkg") // spaces in name intended to test uri path encoding
+ val pkgBindingName = withTimestamp("rule pkg binding")
+ val actionName = withTimestamp("a1 to 1")
+ val pkgActionName = s"$pkgName/$actionName"
+
+ assetHelper.withCleaner(wsk.pkg, pkgName) { (pkg, name) =>
+ pkg.create(name)
}
- it should "not activate an action if the rule is deleted when the trigger is fired" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("ruleDelete")
- val triggerName = withTimestamp("ruleDeleteTrigger")
- val actionName = withTimestamp("ruleDeleteAction")
-
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, Some(defaultAction))
- }
- assetHelper.withCleaner(wsk.rule, ruleName, false) {
- (rule, name) => rule.create(name, triggerName, actionName)
- }
-
- val first = wsk.trigger.fire(triggerName, Map("payload" -> "bogus".toJson))
- wsk.rule.delete(ruleName)
- wsk.trigger.fire(triggerName, Map("payload" -> "bogus2".toJson))
-
- withActivation(wsk.activation, first) {
- activation =>
- // tries to find 2 activations for the action, should only find 1
- val activations = wsk.activation.pollFor(2, Some(actionName), since = Some(activation.start.minusMillis(activationTimeSkewFactorMs)), retries = 30)
-
- activations.length shouldBe 1
- }
+ assetHelper.withCleaner(wsk.pkg, pkgBindingName) { (pkg, name) =>
+ pkg.bind(pkgName, pkgBindingName)
}
- it should "enable and disable a rule and check action is activated only when rule is enabled" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("ruleDisable")
- val triggerName = withTimestamp("ruleDisableTrigger")
- val actionName = withTimestamp("ruleDisableAction")
-
- ruleSetup(Seq(
- (ruleName, triggerName, (actionName, actionName, defaultAction))),
- assetHelper)
-
- val first = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
- wsk.rule.disable(ruleName)
- wsk.trigger.fire(triggerName, Map("payload" -> s"$testString with added words".toJson))
- wsk.rule.enable(ruleName)
- wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
-
- withActivation(wsk.activation, first) {
- triggerActivation =>
- withActivationsFromEntity(wsk.activation, actionName, N = 2, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- activations =>
- val results = activations.map(_.response.result)
- results should contain theSameElementsAs Seq(Some(testResult), Some(testResult))
- }
- }
- }
+ ruleSetup(Seq((ruleName, triggerName, (pkgActionName, s"$pkgBindingName/$actionName", defaultAction))), assetHelper)
- it should "be able to recreate a rule with the same name and match it successfully" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("ruleRecreate")
- val triggerName1 = withTimestamp("ruleRecreateTrigger1")
- val triggerName2 = withTimestamp("ruleRecreateTrigger2")
- val actionName = withTimestamp("ruleRecreateAction")
-
- assetHelper.withCleaner(wsk.trigger, triggerName1) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, Some(defaultAction))
- }
- assetHelper.withCleaner(wsk.rule, ruleName, false) {
- (rule, name) => rule.create(name, triggerName1, actionName)
- }
-
- wsk.rule.delete(ruleName)
-
- assetHelper.withCleaner(wsk.trigger, triggerName2) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) => rule.create(name, triggerName2, actionName)
- }
-
- val first = wsk.trigger.fire(triggerName2, Map("payload" -> testString.toJson))
- withActivation(wsk.activation, first) {
- triggerActivation =>
- withActivationsFromEntity(wsk.activation, actionName, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.response.result shouldBe Some(testResult)
- }
- }
- }
+ val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
- it should "connect two triggers via rules to one action and activate it accordingly" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val triggerName1 = withTimestamp("t2to1a")
- val triggerName2 = withTimestamp("t2to1b")
- val actionName = withTimestamp("a2to1")
-
- ruleSetup(Seq(
- ("r2to1a", triggerName1, (actionName, actionName, defaultAction)),
- ("r2to1b", triggerName2, (actionName, actionName, defaultAction))),
- assetHelper)
-
- val testPayloads = Seq("got three words", "got four words, period")
-
- val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
- wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
-
- withActivation(wsk.activation, run) {
- triggerActivation =>
- withActivationsFromEntity(wsk.activation, actionName, N = 2, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- activations =>
- val results = activations.map(_.response.result)
- val expectedResults = testPayloads.map { payload =>
- Some(JsObject("count" -> payload.split(" ").length.toJson))
- }
-
- results should contain theSameElementsAs expectedResults
- }
- }
- }
+ withActivation(wsk.activation, run) { triggerActivation =>
+ triggerActivation.cause shouldBe None
- it should "connect one trigger to two different actions, invoking them both eventually" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val triggerName = withTimestamp("t1to2")
- val actionName1 = withTimestamp("a1to2a")
- val actionName2 = withTimestamp("a1to2b")
-
- ruleSetup(Seq(
- ("r1to2a", triggerName, (actionName1, actionName1, defaultAction)),
- ("r1to2b", triggerName, (actionName2, actionName2, secondAction))),
- assetHelper)
-
- val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
-
- withActivation(wsk.activation, run) {
- triggerActivation =>
- withActivationsFromEntity(wsk.activation, actionName1, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.response.result shouldBe Some(testResult)
- }
- withActivationsFromEntity(wsk.activation, actionName2, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- _.head.logs.get.mkString(" ") should include(s"hello, $testString")
- }
- }
- }
+ withActivationsFromEntity(
+ wsk.activation,
+ ruleName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.cause shouldBe Some(triggerActivation.activationId)
+ }
- it should "connect two triggers to two different actions, invoking them both eventually" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val triggerName1 = withTimestamp("t1to1a")
- val triggerName2 = withTimestamp("t1to1b")
- val actionName1 = withTimestamp("a1to1a")
- val actionName2 = withTimestamp("a1to1b")
-
- ruleSetup(Seq(
- ("r2to2a", triggerName1, (actionName1, actionName1, defaultAction)),
- ("r2to2b", triggerName1, (actionName2, actionName2, secondAction)),
- ("r2to2c", triggerName2, (actionName1, actionName1, defaultAction)),
- ("r2to2d", triggerName2, (actionName2, actionName2, secondAction))),
- assetHelper)
-
- val testPayloads = Seq("got three words", "got four words, period")
- val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
- wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
-
- withActivation(wsk.activation, run) {
- triggerActivation =>
- withActivationsFromEntity(wsk.activation, actionName1, N = 2, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- activations =>
- val results = activations.map(_.response.result)
- val expectedResults = testPayloads.map { payload =>
- Some(JsObject("count" -> payload.split(" ").length.toJson))
- }
-
- results should contain theSameElementsAs expectedResults
- }
- withActivationsFromEntity(wsk.activation, actionName2, N = 2, since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
- activations =>
- // drops the leftmost 39 characters (timestamp + streamname)
- val logs = activations.map(_.logs.get.map(_.drop(39))).flatten
- val expectedLogs = testPayloads.map { payload => s"hello, $payload!" }
-
- logs should contain theSameElementsAs expectedLogs
- }
- }
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
}
-
- it should "disable a rule and check its status is displayed when listed" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = withTimestamp("ruleDisable")
- val ruleName2 = withTimestamp("ruleEnable")
- val triggerName = withTimestamp("ruleDisableTrigger")
- val actionName = withTimestamp("ruleDisableAction")
-
- ruleSetup(Seq(
- (ruleName, triggerName, (actionName, actionName, defaultAction)),
- (ruleName2, triggerName, (actionName, actionName, defaultAction))),
- assetHelper)
-
- wsk.rule.disable(ruleName)
- val listOutput = wsk.rule.list().stdout.lines
- listOutput.find(_.contains(ruleName2)).get should (include(ruleName2) and include("active"))
- listOutput.find(_.contains(ruleName)).get should (include(ruleName) and include("inactive"))
- wsk.rule.list().stdout should not include ("Unknown")
+ }
+
+ it should "not activate an action if the rule is deleted when the trigger is fired" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = withTimestamp("ruleDelete")
+ val triggerName = withTimestamp("ruleDeleteTrigger")
+ val actionName = withTimestamp("ruleDeleteAction")
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, Some(defaultAction))
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName, false) { (rule, name) =>
+ rule.create(name, triggerName, actionName)
+ }
+
+ val first = wsk.trigger.fire(triggerName, Map("payload" -> "bogus".toJson))
+ wsk.rule.delete(ruleName)
+ wsk.trigger.fire(triggerName, Map("payload" -> "bogus2".toJson))
+
+ withActivation(wsk.activation, first) { activation =>
+ // tries to find 2 activations for the action, should only find 1
+ val activations = wsk.activation.pollFor(
+ 2,
+ Some(actionName),
+ since = Some(activation.start.minusMillis(activationTimeSkewFactorMs)),
+ retries = 30)
+
+ activations.length shouldBe 1
+ }
+ }
+
+ it should "enable and disable a rule and check action is activated only when rule is enabled" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val ruleName = withTimestamp("ruleDisable")
+ val triggerName = withTimestamp("ruleDisableTrigger")
+ val actionName = withTimestamp("ruleDisableAction")
+
+ ruleSetup(Seq((ruleName, triggerName, (actionName, actionName, defaultAction))), assetHelper)
+
+ val first = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+ wsk.rule.disable(ruleName)
+ wsk.trigger.fire(triggerName, Map("payload" -> s"$testString with added words".toJson))
+ wsk.rule.enable(ruleName)
+ wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+
+ withActivation(wsk.activation, first) { triggerActivation =>
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName,
+ N = 2,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
+ val results = activations.map(_.response.result)
+ results should contain theSameElementsAs Seq(Some(testResult), Some(testResult))
+ }
}
+ }
+
+ it should "be able to recreate a rule with the same name and match it successfully" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = withTimestamp("ruleRecreate")
+ val triggerName1 = withTimestamp("ruleRecreateTrigger1")
+ val triggerName2 = withTimestamp("ruleRecreateTrigger2")
+ val actionName = withTimestamp("ruleRecreateAction")
+
+ assetHelper.withCleaner(wsk.trigger, triggerName1) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, Some(defaultAction))
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName, false) { (rule, name) =>
+ rule.create(name, triggerName1, actionName)
+ }
+
+ wsk.rule.delete(ruleName)
+
+ assetHelper.withCleaner(wsk.trigger, triggerName2) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, triggerName2, actionName)
+ }
+
+ val first = wsk.trigger.fire(triggerName2, Map("payload" -> testString.toJson))
+ withActivation(wsk.activation, first) { triggerActivation =>
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
+ }
+ }
+
+ it should "connect two triggers via rules to one action and activate it accordingly" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName1 = withTimestamp("t2to1a")
+ val triggerName2 = withTimestamp("t2to1b")
+ val actionName = withTimestamp("a2to1")
+
+ ruleSetup(
+ Seq(
+ ("r2to1a", triggerName1, (actionName, actionName, defaultAction)),
+ ("r2to1b", triggerName2, (actionName, actionName, defaultAction))),
+ assetHelper)
+
+ val testPayloads = Seq("got three words", "got four words, period")
+
+ val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
+ wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
+
+ withActivation(wsk.activation, run) { triggerActivation =>
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName,
+ N = 2,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
+ val results = activations.map(_.response.result)
+ val expectedResults = testPayloads.map { payload =>
+ Some(JsObject("count" -> payload.split(" ").length.toJson))
+ }
+
+ results should contain theSameElementsAs expectedResults
+ }
+ }
+ }
+
+ it should "connect one trigger to two different actions, invoking them both eventually" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName = withTimestamp("t1to2")
+ val actionName1 = withTimestamp("a1to2a")
+ val actionName2 = withTimestamp("a1to2b")
+
+ ruleSetup(
+ Seq(
+ ("r1to2a", triggerName, (actionName1, actionName1, defaultAction)),
+ ("r1to2b", triggerName, (actionName2, actionName2, secondAction))),
+ assetHelper)
+
+ val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+
+ withActivation(wsk.activation, run) { triggerActivation =>
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName1,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName2,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
+ _.head.logs.get.mkString(" ") should include(s"hello, $testString")
+ }
+ }
+ }
+
+ it should "connect two triggers to two different actions, invoking them both eventually" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName1 = withTimestamp("t1to1a")
+ val triggerName2 = withTimestamp("t1to1b")
+ val actionName1 = withTimestamp("a1to1a")
+ val actionName2 = withTimestamp("a1to1b")
+
+ ruleSetup(
+ Seq(
+ ("r2to2a", triggerName1, (actionName1, actionName1, defaultAction)),
+ ("r2to2b", triggerName1, (actionName2, actionName2, secondAction)),
+ ("r2to2c", triggerName2, (actionName1, actionName1, defaultAction)),
+ ("r2to2d", triggerName2, (actionName2, actionName2, secondAction))),
+ assetHelper)
+
+ val testPayloads = Seq("got three words", "got four words, period")
+ val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
+ wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
+
+ withActivation(wsk.activation, run) { triggerActivation =>
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName1,
+ N = 2,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
+ val results = activations.map(_.response.result)
+ val expectedResults = testPayloads.map { payload =>
+ Some(JsObject("count" -> payload.split(" ").length.toJson))
+ }
+
+ results should contain theSameElementsAs expectedResults
+ }
+ withActivationsFromEntity(
+ wsk.activation,
+ actionName2,
+ N = 2,
+ since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
+ // drops the leftmost 39 characters (timestamp + streamname)
+ val logs = activations.map(_.logs.get.map(_.drop(39))).flatten
+ val expectedLogs = testPayloads.map { payload =>
+ s"hello, $payload!"
+ }
+
+ logs should contain theSameElementsAs expectedLogs
+ }
+ }
+ }
+
+ it should "disable a rule and check its status is displayed when listed" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = withTimestamp("ruleDisable")
+ val ruleName2 = withTimestamp("ruleEnable")
+ val triggerName = withTimestamp("ruleDisableTrigger")
+ val actionName = withTimestamp("ruleDisableAction")
+
+ ruleSetup(
+ Seq(
+ (ruleName, triggerName, (actionName, actionName, defaultAction)),
+ (ruleName2, triggerName, (actionName, actionName, defaultAction))),
+ assetHelper)
+
+ wsk.rule.disable(ruleName)
+ val listOutput = wsk.rule.list().stdout.lines
+ listOutput.find(_.contains(ruleName2)).get should (include(ruleName2) and include("active"))
+ listOutput.find(_.contains(ruleName)).get should (include(ruleName) and include("inactive"))
+ wsk.rule.list().stdout should not include ("Unknown")
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskSdkTests.scala b/tests/src/test/scala/system/basic/WskSdkTests.scala
index ac448b1..62c8d60 100644
--- a/tests/src/test/scala/system/basic/WskSdkTests.scala
+++ b/tests/src/test/scala/system/basic/WskSdkTests.scala
@@ -33,85 +33,83 @@ import common.WskProps
import common.WskTestHelpers
@RunWith(classOf[JUnitRunner])
-class WskSdkTests
- extends TestHelpers
- with WskTestHelpers {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
-
- behavior of "Wsk SDK"
-
- it should "download docker action sdk" in {
- val dir = File.createTempFile("wskinstall", ".tmp")
- dir.delete()
- dir.mkdir() should be(true)
- try {
- wsk.cli(wskprops.overrides ++ Seq("sdk", "install", "docker"), workingDir = dir).
- stdout should include("The docker skeleton is now installed at the current directory.")
-
- val sdk = new File(dir, "dockerSkeleton")
- sdk.exists() should be(true)
- sdk.isDirectory() should be(true)
-
- val dockerfile = new File(sdk, "Dockerfile")
- dockerfile.exists() should be(true)
- dockerfile.isFile() should be(true)
- val lines = FileUtils.readLines(dockerfile)
- // confirm that the image is correct
- lines.get(1) shouldBe "FROM openwhisk/dockerskeleton"
-
- val buildAndPushFile = new File(sdk, "buildAndPush.sh")
- buildAndPushFile.canExecute() should be(true)
-
- // confirm there is no other divergence from the base dockerfile
- val originalDockerfile = WhiskProperties.getFileRelativeToWhiskHome("sdk/docker/Dockerfile")
- val originalLines = FileUtils.readLines(originalDockerfile)
- lines.get(0) shouldBe originalLines.get(0)
- lines.drop(2).mkString("\n") shouldBe originalLines.drop(2).mkString("\n")
- } finally {
- FileUtils.deleteDirectory(dir)
- }
+class WskSdkTests extends TestHelpers with WskTestHelpers {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+
+ behavior of "Wsk SDK"
+
+ it should "download docker action sdk" in {
+ val dir = File.createTempFile("wskinstall", ".tmp")
+ dir.delete()
+ dir.mkdir() should be(true)
+ try {
+ wsk.cli(wskprops.overrides ++ Seq("sdk", "install", "docker"), workingDir = dir).stdout should include(
+ "The docker skeleton is now installed at the current directory.")
+
+ val sdk = new File(dir, "dockerSkeleton")
+ sdk.exists() should be(true)
+ sdk.isDirectory() should be(true)
+
+ val dockerfile = new File(sdk, "Dockerfile")
+ dockerfile.exists() should be(true)
+ dockerfile.isFile() should be(true)
+ val lines = FileUtils.readLines(dockerfile)
+ // confirm that the image is correct
+ lines.get(1) shouldBe "FROM openwhisk/dockerskeleton"
+
+ val buildAndPushFile = new File(sdk, "buildAndPush.sh")
+ buildAndPushFile.canExecute() should be(true)
+
+ // confirm there is no other divergence from the base dockerfile
+ val originalDockerfile = WhiskProperties.getFileRelativeToWhiskHome("sdk/docker/Dockerfile")
+ val originalLines = FileUtils.readLines(originalDockerfile)
+ lines.get(0) shouldBe originalLines.get(0)
+ lines.drop(2).mkString("\n") shouldBe originalLines.drop(2).mkString("\n")
+ } finally {
+ FileUtils.deleteDirectory(dir)
}
-
- it should "download iOS sdk" in {
- val dir = File.createTempFile("wskinstall", ".tmp")
- dir.delete()
- dir.mkdir() should be(true)
-
- wsk.cli(wskprops.overrides ++ Seq("sdk", "install", "iOS"), workingDir = dir).
- stdout should include("Downloaded OpenWhisk iOS starter app. Unzip OpenWhiskIOSStarterApp.zip and open the project in Xcode.")
-
- val sdk = new File(dir, "OpenWhiskIOSStarterApp.zip")
- sdk.exists() should be(true)
- sdk.isFile() should be(true)
- FileUtils.sizeOf(sdk) should be > 20000L
- FileUtils.deleteDirectory(dir)
+ }
+
+ it should "download iOS sdk" in {
+ val dir = File.createTempFile("wskinstall", ".tmp")
+ dir.delete()
+ dir.mkdir() should be(true)
+
+ wsk.cli(wskprops.overrides ++ Seq("sdk", "install", "iOS"), workingDir = dir).stdout should include(
+ "Downloaded OpenWhisk iOS starter app. Unzip OpenWhiskIOSStarterApp.zip and open the project in Xcode.")
+
+ val sdk = new File(dir, "OpenWhiskIOSStarterApp.zip")
+ sdk.exists() should be(true)
+ sdk.isFile() should be(true)
+ FileUtils.sizeOf(sdk) should be > 20000L
+ FileUtils.deleteDirectory(dir)
+ }
+
+ it should "install the bash auto-completion bash script" in {
+ // Use a temp dir for testing to not disturb user's local folder
+ val dir = File.createTempFile("wskinstall", ".tmp")
+ dir.delete()
+ dir.mkdir() should be(true)
+
+ val scriptfilename = "wsk_cli_bash_completion.sh"
+ var scriptfile = new File(dir.getPath(), scriptfilename)
+ try {
+ val stdout = wsk.cli(Seq("sdk", "install", "bashauto"), workingDir = dir, expectedExitCode = SUCCESS_EXIT).stdout
+ stdout should include("is installed in the current directory")
+ val fileContent = FileUtils.readFileToString(scriptfile)
+ fileContent should include("bash completion for wsk")
+ } finally {
+ scriptfile.delete()
+ FileUtils.deleteDirectory(dir)
}
+ }
- it should "install the bash auto-completion bash script" in {
- // Use a temp dir for testing to not disturb user's local folder
- val dir = File.createTempFile("wskinstall", ".tmp")
- dir.delete()
- dir.mkdir() should be(true)
-
- val scriptfilename = "wsk_cli_bash_completion.sh"
- var scriptfile = new File(dir.getPath(), scriptfilename)
- try {
- val stdout = wsk.cli(Seq("sdk", "install", "bashauto"), workingDir = dir, expectedExitCode = SUCCESS_EXIT).stdout
- stdout should include("is installed in the current directory")
- val fileContent = FileUtils.readFileToString(scriptfile)
- fileContent should include("bash completion for wsk")
- } finally {
- scriptfile.delete()
- FileUtils.deleteDirectory(dir)
- }
- }
-
- it should "print bash command completion script to STDOUT" in {
- val msg = "bash completion for wsk" // Subject to change, dependent on Cobra script
+ it should "print bash command completion script to STDOUT" in {
+ val msg = "bash completion for wsk" // Subject to change, dependent on Cobra script
- val stdout = wsk.cli(Seq("sdk", "install", "bashauto", "--stdout")).stdout
- stdout should include(msg)
- }
+ val stdout = wsk.cli(Seq("sdk", "install", "bashauto", "--stdout")).stdout
+ stdout should include(msg)
+ }
}
diff --git a/tests/src/test/scala/system/basic/WskSequenceTests.scala b/tests/src/test/scala/system/basic/WskSequenceTests.scala
index 3a1dbe3..bd5cdc0 100644
--- a/tests/src/test/scala/system/basic/WskSequenceTests.scala
+++ b/tests/src/test/scala/system/basic/WskSequenceTests.scala
@@ -49,481 +49,498 @@ import whisk.http.Messages.sequenceIsTooLong
*/
@RunWith(classOf[JUnitRunner])
-class WskSequenceTests
- extends TestHelpers
- with ScalatestRouteTest
- with WskTestHelpers
- with StreamLogging {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val allowedActionDuration = 120 seconds
- val shortDuration = 10 seconds
-
- behavior of "Wsk Sequence"
-
- it should "invoke a sequence with normal payload and payload with error field" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "sequence"
- val actions = Seq("split", "sort", "head", "cat")
- for (actionName <- actions) {
- val file = TestCLIUtils.getTestActionFilename(s"$actionName.js")
- assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
- }
- }
-
- println(s"Sequence $actions")
- assetHelper.withCleaner(wsk.action, name) {
- val sequence = actions.mkString(",")
- (action, _) => action.create(name, Some(sequence), kind = Some("sequence"), timeout = Some(allowedActionDuration))
- }
-
- val now = "it is now " + new Date()
- val args = Array("what time is it?", now)
- val run = wsk.action.invoke(name, Map("payload" -> args.mkString("\n").toJson))
- withActivation(wsk.activation, run, totalWait = 4 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 4) // 4 activations in this sequence
- activation.cause shouldBe None // topmost sequence
- val result = activation.response.result.get
- result.fields.get("payload") shouldBe defined
- result.fields.get("length") should not be defined
- result.fields.get("lines") shouldBe Some(JsArray(Vector(now.toJson)))
- }
-
- // update action sequence and run it with normal payload
- val newSequence = Seq("split", "sort").mkString(",")
- println(s"Update sequence to $newSequence")
- wsk.action.create(name, Some(newSequence), kind = Some("sequence"), timeout = Some(allowedActionDuration), update = true)
- val secondrun = wsk.action.invoke(name, Map("payload" -> args.mkString("\n").toJson))
- withActivation(wsk.activation, secondrun, totalWait = 2 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 2) // 2 activations in this sequence
- val result = activation.response.result.get
- result.fields.get("length") shouldBe Some(2.toJson)
- result.fields.get("lines") shouldBe Some(args.sortWith(_.compareTo(_) < 0).toArray.toJson)
- }
-
- println("Run sequence with error in payload")
- // run sequence with error in the payload
- // sequence should run with no problems, error should be ignored in this test case
- // result of sequence should be identical to previous invocation above
- val payload = Map("error" -> JsString("irrelevant error string"), "payload" -> args.mkString("\n").toJson)
- val thirdrun = wsk.action.invoke(name, payload)
- withActivation(wsk.activation, thirdrun, totalWait = 2 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 2) // 2 activations in this sequence
- val result = activation.response.result.get
- result.fields.get("length") shouldBe Some(2.toJson)
- result.fields.get("lines") shouldBe Some(args.sortWith(_.compareTo(_) < 0).toArray.toJson)
- }
+class WskSequenceTests extends TestHelpers with ScalatestRouteTest with WskTestHelpers with StreamLogging {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val allowedActionDuration = 120 seconds
+ val shortDuration = 10 seconds
+
+ val whiskConfig = new WhiskConfig(Map(WhiskConfig.actionSequenceMaxLimit -> null))
+ assert(whiskConfig.isValid)
+
+ behavior of "Wsk Sequence"
+
+ it should "invoke a sequence with normal payload and payload with error field" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "sequence"
+ val actions = Seq("split", "sort", "head", "cat")
+ for (actionName <- actions) {
+ val file = TestUtils.getTestActionFilename(s"$actionName.js")
+ assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
+ }
+ }
+
+ println(s"Sequence $actions")
+ assetHelper.withCleaner(wsk.action, name) {
+ val sequence = actions.mkString(",")
+ (action, _) =>
+ action.create(name, Some(sequence), kind = Some("sequence"), timeout = Some(allowedActionDuration))
+ }
+
+ val now = "it is now " + new Date()
+ val args = Array("what time is it?", now)
+ val run = wsk.action.invoke(name, Map("payload" -> args.mkString("\n").toJson))
+ withActivation(wsk.activation, run, totalWait = 4 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 4) // 4 activations in this sequence
+ activation.cause shouldBe None // topmost sequence
+ val result = activation.response.result.get
+ result.fields.get("payload") shouldBe defined
+ result.fields.get("length") should not be defined
+ result.fields.get("lines") shouldBe Some(JsArray(Vector(now.toJson)))
+ }
+
+ // update action sequence and run it with normal payload
+ val newSequence = Seq("split", "sort").mkString(",")
+ println(s"Update sequence to $newSequence")
+ wsk.action.create(
+ name,
+ Some(newSequence),
+ kind = Some("sequence"),
+ timeout = Some(allowedActionDuration),
+ update = true)
+ val secondrun = wsk.action.invoke(name, Map("payload" -> args.mkString("\n").toJson))
+ withActivation(wsk.activation, secondrun, totalWait = 2 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 2) // 2 activations in this sequence
+ val result = activation.response.result.get
+ result.fields.get("length") shouldBe Some(2.toJson)
+ result.fields.get("lines") shouldBe Some(args.sortWith(_.compareTo(_) < 0).toArray.toJson)
+ }
+
+ println("Run sequence with error in payload")
+ // run sequence with error in the payload
+ // sequence should run with no problems, error should be ignored in this test case
+ // result of sequence should be identical to previous invocation above
+ val payload = Map("error" -> JsString("irrelevant error string"), "payload" -> args.mkString("\n").toJson)
+ val thirdrun = wsk.action.invoke(name, payload)
+ withActivation(wsk.activation, thirdrun, totalWait = 2 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 2) // 2 activations in this sequence
+ val result = activation.response.result.get
+ result.fields.get("length") shouldBe Some(2.toJson)
+ result.fields.get("lines") shouldBe Some(args.sortWith(_.compareTo(_) < 0).toArray.toJson)
+ }
+ }
+
+ it should "invoke a sequence with an enclosing sequence action" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val inner_name = "inner_sequence"
+ val outer_name = "outer_sequence"
+ val inner_actions = Seq("sort", "head")
+ val actions = Seq("split") ++ inner_actions ++ Seq("cat")
+ // create atomic actions
+ for (actionName <- actions) {
+ val file = TestUtils.getTestActionFilename(s"$actionName.js")
+ assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
+ }
}
- it should "invoke a sequence with an enclosing sequence action" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val inner_name = "inner_sequence"
- val outer_name = "outer_sequence"
- val inner_actions = Seq("sort", "head")
- val actions = Seq("split") ++ inner_actions ++ Seq("cat")
- // create atomic actions
- for (actionName <- actions) {
- val file = TestCLIUtils.getTestActionFilename(s"$actionName.js")
- assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
- }
- }
-
- // create inner sequence
- assetHelper.withCleaner(wsk.action, inner_name) {
- val inner_sequence = inner_actions.mkString(",")
- (action, _) => action.create(inner_name, Some(inner_sequence), kind = Some("sequence"))
- }
-
- // create outer sequence
- assetHelper.withCleaner(wsk.action, outer_name) {
- val outer_sequence = Seq("split", "inner_sequence", "cat").mkString(",")
- (action, _) => action.create(outer_name, Some(outer_sequence), kind = Some("sequence"))
- }
-
- val now = "it is now " + new Date()
- val args = Array("what time is it?", now)
- val run = wsk.action.invoke(outer_name, Map("payload" -> args.mkString("\n").toJson))
- withActivation(wsk.activation, run, totalWait = 4 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 3) // 3 activations in this sequence
- activation.cause shouldBe None // topmost sequence
- val result = activation.response.result.get
- result.fields.get("payload") shouldBe defined
- result.fields.get("length") should not be defined
- result.fields.get("lines") shouldBe Some(JsArray(Vector(now.toJson)))
- }
+ // create inner sequence
+ assetHelper.withCleaner(wsk.action, inner_name) {
+ val inner_sequence = inner_actions.mkString(",")
+ (action, _) =>
+ action.create(inner_name, Some(inner_sequence), kind = Some("sequence"))
}
- /**
- * s -> echo, x, echo
- * x -> echo
- *
- * update x -> <limit-1> echo -- should work
- * run s -> should stop after <limit> echo
- *
- * This confirms that a dynamic check on the sequence length holds within the system limit.
- * This is different from creating a long sequence up front which will report a length error at create time.
- */
- it should "replace atomic component in a sequence that is too long and report invoke error" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val xName = "xSequence"
- val sName = "sSequence"
- val echo = "echo"
-
- // create echo action
- val file = TestCLIUtils.getTestActionFilename(s"$echo.js")
- assetHelper.withCleaner(wsk.action, echo) { (action, actionName) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
- }
- // create x
- assetHelper.withCleaner(wsk.action, xName) {
- (action, seqName) => action.create(seqName, Some(echo), kind = Some("sequence"))
- }
- // create s
- assetHelper.withCleaner(wsk.action, sName) {
- (action, seqName) => action.create(seqName, Some(s"$echo,$xName,$echo"), kind = Some("sequence"))
- }
-
- // invoke s
- val now = "it is now " + new Date()
- val args = Array("what time is it?", now)
- val argsJson = args.mkString("\n").toJson
- val run = wsk.action.invoke(sName, Map("payload" -> argsJson))
- println(s"RUN: ${run.stdout}")
- withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 3) // 3 activations in this sequence
- val result = activation.response.result.get
- result.fields.get("payload") shouldBe Some(argsJson)
- }
- // update x with limit echo
- val limit = WhiskProperties.getProperty("defaultLimits.actions.sequence.maxLength").toInt
- val manyEcho = for (i <- 1 to limit) yield echo
-
- wsk.action.create(xName, Some(manyEcho.mkString(",")), kind = Some("sequence"), update = true)
-
- val updateRun = wsk.action.invoke(sName, Map("payload" -> argsJson))
- withActivation(wsk.activation, updateRun, totalWait = 2 * allowedActionDuration) {
- activation =>
- activation.response.status shouldBe ("application error")
- checkSequenceLogsAndAnnotations(activation, 2)
- val result = activation.response.result.get
- result.fields.get("error") shouldBe Some(JsString(sequenceIsTooLong))
- // check that inner sequence had only (limit - 1) activations
- val innerSeq = activation.logs.get(1) // the id of the inner sequence activation
- val getInnerSeq = wsk.activation.get(Some(innerSeq))
- withActivation(wsk.activation, getInnerSeq, totalWait = allowedActionDuration) {
- innerSeqActivation =>
- innerSeqActivation.logs.get.size shouldBe (limit - 1)
- innerSeqActivation.cause shouldBe Some(activation.activationId)
- }
- }
+ // create outer sequence
+ assetHelper.withCleaner(wsk.action, outer_name) {
+ val outer_sequence = Seq("split", "inner_sequence", "cat").mkString(",")
+ (action, _) =>
+ action.create(outer_name, Some(outer_sequence), kind = Some("sequence"))
}
- it should "create and run a sequence in a package with parameters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val sName = "sSequence"
-
- // create a package
- val pkgName = "echopackage"
- val pkgStr = "LonelyPackage"
- assetHelper.withCleaner(wsk.pkg, pkgName) {
- (pkg, name) => pkg.create(name, Map("payload" -> JsString(pkgStr)))
- }
- val helloName = "hello"
- val helloWithPkg = s"$pkgName/$helloName"
-
- // create hello action in package
- val file = TestCLIUtils.getTestActionFilename(s"$helloName.js")
- val actionStr = "AtomicAction"
- assetHelper.withCleaner(wsk.action, helloWithPkg) { (action, actionName) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration), parameters = Map("payload" -> JsString(actionStr)))
- }
- // create s
- assetHelper.withCleaner(wsk.action, sName) {
- (action, seqName) => action.create(seqName, Some(helloWithPkg), kind = Some("sequence"))
- }
- val run = wsk.action.invoke(sName)
- // action params trump package params
- checkLogsAtomicAction(0, run, new Regex(actionStr))
- // run with some parameters
- val sequenceStr = "AlmightySequence"
- val sequenceParamRun = wsk.action.invoke(sName, parameters = Map("payload" -> JsString(sequenceStr)))
- // sequence param should be passed to the first atomic action and trump the action params
- checkLogsAtomicAction(0, sequenceParamRun, new Regex(sequenceStr))
- // update action and remove the params by sending an unused param that overrides previous params
- wsk.action.create(name = helloWithPkg, artifact = Some(file), timeout = Some(allowedActionDuration), parameters = Map("param" -> JsString("irrelevant")), update = true)
- val sequenceParamSecondRun = wsk.action.invoke(sName, parameters = Map("payload" -> JsString(sequenceStr)))
- // sequence param should be passed to the first atomic action and trump the package params
- checkLogsAtomicAction(0, sequenceParamSecondRun, new Regex(sequenceStr))
- val pkgParamRun = wsk.action.invoke(sName)
- // no sequence params, no atomic action params used, the pkg params should show up
- checkLogsAtomicAction(0, pkgParamRun, new Regex(pkgStr))
+ val now = "it is now " + new Date()
+ val args = Array("what time is it?", now)
+ val run = wsk.action.invoke(outer_name, Map("payload" -> args.mkString("\n").toJson))
+ withActivation(wsk.activation, run, totalWait = 4 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 3) // 3 activations in this sequence
+ activation.cause shouldBe None // topmost sequence
+ val result = activation.response.result.get
+ result.fields.get("payload") shouldBe defined
+ result.fields.get("length") should not be defined
+ result.fields.get("lines") shouldBe Some(JsArray(Vector(now.toJson)))
}
-
- it should "run a sequence with an action in a package binding with parameters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val packageName = "package1"
- val bindName = "package2"
- val actionName = "print"
- val packageActionName = packageName + "/" + actionName
- val bindActionName = bindName + "/" + actionName
- val packageParams = Map("key1a" -> "value1a".toJson, "key1b" -> "value1b".toJson)
- val bindParams = Map("key2a" -> "value2a".toJson, "key1b" -> "value2b".toJson)
- val actionParams = Map("key0" -> "value0".toJson)
- val file = TestCLIUtils.getTestActionFilename("printParams.js")
- assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
- pkg.create(packageName, packageParams)
- }
- assetHelper.withCleaner(wsk.action, packageActionName) { (action, _) =>
- action.create(packageActionName, Some(file), parameters = actionParams)
- }
- assetHelper.withCleaner(wsk.pkg, bindName) { (pkg, _) =>
- pkg.bind(packageName, bindName, bindParams)
- }
- // sequence
- val sName = "sequenceWithBindingParams"
- assetHelper.withCleaner(wsk.action, sName) {
- (action, seqName) => action.create(seqName, Some(bindActionName), kind = Some("sequence"))
- }
- // Check that inherited parameters are passed to the action.
- val now = new Date().toString()
- val run = wsk.action.invoke(sName, Map("payload" -> now.toJson))
- // action params trump package params
- checkLogsAtomicAction(0, run, new Regex(String.format(".*key0: value0.*key1a: value1a.*key1b: value2b.*key2a: value2a.*payload: %s", now)))
+ }
+
+ /**
+ * s -> echo, x, echo
+ * x -> echo
+ *
+ * update x -> <limit-1> echo -- should work
+ * run s -> should stop after <limit> echo
+ *
+ * This confirms that a dynamic check on the sequence length holds within the system limit.
+ * This is different from creating a long sequence up front which will report a length error at create time.
+ */
+ it should "replace atomic component in a sequence that is too long and report invoke error" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val xName = "xSequence"
+ val sName = "sSequence"
+ val echo = "echo"
+
+ // create echo action
+ val file = TestUtils.getTestActionFilename(s"$echo.js")
+ assetHelper.withCleaner(wsk.action, echo) { (action, actionName) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
}
-
- /**
- * s -> apperror, echo
- * only apperror should run
- */
- it should "stop execution of a sequence (with no payload) on error" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val sName = "sSequence"
- val apperror = "applicationError"
- val echo = "echo"
-
- // create actions
- val actions = Seq(apperror, echo)
- for (actionName <- actions) {
- val file = TestCLIUtils.getTestActionFilename(s"$actionName.js")
- assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
- }
- }
- // create sequence s
- assetHelper.withCleaner(wsk.action, sName) {
- (action, seqName) => action.create(seqName, artifact = Some(actions.mkString(",")), kind = Some("sequence"))
- }
- // run sequence s with no payload
- val run = wsk.action.invoke(sName)
- withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 1) // only the first action should have run
- activation.response.success shouldBe (false)
- // the status should be error
- activation.response.status shouldBe ("application error")
- val result = activation.response.result.get
- // the result of the activation should be the application error
- result shouldBe (JsObject("error" -> JsString("This error thrown on purpose by the action.")))
- }
+ // create x
+ assetHelper.withCleaner(wsk.action, xName) { (action, seqName) =>
+ action.create(seqName, Some(echo), kind = Some("sequence"))
}
-
- /**
- * s -> echo, initforever
- * should run both, but error
- */
- it should "propagate execution error (timeout) from atomic action to sequence" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val sName = "sSequence"
- val initforever = "initforever"
- val echo = "echo"
-
- // create actions
- val actions = Seq(echo, initforever)
- // timeouts for the action; make the one for initforever short
- val timeout = Map(echo -> allowedActionDuration, initforever -> shortDuration)
- for (actionName <- actions) {
- val file = TestCLIUtils.getTestActionFilename(s"$actionName.js")
- assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(timeout(actionName)))
- }
- }
- // create sequence s
- assetHelper.withCleaner(wsk.action, sName) {
- (action, seqName) => action.create(seqName, artifact = Some(actions.mkString(",")), kind = Some("sequence"))
- }
- // run sequence s with no payload
- val run = wsk.action.invoke(sName)
- withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 2) // 2 actions
- activation.response.success shouldBe (false)
- // the status should be error
- //activation.response.status shouldBe("application error")
- val result = activation.response.result.get
- // the result of the activation should be timeout
- result shouldBe (JsObject("error" -> JsString("The action exceeded its time limits of 10000 milliseconds during initialization.")))
- }
+ // create s
+ assetHelper.withCleaner(wsk.action, sName) { (action, seqName) =>
+ action.create(seqName, Some(s"$echo,$xName,$echo"), kind = Some("sequence"))
}
- /**
- * s -> echo, sleep
- * sleep sleeps for 90s, timeout set at 120s
- * should run both, the blocking call should be transformed into a non-blocking call, but finish executing
- */
- it should "execute a sequence in blocking fashion and finish execution even if longer than blocking response timeout" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val sName = "sSequence"
- val sleep = "timeout"
- val echo = "echo"
-
- // create actions
- val actions = Seq(echo, sleep)
- for (actionName <- actions) {
- val file = TestCLIUtils.getTestActionFilename(s"$actionName.js")
- assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
- }
- }
- // create sequence s
- assetHelper.withCleaner(wsk.action, sName) {
- (action, seqName) => action.create(seqName, artifact = Some(actions.mkString(",")), kind = Some("sequence"))
- }
- // run sequence s with sleep equal to payload
- val payload = 65000
- val run = wsk.action.invoke(sName, parameters = Map("payload" -> JsNumber(payload)), blocking = true, expectedExitCode = ACCEPTED)
- withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 3 * allowedActionDuration) {
- activation =>
- checkSequenceLogsAndAnnotations(activation, 2) // 2 actions
- activation.response.success shouldBe (true)
- // the status should be error
- //activation.response.status shouldBe("application error")
- val result = activation.response.result.get
- // the result of the activation should be timeout
- result shouldBe (JsObject("msg" -> JsString(s"[OK] message terminated successfully after $payload milliseconds.")))
- }
+ // invoke s
+ val now = "it is now " + new Date()
+ val args = Array("what time is it?", now)
+ val argsJson = args.mkString("\n").toJson
+ val run = wsk.action.invoke(sName, Map("payload" -> argsJson))
+ println(s"RUN: ${run.stdout}")
+ withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 3) // 3 activations in this sequence
+ val result = activation.response.result.get
+ result.fields.get("payload") shouldBe Some(argsJson)
}
-
- /**
- * sequence s -> echo
- * t trigger with payload
- * rule r: t -> s
- */
- it should "execute a sequence that is part of a rule and pass the trigger parameters to the sequence" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val seqName = "seqRule"
- val actionName = "echo"
- val triggerName = "trigSeq"
- val ruleName = "ruleSeq"
-
- val itIsNow = "it is now " + new Date()
- // set up all entities
- // trigger
- val triggerPayload: Map[String, JsValue] = Map("payload" -> JsString(itIsNow))
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name, parameters = triggerPayload)
- }
- // action
- val file = TestCLIUtils.getTestActionFilename(s"$actionName.js")
- assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
- action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
- }
- // sequence
- assetHelper.withCleaner(wsk.action, seqName) {
- (action, seqName) => action.create(seqName, artifact = Some(actionName), kind = Some("sequence"))
- }
- // rule
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) => rule.create(name, triggerName, seqName)
- }
- // fire trigger
- val run = wsk.trigger.fire(triggerName)
- // check that the sequence was invoked and that the echo action produced the expected result
- checkEchoSeqRuleResult(run, seqName, JsObject(triggerPayload))
- // fire trigger with new payload
- val now = "this is now: " + Instant.now
- val newPayload = Map("payload" -> JsString(now))
- val newRun = wsk.trigger.fire(triggerName, newPayload)
- checkEchoSeqRuleResult(newRun, seqName, JsObject(newPayload))
+ // update x with limit echo
+ val limit = whiskConfig.actionSequenceLimit.toInt
+ val manyEcho = for (i <- 1 to limit) yield echo
+
+ wsk.action.create(xName, Some(manyEcho.mkString(",")), kind = Some("sequence"), update = true)
+
+ val updateRun = wsk.action.invoke(sName, Map("payload" -> argsJson))
+ withActivation(wsk.activation, updateRun, totalWait = 2 * allowedActionDuration) { activation =>
+ activation.response.status shouldBe ("application error")
+ checkSequenceLogsAndAnnotations(activation, 2)
+ val result = activation.response.result.get
+ result.fields.get("error") shouldBe Some(JsString(sequenceIsTooLong))
+ // check that inner sequence had only (limit - 1) activations
+ val innerSeq = activation.logs.get(1) // the id of the inner sequence activation
+ val getInnerSeq = wsk.activation.get(Some(innerSeq))
+ withActivation(wsk.activation, getInnerSeq, totalWait = allowedActionDuration) { innerSeqActivation =>
+ innerSeqActivation.logs.get.size shouldBe (limit - 1)
+ innerSeqActivation.cause shouldBe Some(activation.activationId)
+ }
}
-
- /**
- * checks the result of an echo sequence connected to a trigger through a rule
- * @param triggerFireRun the run result of firing the trigger
- * @param seqName the sequence name
- * @param triggerPayload the payload used for the trigger (that should be reflected in the sequence result)
- */
- private def checkEchoSeqRuleResult(triggerFireRun: RunResult, seqName: String, triggerPayload: JsObject) = {
- withActivation(wsk.activation, triggerFireRun) {
- triggerActivation =>
- withActivationsFromEntity(wsk.activation, seqName, since = Some(triggerActivation.start)) { activationList =>
- activationList.head.response.result shouldBe Some(triggerPayload)
- activationList.head.cause shouldBe None
- }
+ }
+
+ it should "create and run a sequence in a package with parameters" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val sName = "sSequence"
+
+ // create a package
+ val pkgName = "echopackage"
+ val pkgStr = "LonelyPackage"
+ assetHelper.withCleaner(wsk.pkg, pkgName) { (pkg, name) =>
+ pkg.create(name, Map("payload" -> JsString(pkgStr)))
+ }
+ val helloName = "hello"
+ val helloWithPkg = s"$pkgName/$helloName"
+
+ // create hello action in package
+ val file = TestUtils.getTestActionFilename(s"$helloName.js")
+ val actionStr = "AtomicAction"
+ assetHelper.withCleaner(wsk.action, helloWithPkg) { (action, actionName) =>
+ action.create(
+ name = actionName,
+ artifact = Some(file),
+ timeout = Some(allowedActionDuration),
+ parameters = Map("payload" -> JsString(actionStr)))
+ }
+ // create s
+ assetHelper.withCleaner(wsk.action, sName) { (action, seqName) =>
+ action.create(seqName, Some(helloWithPkg), kind = Some("sequence"))
+ }
+ val run = wsk.action.invoke(sName)
+ // action params trump package params
+ checkLogsAtomicAction(0, run, new Regex(actionStr))
+ // run with some parameters
+ val sequenceStr = "AlmightySequence"
+ val sequenceParamRun = wsk.action.invoke(sName, parameters = Map("payload" -> JsString(sequenceStr)))
+ // sequence param should be passed to the first atomic action and trump the action params
+ checkLogsAtomicAction(0, sequenceParamRun, new Regex(sequenceStr))
+ // update action and remove the params by sending an unused param that overrides previous params
+ wsk.action.create(
+ name = helloWithPkg,
+ artifact = Some(file),
+ timeout = Some(allowedActionDuration),
+ parameters = Map("param" -> JsString("irrelevant")),
+ update = true)
+ val sequenceParamSecondRun = wsk.action.invoke(sName, parameters = Map("payload" -> JsString(sequenceStr)))
+ // sequence param should be passed to the first atomic action and trump the package params
+ checkLogsAtomicAction(0, sequenceParamSecondRun, new Regex(sequenceStr))
+ val pkgParamRun = wsk.action.invoke(sName)
+ // no sequence params, no atomic action params used, the pkg params should show up
+ checkLogsAtomicAction(0, pkgParamRun, new Regex(pkgStr))
+ }
+
+ it should "run a sequence with an action in a package binding with parameters" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val packageName = "package1"
+ val bindName = "package2"
+ val actionName = "print"
+ val packageActionName = packageName + "/" + actionName
+ val bindActionName = bindName + "/" + actionName
+ val packageParams = Map("key1a" -> "value1a".toJson, "key1b" -> "value1b".toJson)
+ val bindParams = Map("key2a" -> "value2a".toJson, "key1b" -> "value2b".toJson)
+ val actionParams = Map("key0" -> "value0".toJson)
+ val file = TestUtils.getTestActionFilename("printParams.js")
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, packageParams)
+ }
+ assetHelper.withCleaner(wsk.action, packageActionName) { (action, _) =>
+ action.create(packageActionName, Some(file), parameters = actionParams)
+ }
+ assetHelper.withCleaner(wsk.pkg, bindName) { (pkg, _) =>
+ pkg.bind(packageName, bindName, bindParams)
+ }
+ // sequence
+ val sName = "sequenceWithBindingParams"
+ assetHelper.withCleaner(wsk.action, sName) { (action, seqName) =>
+ action.create(seqName, Some(bindActionName), kind = Some("sequence"))
+ }
+ // Check that inherited parameters are passed to the action.
+ val now = new Date().toString()
+ val run = wsk.action.invoke(sName, Map("payload" -> now.toJson))
+ // action params trump package params
+ checkLogsAtomicAction(
+ 0,
+ run,
+ new Regex(String.format(".*key0: value0.*key1a: value1a.*key1b: value2b.*key2a: value2a.*payload: %s", now)))
+ }
+
+ /**
+ * s -> apperror, echo
+ * only apperror should run
+ */
+ it should "stop execution of a sequence (with no payload) on error" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val sName = "sSequence"
+ val apperror = "applicationError"
+ val echo = "echo"
+
+ // create actions
+ val actions = Seq(apperror, echo)
+ for (actionName <- actions) {
+ val file = TestUtils.getTestActionFilename(s"$actionName.js")
+ assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
}
- }
-
- /**
- * checks logs for the activation of a sequence (length/size and ids)
- * checks that the cause field for composing atomic actions is set properly
- * checks duration
- * checks memory
- */
- private def checkSequenceLogsAndAnnotations(activation: ActivationResult, size: Int) = {
- activation.logs shouldBe defined
- // check that the logs are what they are supposed to be (activation ids)
- // check that the cause field is properly set for these activations
- activation.logs.get.size shouldBe (size) // the number of activations in this sequence
- var totalTime: Long = 0
- var maxMemory: Long = 0
- for (id <- activation.logs.get) {
- withActivation(wsk.activation, id, initialWait = 1 second, pollPeriod = 60 seconds, totalWait = allowedActionDuration) {
- componentActivation =>
- componentActivation.cause shouldBe defined
- componentActivation.cause.get shouldBe (activation.activationId)
- // check causedBy
- val causedBy = componentActivation.getAnnotationValue("causedBy")
- causedBy shouldBe defined
- causedBy.get shouldBe (JsString("sequence"))
- totalTime += componentActivation.duration
- // extract memory
- val mem = extractMemoryAnnotation(componentActivation)
- maxMemory = maxMemory max mem
- }
+ }
+ // create sequence s
+ assetHelper.withCleaner(wsk.action, sName) { (action, seqName) =>
+ action.create(seqName, artifact = Some(actions.mkString(",")), kind = Some("sequence"))
+ }
+ // run sequence s with no payload
+ val run = wsk.action.invoke(sName)
+ withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 1) // only the first action should have run
+ activation.response.success shouldBe (false)
+ // the status should be error
+ activation.response.status shouldBe ("application error")
+ val result = activation.response.result.get
+ // the result of the activation should be the application error
+ result shouldBe (JsObject("error" -> JsString("This error thrown on purpose by the action.")))
+ }
+ }
+
+ /**
+ * s -> echo, initforever
+ * should run both, but error
+ */
+ it should "propagate execution error (timeout) from atomic action to sequence" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val sName = "sSequence"
+ val initforever = "initforever"
+ val echo = "echo"
+
+ // create actions
+ val actions = Seq(echo, initforever)
+ // timeouts for the action; make the one for initforever short
+ val timeout = Map(echo -> allowedActionDuration, initforever -> shortDuration)
+ for (actionName <- actions) {
+ val file = TestUtils.getTestActionFilename(s"$actionName.js")
+ assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(timeout(actionName)))
}
- // extract duration
- activation.duration shouldBe (totalTime)
+ }
+ // create sequence s
+ assetHelper.withCleaner(wsk.action, sName) { (action, seqName) =>
+ action.create(seqName, artifact = Some(actions.mkString(",")), kind = Some("sequence"))
+ }
+ // run sequence s with no payload
+ val run = wsk.action.invoke(sName)
+ withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 2) // 2 actions
+ activation.response.success shouldBe (false)
+ // the status should be error
+ //activation.response.status shouldBe("application error")
+ val result = activation.response.result.get
+ // the result of the activation should be timeout
+ result shouldBe (JsObject(
+ "error" -> JsString("The action exceeded its time limits of 10000 milliseconds during initialization.")))
+ }
+ }
+
+ /**
+ * s -> echo, sleep
+ * sleep sleeps for 90s, timeout set at 120s
+ * should run both, the blocking call should be transformed into a non-blocking call, but finish executing
+ */
+ it should "execute a sequence in blocking fashion and finish execution even if longer than blocking response timeout" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val sName = "sSequence"
+ val sleep = "timeout"
+ val echo = "echo"
+
+ // create actions
+ val actions = Seq(echo, sleep)
+ for (actionName <- actions) {
+ val file = TestUtils.getTestActionFilename(s"$actionName.js")
+ assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
+ }
+ }
+ // create sequence s
+ assetHelper.withCleaner(wsk.action, sName) { (action, seqName) =>
+ action.create(seqName, artifact = Some(actions.mkString(",")), kind = Some("sequence"))
+ }
+ // run sequence s with sleep equal to payload
+ val payload = 65000
+ val run = wsk.action.invoke(
+ sName,
+ parameters = Map("payload" -> JsNumber(payload)),
+ blocking = true,
+ expectedExitCode = ACCEPTED)
+ withActivation(wsk.activation, run, initialWait = 5 seconds, totalWait = 3 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 2) // 2 actions
+ activation.response.success shouldBe (true)
+ // the status should be error
+ //activation.response.status shouldBe("application error")
+ val result = activation.response.result.get
+ // the result of the activation should be timeout
+ result shouldBe (JsObject(
+ "msg" -> JsString(s"[OK] message terminated successfully after $payload milliseconds.")))
+ }
+ }
+
+ /**
+ * sequence s -> echo
+ * t trigger with payload
+ * rule r: t -> s
+ */
+ it should "execute a sequence that is part of a rule and pass the trigger parameters to the sequence" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val seqName = "seqRule"
+ val actionName = "echo"
+ val triggerName = "trigSeq"
+ val ruleName = "ruleSeq"
+
+ val itIsNow = "it is now " + new Date()
+ // set up all entities
+ // trigger
+ val triggerPayload: Map[String, JsValue] = Map("payload" -> JsString(itIsNow))
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name, parameters = triggerPayload)
+ }
+ // action
+ val file = TestUtils.getTestActionFilename(s"$actionName.js")
+ assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
+ action.create(name = actionName, artifact = Some(file), timeout = Some(allowedActionDuration))
+ }
+ // sequence
+ assetHelper.withCleaner(wsk.action, seqName) { (action, seqName) =>
+ action.create(seqName, artifact = Some(actionName), kind = Some("sequence"))
+ }
+ // rule
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, triggerName, seqName)
+ }
+ // fire trigger
+ val run = wsk.trigger.fire(triggerName)
+ // check that the sequence was invoked and that the echo action produced the expected result
+ checkEchoSeqRuleResult(run, seqName, JsObject(triggerPayload))
+ // fire trigger with new payload
+ val now = "this is now: " + Instant.now
+ val newPayload = Map("payload" -> JsString(now))
+ val newRun = wsk.trigger.fire(triggerName, newPayload)
+ checkEchoSeqRuleResult(newRun, seqName, JsObject(newPayload))
+ }
+
+ /**
+ * checks the result of an echo sequence connected to a trigger through a rule
+ * @param triggerFireRun the run result of firing the trigger
+ * @param seqName the sequence name
+ * @param triggerPayload the payload used for the trigger (that should be reflected in the sequence result)
+ */
+ private def checkEchoSeqRuleResult(triggerFireRun: RunResult, seqName: String, triggerPayload: JsObject) = {
+ withActivation(wsk.activation, triggerFireRun) { triggerActivation =>
+ withActivationsFromEntity(wsk.activation, seqName, since = Some(triggerActivation.start)) { activationList =>
+ activationList.head.response.result shouldBe Some(triggerPayload)
+ activationList.head.cause shouldBe None
+ }
+ }
+ }
+
+ /**
+ * checks logs for the activation of a sequence (length/size and ids)
+ * checks that the cause field for composing atomic actions is set properly
+ * checks duration
+ * checks memory
+ */
+ private def checkSequenceLogsAndAnnotations(activation: ActivationResult, size: Int) = {
+ activation.logs shouldBe defined
+ // check that the logs are what they are supposed to be (activation ids)
+ // check that the cause field is properly set for these activations
+ activation.logs.get.size shouldBe (size) // the number of activations in this sequence
+ var totalTime: Long = 0
+ var maxMemory: Long = 0
+ for (id <- activation.logs.get) {
+ withActivation(
+ wsk.activation,
+ id,
+ initialWait = 1 second,
+ pollPeriod = 60 seconds,
+ totalWait = allowedActionDuration) { componentActivation =>
+ componentActivation.cause shouldBe defined
+ componentActivation.cause.get shouldBe (activation.activationId)
+ // check causedBy
+ val causedBy = componentActivation.getAnnotationValue("causedBy")
+ causedBy shouldBe defined
+ causedBy.get shouldBe (JsString("sequence"))
+ totalTime += componentActivation.duration
// extract memory
- activation.annotations shouldBe defined
- val memory = extractMemoryAnnotation(activation)
- memory shouldBe (maxMemory)
+ val mem = extractMemoryAnnotation(componentActivation)
+ maxMemory = maxMemory max mem
+ }
}
-
- /** checks that the logs of the idx-th atomic action from a sequence contains logsStr */
- private def checkLogsAtomicAction(atomicActionIdx: Int, run: RunResult, regex: Regex) {
- withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) { activation =>
- checkSequenceLogsAndAnnotations(activation, 1)
- val componentId = activation.logs.get(atomicActionIdx)
- val getComponentActivation = wsk.activation.get(Some(componentId))
- withActivation(wsk.activation, getComponentActivation, totalWait = allowedActionDuration) { componentActivation =>
- println(componentActivation)
- componentActivation.logs shouldBe defined
- val logs = componentActivation.logs.get.mkString(" ")
- regex.findFirstIn(logs) shouldBe defined
- }
- }
+ // extract duration
+ activation.duration shouldBe (totalTime)
+ // extract memory
+ activation.annotations shouldBe defined
+ val memory = extractMemoryAnnotation(activation)
+ memory shouldBe (maxMemory)
+ }
+
+ /** checks that the logs of the idx-th atomic action from a sequence contains logsStr */
+ private def checkLogsAtomicAction(atomicActionIdx: Int, run: RunResult, regex: Regex) {
+ withActivation(wsk.activation, run, totalWait = 2 * allowedActionDuration) { activation =>
+ checkSequenceLogsAndAnnotations(activation, 1)
+ val componentId = activation.logs.get(atomicActionIdx)
+ val getComponentActivation = wsk.activation.get(Some(componentId))
+ withActivation(wsk.activation, getComponentActivation, totalWait = allowedActionDuration) { componentActivation =>
+ println(componentActivation)
+ componentActivation.logs shouldBe defined
+ val logs = componentActivation.logs.get.mkString(" ")
+ regex.findFirstIn(logs) shouldBe defined
+ }
}
+ }
- private def extractMemoryAnnotation(activation: ActivationResult): Long = {
- val limits = activation.getAnnotationValue("limits")
- limits shouldBe defined
- limits.get.asJsObject.getFields("memory")(0).convertTo[Long]
- }
+ private def extractMemoryAnnotation(activation: ActivationResult): Long = {
+ val limits = activation.getAnnotationValue("limits")
+ limits shouldBe defined
+ limits.get.asJsObject.getFields("memory")(0).convertTo[Long]
+ }
}
diff --git a/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala b/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala
index 66ebd90..c42e2e8 100644
--- a/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala
+++ b/tests/src/test/scala/whisk/core/admin/WskAdminTests.scala
@@ -33,141 +33,150 @@ import whisk.core.entity.Subject
import common.TestUtils
@RunWith(classOf[JUnitRunner])
-class WskAdminTests
- extends TestHelpers
- with Matchers {
-
- behavior of "Wsk Admin CLI"
-
- it should "confirm wskadmin exists" in {
- WskAdmin.exists
+class WskAdminTests extends TestHelpers with Matchers {
+
+ behavior of "Wsk Admin CLI"
+
+ it should "confirm wskadmin exists" in {
+ WskAdmin.exists
+ }
+
+ it should "CRD a subject" in {
+ val wskadmin = new RunWskAdminCmd {}
+ val auth = AuthKey()
+ val subject = Subject().asString
+ try {
+ println(s"CRD subject: $subject")
+ val create = wskadmin.cli(Seq("user", "create", subject))
+ val get = wskadmin.cli(Seq("user", "get", subject))
+ create.stdout should be(get.stdout)
+
+ val authkey = get.stdout.trim
+ authkey should include(":")
+ authkey.split(":")(0).length should be(36)
+ authkey.split(":")(1).length should be >= 64
+
+ wskadmin.cli(Seq("user", "whois", authkey)).stdout.trim should be(
+ Seq(s"subject: $subject", s"namespace: $subject").mkString("\n"))
+
+ whisk.utils.retry({
+ // reverse lookup by namespace
+ wskadmin.cli(Seq("user", "list", "-k", subject)).stdout.trim should be(authkey)
+ }, 10, Some(1.second))
+
+ wskadmin.cli(Seq("user", "delete", subject)).stdout should include("Subject deleted")
+
+ // recreate with explicit
+ val newspace = s"${subject}.myspace"
+ wskadmin.cli(Seq("user", "create", subject, "-ns", newspace, "-u", auth.compact))
+
+ whisk.utils.retry({
+ // reverse lookup by namespace
+ wskadmin.cli(Seq("user", "list", "-k", newspace)).stdout.trim should be(auth.compact)
+ }, 10, Some(1.second))
+
+ wskadmin.cli(Seq("user", "get", subject, "-ns", newspace)).stdout.trim should be(auth.compact)
+
+ // delete namespace
+ wskadmin.cli(Seq("user", "delete", subject, "-ns", newspace)).stdout should include("Namespace deleted")
+ } finally {
+ wskadmin.cli(Seq("user", "delete", subject)).stdout should include("Subject deleted")
}
-
- it should "CRD a subject" in {
- val wskadmin = new RunWskAdminCmd {}
- val auth = AuthKey()
- val subject = Subject().asString
- try {
- println(s"CRD subject: $subject")
- val create = wskadmin.cli(Seq("user", "create", subject))
- val get = wskadmin.cli(Seq("user", "get", subject))
- create.stdout should be(get.stdout)
-
- val authkey = get.stdout.trim
- authkey should include(":")
- authkey.split(":")(0).length should be(36)
- authkey.split(":")(1).length should be >= 64
-
- wskadmin.cli(Seq("user", "whois", authkey)).stdout.trim should be(Seq(s"subject: $subject", s"namespace: $subject").mkString("\n"))
-
- whisk.utils.retry({
- // reverse lookup by namespace
- wskadmin.cli(Seq("user", "list", "-k", subject)).stdout.trim should be(authkey)
- }, 10, Some(1.second))
-
- wskadmin.cli(Seq("user", "delete", subject)).stdout should include("Subject deleted")
-
- // recreate with explicit
- val newspace = s"${subject}.myspace"
- wskadmin.cli(Seq("user", "create", subject, "-ns", newspace, "-u", auth.compact))
-
- whisk.utils.retry({
- // reverse lookup by namespace
- wskadmin.cli(Seq("user", "list", "-k", newspace)).stdout.trim should be(auth.compact)
- }, 10, Some(1.second))
-
- wskadmin.cli(Seq("user", "get", subject, "-ns", newspace)).stdout.trim should be(auth.compact)
-
- // delete namespace
- wskadmin.cli(Seq("user", "delete", subject, "-ns", newspace)).stdout should include("Namespace deleted")
- } finally {
- wskadmin.cli(Seq("user", "delete", subject)).stdout should include("Subject deleted")
- }
+ }
+
+ it should "verify guest account installed correctly" in {
+ val wskadmin = new RunWskAdminCmd {}
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val ns = wsk.namespace.whois()
+ wskadmin.cli(Seq("user", "get", ns)).stdout.trim should be(wskprops.authKey)
+ }
+
+ it should "block and unblock a user respectively" in {
+ val wskadmin = new RunWskAdminCmd {}
+ val auth = AuthKey()
+ val subject1 = Subject().asString
+ val subject2 = Subject().asString
+ val commonNamespace = "testspace"
+ try {
+ wskadmin.cli(Seq("user", "create", subject1, "-ns", commonNamespace, "-u", auth.compact))
+ wskadmin.cli(Seq("user", "create", subject2, "-ns", commonNamespace))
+
+ whisk.utils.retry({
+ // reverse lookup by namespace
+ val out = wskadmin.cli(Seq("user", "list", "-p", "2", "-k", commonNamespace)).stdout.trim
+ out should include(auth.compact)
+ out.lines should have size 2
+ }, 10, Some(1.second))
+
+ // block the user
+ wskadmin.cli(Seq("user", "block", subject1))
+
+ // wait until the user can no longer be found
+ whisk.utils.retry({
+ wskadmin.cli(Seq("user", "list", "-p", "2", "-k", commonNamespace)).stdout.trim.lines should have size 1
+ }, 10, Some(1.second))
+
+ // unblock the user
+ wskadmin.cli(Seq("user", "unblock", subject1))
+
+ // wait until the user can be found again
+ whisk.utils.retry({
+ val out = wskadmin.cli(Seq("user", "list", "-p", "2", "-k", commonNamespace)).stdout.trim
+ out should include(auth.compact)
+ out.lines should have size 2
+ }, 10, Some(1.second))
+ } finally {
+ wskadmin.cli(Seq("user", "delete", subject1)).stdout should include("Subject deleted")
+ wskadmin.cli(Seq("user", "delete", subject2)).stdout should include("Subject deleted")
}
-
- it should "verify guest account installed correctly" in {
- val wskadmin = new RunWskAdminCmd {}
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val ns = wsk.namespace.whois()
- wskadmin.cli(Seq("user", "get", ns)).stdout.trim should be(wskprops.authKey)
+ }
+
+ it should "not allow edits on a blocked subject" in {
+ val wskadmin = new RunWskAdminCmd {}
+ val subject = Subject().asString
+ try {
+ // initially create the subject
+ wskadmin.cli(Seq("user", "create", subject))
+ // editing works
+ wskadmin.cli(Seq("user", "create", subject, "-ns", "testspace1"))
+ // block it
+ wskadmin.cli(Seq("user", "block", subject))
+ // Try to add a namespace, doesn't work
+ wskadmin.cli(Seq("user", "create", subject, "-ns", "testspace2"), expectedExitCode = TestUtils.ERROR_EXIT)
+ // Unblock the user
+ wskadmin.cli(Seq("user", "unblock", subject))
+ // Adding a namespace works
+ wskadmin.cli(Seq("user", "create", subject, "-ns", "testspace2"))
+ } finally {
+ wskadmin.cli(Seq("user", "delete", subject)).stdout should include("Subject deleted")
}
-
- it should "block and unblock a user respectively" in {
- val wskadmin = new RunWskAdminCmd {}
- val auth = AuthKey()
- val subject1 = Subject().asString
- val subject2 = Subject().asString
- val commonNamespace = "testspace"
- try {
- wskadmin.cli(Seq("user", "create", subject1, "-ns", commonNamespace, "-u", auth.compact))
- wskadmin.cli(Seq("user", "create", subject2, "-ns", commonNamespace))
-
- whisk.utils.retry({
- // reverse lookup by namespace
- val out = wskadmin.cli(Seq("user", "list", "-p", "2", "-k", commonNamespace)).stdout.trim
- out should include(auth.compact)
- out.lines should have size 2
- }, 10, Some(1.second))
-
- // block the user
- wskadmin.cli(Seq("user", "block", subject1))
-
- // wait until the user can no longer be found
- whisk.utils.retry({
- wskadmin.cli(Seq("user", "list", "-p", "2", "-k", commonNamespace)).stdout.trim.lines should have size 1
- }, 10, Some(1.second))
-
- // unblock the user
- wskadmin.cli(Seq("user", "unblock", subject1))
-
- // wait until the user can be found again
- whisk.utils.retry({
- val out = wskadmin.cli(Seq("user", "list", "-p", "2", "-k", commonNamespace)).stdout.trim
- out should include(auth.compact)
- out.lines should have size 2
- }, 10, Some(1.second))
- } finally {
- wskadmin.cli(Seq("user", "delete", subject1)).stdout should include("Subject deleted")
- wskadmin.cli(Seq("user", "delete", subject2)).stdout should include("Subject deleted")
- }
- }
-
- it should "not allow edits on a blocked subject" in {
- val wskadmin = new RunWskAdminCmd {}
- val subject = Subject().asString
- try {
- // initially create the subject
- wskadmin.cli(Seq("user", "create", subject))
- // editing works
- wskadmin.cli(Seq("user", "create", subject, "-ns", "testspace1"))
- // block it
- wskadmin.cli(Seq("user", "block", subject))
- // Try to add a namespace, doesn't work
- wskadmin.cli(Seq("user", "create", subject, "-ns", "testspace2"), expectedExitCode = TestUtils.ERROR_EXIT)
- // Unblock the user
- wskadmin.cli(Seq("user", "unblock", subject))
- // Adding a namespace works
- wskadmin.cli(Seq("user", "create", subject, "-ns", "testspace2"))
- } finally {
- wskadmin.cli(Seq("user", "delete", subject)).stdout should include("Subject deleted")
- }
- }
-
- it should "adjust throttles for namespace" in {
- val wskadmin = new RunWskAdminCmd {}
- val subject = Subject().asString
- try {
- // set some limits
- wskadmin.cli(Seq("limits", "set", subject, "--invocationsPerMinute", "1", "--firesPerMinute", "2", "--concurrentInvocations", "3"))
- // check correctly set
- val lines = wskadmin.cli(Seq("limits", "get", subject)).stdout.lines.toSeq
- lines should have size 3
- lines(0) shouldBe "invocationsPerMinute = 1"
- lines(1) shouldBe "firesPerMinute = 2"
- lines(2) shouldBe "concurrentInvocations = 3"
- } finally {
- wskadmin.cli(Seq("limits", "delete", subject)).stdout should include("Limits deleted")
- }
+ }
+
+ it should "adjust throttles for namespace" in {
+ val wskadmin = new RunWskAdminCmd {}
+ val subject = Subject().asString
+ try {
+ // set some limits
+ wskadmin.cli(
+ Seq(
+ "limits",
+ "set",
+ subject,
+ "--invocationsPerMinute",
+ "1",
+ "--firesPerMinute",
+ "2",
+ "--concurrentInvocations",
+ "3"))
+ // check correctly set
+ val lines = wskadmin.cli(Seq("limits", "get", subject)).stdout.lines.toSeq
+ lines should have size 3
+ lines(0) shouldBe "invocationsPerMinute = 1"
+ lines(1) shouldBe "firesPerMinute = 2"
+ lines(2) shouldBe "concurrentInvocations = 3"
+ } finally {
+ wskadmin.cli(Seq("limits", "delete", subject)).stdout should include("Limits deleted")
}
+ }
}
diff --git a/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala b/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
index 28f9b0f..fa56de6 100644
--- a/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
+++ b/tests/src/test/scala/whisk/core/apigw/actions/test/ApiGwRoutemgmtActionTests.scala
@@ -36,20 +36,19 @@ import common.WskTestHelpers
import spray.json._
import spray.json.DefaultJsonProtocol._
-case class ApiAction(
- name: String,
- namespace: String,
- backendMethod: String = "POST",
- backendUrl: String,
- authkey: String) {
- def toJson(): JsObject = {
- return JsObject(
- "name" -> name.toJson,
- "namespace" -> namespace.toJson,
- "backendMethod" -> backendMethod.toJson,
- "backendUrl" -> backendUrl.toJson,
- "authkey" -> authkey.toJson)
- }
+case class ApiAction(name: String,
+ namespace: String,
+ backendMethod: String = "POST",
+ backendUrl: String,
+ authkey: String) {
+ def toJson(): JsObject = {
+ return JsObject(
+ "name" -> name.toJson,
+ "namespace" -> namespace.toJson,
+ "backendMethod" -> backendMethod.toJson,
+ "backendUrl" -> backendUrl.toJson,
+ "authkey" -> authkey.toJson)
+ }
}
/**
@@ -64,284 +63,532 @@ class ApiGwRoutemgmtActionTests
with JsHelpers
with StreamLogging {
- val systemId = "whisk.system"
- implicit val wskprops = WskProps(authKey = WskAdmin.listKeys(systemId)(0)._1, namespace = systemId)
- val wsk = new Wsk
-
- def getApis(
- bpOrName: Option[String],
- relpath: Option[String] = None,
- operation: Option[String] = None,
- docid: Option[String] = None,
- accesstoken: Option[String] = Some("AnAccessToken"),
- spaceguid: Option[String] = Some("ASpaceGuid") ): Vector[JsValue] = {
- val parms = Map[String, JsValue]() ++
- Map("__ow_user" -> wskprops.namespace.toJson) ++
- { bpOrName map { b => Map("basepath" -> b.toJson) } getOrElse Map[String, JsValue]() } ++
- { relpath map { r => Map("relpath" -> r.toJson) } getOrElse Map[String, JsValue]() } ++
- { operation map { o => Map("operation" -> o.toJson) } getOrElse Map[String, JsValue]() } ++
- { docid map { d => Map("docid" -> d.toJson) } getOrElse Map[String, JsValue]() } ++
- { accesstoken map { t => Map("accesstoken" -> t.toJson) } getOrElse Map[String, JsValue]() } ++
- { spaceguid map { s => Map("spaceguid" -> s.toJson) } getOrElse Map[String, JsValue]() }
+ val systemId = "whisk.system"
+ implicit val wskprops = WskProps(authKey = WskAdmin.listKeys(systemId)(0)._1, namespace = systemId)
+ val wsk = new Wsk
- val rr = wsk.action.invoke(
- name = "apimgmt/getApi",
- parameters = parms,
- blocking = true,
- result = true,
- expectedExitCode = SUCCESS_EXIT)(wskprops)
- var apiJsArray: JsArray =
- try {
- var apisobj = rr.stdout.parseJson.asJsObject.fields("apis")
- apisobj.convertTo[JsArray]
- } catch {
- case e: Exception =>
- JsArray.empty
- }
- return apiJsArray.elements
+ def getApis(bpOrName: Option[String],
+ relpath: Option[String] = None,
+ operation: Option[String] = None,
+ docid: Option[String] = None,
+ accesstoken: Option[String] = Some("AnAccessToken"),
+ spaceguid: Option[String] = Some("ASpaceGuid")): Vector[JsValue] = {
+ val parms = Map[String, JsValue]() ++
+ Map("__ow_user" -> wskprops.namespace.toJson) ++ {
+ bpOrName map { b =>
+ Map("basepath" -> b.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ relpath map { r =>
+ Map("relpath" -> r.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ operation map { o =>
+ Map("operation" -> o.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ docid map { d =>
+ Map("docid" -> d.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ accesstoken map { t =>
+ Map("accesstoken" -> t.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ spaceguid map { s =>
+ Map("spaceguid" -> s.toJson)
+ } getOrElse Map[String, JsValue]()
}
- def createApi(
- namespace: Option[String] = Some("_"),
- basepath: Option[String] = Some("/"),
- relpath: Option[String],
- operation: Option[String],
- apiname: Option[String],
- action: Option[ApiAction],
- swagger: Option[String] = None,
- accesstoken: Option[String] = Some("AnAccessToken"),
- spaceguid: Option[String] = Some("ASpaceGuid"),
- expectedExitCode: Int = SUCCESS_EXIT): RunResult = {
- val parms = Map[String, JsValue]() ++
- { namespace map { n => Map("namespace" -> n.toJson) } getOrElse Map[String, JsValue]() } ++
- { basepath map { b => Map("gatewayBasePath" -> b.toJson) } getOrElse Map[String, JsValue]() } ++
- { relpath map { r => Map("gatewayPath" -> r.toJson) } getOrElse Map[String, JsValue]() } ++
- { operation map { o => Map("gatewayMethod" -> o.toJson) } getOrElse Map[String, JsValue]() } ++
- { apiname map { an => Map("apiName" -> an.toJson) } getOrElse Map[String, JsValue]() } ++
- { action map { a => Map("action" -> a.toJson) } getOrElse Map[String, JsValue]() } ++
- { swagger map { s => Map("swagger" -> s.toJson) } getOrElse Map[String, JsValue]() }
- val parm = Map[String, JsValue]("apidoc" -> JsObject(parms)) ++
- { namespace map { n => Map("__ow_user" -> n.toJson) } getOrElse Map[String, JsValue]() } ++
- { accesstoken map { t => Map("accesstoken" -> t.toJson) } getOrElse Map[String, JsValue]() } ++
- { spaceguid map { s => Map("spaceguid" -> s.toJson) } getOrElse Map[String, JsValue]() }
+ val rr = wsk.action.invoke(
+ name = "apimgmt/getApi",
+ parameters = parms,
+ blocking = true,
+ result = true,
+ expectedExitCode = SUCCESS_EXIT)(wskprops)
+ var apiJsArray: JsArray =
+ try {
+ var apisobj = rr.stdout.parseJson.asJsObject.fields("apis")
+ apisobj.convertTo[JsArray]
+ } catch {
+ case e: Exception =>
+ JsArray.empty
+ }
+ return apiJsArray.elements
+ }
- val rr = wsk.action.invoke(
- name = "apimgmt/createApi",
- parameters = parm,
- blocking = true,
- result = true,
- expectedExitCode = expectedExitCode)(wskprops)
- return rr
+ def createApi(namespace: Option[String] = Some("_"),
+ basepath: Option[String] = Some("/"),
+ relpath: Option[String],
+ operation: Option[String],
+ apiname: Option[String],
+ action: Option[ApiAction],
+ swagger: Option[String] = None,
+ accesstoken: Option[String] = Some("AnAccessToken"),
+ spaceguid: Option[String] = Some("ASpaceGuid"),
+ expectedExitCode: Int = SUCCESS_EXIT): RunResult = {
+ val parms = Map[String, JsValue]() ++ {
+ namespace map { n =>
+ Map("namespace" -> n.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ basepath map { b =>
+ Map("gatewayBasePath" -> b.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ relpath map { r =>
+ Map("gatewayPath" -> r.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ operation map { o =>
+ Map("gatewayMethod" -> o.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ apiname map { an =>
+ Map("apiName" -> an.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ action map { a =>
+ Map("action" -> a.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ swagger map { s =>
+ Map("swagger" -> s.toJson)
+ } getOrElse Map[String, JsValue]()
+ }
+ val parm = Map[String, JsValue]("apidoc" -> JsObject(parms)) ++ {
+ namespace map { n =>
+ Map("__ow_user" -> n.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ accesstoken map { t =>
+ Map("accesstoken" -> t.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ spaceguid map { s =>
+ Map("spaceguid" -> s.toJson)
+ } getOrElse Map[String, JsValue]()
}
- def deleteApi(
- namespace: Option[String] = Some("_"),
- basepath: Option[String] = Some("/"),
- relpath: Option[String] = None,
- operation: Option[String] = None,
- apiname: Option[String] = None,
- accesstoken: Option[String] = Some("AnAccessToken"),
- spaceguid: Option[String] = Some("ASpaceGuid"),
- expectedExitCode: Int = SUCCESS_EXIT): RunResult = {
- val parms = Map[String, JsValue]() ++
- { namespace map { n => Map("__ow_user" -> n.toJson) } getOrElse Map[String, JsValue]() } ++
- { basepath map { b => Map("basepath" -> b.toJson) } getOrElse Map[String, JsValue]() } ++
- { relpath map { r => Map("relpath" -> r.toJson) } getOrElse Map[String, JsValue]() } ++
- { operation map { o => Map("operation" -> o.toJson) } getOrElse Map[String, JsValue]() } ++
- { apiname map { an => Map("apiname" -> an.toJson) } getOrElse Map[String, JsValue]() } ++
- { accesstoken map { t => Map("accesstoken" -> t.toJson) } getOrElse Map[String, JsValue]() } ++
- { spaceguid map { s => Map("spaceguid" -> s.toJson) } getOrElse Map[String, JsValue]() }
+ val rr = wsk.action.invoke(
+ name = "apimgmt/createApi",
+ parameters = parm,
+ blocking = true,
+ result = true,
+ expectedExitCode = expectedExitCode)(wskprops)
+ return rr
+ }
- val rr = wsk.action.invoke(
- name = "apimgmt/deleteApi",
- parameters = parms,
- blocking = true,
- result = true,
- expectedExitCode = expectedExitCode)(wskprops)
- return rr
+ def deleteApi(namespace: Option[String] = Some("_"),
+ basepath: Option[String] = Some("/"),
+ relpath: Option[String] = None,
+ operation: Option[String] = None,
+ apiname: Option[String] = None,
+ accesstoken: Option[String] = Some("AnAccessToken"),
+ spaceguid: Option[String] = Some("ASpaceGuid"),
+ expectedExitCode: Int = SUCCESS_EXIT): RunResult = {
+ val parms = Map[String, JsValue]() ++ {
+ namespace map { n =>
+ Map("__ow_user" -> n.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ basepath map { b =>
+ Map("basepath" -> b.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ relpath map { r =>
+ Map("relpath" -> r.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ operation map { o =>
+ Map("operation" -> o.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ apiname map { an =>
+ Map("apiname" -> an.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ accesstoken map { t =>
+ Map("accesstoken" -> t.toJson)
+ } getOrElse Map[String, JsValue]()
+ } ++ {
+ spaceguid map { s =>
+ Map("spaceguid" -> s.toJson)
+ } getOrElse Map[String, JsValue]()
}
- def apiMatch(
- apiarr: Vector[JsValue],
- basepath: String = "/",
- relpath: String = "",
- operation: String = "",
- apiname: String = "",
- action: ApiAction = null): Boolean = {
- var matches: Boolean = false
- for (api <- apiarr) {
- val basepathExists = JsObjectHelper(api.asJsObject).fieldPathExists("value", "apidoc", "basePath")
- if (basepathExists) {
- System.out.println("basePath exists")
- val basepathMatches = (JsObjectHelper(api.asJsObject).getFieldPath("value", "apidoc", "basePath").get.convertTo[String] == basepath)
- if (basepathMatches) {
- System.out.println("basePath matches: " + basepath)
- val apinameExists = JsObjectHelper(api.asJsObject).fieldPathExists("value", "apidoc", "info", "title")
- if (apinameExists) {
- System.out.println("api name exists")
- val apinameMatches = (JsObjectHelper(api.asJsObject).getFieldPath("value", "apidoc", "info", "title").get.convertTo[String] == apiname)
- if (apinameMatches) {
- System.out.println("api name matches: " + apiname)
- val endpointMatches = JsObjectHelper(api.asJsObject).fieldPathExists("value", "apidoc", "paths", relpath, operation)
- if (endpointMatches) {
- System.out.println("endpoint exists/matches : " + relpath + " " + operation)
- val actionConfig = JsObjectHelper(api.asJsObject).getFieldPath("value", "apidoc", "paths", relpath, operation, "x-openwhisk").get.asJsObject
- val actionMatches = actionMatch(actionConfig, action)
- if (actionMatches) {
- System.out.println("endpoint action matches")
- matches = true;
- }
- }
- }
- }
+ val rr = wsk.action.invoke(
+ name = "apimgmt/deleteApi",
+ parameters = parms,
+ blocking = true,
+ result = true,
+ expectedExitCode = expectedExitCode)(wskprops)
+ return rr
+ }
+
+ def apiMatch(apiarr: Vector[JsValue],
+ basepath: String = "/",
+ relpath: String = "",
+ operation: String = "",
+ apiname: String = "",
+ action: ApiAction = null): Boolean = {
+ var matches: Boolean = false
+ for (api <- apiarr) {
+ val basepathExists = JsObjectHelper(api.asJsObject).fieldPathExists("value", "apidoc", "basePath")
+ if (basepathExists) {
+ System.out.println("basePath exists")
+ val basepathMatches = (JsObjectHelper(api.asJsObject)
+ .getFieldPath("value", "apidoc", "basePath")
+ .get
+ .convertTo[String] == basepath)
+ if (basepathMatches) {
+ System.out.println("basePath matches: " + basepath)
+ val apinameExists = JsObjectHelper(api.asJsObject).fieldPathExists("value", "apidoc", "info", "title")
+ if (apinameExists) {
+ System.out.println("api name exists")
+ val apinameMatches = (JsObjectHelper(api.asJsObject)
+ .getFieldPath("value", "apidoc", "info", "title")
+ .get
+ .convertTo[String] == apiname)
+ if (apinameMatches) {
+ System.out.println("api name matches: " + apiname)
+ val endpointMatches =
+ JsObjectHelper(api.asJsObject).fieldPathExists("value", "apidoc", "paths", relpath, operation)
+ if (endpointMatches) {
+ System.out.println("endpoint exists/matches : " + relpath + " " + operation)
+ val actionConfig = JsObjectHelper(api.asJsObject)
+ .getFieldPath("value", "apidoc", "paths", relpath, operation, "x-openwhisk")
+ .get
+ .asJsObject
+ val actionMatches = actionMatch(actionConfig, action)
+ if (actionMatches) {
+ System.out.println("endpoint action matches")
+ matches = true;
}
+ }
}
+ }
}
- return matches
+ }
}
+ return matches
+ }
- def actionMatch(
- jsAction: JsObject,
- action: ApiAction): Boolean = {
- System.out.println("actionMatch: url " + jsAction.fields("url").convertTo[String] + "; backendUrl " + action.backendUrl)
- System.out.println("actionMatch: namespace " + jsAction.fields("namespace").convertTo[String] + "; namespace " + action.namespace)
- System.out.println("actionMatch: action " + jsAction.fields("action").convertTo[String] + "; action " + action.name)
- val matches = jsAction.fields("url").convertTo[String] == action.backendUrl &&
- jsAction.fields("namespace").convertTo[String] == action.namespace &&
- jsAction.fields("action").convertTo[String] == action.name
- return matches
- }
+ def actionMatch(jsAction: JsObject, action: ApiAction): Boolean = {
+ System.out.println(
+ "actionMatch: url " + jsAction.fields("url").convertTo[String] + "; backendUrl " + action.backendUrl)
+ System.out.println(
+ "actionMatch: namespace " + jsAction.fields("namespace").convertTo[String] + "; namespace " + action.namespace)
+ System.out.println("actionMatch: action " + jsAction.fields("action").convertTo[String] + "; action " + action.name)
+ val matches = jsAction.fields("url").convertTo[String] == action.backendUrl &&
+ jsAction.fields("namespace").convertTo[String] == action.namespace &&
+ jsAction.fields("action").convertTo[String] == action.name
+ return matches
+ }
- behavior of "API Gateway apimgmt action parameter validation"
+ behavior of "API Gateway apimgmt action parameter validation"
- it should "verify successful creation of a new API" in {
- val testName = "APIGWTEST1"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val actionNamespace = wskprops.namespace
- val actionUrl = "https://some.whisk.host/api/v1/web/" + actionNamespace + "/default/" + actionName + ".json"
- val actionAuthKey = testName + "_authkey"
- val testaction = ApiAction(name = actionName, namespace = actionNamespace, backendUrl = actionUrl, authkey = actionAuthKey)
+ it should "verify successful creation of a new API" in {
+ val testName = "APIGWTEST1"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val actionNamespace = wskprops.namespace
+ val actionUrl = "https://some.whisk.host/api/v1/web/" + actionNamespace + "/default/" + actionName + ".json"
+ val actionAuthKey = testName + "_authkey"
+ val testaction =
+ ApiAction(name = actionName, namespace = actionNamespace, backendUrl = actionUrl, authkey = actionAuthKey)
- try {
- val createResult = createApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), relpath = Some(testrelpath),
- operation = Some(testurlop), apiname = Some(testapiname), action = Some(testaction))
- JsObjectHelper(createResult.stdout.parseJson.asJsObject).fieldPathExists("apidoc") should be(true)
- val apiVector = getApis(bpOrName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- apiVector.size should be > 0
- apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(true)
- } finally {
- val deleteResult = deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), expectedExitCode = DONTCARE_EXIT)
- }
+ try {
+ val createResult = createApi(
+ namespace = Some(wskprops.namespace),
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ apiname = Some(testapiname),
+ action = Some(testaction))
+ JsObjectHelper(createResult.stdout.parseJson.asJsObject).fieldPathExists("apidoc") should be(true)
+ val apiVector = getApis(bpOrName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ apiVector.size should be > 0
+ apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(true)
+ } finally {
+ val deleteResult =
+ deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), expectedExitCode = DONTCARE_EXIT)
}
+ }
- it should "verify successful API deletion using basepath" in {
- val testName = "APIGWTEST2"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val actionNamespace = wskprops.namespace
- val actionUrl = "https://some.whisk.host/api/v1/web/" + actionNamespace + "/default/" + actionName + ".json"
- val actionAuthKey = testName + "_authkey"
- val testaction = ApiAction(name = actionName, namespace = actionNamespace, backendUrl = actionUrl, authkey = actionAuthKey)
+ it should "verify successful API deletion using basepath" in {
+ val testName = "APIGWTEST2"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val actionNamespace = wskprops.namespace
+ val actionUrl = "https://some.whisk.host/api/v1/web/" + actionNamespace + "/default/" + actionName + ".json"
+ val actionAuthKey = testName + "_authkey"
+ val testaction =
+ ApiAction(name = actionName, namespace = actionNamespace, backendUrl = actionUrl, authkey = actionAuthKey)
- try {
- val createResult = createApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), relpath = Some(testrelpath),
- operation = Some(testurlop), apiname = Some(testapiname), action = Some(testaction))
- JsObjectHelper(createResult.stdout.parseJson.asJsObject).fieldPathExists("apidoc") should be(true)
- var apiVector = getApis(bpOrName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- apiVector.size should be > 0
- apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(true)
- val deleteResult = deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath))
- apiVector = getApis(bpOrName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(false)
- } finally {
- val deleteResult = deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), expectedExitCode = DONTCARE_EXIT)
- }
+ try {
+ val createResult = createApi(
+ namespace = Some(wskprops.namespace),
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ apiname = Some(testapiname),
+ action = Some(testaction))
+ JsObjectHelper(createResult.stdout.parseJson.asJsObject).fieldPathExists("apidoc") should be(true)
+ var apiVector = getApis(bpOrName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ apiVector.size should be > 0
+ apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(true)
+ val deleteResult = deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath))
+ apiVector = getApis(bpOrName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(false)
+ } finally {
+ val deleteResult =
+ deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), expectedExitCode = DONTCARE_EXIT)
}
+ }
- it should "verify successful addition of new relative path to existing API" in {
- val testName = "APIGWTEST3"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testnewurlop = "delete"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val actionNamespace = wskprops.namespace
- val actionUrl = "https://some.whisk.host/api/v1/web/" + actionNamespace + "/default/" + actionName + ".json"
- val actionAuthKey = testName + "_authkey"
- val testaction = ApiAction(name = actionName, namespace = actionNamespace, backendUrl = actionUrl, authkey = actionAuthKey)
+ it should "verify successful addition of new relative path to existing API" in {
+ val testName = "APIGWTEST3"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testnewurlop = "delete"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val actionNamespace = wskprops.namespace
+ val actionUrl = "https://some.whisk.host/api/v1/web/" + actionNamespace + "/default/" + actionName + ".json"
+ val actionAuthKey = testName + "_authkey"
+ val testaction =
+ ApiAction(name = actionName, namespace = actionNamespace, backendUrl = actionUrl, authkey = actionAuthKey)
- try {
- var createResult = createApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), relpath = Some(testrelpath),
- operation = Some(testurlop), apiname = Some(testapiname), action = Some(testaction))
- createResult = createApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), relpath = Some(testnewrelpath),
- operation = Some(testnewurlop), apiname = Some(testapiname), action = Some(testaction))
- JsObjectHelper(createResult.stdout.parseJson.asJsObject).fieldPathExists("apidoc") should be(true)
- var apiVector = getApis(bpOrName = Some(testbasepath))
- apiVector.size should be > 0
- apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(true)
- apiMatch(apiVector, testbasepath, testnewrelpath, testnewurlop, testapiname, testaction) should be(true)
- } finally {
- val deleteResult = deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), expectedExitCode = DONTCARE_EXIT)
- }
+ try {
+ var createResult = createApi(
+ namespace = Some(wskprops.namespace),
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ apiname = Some(testapiname),
+ action = Some(testaction))
+ createResult = createApi(
+ namespace = Some(wskprops.namespace),
+ basepath = Some(testbasepath),
+ relpath = Some(testnewrelpath),
+ operation = Some(testnewurlop),
+ apiname = Some(testapiname),
+ action = Some(testaction))
+ JsObjectHelper(createResult.stdout.parseJson.asJsObject).fieldPathExists("apidoc") should be(true)
+ var apiVector = getApis(bpOrName = Some(testbasepath))
+ apiVector.size should be > 0
+ apiMatch(apiVector, testbasepath, testrelpath, testurlop, testapiname, testaction) should be(true)
+ apiMatch(apiVector, testbasepath, testnewrelpath, testnewurlop, testapiname, testaction) should be(true)
+ } finally {
+ val deleteResult =
+ deleteApi(namespace = Some(wskprops.namespace), basepath = Some(testbasepath), expectedExitCode = DONTCARE_EXIT)
}
+ }
- it should "reject apimgmt actions that are invoked with not enough parameters" in {
- val invalidArgs = Seq(
- //getApi
- ("/whisk.system/apimgmt/getApi", ANY_ERROR_EXIT, "Invalid authentication.", Seq()),
+ it should "reject apimgmt actions that are invoked with not enough parameters" in {
+ val invalidArgs = Seq(
+ //getApi
+ ("/whisk.system/apimgmt/getApi", ANY_ERROR_EXIT, "Invalid authentication.", Seq()),
+ //deleteApi
+ (
+ "/whisk.system/apimgmt/deleteApi",
+ ANY_ERROR_EXIT,
+ "Invalid authentication.",
+ Seq("-p", "basepath", "/ApiGwRoutemgmtActionTests_bp")),
+ (
+ "/whisk.system/apimgmt/deleteApi",
+ ANY_ERROR_EXIT,
+ "basepath is required",
+ Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN")),
+ (
+ "/whisk.system/apimgmt/deleteApi",
+ ANY_ERROR_EXIT,
+ "When specifying an operation, the path is required",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "basepath",
+ "/ApiGwRoutemgmtActionTests_bp",
+ "-p",
+ "operation",
+ "get")),
+ //createApi
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc is required",
+ Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc is missing the namespace field",
+ Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", "{}")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc is missing the gatewayBasePath field",
+ Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_"}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc is missing the gatewayPath field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp"}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc is missing the gatewayMethod field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp"}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc is missing the action field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get"}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "action is missing the backendMethod field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{}}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "action is missing the backendUrl field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post"}}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "action is missing the namespace field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL"}}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "action is missing the name field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL","namespace":"_"}}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "action is missing the authkey field",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL","namespace":"_","name":"N"}}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "swagger and gatewayBasePath are mutually exclusive and cannot be specified together",
+ Seq(
+ "-p",
+ "__ow_user",
+ "_",
+ "-p",
+ "accesstoken",
+ "TOKEN",
+ "-p",
+ "apidoc",
+ """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL","namespace":"_","name":"N","authkey":"XXXX"},"swagger":{}}""")),
+ (
+ "/whisk.system/apimgmt/createApi",
+ ANY_ERROR_EXIT,
+ "apidoc field cannot be parsed. Ensure it is valid JSON",
+ Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", "{1:[}}}")))
- //deleteApi
- ("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "Invalid authentication.", Seq("-p", "basepath", "/ApiGwRoutemgmtActionTests_bp")),
- ("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "basepath is required", Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN")),
- ("/whisk.system/apimgmt/deleteApi", ANY_ERROR_EXIT, "When specifying an operation, the path is required",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "basepath", "/ApiGwRoutemgmtActionTests_bp", "-p", "operation", "get")),
-
- //createApi
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc is required", Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc is missing the namespace field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", "{}")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc is missing the gatewayBasePath field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_"}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc is missing the gatewayPath field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp"}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc is missing the gatewayMethod field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp"}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc is missing the action field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get"}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "action is missing the backendMethod field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{}}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "action is missing the backendUrl field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post"}}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "action is missing the namespace field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL"}}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "action is missing the name field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL","namespace":"_"}}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "action is missing the authkey field",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL","namespace":"_","name":"N"}}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "swagger and gatewayBasePath are mutually exclusive and cannot be specified together",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", """{"namespace":"_","gatewayBasePath":"/ApiGwRoutemgmtActionTests_bp","gatewayPath":"ApiGwRoutemgmtActionTests_rp","gatewayMethod":"get","action":{"backendMethod":"post","backendUrl":"URL","namespace":"_","name":"N","authkey":"XXXX"},"swagger":{}}""")),
- ("/whisk.system/apimgmt/createApi", ANY_ERROR_EXIT, "apidoc field cannot be parsed. Ensure it is valid JSON",
- Seq("-p", "__ow_user", "_", "-p", "accesstoken", "TOKEN", "-p", "apidoc", "{1:[}}}")))
-
- invalidArgs foreach {
- case (action: String, exitcode: Int, errmsg: String, params: Seq[String]) =>
- val cmd: Seq[String] = Seq("action",
- "invoke",
- action,
- "-i", "-b", "-r",
- "--apihost", wskprops.apihost,
- "--auth", wskprops.authKey) ++ params
- val rr = wsk.cli(cmd, expectedExitCode = exitcode)
- rr.stderr should include regex (errmsg)
- }
+ invalidArgs foreach {
+ case (action: String, exitcode: Int, errmsg: String, params: Seq[String]) =>
+ val cmd: Seq[String] = Seq(
+ "action",
+ "invoke",
+ action,
+ "-i",
+ "-b",
+ "-r",
+ "--apihost",
+ wskprops.apihost,
+ "--auth",
+ wskprops.authKey) ++ params
+ val rr = wsk.cli(cmd, expectedExitCode = exitcode)
+ rr.stderr should include regex (errmsg)
}
+ }
}
diff --git a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
index ac0f01c..5b7c304 100644
--- a/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/ApiGwTests.scala
@@ -43,189 +43,209 @@ import common.WskTestHelpers
* Tests for testing the CLI "api" subcommand. Most of these tests require a deployed backend.
*/
@RunWith(classOf[JUnitRunner])
-class ApiGwTests
- extends TestHelpers
- with WskTestHelpers
- with BeforeAndAfterEach
- with BeforeAndAfterAll {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val clinamespace = wsk.namespace.whois()
-
- // This test suite makes enough CLI invocations in 60 seconds to trigger the OpenWhisk
- // throttling restriction. To avoid CLI failures due to being throttled, track the
- // CLI invocation calls and when at the throttle limit, pause the next CLI invocation
- // with exactly enough time to relax the throttling.
- val maxActionsPerMin = WhiskProperties.getMaxActionInvokesPerMinute()
- val invocationTimes = new ArrayBuffer[Instant]()
-
- // Custom CLI properties file
- val cliWskPropsFile = File.createTempFile("wskprops", ".tmp")
-
- /**
- * Expected to be called before each test.
- * Assumes that each test will not invoke more than 5 actions and
- * settle the throttle when there isn't enough capacity to handle the test.
- */
- def checkThrottle(maxInvocationsBeforeThrottle: Int = maxActionsPerMin, expectedActivationsPerTest: Int = 5) = {
- val t = Instant.now
- val tminus60 = t.minusSeconds(60)
- val invocationsLast60Seconds = invocationTimes.filter(_.isAfter(tminus60)).sorted
- val invocationCount = invocationsLast60Seconds.length
- println(s"Action invokes within last minute: ${invocationCount}")
-
- if (invocationCount >= maxInvocationsBeforeThrottle) {
- // Instead of waiting a fixed 60 seconds to settle the throttle,
- // calculate a wait time that will clear out about half of the
- // current invocations (assuming even distribution) from the
- // next 60 second period.
- val oldestInvocationInLast60Seconds = invocationsLast60Seconds.head
-
- // Take the oldest invocation time in this 60 second period. To clear
- // this invocation from the next 60 second period, the wait time will be
- // (60sec - oldest invocation's delta time away from the period end).
- // This will clear all of the invocations from the next period at the
- // expense of potentially waiting uncessarily long. Instead, this calculation
- // halves the delta time as a compromise.
- val throttleTime = 60.seconds.toMillis - ((t.toEpochMilli - oldestInvocationInLast60Seconds.toEpochMilli) / 2)
- println(s"Waiting ${throttleTime} milliseconds to settle the throttle")
- Thread.sleep(throttleTime)
- }
-
- invocationTimes += Instant.now
+class ApiGwTests extends TestHelpers with WskTestHelpers with BeforeAndAfterEach with BeforeAndAfterAll {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val clinamespace = wsk.namespace.whois()
+
+ // This test suite makes enough CLI invocations in 60 seconds to trigger the OpenWhisk
+ // throttling restriction. To avoid CLI failures due to being throttled, track the
+ // CLI invocation calls and when at the throttle limit, pause the next CLI invocation
+ // with exactly enough time to relax the throttling.
+ val maxActionsPerMin = WhiskProperties.getMaxActionInvokesPerMinute()
+ val invocationTimes = new ArrayBuffer[Instant]()
+
+ // Custom CLI properties file
+ val cliWskPropsFile = File.createTempFile("wskprops", ".tmp")
+
+ /**
+ * Expected to be called before each test.
+ * Assumes that each test will not invoke more than 5 actions and
+ * settle the throttle when there isn't enough capacity to handle the test.
+ */
+ def checkThrottle(maxInvocationsBeforeThrottle: Int = maxActionsPerMin, expectedActivationsPerTest: Int = 5) = {
+ val t = Instant.now
+ val tminus60 = t.minusSeconds(60)
+ val invocationsLast60Seconds = invocationTimes.filter(_.isAfter(tminus60)).sorted
+ val invocationCount = invocationsLast60Seconds.length
+ println(s"Action invokes within last minute: ${invocationCount}")
+
+ if (invocationCount >= maxInvocationsBeforeThrottle) {
+ // Instead of waiting a fixed 60 seconds to settle the throttle,
+ // calculate a wait time that will clear out about half of the
+ // current invocations (assuming even distribution) from the
+ // next 60 second period.
+ val oldestInvocationInLast60Seconds = invocationsLast60Seconds.head
+
+ // Take the oldest invocation time in this 60 second period. To clear
+ // this invocation from the next 60 second period, the wait time will be
+ // (60sec - oldest invocation's delta time away from the period end).
+ // This will clear all of the invocations from the next period at the
+ // expense of potentially waiting uncessarily long. Instead, this calculation
+ // halves the delta time as a compromise.
+ val throttleTime = 60.seconds.toMillis - ((t.toEpochMilli - oldestInvocationInLast60Seconds.toEpochMilli) / 2)
+ println(s"Waiting ${throttleTime} milliseconds to settle the throttle")
+ Thread.sleep(throttleTime)
}
- override def beforeEach() = {
- //checkThrottle()
- }
-
- /*
- * Create a CLI properties file for use by the tests
- */
- override def beforeAll() = {
- cliWskPropsFile.deleteOnExit()
- val wskprops = WskProps(token = "SOME TOKEN")
- wskprops.writeFile(cliWskPropsFile)
- println(s"wsk temporary props file created here: ${cliWskPropsFile.getCanonicalPath()}")
- }
-
- /*
- * Forcibly clear the throttle so that downstream tests are not affected by
- * this test suite
- */
- override def afterAll() = {
- // Check and settle the throttle so that this test won't cause issues with and follow on tests
- checkThrottle(30)
- }
-
- def apiCreate(
- basepath: Option[String] = None,
- relpath: Option[String] = None,
- operation: Option[String] = None,
- action: Option[String] = None,
- apiname: Option[String] = None,
- swagger: Option[String] = None,
- responsetype: Option[String] = None,
- expectedExitCode: Int = SUCCESS_EXIT,
- cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath()))(implicit wskpropsOverride: WskProps): RunResult = {
-
- checkThrottle()
- wsk.api.create(basepath, relpath, operation, action, apiname, swagger, responsetype, expectedExitCode, cliCfgFile)(wskpropsOverride)
- }
-
- def apiList(
- basepathOrApiName: Option[String] = None,
- relpath: Option[String] = None,
- operation: Option[String] = None,
- limit: Option[Int] = None,
- since: Option[Instant] = None,
- full: Option[Boolean] = None,
- nameSort: Option[Boolean] = None,
- expectedExitCode: Int = SUCCESS_EXIT,
- cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath())): RunResult = {
-
- checkThrottle()
- wsk.api.list(basepathOrApiName, relpath, operation, limit, since, full, nameSort, expectedExitCode, cliCfgFile)
- }
-
- def apiGet(
- basepathOrApiName: Option[String] = None,
- full: Option[Boolean] = None,
- expectedExitCode: Int = SUCCESS_EXIT,
- cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath()),
- format: Option[String] = None): RunResult = {
-
- checkThrottle()
- wsk.api.get(basepathOrApiName, full, expectedExitCode, cliCfgFile, format)
- }
-
- def apiDelete(
- basepathOrApiName: String,
- relpath: Option[String] = None,
- operation: Option[String] = None,
- expectedExitCode: Int = SUCCESS_EXIT,
- cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath())): RunResult = {
-
- checkThrottle()
- wsk.api.delete(basepathOrApiName, relpath, operation, expectedExitCode, cliCfgFile)
- }
-
- behavior of "Wsk api"
-
- it should "reject an api commands with an invalid path parameter" in {
- val badpath = "badpath"
-
- var rr = apiCreate(basepath = Some("/basepath"), relpath = Some(badpath), operation = Some("GET"), action = Some("action"), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"'${badpath}' must begin with '/'")
-
- rr = apiDelete(basepathOrApiName = "/basepath", relpath = Some(badpath), operation = Some("GET"), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"'${badpath}' must begin with '/'")
-
- rr = apiList(basepathOrApiName = Some("/basepath"), relpath = Some(badpath), operation = Some("GET"), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"'${badpath}' must begin with '/'")
- }
-
- it should "reject an api commands with an invalid verb parameter" in {
- val badverb = "badverb"
-
- var rr = apiCreate(basepath = Some("/basepath"), relpath = Some("/path"), operation = Some(badverb), action = Some("action"), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"'${badverb}' is not a valid API verb. Valid values are:")
-
- rr = apiDelete(basepathOrApiName = "/basepath", relpath = Some("/path"), operation = Some(badverb), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"'${badverb}' is not a valid API verb. Valid values are:")
-
- rr = apiList(basepathOrApiName = Some("/basepath"), relpath = Some("/path"), operation = Some(badverb), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"'${badverb}' is not a valid API verb. Valid values are:")
- }
-
- it should "reject an api create command that specifies a nonexistent configuration file" in {
- val configfile = "/nonexistent/file"
-
- val rr = apiCreate(swagger = Some(configfile), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"Error reading swagger file '${configfile}':")
- }
-
- it should "reject an api create command specifying a non-JSON configuration file" in {
- val file = File.createTempFile("api.json", ".txt")
- file.deleteOnExit()
- val filename = file.getAbsolutePath()
-
- val bw = new BufferedWriter(new FileWriter(file))
- bw.write("a=A")
- bw.close()
-
- val rr = apiCreate(swagger = Some(filename), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"Error parsing swagger file '${filename}':")
- }
-
- it should "reject an api create command specifying a non-swagger JSON configuration file" in {
- val file = File.createTempFile("api.json", ".txt")
- file.deleteOnExit()
- val filename = file.getAbsolutePath()
-
- val bw = new BufferedWriter(new FileWriter(file))
- bw.write("""|{
+ invocationTimes += Instant.now
+ }
+
+ override def beforeEach() = {
+ //checkThrottle()
+ }
+
+ /*
+ * Create a CLI properties file for use by the tests
+ */
+ override def beforeAll() = {
+ cliWskPropsFile.deleteOnExit()
+ val wskprops = WskProps(token = "SOME TOKEN")
+ wskprops.writeFile(cliWskPropsFile)
+ println(s"wsk temporary props file created here: ${cliWskPropsFile.getCanonicalPath()}")
+ }
+
+ /*
+ * Forcibly clear the throttle so that downstream tests are not affected by
+ * this test suite
+ */
+ override def afterAll() = {
+ // Check and settle the throttle so that this test won't cause issues with and follow on tests
+ checkThrottle(30)
+ }
+
+ def apiCreate(basepath: Option[String] = None,
+ relpath: Option[String] = None,
+ operation: Option[String] = None,
+ action: Option[String] = None,
+ apiname: Option[String] = None,
+ swagger: Option[String] = None,
+ responsetype: Option[String] = None,
+ expectedExitCode: Int = SUCCESS_EXIT,
+ cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath()))(
+ implicit wskpropsOverride: WskProps): RunResult = {
+
+ checkThrottle()
+ wsk.api.create(basepath, relpath, operation, action, apiname, swagger, responsetype, expectedExitCode, cliCfgFile)(
+ wskpropsOverride)
+ }
+
+ def apiList(basepathOrApiName: Option[String] = None,
+ relpath: Option[String] = None,
+ operation: Option[String] = None,
+ limit: Option[Int] = None,
+ since: Option[Instant] = None,
+ full: Option[Boolean] = None,
+ nameSort: Option[Boolean] = None,
+ expectedExitCode: Int = SUCCESS_EXIT,
+ cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath())): RunResult = {
+
+ checkThrottle()
+ wsk.api.list(basepathOrApiName, relpath, operation, limit, since, full, nameSort, expectedExitCode, cliCfgFile)
+ }
+
+ def apiGet(basepathOrApiName: Option[String] = None,
+ full: Option[Boolean] = None,
+ expectedExitCode: Int = SUCCESS_EXIT,
+ cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath()),
+ format: Option[String] = None): RunResult = {
+
+ checkThrottle()
+ wsk.api.get(basepathOrApiName, full, expectedExitCode, cliCfgFile, format)
+ }
+
+ def apiDelete(basepathOrApiName: String,
+ relpath: Option[String] = None,
+ operation: Option[String] = None,
+ expectedExitCode: Int = SUCCESS_EXIT,
+ cliCfgFile: Option[String] = Some(cliWskPropsFile.getCanonicalPath())): RunResult = {
+
+ checkThrottle()
+ wsk.api.delete(basepathOrApiName, relpath, operation, expectedExitCode, cliCfgFile)
+ }
+
+ behavior of "Wsk api"
+
+ it should "reject an api commands with an invalid path parameter" in {
+ val badpath = "badpath"
+
+ var rr = apiCreate(
+ basepath = Some("/basepath"),
+ relpath = Some(badpath),
+ operation = Some("GET"),
+ action = Some("action"),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"'${badpath}' must begin with '/'")
+
+ rr = apiDelete(
+ basepathOrApiName = "/basepath",
+ relpath = Some(badpath),
+ operation = Some("GET"),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"'${badpath}' must begin with '/'")
+
+ rr = apiList(
+ basepathOrApiName = Some("/basepath"),
+ relpath = Some(badpath),
+ operation = Some("GET"),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"'${badpath}' must begin with '/'")
+ }
+
+ it should "reject an api commands with an invalid verb parameter" in {
+ val badverb = "badverb"
+
+ var rr = apiCreate(
+ basepath = Some("/basepath"),
+ relpath = Some("/path"),
+ operation = Some(badverb),
+ action = Some("action"),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"'${badverb}' is not a valid API verb. Valid values are:")
+
+ rr = apiDelete(
+ basepathOrApiName = "/basepath",
+ relpath = Some("/path"),
+ operation = Some(badverb),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"'${badverb}' is not a valid API verb. Valid values are:")
+
+ rr = apiList(
+ basepathOrApiName = Some("/basepath"),
+ relpath = Some("/path"),
+ operation = Some(badverb),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"'${badverb}' is not a valid API verb. Valid values are:")
+ }
+
+ it should "reject an api create command that specifies a nonexistent configuration file" in {
+ val configfile = "/nonexistent/file"
+
+ val rr = apiCreate(swagger = Some(configfile), expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"Error reading swagger file '${configfile}':")
+ }
+
+ it should "reject an api create command specifying a non-JSON configuration file" in {
+ val file = File.createTempFile("api.json", ".txt")
+ file.deleteOnExit()
+ val filename = file.getAbsolutePath()
+
+ val bw = new BufferedWriter(new FileWriter(file))
+ bw.write("a=A")
+ bw.close()
+
+ val rr = apiCreate(swagger = Some(filename), expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"Error parsing swagger file '${filename}':")
+ }
+
+ it should "reject an api create command specifying a non-swagger JSON configuration file" in {
+ val file = File.createTempFile("api.json", ".txt")
+ file.deleteOnExit()
+ val filename = file.getAbsolutePath()
+
+ val bw = new BufferedWriter(new FileWriter(file))
+ bw.write("""|{
| "swagger": "2.0",
| "info": {
| "title": "My API",
@@ -238,706 +258,807 @@ class ApiGwTests
| }
| }
|}""".stripMargin)
- bw.close()
-
- val rr = apiCreate(swagger = Some(filename), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"Swagger file is invalid (missing basePath, info, paths, or swagger fields")
+ bw.close()
+
+ val rr = apiCreate(swagger = Some(filename), expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"Swagger file is invalid (missing basePath, info, paths, or swagger fields")
+ }
+
+ it should "verify full list output" in {
+ val testName = "CLI_APIGWTEST_RO1"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ println("cli namespace: " + clinamespace)
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ println("api create: " + rr.stdout)
+ rr.stdout should include("ok: created API")
+ rr = apiList(
+ basepathOrApiName = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ full = Some(true))
+ println("api list: " + rr.stdout)
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"Action:\\s+/${clinamespace}/${actionName}\n")
+ rr.stdout should include regex (s"Verb:\\s+${testurlop}\n")
+ rr.stdout should include regex (s"Base path:\\s+${testbasepath}\n")
+ rr.stdout should include regex (s"Path:\\s+${testrelpath}\n")
+ rr.stdout should include regex (s"API Name:\\s+${testapiname}\n")
+ rr.stdout should include regex (s"URL:\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath)
}
-
- it should "verify full list output" in {
- val testName = "CLI_APIGWTEST_RO1"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- println("cli namespace: " + clinamespace)
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- println("api create: " + rr.stdout)
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), full = Some(true))
- println("api list: " + rr.stdout)
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"Action:\\s+/${clinamespace}/${actionName}\n")
- rr.stdout should include regex (s"Verb:\\s+${testurlop}\n")
- rr.stdout should include regex (s"Base path:\\s+${testbasepath}\n")
- rr.stdout should include regex (s"Path:\\s+${testrelpath}\n")
- rr.stdout should include regex (s"API Name:\\s+${testapiname}\n")
- rr.stdout should include regex (s"URL:\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath)
- }
+ }
+
+ it should "verify successful creation and deletion of a new API" in {
+ val testName = "CLI_APIGWTEST1"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path/with/sub_paths/in/it"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ println("cli namespace: " + clinamespace)
+
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr = apiGet(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include regex (s""""operationId":\\s+"getPathWithSub_pathsInIt"""")
+ val deleteresult = apiDelete(basepathOrApiName = testbasepath)
+ deleteresult.stdout should include("ok: deleted API")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify successful creation and deletion of a new API" in {
- val testName = "CLI_APIGWTEST1"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path/with/sub_paths/in/it"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- println("cli namespace: " + clinamespace)
-
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr = apiGet(basepathOrApiName = Some(testbasepath))
- rr.stdout should include regex (s""""operationId":\\s+"getPathWithSub_pathsInIt"""")
- val deleteresult = apiDelete(basepathOrApiName = testbasepath)
- deleteresult.stdout should include("ok: deleted API")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify get API name " in {
+ val testName = "CLI_APIGWTEST3"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiGet(basepathOrApiName = Some(testapiname))
+ rr.stdout should include(testbasepath)
+ rr.stdout should include(s"${actionName}")
+ rr.stdout should include regex (""""cors":\s*\{\s*\n\s*"enabled":\s*true""")
+ rr.stdout should include regex (s""""target-url":\\s+.*${actionName}.json""")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify get API name " in {
- val testName = "CLI_APIGWTEST3"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiGet(basepathOrApiName = Some(testapiname))
- rr.stdout should include(testbasepath)
- rr.stdout should include(s"${actionName}")
- rr.stdout should include regex (""""cors":\s*\{\s*\n\s*"enabled":\s*true""")
- rr.stdout should include regex (s""""target-url":\\s+.*${actionName}.json""")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify delete API name " in {
+ val testName = "CLI_APIGWTEST4"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiDelete(basepathOrApiName = testapiname)
+ rr.stdout should include("ok: deleted API")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify delete API name " in {
- val testName = "CLI_APIGWTEST4"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiDelete(basepathOrApiName = testapiname)
- rr.stdout should include("ok: deleted API")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify delete API basepath " in {
+ val testName = "CLI_APIGWTEST5"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiDelete(basepathOrApiName = testbasepath)
+ rr.stdout should include("ok: deleted API")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify delete API basepath " in {
- val testName = "CLI_APIGWTEST5"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiDelete(basepathOrApiName = testbasepath)
- rr.stdout should include("ok: deleted API")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify adding endpoints to existing api" in {
+ val testName = "CLI_APIGWTEST6"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path2"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val newEndpoint = "/newEndpoint"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(newEndpoint),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr.stdout should include(testbasepath + newEndpoint)
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify adding endpoints to existing api" in {
- val testName = "CLI_APIGWTEST6"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path2"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val newEndpoint = "/newEndpoint"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiCreate(basepath = Some(testbasepath), relpath = Some(newEndpoint), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr.stdout should include(testbasepath + newEndpoint)
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify successful creation with swagger doc as input" in {
+ // NOTE: These values must match the swagger file contents
+ val testName = "CLI_APIGWTEST7"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val swaggerPath = TestUtils.getTestApiGwFilename("testswaggerdoc1")
+ try {
+ var rr = apiCreate(swagger = Some(swaggerPath))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ println("list stdout: " + rr.stdout)
+ println("list stderr: " + rr.stderr)
+ rr.stdout should include("ok: APIs")
+ // Actual CLI namespace will vary from local dev to automated test environments, so don't check
+ rr.stdout should include regex (s"/[@\\w._\\-]+/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify successful creation with swagger doc as input" in {
- // NOTE: These values must match the swagger file contents
- val testName = "CLI_APIGWTEST7"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val swaggerPath = TestCLIUtils.getTestApiGwFilename("testswaggerdoc1")
- try {
- var rr = apiCreate(swagger = Some(swaggerPath))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- println("list stdout: " + rr.stdout)
- println("list stderr: " + rr.stderr)
- rr.stdout should include("ok: APIs")
- // Actual CLI namespace will vary from local dev to automated test environments, so don't check
- rr.stdout should include regex (s"/[@\\w._\\-]+/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify adding endpoints to two existing apis" in {
+ val testName = "CLI_APIGWTEST8"
+ val testbasepath = "/" + testName + "_bp"
+ val testbasepath2 = "/" + testName + "_bp2"
+ val testrelpath = "/path2"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val testapiname2 = testName + " API Name 2"
+ val actionName = testName + "_action"
+ val newEndpoint = "/newEndpoint"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiCreate(
+ basepath = Some(testbasepath2),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname2))
+ rr.stdout should include("ok: created API")
+
+ // Update both APIs - each with a new endpoint
+ rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(newEndpoint),
+ operation = Some(testurlop),
+ action = Some(actionName))
+ rr.stdout should include("ok: created API")
+ rr = apiCreate(
+ basepath = Some(testbasepath2),
+ relpath = Some(newEndpoint),
+ operation = Some(testurlop),
+ action = Some(actionName))
+ rr.stdout should include("ok: created API")
+
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr.stdout should include(testbasepath + newEndpoint)
+
+ rr = apiList(basepathOrApiName = Some(testbasepath2))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath2 + testrelpath)
+ rr.stdout should include(testbasepath2 + newEndpoint)
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath2, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify adding endpoints to two existing apis" in {
- val testName = "CLI_APIGWTEST8"
- val testbasepath = "/" + testName + "_bp"
- val testbasepath2 = "/" + testName + "_bp2"
- val testrelpath = "/path2"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val testapiname2 = testName + " API Name 2"
- val actionName = testName + "_action"
- val newEndpoint = "/newEndpoint"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiCreate(basepath = Some(testbasepath2), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname2))
- rr.stdout should include("ok: created API")
-
- // Update both APIs - each with a new endpoint
- rr = apiCreate(basepath = Some(testbasepath), relpath = Some(newEndpoint), operation = Some(testurlop), action = Some(actionName))
- rr.stdout should include("ok: created API")
- rr = apiCreate(basepath = Some(testbasepath2), relpath = Some(newEndpoint), operation = Some(testurlop), action = Some(actionName))
- rr.stdout should include("ok: created API")
-
- rr = apiList(basepathOrApiName = Some(testbasepath))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr.stdout should include(testbasepath + newEndpoint)
-
- rr = apiList(basepathOrApiName = Some(testbasepath2))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath2 + testrelpath)
- rr.stdout should include(testbasepath2 + newEndpoint)
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath2, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify successful creation of a new API using an action name using all allowed characters" in {
+ // Be aware: full action name is close to being truncated by the 'list' command
+ // e.g. /lime@us.ibm.com/CLI_APIGWTEST9a-c@t ion is currently at the 40 char 'list' display max
+ val testName = "CLI_APIGWTEST9"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "a-c@t ion"
+ try {
+ println("cli namespace: " + clinamespace)
+
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ val deleteresult = apiDelete(basepathOrApiName = testbasepath)
+ deleteresult.stdout should include("ok: deleted API")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify successful creation of a new API using an action name using all allowed characters" in {
- // Be aware: full action name is close to being truncated by the 'list' command
- // e.g. /lime@us.ibm.com/CLI_APIGWTEST9a-c@t ion is currently at the 40 char 'list' display max
- val testName = "CLI_APIGWTEST9"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "a-c@t ion"
- try {
- println("cli namespace: " + clinamespace)
-
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- val deleteresult = apiDelete(basepathOrApiName = testbasepath)
- deleteresult.stdout should include("ok: deleted API")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify failed creation with invalid swagger doc as input" in {
+ val testName = "CLI_APIGWTEST10"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val swaggerPath = TestUtils.getTestApiGwFilename(s"testswaggerdocinvalid")
+ try {
+ val rr = apiCreate(swagger = Some(swaggerPath), expectedExitCode = ANY_ERROR_EXIT)
+ println("api create stdout: " + rr.stdout)
+ println("api create stderr: " + rr.stderr)
+ rr.stderr should include(s"Swagger file is invalid")
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify failed creation with invalid swagger doc as input" in {
- val testName = "CLI_APIGWTEST10"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val swaggerPath = TestCLIUtils.getTestApiGwFilename(s"testswaggerdocinvalid")
- try {
- val rr = apiCreate(swagger = Some(swaggerPath), expectedExitCode = ANY_ERROR_EXIT)
- println("api create stdout: " + rr.stdout)
- println("api create stderr: " + rr.stderr)
- rr.stderr should include(s"Swagger file is invalid")
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify delete basepath/path " in {
+ val testName = "CLI_APIGWTEST11"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ var rr2 = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testnewrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr2.stdout should include("ok: created API")
+ rr = apiDelete(basepathOrApiName = testbasepath, relpath = Some(testrelpath))
+ rr.stdout should include("ok: deleted " + testrelpath + " from " + testbasepath)
+ rr2 = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testnewrelpath))
+ rr2.stdout should include("ok: APIs")
+ rr2.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr2.stdout should include(testbasepath + testnewrelpath)
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify delete basepath/path " in {
- val testName = "CLI_APIGWTEST11"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- var rr2 = apiCreate(basepath = Some(testbasepath), relpath = Some(testnewrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr2.stdout should include("ok: created API")
- rr = apiDelete(basepathOrApiName = testbasepath, relpath = Some(testrelpath))
- rr.stdout should include("ok: deleted " + testrelpath + " from " + testbasepath)
- rr2 = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testnewrelpath))
- rr2.stdout should include("ok: APIs")
- rr2.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr2.stdout should include(testbasepath + testnewrelpath)
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify delete single operation from existing API basepath/path/operation(s) " in {
+ val testName = "CLI_APIGWTEST12"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path2"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testurlop2 = "post"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop2),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr = apiDelete(basepathOrApiName = testbasepath, relpath = Some(testrelpath), operation = Some(testurlop2))
+ rr.stdout should include("ok: deleted " + testrelpath + " " + "POST" + " from " + testbasepath)
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify delete single operation from existing API basepath/path/operation(s) " in {
- val testName = "CLI_APIGWTEST12"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path2"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testurlop2 = "post"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop2), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr = apiDelete(basepathOrApiName = testbasepath, relpath = Some(testrelpath), operation = Some(testurlop2))
- rr.stdout should include("ok: deleted " + testrelpath + " " + "POST" + " from " + testbasepath)
- rr = apiList(basepathOrApiName = Some(testbasepath))
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify successful creation with complex swagger doc as input" in {
+ val testName = "CLI_APIGWTEST13"
+ val testbasepath = "/test1/v1"
+ val testrelpath = "/whisk_system/utils/echo"
+ val testrelpath2 = "/whisk_system/utils/split"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = "test1a"
+ val swaggerPath = TestUtils.getTestApiGwFilename(s"testswaggerdoc2")
+ try {
+ var rr = apiCreate(swagger = Some(swaggerPath))
+ println("api create stdout: " + rr.stdout)
+ println("api create stderror: " + rr.stderr)
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include("ok: APIs")
+ // Actual CLI namespace will vary from local dev to automated test environments, so don't check
+ rr.stdout should include regex (s"/[@\\w._\\-]+/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr.stdout should include(testbasepath + testrelpath2)
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify successful creation with complex swagger doc as input" in {
- val testName = "CLI_APIGWTEST13"
- val testbasepath = "/test1/v1"
- val testrelpath = "/whisk_system/utils/echo"
- val testrelpath2 = "/whisk_system/utils/split"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = "test1a"
- val swaggerPath = TestCLIUtils.getTestApiGwFilename(s"testswaggerdoc2")
- try {
- var rr = apiCreate(swagger = Some(swaggerPath))
- println("api create stdout: " + rr.stdout)
- println("api create stderror: " + rr.stderr)
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath))
- rr.stdout should include("ok: APIs")
- // Actual CLI namespace will vary from local dev to automated test environments, so don't check
- rr.stdout should include regex (s"/[@\\w._\\-]+/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr.stdout should include(testbasepath + testrelpath2)
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify successful creation and deletion with multiple base paths" in {
+ val testName = "CLI_APIGWTEST14"
+ val testbasepath = "/" + testName + "_bp"
+ val testbasepath2 = "/" + testName + "_bp2"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val testapiname2 = testName + " API Name 2"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ var rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr = apiCreate(
+ basepath = Some(testbasepath2),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname2))
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath2), relpath = Some(testrelpath), operation = Some(testurlop))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath2 + testrelpath)
+ rr = apiDelete(basepathOrApiName = testbasepath2)
+ rr.stdout should include("ok: deleted API")
+ rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
+ rr.stdout should include("ok: APIs")
+ rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ rr = apiDelete(basepathOrApiName = testbasepath)
+ rr.stdout should include("ok: deleted API")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath2, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "verify successful creation and deletion with multiple base paths" in {
- val testName = "CLI_APIGWTEST14"
- val testbasepath = "/" + testName + "_bp"
- val testbasepath2 = "/" + testName + "_bp2"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val testapiname2 = testName + " API Name 2"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- var rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr = apiCreate(basepath = Some(testbasepath2), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname2))
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath2), relpath = Some(testrelpath), operation = Some(testurlop))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath2 + testrelpath)
- rr = apiDelete(basepathOrApiName = testbasepath2)
- rr.stdout should include("ok: deleted API")
- rr = apiList(basepathOrApiName = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop))
- rr.stdout should include("ok: APIs")
- rr.stdout should include regex (s"/${clinamespace}/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- rr = apiDelete(basepathOrApiName = testbasepath)
- rr.stdout should include("ok: deleted API")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath2, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "reject an API created with a non-existent action" in {
+ val testName = "CLI_APIGWTEST15"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ val rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include("does not exist")
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "reject an API created with a non-existent action" in {
- val testName = "CLI_APIGWTEST15"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- val rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include("does not exist")
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "reject an API created with an action that is not a web action" in {
+ val testName = "CLI_APIGWTEST16"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ try {
+ // Create the action for the API. It must NOT be a "web-action" action for this test
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT)
+
+ val rr = apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include("is not a web action")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "reject an API created with an action that is not a web action" in {
- val testName = "CLI_APIGWTEST16"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- try {
- // Create the action for the API. It must NOT be a "web-action" action for this test
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT)
-
- val rr = apiCreate(basepath = Some(testbasepath), relpath = Some(testrelpath), operation = Some(testurlop), action = Some(actionName), apiname = Some(testapiname), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include("is not a web action")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
- }
-
- it should "verify API with http response type " in {
- val testName = "CLI_APIGWTEST17"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val responseType = "http"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- apiCreate(
- basepath = Some(testbasepath),
- relpath = Some(testrelpath),
- operation = Some(testurlop),
- action = Some(actionName),
- apiname = Some(testapiname),
- responsetype = Some(responseType))
- .stdout should include("ok: created API")
-
- val rr = apiGet(basepathOrApiName = Some(testapiname))
- rr.stdout should include(testbasepath)
- rr.stdout should include(s"${actionName}")
- rr.stdout should include regex (s""""target-url":\\s+.*${actionName}.${responseType}""")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
- }
-
- it should "reject API export when export type is invalid" in {
- val testName = "CLI_APIGWTEST18"
- val testbasepath = "/" + testName + "_bp"
-
- val rr = apiGet(basepathOrApiName = Some(testbasepath), format = Some("BadType"), expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include("Invalid format type")
- }
-
- it should "successfully export an API in YAML format" in {
- val testName = "CLI_APIGWTEST19"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val responseType = "http"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- apiCreate(
- basepath = Some(testbasepath),
- relpath = Some(testrelpath),
- operation = Some(testurlop),
- action = Some(actionName),
- apiname = Some(testapiname),
- responsetype = Some(responseType))
- .stdout should include("ok: created API")
-
- val rr = apiGet(basepathOrApiName = Some(testapiname), format = Some("yaml"))
- rr.stdout should include(s"basePath: ${testbasepath}")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "verify API with http response type " in {
+ val testName = "CLI_APIGWTEST17"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val responseType = "http"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ responsetype = Some(responseType)).stdout should include("ok: created API")
+
+ val rr = apiGet(basepathOrApiName = Some(testapiname))
+ rr.stdout should include(testbasepath)
+ rr.stdout should include(s"${actionName}")
+ rr.stdout should include regex (s""""target-url":\\s+.*${actionName}.${responseType}""")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "successfully export an API when JSON format is explcitly specified" in {
- val testName = "CLI_APIGWTEST20"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testnewrelpath = "/path_new"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
- val responseType = "http"
- try {
- // Create the action for the API. It must be a "web-action" action.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- apiCreate(
- basepath = Some(testbasepath),
- relpath = Some(testrelpath),
- operation = Some(testurlop),
- action = Some(actionName),
- apiname = Some(testapiname),
- responsetype = Some(responseType))
- .stdout should include("ok: created API")
-
- val rr = apiGet(basepathOrApiName = Some(testapiname), format = Some("json"))
- rr.stdout should include(testbasepath)
- rr.stdout should include(s"${actionName}")
- rr.stdout should include regex (s""""target-url":\\s+.*${actionName}.${responseType}""")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "reject API export when export type is invalid" in {
+ val testName = "CLI_APIGWTEST18"
+ val testbasepath = "/" + testName + "_bp"
+
+ val rr = apiGet(basepathOrApiName = Some(testbasepath), format = Some("BadType"), expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include("Invalid format type")
+ }
+
+ it should "successfully export an API in YAML format" in {
+ val testName = "CLI_APIGWTEST19"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val responseType = "http"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ responsetype = Some(responseType)).stdout should include("ok: created API")
+
+ val rr = apiGet(basepathOrApiName = Some(testapiname), format = Some("yaml"))
+ rr.stdout should include(s"basePath: ${testbasepath}")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "successfully create an API from a YAML formatted API configuration file" in {
- val testName = "CLI_APIGWTEST21"
- val testbasepath = "/bp"
- val testrelpath = "/rp"
- val testurlop = "get"
- val testapiname = testbasepath
- val actionName = "webhttpecho"
- val swaggerPath = TestCLIUtils.getTestApiGwFilename(s"local.api.yaml")
- try {
- var rr = apiCreate(swagger = Some(swaggerPath))
- println("api create stdout: " + rr.stdout)
- println("api create stderror: " + rr.stderr)
- rr.stdout should include("ok: created API")
- rr = apiList(basepathOrApiName = Some(testbasepath))
- rr.stdout should include("ok: APIs")
- // Actual CLI namespace will vary from local dev to automated test environments, so don't check
- rr.stdout should include regex (s"/[@\\w._\\-]+/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
- rr.stdout should include(testbasepath + testrelpath)
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "successfully export an API when JSON format is explcitly specified" in {
+ val testName = "CLI_APIGWTEST20"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testnewrelpath = "/path_new"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+ val responseType = "http"
+ try {
+ // Create the action for the API. It must be a "web-action" action.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ responsetype = Some(responseType)).stdout should include("ok: created API")
+
+ val rr = apiGet(basepathOrApiName = Some(testapiname), format = Some("json"))
+ rr.stdout should include(testbasepath)
+ rr.stdout should include(s"${actionName}")
+ rr.stdout should include regex (s""""target-url":\\s+.*${actionName}.${responseType}""")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "reject creation of an API from invalid YAML formatted API configuration file" in {
- val testName = "CLI_APIGWTEST22"
- val testbasepath = "/" + testName + "_bp"
- val swaggerPath = TestCLIUtils.getTestApiGwFilename(s"local.api.bad.yaml")
- try {
- val rr = apiCreate(swagger = Some(swaggerPath), expectedExitCode = ANY_ERROR_EXIT)
- println("api create stdout: " + rr.stdout)
- println("api create stderror: " + rr.stderr)
- rr.stderr should include("Unable to parse YAML configuration file")
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "successfully create an API from a YAML formatted API configuration file" in {
+ val testName = "CLI_APIGWTEST21"
+ val testbasepath = "/bp"
+ val testrelpath = "/rp"
+ val testurlop = "get"
+ val testapiname = testbasepath
+ val actionName = "webhttpecho"
+ val swaggerPath = TestUtils.getTestApiGwFilename(s"local.api.yaml")
+ try {
+ var rr = apiCreate(swagger = Some(swaggerPath))
+ println("api create stdout: " + rr.stdout)
+ println("api create stderror: " + rr.stderr)
+ rr.stdout should include("ok: created API")
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ rr.stdout should include("ok: APIs")
+ // Actual CLI namespace will vary from local dev to automated test environments, so don't check
+ rr.stdout should include regex (s"/[@\\w._\\-]+/${actionName}\\s+${testurlop}\\s+${testapiname}\\s+")
+ rr.stdout should include(testbasepath + testrelpath)
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "reject deletion of a non-existent api" in {
- val nonexistentApi = "/not-there"
-
- val rr = apiDelete(basepathOrApiName = nonexistentApi, expectedExitCode = ANY_ERROR_EXIT)
- rr.stderr should include(s"API '${nonexistentApi}' does not exist")
+ }
+
+ it should "reject creation of an API from invalid YAML formatted API configuration file" in {
+ val testName = "CLI_APIGWTEST22"
+ val testbasepath = "/" + testName + "_bp"
+ val swaggerPath = TestUtils.getTestApiGwFilename(s"local.api.bad.yaml")
+ try {
+ val rr = apiCreate(swagger = Some(swaggerPath), expectedExitCode = ANY_ERROR_EXIT)
+ println("api create stdout: " + rr.stdout)
+ println("api create stderror: " + rr.stderr)
+ rr.stderr should include("Unable to parse YAML configuration file")
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "successfully list an API whose endpoints are not mapped to actions" in {
- val testName = "CLI_APIGWTEST23"
- val testapiname = "A descriptive name"
- val testbasepath = "/NoActions"
- val testrelpath = "/"
- val testops: Seq[String] = Seq("put", "delete", "get", "head", "options", "patch", "post")
- val swaggerPath = TestCLIUtils.getTestApiGwFilename(s"endpoints.without.action.swagger.json")
-
- try {
- var rr = apiCreate(swagger = Some(swaggerPath))
- println("api create stdout: " + rr.stdout)
- println("api create stderror: " + rr.stderr)
- rr.stdout should include("ok: created API")
-
- rr = apiList(basepathOrApiName = Some(testbasepath))
- println("api list:\n" + rr.stdout)
- testops foreach { testurlop =>
- rr.stdout should include regex (s"\\s+${testurlop}\\s+${testapiname}\\s+")
- }
- rr.stdout should include(testbasepath + testrelpath)
-
- rr = apiList(basepathOrApiName = Some(testbasepath), full = Some(true))
- println("api full list:\n" + rr.stdout)
- testops foreach { testurlop =>
- rr.stdout should include regex (s"Verb:\\s+${testurlop}")
- }
- rr.stdout should include(testbasepath + testrelpath)
-
- } finally {
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "reject deletion of a non-existent api" in {
+ val nonexistentApi = "/not-there"
+
+ val rr = apiDelete(basepathOrApiName = nonexistentApi, expectedExitCode = ANY_ERROR_EXIT)
+ rr.stderr should include(s"API '${nonexistentApi}' does not exist")
+ }
+
+ it should "successfully list an API whose endpoints are not mapped to actions" in {
+ val testName = "CLI_APIGWTEST23"
+ val testapiname = "A descriptive name"
+ val testbasepath = "/NoActions"
+ val testrelpath = "/"
+ val testops: Seq[String] = Seq("put", "delete", "get", "head", "options", "patch", "post")
+ val swaggerPath = TestUtils.getTestApiGwFilename(s"endpoints.without.action.swagger.json")
+
+ try {
+ var rr = apiCreate(swagger = Some(swaggerPath))
+ println("api create stdout: " + rr.stdout)
+ println("api create stderror: " + rr.stderr)
+ rr.stdout should include("ok: created API")
+
+ rr = apiList(basepathOrApiName = Some(testbasepath))
+ println("api list:\n" + rr.stdout)
+ testops foreach { testurlop =>
+ rr.stdout should include regex (s"\\s+${testurlop}\\s+${testapiname}\\s+")
+ }
+ rr.stdout should include(testbasepath + testrelpath)
+
+ rr = apiList(basepathOrApiName = Some(testbasepath), full = Some(true))
+ println("api full list:\n" + rr.stdout)
+ testops foreach { testurlop =>
+ rr.stdout should include regex (s"Verb:\\s+${testurlop}")
+ }
+ rr.stdout should include(testbasepath + testrelpath)
+
+ } finally {
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "reject creation of an API with invalid auth key" in {
- val testName = "CLI_APIGWTEST24"
- val testbasepath = "/" + testName + "_bp"
- val testrelpath = "/path"
- val testurlop = "get"
- val testapiname = testName + " API Name"
- val actionName = testName + "_action"
-
- try {
- // Create the action for the API.
- val file = TestCLIUtils.getTestActionFilename(s"echo.js")
- wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
-
- // Set an invalid auth key
- val badWskProps = WskProps(authKey = "bad-auth-key")
-
- apiCreate(
- basepath = Some(testbasepath),
- relpath = Some(testrelpath),
- operation = Some(testurlop),
- action = Some(actionName),
- apiname = Some(testapiname),
- expectedExitCode = ANY_ERROR_EXIT)(badWskProps)
- .stderr should include("The supplied authentication is invalid")
- } finally {
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "reject creation of an API with invalid auth key" in {
+ val testName = "CLI_APIGWTEST24"
+ val testbasepath = "/" + testName + "_bp"
+ val testrelpath = "/path"
+ val testurlop = "get"
+ val testapiname = testName + " API Name"
+ val actionName = testName + "_action"
+
+ try {
+ // Create the action for the API.
+ val file = TestUtils.getTestActionFilename(s"echo.js")
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+
+ // Set an invalid auth key
+ val badWskProps = WskProps(authKey = "bad-auth-key")
+
+ apiCreate(
+ basepath = Some(testbasepath),
+ relpath = Some(testrelpath),
+ operation = Some(testurlop),
+ action = Some(actionName),
+ apiname = Some(testapiname),
+ expectedExitCode = ANY_ERROR_EXIT)(badWskProps).stderr should include("The supplied authentication is invalid")
+ } finally {
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
+ apiDelete(basepathOrApiName = testbasepath, expectedExitCode = DONTCARE_EXIT)
}
-
- it should "list api alphabetically by Base/Rel/Verb" in {
- val baseName = "/BaseTestPathApiList"
- val actionName = "actionName"
- val file = TestCLIUtils.getTestActionFilename(s"echo-web-http.js")
- try {
- // Create Action for apis
- var action = wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
- println("action creation: " + action.stdout)
- // Create apis
- for (i <- 1 to 3) {
- val base = s"$baseName$i"
- var api = apiCreate(
- basepath = Some(base),
- relpath = Some("/relPath"),
- operation = Some("GET"),
- action = Some(actionName))
- println("api creation: " + api.stdout)
- }
- val original = apiList(nameSort = Some(true)).stdout
- val originalFull = apiList(full = Some(true), nameSort = Some(true)).stdout
- val scalaSorted = List(s"${baseName}1" + "/", s"${baseName}2" + "/", s"${baseName}3" + "/")
- val regex = s"${baseName}[1-3]/".r
- val list = (regex.findAllMatchIn(original)).toList
- val listFull = (regex.findAllMatchIn(originalFull)).toList
-
- scalaSorted.toString shouldEqual list.toString
- scalaSorted.toString shouldEqual listFull.toString
- } finally {
- // Clean up Apis
- for (i <- 1 to 3) {
- apiDelete(basepathOrApiName = s"${baseName}$i", expectedExitCode = DONTCARE_EXIT)
- }
- wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
- }
+ }
+
+ it should "list api alphabetically by Base/Rel/Verb" in {
+ val baseName = "/BaseTestPathApiList"
+ val actionName = "actionName"
+ val file = TestUtils.getTestActionFilename(s"echo-web-http.js")
+ try {
+ // Create Action for apis
+ var action =
+ wsk.action.create(name = actionName, artifact = Some(file), expectedExitCode = SUCCESS_EXIT, web = Some("true"))
+ println("action creation: " + action.stdout)
+ // Create apis
+ for (i <- 1 to 3) {
+ val base = s"$baseName$i"
+ var api = apiCreate(
+ basepath = Some(base),
+ relpath = Some("/relPath"),
+ operation = Some("GET"),
+ action = Some(actionName))
+ println("api creation: " + api.stdout)
+ }
+ val original = apiList(nameSort = Some(true)).stdout
+ val originalFull = apiList(full = Some(true), nameSort = Some(true)).stdout
+ val scalaSorted = List(s"${baseName}1" + "/", s"${baseName}2" + "/", s"${baseName}3" + "/")
+ val regex = s"${baseName}[1-3]/".r
+ val list = (regex.findAllMatchIn(original)).toList
+ val listFull = (regex.findAllMatchIn(originalFull)).toList
+
+ scalaSorted.toString shouldEqual list.toString
+ scalaSorted.toString shouldEqual listFull.toString
+ } finally {
+ // Clean up Apis
+ for (i <- 1 to 3) {
+ apiDelete(basepathOrApiName = s"${baseName}$i", expectedExitCode = DONTCARE_EXIT)
+ }
+ wsk.action.delete(name = actionName, expectedExitCode = DONTCARE_EXIT)
}
+ }
}
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskActionSequenceTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskActionSequenceTests.scala
index 84d4a82..f48d0ac 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskActionSequenceTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskActionSequenceTests.scala
@@ -32,55 +32,54 @@ import whisk.core.entity.EntityPath
* Tests creation and retrieval of a sequence action
*/
@RunWith(classOf[JUnitRunner])
-class WskActionSequenceTests
- extends TestHelpers
- with WskTestHelpers {
+class WskActionSequenceTests extends TestHelpers with WskTestHelpers {
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val defaultNamespace = EntityPath.DEFAULT.asString
- val namespace = wsk.namespace.whois()
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val defaultNamespace = EntityPath.DEFAULT.asString
+ val namespace = wsk.namespace.whois()
- behavior of "Wsk Action Sequence"
+ behavior of "Wsk Action Sequence"
- it should "create, and get an action sequence" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "actionSeq"
- val packageName = "samples"
- val helloName = "hello"
- val catName = "cat"
- val fullHelloActionName = s"/$defaultNamespace/$packageName/$helloName"
- val fullCatActionName = s"/$defaultNamespace/$packageName/$catName"
+ it should "create, and get an action sequence" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "actionSeq"
+ val packageName = "samples"
+ val helloName = "hello"
+ val catName = "cat"
+ val fullHelloActionName = s"/$defaultNamespace/$packageName/$helloName"
+ val fullCatActionName = s"/$defaultNamespace/$packageName/$catName"
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, _) => pkg.create(packageName, shared = Some(true))(wp)
- }
-
- assetHelper.withCleaner(wsk.action, fullHelloActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- (action, _) => action.create(fullHelloActionName, file)(wp)
- }
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, shared = Some(true))(wp)
+ }
- assetHelper.withCleaner(wsk.action, fullCatActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("cat.js"))
- (action, _) => action.create(fullCatActionName, file)(wp)
- }
+ assetHelper.withCleaner(wsk.action, fullHelloActionName) {
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ (action, _) =>
+ action.create(fullHelloActionName, file)(wp)
+ }
- val artifacts = s"$fullHelloActionName,$fullCatActionName"
- val kindValue = JsString("sequence")
- val compValue = JsArray(
- JsString(resolveDefaultNamespace(fullHelloActionName)),
- JsString(resolveDefaultNamespace(fullCatActionName)))
+ assetHelper.withCleaner(wsk.action, fullCatActionName) {
+ val file = Some(TestUtils.getTestActionFilename("cat.js"))
+ (action, _) =>
+ action.create(fullCatActionName, file)(wp)
+ }
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(artifacts), kind = Some("sequence"))
- }
+ val artifacts = s"$fullHelloActionName,$fullCatActionName"
+ val kindValue = JsString("sequence")
+ val compValue = JsArray(
+ JsString(resolveDefaultNamespace(fullHelloActionName)),
+ JsString(resolveDefaultNamespace(fullCatActionName)))
- val stdout = wsk.action.get(name).stdout
- assert(stdout.startsWith(s"ok: got action $name\n"))
- wsk.parseJsonString(stdout).fields("exec").asJsObject.fields("components") shouldBe compValue
- wsk.parseJsonString(stdout).fields("exec").asJsObject.fields("kind") shouldBe kindValue
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(artifacts), kind = Some("sequence"))
}
- private def resolveDefaultNamespace(actionName: String) = actionName.replace("/_/", s"/$namespace/")
+ val stdout = wsk.action.get(name).stdout
+ assert(stdout.startsWith(s"ok: got action $name\n"))
+ wsk.parseJsonString(stdout).fields("exec").asJsObject.fields("components") shouldBe compValue
+ wsk.parseJsonString(stdout).fields("exec").asJsObject.fields("kind") shouldBe kindValue
+ }
+
+ private def resolveDefaultNamespace(actionName: String) = actionName.replace("/_/", s"/$namespace/")
}
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
index f14a092..03b4fc3 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskBasicUsageTests.scala
@@ -53,1375 +53,354 @@ import whisk.http.Messages
* Tests for basic CLI usage. Some of these tests require a deployed backend.
*/
@RunWith(classOf[JUnitRunner])
-class WskBasicUsageTests
- extends TestHelpers
- with WskTestHelpers {
-
- implicit val wskprops = WskProps()
- val wsk = new Wsk
- val defaultAction = Some(TestCLIUtils.getTestActionFilename("hello.js"))
-
- behavior of "Wsk CLI usage"
-
- it should "show help and usage info" in {
- val stdout = wsk.cli(Seq()).stdout
- stdout should include regex ("""(?i)Usage:""")
- stdout should include regex ("""(?i)Flags""")
- stdout should include regex ("""(?i)Available commands""")
- stdout should include regex ("""(?i)--help""")
- }
-
- it should "show help and usage info using the default language" in {
- val env = Map("LANG" -> "de_DE")
- // Call will fail with exit code 2 if language not supported
- wsk.cli(Seq("-h"), env = env)
- }
-
- it should "show cli build version" in {
- val stdout = wsk.cli(Seq("property", "get", "--cliversion")).stdout
- stdout should include regex ("""(?i)whisk CLI version\s+201.*""")
- }
-
- it should "show api version" in {
- val stdout = wsk.cli(Seq("property", "get", "--apiversion")).stdout
- stdout should include regex ("""(?i)whisk API version\s+v1""")
- }
-
- it should "reject bad command" in {
- val result = wsk.cli(Seq("bogus"), expectedExitCode = ERROR_EXIT)
- result.stderr should include regex ("""(?i)Run 'wsk --help' for 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)
- }
-
- wsk.action.invoke(fullQualifiedName).stdout should include(s"ok: invoked /$fullQualifiedName")
- wsk.action.get(fullQualifiedName).stdout should include(s"ok: got action ${packageName}/${actionName}")
- }
-
-
- behavior of "Wsk actions"
-
- it should "reject creating entities with invalid names" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val names = Seq(
- ("", ERROR_EXIT),
- (" ", BAD_REQUEST),
- ("hi+there", BAD_REQUEST),
- ("$hola", BAD_REQUEST),
- ("dora?", BAD_REQUEST),
- ("|dora|dora?", BAD_REQUEST))
-
- names foreach {
- case (name, ec) =>
- assetHelper.withCleaner(wsk.action, name, confirmDelete = false) {
- (action, _) => action.create(name, defaultAction, expectedExitCode = ec)
- }
- }
- }
-
- it should "reject create with missing file" in {
- wsk.action.create("missingFile", Some("notfound"),
- expectedExitCode = MISUSE_EXIT).
- stderr should include("not a valid file")
- }
-
- it should "reject action update when specified file is missing" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- // Create dummy action to update
- val name = "updateMissingFile"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- assetHelper.withCleaner(wsk.action, name) { (action, name) => action.create(name, file) }
- // Update it with a missing file
- wsk.action.create(name, Some("notfound"), update = true, expectedExitCode = MISUSE_EXIT)
- }
-
- it should "reject action update for sequence with no components" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "updateMissingComponents"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- assetHelper.withCleaner(wsk.action, name) { (action, name) => action.create(name, file) }
- wsk.action.create(name, None, update = true, kind = Some("sequence"), expectedExitCode = MISUSE_EXIT)
- }
-
- it should "create, and get an action to verify parameter and annotation parsing" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "actionAnnotations"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, annotations = getValidJSONTestArgInput,
- parameters = getValidJSONTestArgInput)
- }
-
- val stdout = wsk.action.get(name).stdout
- assert(stdout.startsWith(s"ok: got action $name\n"))
-
- 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 an action to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "actionAnnotAndParamParsing"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- val argInput = Some(TestCLIUtils.getTestActionFilename("validInput1.json"))
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, annotationFile = argInput, parameterFile = argInput)
- }
-
- val stdout = wsk.action.get(name).stdout
- assert(stdout.startsWith(s"ok: got action $name\n"))
-
- 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 an action with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "actionEscapes"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, parameters = getEscapedJSONTestArgInput,
- annotations = getEscapedJSONTestArgInput)
- }
-
- val stdout = wsk.action.get(name).stdout
- assert(stdout.startsWith(s"ok: got action $name\n"))
-
- 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 "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(TestCLIUtils.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.ContainerError)
- }
- }
-
- 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(TestCLIUtils.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.ApplicationError)
- }
- }
-
- 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(TestCLIUtils.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.ContainerError)
- }
- }
-
- it should "retrieve the last activation using --last flag" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val auth: Seq[String] = Seq("--auth", wskprops.authKey)
- val includeStr = "hello, undefined!"
-
- assetHelper.withCleaner(wsk.action, "lastName") {
- (action, _) => wsk.action.create("lastName", defaultAction)
- }
- retry({
- val lastInvoke = wsk.action.invoke("lastName")
- withActivation(wsk.activation, lastInvoke) { activation =>
- val lastFlag = Seq(
- (Seq("activation", "get", "publish", "--last"), activation.activationId),
- (Seq("activation", "get", "--last"), activation.activationId),
- (Seq("activation", "logs", "--last"), includeStr),
- (Seq("activation", "result", "--last"), includeStr))
-
- retry({
- lastFlag foreach {
- case (cmd, output) =>
- val stdout = wsk.cli(cmd ++ wskprops.overrides ++ auth, expectedExitCode = SUCCESS_EXIT).stdout
- stdout should include(output)
- }
- }, waitBeforeRetry = Some(500.milliseconds))
-
- }
- }, waitBeforeRetry = Some(1.second), N = 5)
- }
-
- 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(TestCLIUtils.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())
- activation.logs shouldBe Some(List())
- 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(TestCLIUtils.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 "report error when creating an action with unknown kind" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val rr = assetHelper.withCleaner(wsk.action, "invalid kind", confirmDelete = false) {
- (action, name) => action.create(name, Some(TestCLIUtils.getTestActionFilename("echo.js")), kind = Some("foobar"), expectedExitCode = BAD_REQUEST)
- }
- rr.stderr should include regex "kind 'foobar' not in Set"
- }
-
- it should "report error when creating an action with zip but without kind" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "zipWithNoKind"
- val zippedPythonAction = Some(TestCLIUtils.getTestActionFilename("python.zip"))
- val createResult = assetHelper.withCleaner(wsk.action, name, confirmDelete = false) {
- (action, _) =>
- action.create(name, zippedPythonAction, expectedExitCode = ANY_ERROR_EXIT)
- }
-
- createResult.stderr should include regex "requires specifying the action kind"
- }
-
- 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.ApplicationError)
- 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(TestCLIUtils.getTestActionFilename("helloOpenwhiskPackage.js")))
- }
-
- 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 = TestUtils.NOT_FOUND)
- }
-
- it should "invoke an action receiving context properties" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val namespace = wsk.namespace.whois()
- val name = "context"
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.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("api_key") shouldBe wskprops.authKey
- fields("namespace") shouldBe namespace
- fields("action_name") shouldBe s"/$namespace/$name"
- 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(TestCLIUtils.getTestActionFilename("echo.js")))
- }
- val args = Map("hello" -> "Robert".toJson)
- val run = wsk.action.invoke(name, args, blocking = true, result = true)
- 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(TestCLIUtils.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(TestCLIUtils.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.ApplicationError)
- 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) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file)
- }
-
- 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"
-
- wsk.action.create(name, file, web = Some(flag), update = true)
-
- val stdout = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
- assert(stdout.startsWith(s"ok: got action $name, displaying field annotations\n"))
- removeCLIHeader(stdout).parseJson shouldBe JsArray(
- JsObject(
- "key" -> JsString("exec"),
- "value" -> JsString("nodejs:6")),
- JsObject(
- "key" -> JsString("web-export"),
- "value" -> JsBoolean(webEnabled || rawEnabled)),
- JsObject(
- "key" -> JsString("raw-http"),
- "value" -> JsBoolean(rawEnabled)),
- JsObject(
- "key" -> JsString("final"),
- "value" -> JsBoolean(webEnabled || rawEnabled)))
- }
- }
-
- it should "ensure action update with --web flag only copies existing annotations when new annotations are not provided" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
- val createKey = "createKey"
- val createValue = JsString("createValue")
- val updateKey = "updateKey"
- val updateValue = JsString("updateValue")
- val origKey = "origKey"
- val origValue = JsString("origValue")
- val overwrittenValue = JsString("overwrittenValue")
- val createAnnots = Map(createKey -> createValue, origKey -> origValue)
- val updateAnnots = Map(updateKey -> updateValue, origKey -> overwrittenValue)
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file, annotations = createAnnots)
- }
-
- wsk.action.create(name, file, web = Some("true"), update = true)
-
- val existinAnnots = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
- assert(existinAnnots.startsWith(s"ok: got action $name, displaying field annotations\n"))
- removeCLIHeader(existinAnnots).parseJson shouldBe JsArray(
- JsObject(
- "key" -> JsString("web-export"),
- "value" -> JsBoolean(true)),
- JsObject(
- "key" -> JsString(origKey),
- "value" -> origValue),
- JsObject(
- "key" -> JsString("raw-http"),
- "value" -> JsBoolean(false)),
- JsObject(
- "key" -> JsString("final"),
- "value" -> JsBoolean(true)),
- JsObject(
- "key" -> JsString(createKey),
- "value" -> createValue),
- JsObject(
- "key" -> JsString("exec"),
- "value" -> JsString("nodejs:6")))
-
- wsk.action.create(name, file, web = Some("true"), update = true, annotations = updateAnnots)
-
- val updatedAnnots = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
- assert(updatedAnnots.startsWith(s"ok: got action $name, displaying field annotations\n"))
- removeCLIHeader(updatedAnnots).parseJson shouldBe JsArray(
- JsObject(
- "key" -> JsString("web-export"),
- "value" -> JsBoolean(true)),
- JsObject(
- "key" -> JsString(origKey),
- "value" -> overwrittenValue),
- JsObject(
- "key" -> JsString(updateKey),
- "value" -> updateValue),
- JsObject(
- "key" -> JsString("raw-http"),
- "value" -> JsBoolean(false)),
- JsObject(
- "key" -> JsString("final"),
- "value" -> JsBoolean(true)),
- JsObject(
- "key" -> JsString("exec"),
- "value" -> JsString("nodejs:6")))
- }
-
- it should "ensure action update creates an action with --web flag" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file, web = Some("true"), update = true)
- }
-
- val stdout = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
- assert(stdout.startsWith(s"ok: got action $name, displaying field annotations\n"))
- removeCLIHeader(stdout).parseJson shouldBe JsArray(
- JsObject(
- "key" -> JsString("web-export"),
- "value" -> JsBoolean(true)),
- JsObject(
- "key" -> JsString("raw-http"),
- "value" -> JsBoolean(false)),
- JsObject(
- "key" -> JsString("final"),
- "value" -> JsBoolean(true)),
- JsObject(
- "key" -> JsString("exec"),
- "value" -> JsString("nodejs:6")))
- }
-
- it should "reject action create and update with invalid web flag input" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
- val invalidInput = "bogus"
- val errorMsg = s"Invalid argument '$invalidInput' for --web flag. Valid input consist of 'yes', 'true', 'raw', 'false', or 'no'."
- wsk.action.create(name, file, web = Some(invalidInput), expectedExitCode = MISUSE_EXIT).stderr should include(errorMsg)
- wsk.action.create(name, file, web = Some(invalidInput), update = true, expectedExitCode = MISUSE_EXIT).stderr should include(errorMsg)
- }
-
- it should "invoke action while not encoding &, <, > characters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "nonescape"
- val file = Some(TestCLIUtils.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
- }
- }
-
- it should "get an action URL" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val actionName = "action name@_-."
- val packageName = "package name@_-."
- val defaultPackageName = "default"
- val webActionName = "web action name@_-."
- val nonExistentActionName = "non-existence action"
- val packagedAction = s"$packageName/$actionName"
- val packagedWebAction = s"$packageName/$webActionName"
- val namespace = wsk.namespace.whois()
- val encodedActionName = URLEncoder.encode(actionName, StandardCharsets.UTF_8.name).replace("+", "%20")
- val encodedPackageName = URLEncoder.encode(packageName, StandardCharsets.UTF_8.name).replace("+", "%20")
- val encodedWebActionName = URLEncoder.encode(webActionName, StandardCharsets.UTF_8.name).replace("+", "%20")
- val encodedNamespace = URLEncoder.encode(namespace, StandardCharsets.UTF_8.name).replace("+", "%20")
- val actionPath = "https://%s/api/%s/namespaces/%s/actions/%s"
- val packagedActionPath = s"$actionPath/%s"
- val webActionPath = "https://%s/api/%s/web/%s/%s/%s"
-
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, _) => action.create(actionName, defaultAction)
- }
-
- assetHelper.withCleaner(wsk.action, webActionName) {
- (action, _) => action.create(webActionName, defaultAction, web = Some("true"))
- }
-
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, _) => pkg.create(packageName)
- }
-
- assetHelper.withCleaner(wsk.action, packagedAction) {
- (action, _) => action.create(packagedAction, defaultAction)
- }
-
- assetHelper.withCleaner(wsk.action, packagedWebAction) {
- (action, _) => action.create(packagedWebAction, defaultAction, web = Some("true"))
- }
-
- wsk.action.get(actionName, url = Some(true)).
- stdout should include(actionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedActionName))
-
- // Ensure url flag works when a field filter and summary flag are specified
- wsk.action.get(actionName, url = Some(true), fieldFilter = Some("field"), summary = true).
- stdout should include(actionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedActionName))
-
- wsk.action.get(webActionName, url = Some(true)).
- stdout should include(webActionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, defaultPackageName, encodedWebActionName))
-
- wsk.action.get(packagedAction, url = Some(true)).
- stdout should include(packagedActionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedPackageName, encodedActionName))
-
- wsk.action.get(packagedWebAction, url = Some(true)).
- stdout should include(webActionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedPackageName, encodedWebActionName))
-
- wsk.action.get(nonExistentActionName, url = Some(true), expectedExitCode = NOT_FOUND)
- }
- it should "limit length of HTTP request and response bodies for --verbose" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "limitVerbose"
- val msg = "will be truncated"
- val params = Seq("-p", "bigValue", "a" * 1000)
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, Some(TestCLIUtils.getTestActionFilename("echo.js")))
- }
-
- val truncated = wsk.cli(Seq("action", "invoke", name, "-b", "-v", "--auth", wskprops.authKey) ++ params ++ wskprops.overrides).stdout
- msg.r.findAllIn(truncated).length shouldBe 2
-
- val notTruncated = wsk.cli(Seq("action", "invoke", name, "-b", "-d", "--auth", wskprops.authKey) ++ params ++ wskprops.overrides).stdout
- msg.r.findAllIn(notTruncated).length shouldBe 0
- }
-
- it should "denote bound and finalized action parameters for action summaries" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val nameBoundParams = "actionBoundParams"
- val nameFinalParams = "actionFinalParams"
- val paramAnnot = "paramAnnot"
- val paramOverlap = "paramOverlap"
- val paramBound = "paramBound"
- val annots = Map(
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString(paramAnnot),
- "description" -> JsString("Annotated")),
- JsObject(
- "name" -> JsString(paramOverlap),
- "description" -> JsString("Annotated And Bound"))))
- val annotsFinal = Map(
- "final" -> JsBoolean(true),
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString(paramAnnot),
- "description" -> JsString("Annotated Parameter description")),
- JsObject(
- "name" -> JsString(paramOverlap),
- "description" -> JsString("Annotated And Bound"))))
- val paramsBound = Map(
- paramBound -> JsString("Bound"),
- paramOverlap -> JsString("Bound And Annotated"))
-
- assetHelper.withCleaner(wsk.action, nameBoundParams) {
- (action, _) =>
- action.create(nameBoundParams, defaultAction, annotations = annots, parameters = paramsBound)
- }
- assetHelper.withCleaner(wsk.action, nameFinalParams) {
- (action, _) =>
- action.create(nameFinalParams, defaultAction, annotations = annotsFinal, parameters = paramsBound)
- }
-
- val stdoutBound = wsk.action.get(nameBoundParams, summary = true).stdout
- val stdoutFinal = wsk.action.get(nameFinalParams, summary = true).stdout
-
- stdoutBound should include (
- s"(parameters: $paramAnnot, *$paramBound, *$paramOverlap)")
- stdoutFinal should include (
- s"(parameters: $paramAnnot, **$paramBound, **$paramOverlap)")
- }
-
- it should "create, and get an action summary without a description and/or defined parameters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val actNameNoParams = "actionNoParams"
- val actNameNoDesc = "actionNoDesc"
- val actNameNoDescOrParams = "actionNoDescOrParams"
- val desc = "Action description"
- val descFromParamsResp = "Returns a result based on parameters"
- val annotsNoParams = Map(
- "description" -> JsString(desc)
- )
- val annotsNoDesc = Map(
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
-
- assetHelper.withCleaner(wsk.action, actNameNoDesc) {
- (action, _) =>
- action.create(actNameNoDesc, defaultAction, annotations = annotsNoDesc)
- }
- assetHelper.withCleaner(wsk.action, actNameNoParams) {
- (action, _) =>
- action.create(actNameNoParams, defaultAction, annotations = annotsNoParams)
- }
- assetHelper.withCleaner(wsk.action, actNameNoDescOrParams) {
- (action, _) =>
- action.create(actNameNoDescOrParams, defaultAction)
- }
-
- val stdoutNoDesc = wsk.action.get(actNameNoDesc, summary = true).stdout
- val stdoutNoParams = wsk.action.get(actNameNoParams, summary = true).stdout
- val stdoutNoDescOrParams = wsk.action.get(actNameNoDescOrParams, summary = true).stdout
- val namespace = wsk.namespace.whois()
-
- stdoutNoDesc should include regex (
- s"(?i)action /${namespace}/${actNameNoDesc}: ${descFromParamsResp} paramName1 and paramName2\\s*\\(parameters: paramName1, paramName2\\)")
- stdoutNoParams should include regex (
- s"(?i)action /${namespace}/${actNameNoParams}: ${desc}\\s*\\(parameters: none defined\\)")
- stdoutNoDescOrParams should include regex (
- s"(?i)action /${namespace}/${actNameNoDescOrParams}\\s*\\(parameters: none defined\\)")
- }
-
- behavior of "Wsk packages"
-
- it should "create, and delete a package" in {
- val name = "createDeletePackage"
- wsk.pkg.create(name).stdout should include(s"ok: created package $name")
- wsk.pkg.delete(name).stdout should include(s"ok: deleted package $name")
- }
-
- 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 stdout = wsk.pkg.get(name).stdout
- assert(stdout.startsWith(s"ok: got package $name\n"))
-
- 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 package to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "packageAnnotAndParamFileParsing"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- val argInput = Some(TestCLIUtils.getTestActionFilename("validInput1.json"))
-
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) =>
- pkg.create(name, annotationFile = argInput, parameterFile = argInput)
- }
-
- val stdout = wsk.pkg.get(name).stdout
- assert(stdout.startsWith(s"ok: got package $name\n"))
-
- 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 stdout = wsk.pkg.get(name).stdout
- assert(stdout.startsWith(s"ok: got package $name\n"))
-
- 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 "report conformance error accessing action as package" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "aAsP"
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file)
- }
-
- wsk.pkg.get(name, expectedExitCode = CONFLICT).
- stderr should include(Messages.conformanceMessage)
-
- wsk.pkg.bind(name, "bogus", expectedExitCode = CONFLICT).
- stderr should include(Messages.requestedBindingIsNotValid)
-
- wsk.pkg.bind("bogus", "alsobogus", expectedExitCode = BAD_REQUEST).
- stderr should include(Messages.bindingDoesNotExist)
-
- }
-
- it should "create, and get a package summary without a description and/or parameters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val pkgNoDesc = "pkgNoDesc"
- val pkgNoParams = "pkgNoParams"
- val pkgNoDescOrParams = "pkgNoDescOrParams"
- val pkgDesc = "Package description"
- val descFromParams = "Returns a result based on parameters"
- val namespace = wsk.namespace.whois()
- val qualpkgNoDesc = s"/${namespace}/${pkgNoDesc}"
- val qualpkgNoParams = s"/${namespace}/${pkgNoParams}"
- val qualpkgNoDescOrParams = s"/${namespace}/${pkgNoDescOrParams}"
-
- val pkgAnnotsNoParams = Map(
- "description" -> JsString(pkgDesc)
- )
- val pkgAnnotsNoDesc = Map(
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
-
- assetHelper.withCleaner(wsk.pkg, pkgNoDesc) {
- (pkg, _) =>
- pkg.create(pkgNoDesc, annotations = pkgAnnotsNoDesc)
- }
- assetHelper.withCleaner(wsk.pkg, pkgNoParams) {
- (pkg, _) =>
- pkg.create(pkgNoParams, annotations = pkgAnnotsNoParams)
- }
- assetHelper.withCleaner(wsk.pkg, pkgNoDescOrParams) {
- (pkg, _) =>
- pkg.create(pkgNoDescOrParams)
- }
-
- val stdoutNoDescPkg = wsk.pkg.get(pkgNoDesc, summary = true).stdout
- val stdoutNoParamsPkg = wsk.pkg.get(pkgNoParams, summary = true).stdout
- val stdoutNoDescOrParams = wsk.pkg.get(pkgNoDescOrParams, summary = true).stdout
-
- stdoutNoDescPkg should include regex (
- s"(?i)package ${qualpkgNoDesc}: ${descFromParams} paramName1 and paramName2\\s*\\(parameters: paramName1, paramName2\\)")
- stdoutNoParamsPkg should include regex (
- s"(?i)package ${qualpkgNoParams}: ${pkgDesc}\\s*\\(parameters: none defined\\)")
- stdoutNoDescOrParams should include regex (
- s"(?i)package ${qualpkgNoDescOrParams}\\s*\\(parameters: none defined\\)")
- }
-
- it should "denote bound package parameters for package summaries" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val pkgBoundParams = "pkgBoundParams"
- val pkgParamAnnot = "pkgParamAnnot"
- val pkgParamOverlap = "pkgParamOverlap"
- val pkgParamBound = "pkgParamBound"
- val pkgAnnots = Map(
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString(pkgParamAnnot),
- "description" -> JsString("Annotated")),
- JsObject(
- "name" -> JsString(pkgParamOverlap),
- "description" -> JsString("Annotated And Bound"))))
- val pkgParamsBound = Map(
- pkgParamBound -> JsString("Bound"),
- pkgParamOverlap -> JsString("Bound And Annotated"))
-
- assetHelper.withCleaner(wsk.pkg, pkgBoundParams) {
- (pkg, _) =>
- pkg.create(pkgBoundParams, annotations = pkgAnnots, parameters = pkgParamsBound)
- }
-
- val pkgStdoutBound = wsk.pkg.get(pkgBoundParams, summary = true).stdout
-
- pkgStdoutBound should include (s"(parameters: $pkgParamAnnot, *$pkgParamBound, *$pkgParamOverlap)")
- }
-
- 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
- assert(stdout.startsWith(s"ok: got trigger $name\n"))
-
- 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(TestCLIUtils.getTestActionFilename("hello.js"))
- val argInput = Some(TestCLIUtils.getTestActionFilename("validInput1.json"))
-
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) =>
- trigger.create(name, annotationFile = argInput, parameterFile = argInput)
- }
-
- val stdout = wsk.trigger.get(name).stdout
- assert(stdout.startsWith(s"ok: got trigger $name\n"))
-
- 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 "display a trigger summary when --summary flag is used with 'wsk trigger get'" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val triggerName = "mySummaryTrigger"
- assetHelper.withCleaner(wsk.trigger, triggerName, confirmDelete = false) {
- (trigger, name) => trigger.create(name)
- }
-
- // Summary namespace should match one of the allowable namespaces (typically 'guest')
- val ns = wsk.namespace.whois()
- val stdout = wsk.trigger.get(triggerName, summary = true).stdout
- stdout should include regex (s"(?i)trigger\\s+/$ns/$triggerName")
- }
-
- 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
- assert(stdout.startsWith(s"ok: got trigger $name\n"))
-
- 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 = NOT_FOUND)
-
- trigger.create(name, feed = Some(s"bogus/feed"), expectedExitCode = ANY_ERROR_EXIT).
- exitCode should equal(NOT_FOUND)
- trigger.get(name, expectedExitCode = NOT_FOUND)
- }
- }
-
- it should "denote bound trigger parameters for trigger summaries" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val trgBoundParams = "trgBoundParams"
- val trgParamAnnot = "trgParamAnnot"
- val trgParamOverlap = "trgParamOverlap"
- val trgParamBound = "trgParamBound"
- val trgAnnots = Map(
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString(trgParamAnnot),
- "description" -> JsString("Annotated")),
- JsObject(
- "name" -> JsString(trgParamOverlap),
- "description" -> JsString("Annotated And Bound"))))
- val trgParamsBound = Map(
- trgParamBound -> JsString("Bound"),
- trgParamOverlap -> JsString("Bound And Annotated"))
-
- assetHelper.withCleaner(wsk.trigger, trgBoundParams) {
- (trigger, _) =>
- trigger.create(trgBoundParams, annotations = trgAnnots, parameters = trgParamsBound)
- }
-
- val trgStdoutBound = wsk.trigger.get(trgBoundParams, summary = true).stdout
-
- trgStdoutBound should include (s"(parameters: $trgParamAnnot, *$trgParamBound, *$trgParamOverlap)")
- }
-
- it should "create, and get a trigger summary without a description and/or parameters" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val trgNoDesc = "trgNoDesc"
- val trgNoParams = "trgNoParams"
- val trgNoDescOrParams = "trgNoDescOrParams"
- val trgDesc = "Package description"
- val descFromParams = "Returns a result based on parameters"
- val namespace = wsk.namespace.whois()
- val qualtrgNoDesc = s"/${namespace}/${trgNoDesc}"
- val qualtrgNoParams = s"/${namespace}/${trgNoParams}"
- val qualtrgNoDescOrParams = s"/${namespace}/${trgNoDescOrParams}"
-
- val trgAnnotsNoParams = Map(
- "description" -> JsString(trgDesc)
- )
- val trgAnnotsNoDesc = Map(
- "parameters" -> JsArray(
- JsObject(
- "name" -> JsString("paramName1"),
- "description" -> JsString("Parameter description 1")),
- JsObject(
- "name" -> JsString("paramName2"),
- "description" -> JsString("Parameter description 2"))))
-
- assetHelper.withCleaner(wsk.trigger, trgNoDesc) {
- (trigger, _) =>
- trigger.create(trgNoDesc, annotations = trgAnnotsNoDesc)
- }
- assetHelper.withCleaner(wsk.trigger, trgNoParams) {
- (trigger, _) =>
- trigger.create(trgNoParams, annotations = trgAnnotsNoParams)
- }
- assetHelper.withCleaner(wsk.trigger, trgNoDescOrParams) {
- (trigger, _) =>
- trigger.create(trgNoDescOrParams)
- }
-
- val stdoutNoDescPkg = wsk.trigger.get(trgNoDesc, summary = true).stdout
- val stdoutNoParamsPkg = wsk.trigger.get(trgNoParams, summary = true).stdout
- val stdoutNoDescOrParams = wsk.trigger.get(trgNoDescOrParams, summary = true).stdout
-
- stdoutNoDescPkg should include regex (
- s"(?i)trigger ${qualtrgNoDesc}: ${descFromParams} paramName1 and paramName2\\s*\\(parameters: paramName1, paramName2\\)")
- stdoutNoParamsPkg should include regex (
- s"(?i)trigger ${qualtrgNoParams}: ${trgDesc}\\s*\\(parameters: none defined\\)")
- stdoutNoDescOrParams should include regex (
- s"(?i)trigger ${qualtrgNoDescOrParams}\\s*\\(parameters: none defined\\)")
- }
-
- behavior of "Wsk entity list formatting"
-
- it should "create, and list a package with a long name" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "x" * 70
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) =>
- pkg.create(name)
- }
- retry({
- wsk.pkg.list().stdout should include(s"$name private")
- }, 5, Some(1 second))
- }
-
- it should "create, and list an action with a long name" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "x" * 70
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file)
- }
- retry({
- wsk.action.list().stdout should include(s"$name private nodejs")
- }, 5, Some(1 second))
- }
-
- it should "create, and list a trigger with a long name" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "x" * 70
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) =>
- trigger.create(name)
- }
- retry({
- wsk.trigger.list().stdout should include(s"$name private")
- }, 5, Some(1 second))
- }
-
- it should "create, and list a rule with a long name" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val ruleName = "x" * 70
- val triggerName = "listRulesTrigger"
- val actionName = "listRulesAction";
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- assetHelper.withCleaner(wsk.rule, ruleName) {
- (rule, name) =>
- rule.create(name, trigger = triggerName, action = actionName)
- }
- retry({
- wsk.rule.list().stdout should include(s"$ruleName private")
- }, 5, Some(1 second))
- }
-
- it should "return a list of alphabetized actions" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- // Declare 4 actions, create them out of alphabetical order
- val actionName = "actionAlphaTest"
- for (i <- 1 to 3) {
- val name = s"$actionName$i"
- assetHelper.withCleaner(wsk.action, name) {
- (action, name) =>
- action.create(name, defaultAction)
- }
- }
- retry({
- val original = wsk.action.list(nameSort = Some(true)).stdout
- // Create list with action names in correct order
- val scalaSorted = List(s"${actionName}1", s"${actionName}2", s"${actionName}3")
- // Filter out everything not previously created
- val regex = s"${actionName}[1-3]".r
- // Retrieve action names into list as found in original
- val list = (regex.findAllMatchIn(original)).toList
- scalaSorted.toString shouldEqual list.toString
- }, 5, Some(1 second))
- }
-
- it should "return an alphabetized list with default package actions on top" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- // Declare 4 actions, create them out of alphabetical order
- val actionName = "actionPackageAlphaTest"
- val packageName = "packageAlphaTest"
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, actionName) =>
- action.create(actionName, defaultAction)
- }
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, packageName) =>
- pkg.create(packageName)
- }
- for (i <- 1 to 3) {
- val name = s"${packageName}/${actionName}$i"
- assetHelper.withCleaner(wsk.action, name) {
- (action, name) =>
- action.create(name, defaultAction)
- }
- }
- retry({
- val original = wsk.action.list(nameSort = Some(true)).stdout
- // Create list with action names in correct order
- val scalaSorted = List(s"$actionName", s"${packageName}/${actionName}1", s"${packageName}/${actionName}2", s"${packageName}/${actionName}3")
- // Filter out everything not previously created
- val regexNoPackage = s"$actionName".r
- val regexWithPackage = s"${packageName}/${actionName}[1-3]".r
- // Retrieve action names into list as found in original
- val list = regexNoPackage.findFirstIn(original).get :: (regexWithPackage.findAllMatchIn(original)).toList
- scalaSorted.toString shouldEqual list.toString
- }, 5, Some(1 second))
- }
-
- it should "return a list of alphabetized packages" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- // Declare 3 packages, create them out of alphabetical order
- val packageName = "pkgAlphaTest"
- for (i <- 1 to 3) {
- val name = s"$packageName$i"
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, name) =>
- pkg.create(name)
- }
- }
- retry({
- val original = wsk.pkg.list(nameSort = Some(true)).stdout
- // Create list with package names in correct order
- val scalaSorted = List(s"${packageName}1", s"${packageName}2", s"${packageName}3")
- // Filter out everything not previously created
- val regex = s"${packageName}[1-3]".r
- // Retrieve package names into list as found in original
- val list = (regex.findAllMatchIn(original)).toList
- scalaSorted.toString shouldEqual list.toString
- }, 5, Some(1 second))
+class WskBasicUsageTests extends TestHelpers with WskTestHelpers {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
+ val defaultAction = Some(TestUtils.getTestActionFilename("hello.js"))
+
+ behavior of "Wsk CLI usage"
+
+ it should "show help and usage info" in {
+ val stdout = wsk.cli(Seq()).stdout
+ stdout should include regex ("""(?i)Usage:""")
+ stdout should include regex ("""(?i)Flags""")
+ stdout should include regex ("""(?i)Available commands""")
+ stdout should include regex ("""(?i)--help""")
+ }
+
+ it should "show help and usage info using the default language" in {
+ val env = Map("LANG" -> "de_DE")
+ // Call will fail with exit code 2 if language not supported
+ wsk.cli(Seq("-h"), env = env)
+ }
+
+ it should "show cli build version" in {
+ val stdout = wsk.cli(Seq("property", "get", "--cliversion")).stdout
+ stdout should include regex ("""(?i)whisk CLI version\s+201.*""")
+ }
+
+ it should "show api version" in {
+ val stdout = wsk.cli(Seq("property", "get", "--apiversion")).stdout
+ stdout should include regex ("""(?i)whisk API version\s+v1""")
+ }
+
+ it should "reject bad command" in {
+ val result = wsk.cli(Seq("bogus"), expectedExitCode = ERROR_EXIT)
+ result.stderr should include regex ("""(?i)Run 'wsk --help' for 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)
+ }
+
+ wsk.action.invoke(fullQualifiedName).stdout should include(s"ok: invoked /$fullQualifiedName")
+ wsk.action.get(fullQualifiedName).stdout should include(s"ok: got action ${packageName}/${actionName}")
+ }
+
+ behavior of "Wsk actions"
+
+ it should "reject creating entities with invalid names" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val names = Seq(
+ ("", ERROR_EXIT),
+ (" ", BAD_REQUEST),
+ ("hi+there", BAD_REQUEST),
+ ("$hola", BAD_REQUEST),
+ ("dora?", BAD_REQUEST),
+ ("|dora|dora?", BAD_REQUEST))
+
+ names foreach {
+ case (name, ec) =>
+ assetHelper.withCleaner(wsk.action, name, confirmDelete = false) { (action, _) =>
+ action.create(name, defaultAction, expectedExitCode = ec)
+ }
}
+ }
+
+ it should "reject create with missing file" in {
+ wsk.action.create("missingFile", Some("notfound"), expectedExitCode = MISUSE_EXIT).stderr should include(
+ "not a valid file")
+ }
+
+ it should "reject action update when specified file is missing" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ // Create dummy action to update
+ val name = "updateMissingFile"
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ assetHelper.withCleaner(wsk.action, name) { (action, name) =>
+ action.create(name, file)
+ }
+ // Update it with a missing file
+ wsk.action.create(name, Some("notfound"), update = true, expectedExitCode = MISUSE_EXIT)
+ }
+
+ it should "reject action update for sequence with no components" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "updateMissingComponents"
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ assetHelper.withCleaner(wsk.action, name) { (action, name) =>
+ action.create(name, file)
+ }
+ wsk.action.create(name, None, update = true, kind = Some("sequence"), expectedExitCode = MISUSE_EXIT)
+ }
+
+ 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 stdout = wsk.action.get(name).stdout
+ assert(stdout.startsWith(s"ok: got action $name\n"))
+
+ 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 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 stdout = wsk.action.get(name).stdout
+ assert(stdout.startsWith(s"ok: got action $name\n"))
+
+ 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 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 stdout = wsk.action.get(name).stdout
+ assert(stdout.startsWith(s"ok: got action $name\n"))
+
+ 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 "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.ContainerError)
+ }
+ }
+
+ 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.ApplicationError)
+ }
+ }
+
+ 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.ContainerError)
+ }
+ }
+
+ it should "retrieve the last activation using --last flag" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val auth: Seq[String] = Seq("--auth", wskprops.authKey)
+ val includeStr = "hello, undefined!"
+
+ assetHelper.withCleaner(wsk.action, "lastName") { (action, _) =>
+ wsk.action.create("lastName", defaultAction)
+ }
+ retry(
+ {
+ val lastInvoke = wsk.action.invoke("lastName")
+ withActivation(wsk.activation, lastInvoke) {
+ activation =>
+ val lastFlag = Seq(
+ (Seq("activation", "get", "publish", "--last"), activation.activationId),
+ (Seq("activation", "get", "--last"), activation.activationId),
+ (Seq("activation", "logs", "--last"), includeStr),
+ (Seq("activation", "result", "--last"), includeStr))
- it should "return a list of alphabetized triggers" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- // Declare 4 triggers, create them out of alphabetical order
- val triggerName = "triggerAlphaTest"
- for (i <- 1 to 3) {
- val name = s"$triggerName$i"
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, name) =>
- trigger.create(name)
- }
- }
retry({
- val original = wsk.trigger.list(nameSort = Some(true)).stdout
- // Create list with trigger names in correct order
- val scalaSorted = List(s"${triggerName}1", s"${triggerName}2", s"${triggerName}3")
- // Filter out everything not previously created
- val regex = s"${triggerName}[1-3]".r
- // Retrieve trigger names into list as found in original
- val list = (regex.findAllMatchIn(original)).toList
- scalaSorted.toString shouldEqual list.toString
- }, 5, Some(1 second))
- }
+ lastFlag foreach {
+ case (cmd, output) =>
+ val stdout = wsk.cli(cmd ++ wskprops.overrides ++ auth, expectedExitCode = SUCCESS_EXIT).stdout
+ stdout should include(output)
+ }
+ }, waitBeforeRetry = Some(500.milliseconds))
- it should "return a list of alphabetized rules" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- // Declare a trigger and an action for the purposes of creating rules
- val triggerName = "listRulesTrigger"
- val actionName = "listRulesAction"
-
- assetHelper.withCleaner(wsk.trigger, triggerName) {
- (trigger, name) => trigger.create(name)
- }
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, name) => action.create(name, defaultAction)
- }
- // Declare 3 rules, create them out of alphabetical order
- val ruleName = "ruleAlphaTest"
- for (i <- 1 to 3) {
- val name = s"$ruleName$i"
- assetHelper.withCleaner(wsk.rule, name) {
- (rule, name) =>
- rule.create(name, trigger = triggerName, action = actionName)
- }
- }
- retry({
- val original = wsk.rule.list(nameSort = Some(true)).stdout
- // Create list with rule names in correct order
- val scalaSorted = List(s"${ruleName}1", s"${ruleName}2", s"${ruleName}3")
- // Filter out everything not previously created
- val regex = s"${ruleName}[1-3]".r
- // Retrieve rule names into list as found in original
- val list = (regex.findAllMatchIn(original)).toList
- scalaSorted.toString shouldEqual list.toString
- })
- }
-
- behavior of "Wsk params and annotations"
-
- it should "reject commands that are executed with invalid JSON for annotations and parameters" in {
- val invalidJSONInputs = getInvalidJSONInput
- val invalidJSONFiles = Seq(
- TestCLIUtils.getTestActionFilename("malformed.js"),
- TestCLIUtils.getTestActionFilename("invalidInput1.json"),
- TestCLIUtils.getTestActionFilename("invalidInput2.json"),
- TestCLIUtils.getTestActionFilename("invalidInput3.json"),
- TestCLIUtils.getTestActionFilename("invalidInput4.json"))
- val paramCmds = Seq(
- Seq("action", "create", "actionName", TestCLIUtils.getTestActionFilename("hello.js")),
- Seq("action", "update", "actionName", TestCLIUtils.getTestActionFilename("hello.js")),
- Seq("action", "invoke", "actionName"),
- Seq("package", "create", "packageName"),
- Seq("package", "update", "packageName"),
- Seq("package", "bind", "packageName", "boundPackageName"),
- Seq("trigger", "create", "triggerName"),
- Seq("trigger", "update", "triggerName"),
- Seq("trigger", "fire", "triggerName"))
- val annotCmds = Seq(
- Seq("action", "create", "actionName", TestCLIUtils.getTestActionFilename("hello.js")),
- Seq("action", "update", "actionName", TestCLIUtils.getTestActionFilename("hello.js")),
- Seq("package", "create", "packageName"),
- Seq("package", "update", "packageName"),
- Seq("package", "bind", "packageName", "boundPackageName"),
- Seq("trigger", "create", "triggerName"),
- Seq("trigger", "update", "triggerName"))
-
- for (cmd <- paramCmds) {
- for (invalid <- invalidJSONInputs) {
- wsk.cli(cmd ++ Seq("-p", "key", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT)
- .stderr should include("Invalid parameter argument")
- }
-
- for (invalid <- invalidJSONFiles) {
- wsk.cli(cmd ++ Seq("-P", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT)
- .stderr should include("Invalid parameter argument")
-
- }
}
-
- for (cmd <- annotCmds) {
- for (invalid <- invalidJSONInputs) {
- wsk.cli(cmd ++ Seq("-a", "key", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT)
- .stderr should include("Invalid annotation argument")
- }
-
- for (invalid <- invalidJSONFiles) {
- wsk.cli(cmd ++ Seq("-A", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT)
- .stderr should include("Invalid annotation argument")
- }
+ },
+ waitBeforeRetry = Some(1.second),
+ N = 5)
+ }
+
+ 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())
+ activation.logs shouldBe Some(List())
+ 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 "report error when creating an action with unknown kind" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val rr = assetHelper.withCleaner(wsk.action, "invalid kind", confirmDelete = false) { (action, name) =>
+ action.create(
+ name,
+ Some(TestUtils.getTestActionFilename("echo.js")),
+ kind = Some("foobar"),
+ expectedExitCode = BAD_REQUEST)
+ }
+ rr.stderr should include regex "kind 'foobar' not in Set"
+ }
+
+ it should "report error when creating an action with zip but without kind" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "zipWithNoKind"
+ val zippedPythonAction = Some(TestUtils.getTestActionFilename("python.zip"))
+ val createResult = assetHelper.withCleaner(wsk.action, name, confirmDelete = false) { (action, _) =>
+ action.create(name, zippedPythonAction, expectedExitCode = ANY_ERROR_EXIT)
+ }
+
+ createResult.stderr should include regex "requires specifying the action kind"
+ }
+
+ 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.ApplicationError)
+ 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
}
+<<<<<<< HEAD
}
it should "reject commands that are executed with a missing or invalid parameter or annotation file" in {
@@ -1476,246 +455,1207 @@ class WskBasicUsageTests
val stderr = wsk.cli(cmd, expectedExitCode = MISUSE_EXIT).stderr
stderr should include(err)
stderr should include("Run 'wsk --help' for usage.")
+=======
+ }
+ }
+
+ 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")))
+ }
+
+ 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 = TestUtils.NOT_FOUND)
+ }
+
+ it should "invoke an action receiving context properties" 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")))
+ }
+
+ 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("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)
+ 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.ApplicationError)
+ 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) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file)
+ }
+
+ 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"
+
+ wsk.action.create(name, file, web = Some(flag), update = true)
+
+ val stdout = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
+ assert(stdout.startsWith(s"ok: got action $name, displaying field annotations\n"))
+ removeCLIHeader(stdout).parseJson shouldBe JsArray(
+ JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")),
+ JsObject("key" -> JsString("web-export"), "value" -> JsBoolean(webEnabled || rawEnabled)),
+ JsObject("key" -> JsString("raw-http"), "value" -> JsBoolean(rawEnabled)),
+ JsObject("key" -> JsString("final"), "value" -> JsBoolean(webEnabled || rawEnabled)))
+ }
+ }
+
+ it should "ensure action update with --web flag only copies existing annotations when new annotations are not provided" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ val createKey = "createKey"
+ val createValue = JsString("createValue")
+ val updateKey = "updateKey"
+ val updateValue = JsString("updateValue")
+ val origKey = "origKey"
+ val origValue = JsString("origValue")
+ val overwrittenValue = JsString("overwrittenValue")
+ val createAnnots = Map(createKey -> createValue, origKey -> origValue)
+ val updateAnnots = Map(updateKey -> updateValue, origKey -> overwrittenValue)
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, annotations = createAnnots)
+ }
+
+ wsk.action.create(name, file, web = Some("true"), update = true)
+
+ val existinAnnots = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
+ assert(existinAnnots.startsWith(s"ok: got action $name, displaying field annotations\n"))
+ removeCLIHeader(existinAnnots).parseJson shouldBe JsArray(
+ JsObject("key" -> JsString("web-export"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString(origKey), "value" -> origValue),
+ JsObject("key" -> JsString("raw-http"), "value" -> JsBoolean(false)),
+ JsObject("key" -> JsString("final"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString(createKey), "value" -> createValue),
+ JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
+
+ wsk.action.create(name, file, web = Some("true"), update = true, annotations = updateAnnots)
+
+ val updatedAnnots = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
+ assert(updatedAnnots.startsWith(s"ok: got action $name, displaying field annotations\n"))
+ removeCLIHeader(updatedAnnots).parseJson shouldBe JsArray(
+ JsObject("key" -> JsString("web-export"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString(origKey), "value" -> overwrittenValue),
+ JsObject("key" -> JsString(updateKey), "value" -> updateValue),
+ JsObject("key" -> JsString("raw-http"), "value" -> JsBoolean(false)),
+ JsObject("key" -> JsString("final"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
+ }
+
+ it should "ensure action update creates an action with --web flag" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ 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)
+ }
+
+ val stdout = wsk.action.get(name, fieldFilter = Some("annotations")).stdout
+ assert(stdout.startsWith(s"ok: got action $name, displaying field annotations\n"))
+ removeCLIHeader(stdout).parseJson shouldBe JsArray(
+ JsObject("key" -> JsString("web-export"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString("raw-http"), "value" -> JsBoolean(false)),
+ JsObject("key" -> JsString("final"), "value" -> JsBoolean(true)),
+ JsObject("key" -> JsString("exec"), "value" -> JsString("nodejs:6")))
+ }
+
+ it should "reject action create and update with invalid web flag input" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ val invalidInput = "bogus"
+ val errorMsg =
+ s"Invalid argument '$invalidInput' for --web flag. Valid input consist of 'yes', 'true', 'raw', 'false', or 'no'."
+ wsk.action.create(name, file, web = Some(invalidInput), expectedExitCode = MISUSE_EXIT).stderr should include(
+ errorMsg)
+ wsk.action
+ .create(name, file, web = Some(invalidInput), update = true, expectedExitCode = MISUSE_EXIT)
+ .stderr should include(errorMsg)
+ }
+
+ 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
+ }
+ }
+
+ it should "get an action URL" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val actionName = "action name@_-."
+ val packageName = "package name@_-."
+ val defaultPackageName = "default"
+ val webActionName = "web action name@_-."
+ val nonExistentActionName = "non-existence action"
+ val packagedAction = s"$packageName/$actionName"
+ val packagedWebAction = s"$packageName/$webActionName"
+ val namespace = wsk.namespace.whois()
+ val encodedActionName = URLEncoder.encode(actionName, StandardCharsets.UTF_8.name).replace("+", "%20")
+ val encodedPackageName = URLEncoder.encode(packageName, StandardCharsets.UTF_8.name).replace("+", "%20")
+ val encodedWebActionName = URLEncoder.encode(webActionName, StandardCharsets.UTF_8.name).replace("+", "%20")
+ val encodedNamespace = URLEncoder.encode(namespace, StandardCharsets.UTF_8.name).replace("+", "%20")
+ val actionPath = "https://%s/api/%s/namespaces/%s/actions/%s"
+ val packagedActionPath = s"$actionPath/%s"
+ val webActionPath = "https://%s/api/%s/web/%s/%s/%s"
+
+ assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+ action.create(actionName, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.action, webActionName) { (action, _) =>
+ action.create(webActionName, defaultAction, web = Some("true"))
+ }
+
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName)
+ }
+
+ assetHelper.withCleaner(wsk.action, packagedAction) { (action, _) =>
+ action.create(packagedAction, defaultAction)
+ }
+
+ assetHelper.withCleaner(wsk.action, packagedWebAction) { (action, _) =>
+ action.create(packagedWebAction, defaultAction, web = Some("true"))
+ }
+
+ wsk.action.get(actionName, url = Some(true)).stdout should include(
+ actionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedActionName))
+
+ // Ensure url flag works when a field filter and summary flag are specified
+ wsk.action.get(actionName, url = Some(true), fieldFilter = Some("field"), summary = true).stdout should include(
+ actionPath.format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedActionName))
+
+ wsk.action.get(webActionName, url = Some(true)).stdout should include(
+ webActionPath
+ .format(wskprops.apihost, wskprops.apiversion, encodedNamespace, defaultPackageName, encodedWebActionName))
+
+ wsk.action.get(packagedAction, url = Some(true)).stdout should include(
+ packagedActionPath
+ .format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedPackageName, encodedActionName))
+
+ wsk.action.get(packagedWebAction, url = Some(true)).stdout should include(
+ webActionPath
+ .format(wskprops.apihost, wskprops.apiversion, encodedNamespace, encodedPackageName, encodedWebActionName))
+
+ wsk.action.get(nonExistentActionName, url = Some(true), expectedExitCode = NOT_FOUND)
+ }
+ it should "limit length of HTTP request and response bodies for --verbose" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "limitVerbose"
+ val msg = "will be truncated"
+ val params = Seq("-p", "bigValue", "a" * 1000)
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("echo.js")))
+ }
+
+ val truncated = wsk
+ .cli(Seq("action", "invoke", name, "-b", "-v", "--auth", wskprops.authKey) ++ params ++ wskprops.overrides)
+ .stdout
+ msg.r.findAllIn(truncated).length shouldBe 2
+
+ val notTruncated = wsk
+ .cli(Seq("action", "invoke", name, "-b", "-d", "--auth", wskprops.authKey) ++ params ++ wskprops.overrides)
+ .stdout
+ msg.r.findAllIn(notTruncated).length shouldBe 0
+ }
+
+ it should "denote bound and finalized action parameters for action summaries" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val nameBoundParams = "actionBoundParams"
+ val nameFinalParams = "actionFinalParams"
+ val paramAnnot = "paramAnnot"
+ val paramOverlap = "paramOverlap"
+ val paramBound = "paramBound"
+ val annots = Map(
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString(paramAnnot), "description" -> JsString("Annotated")),
+ JsObject("name" -> JsString(paramOverlap), "description" -> JsString("Annotated And Bound"))))
+ val annotsFinal = Map(
+ "final" -> JsBoolean(true),
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString(paramAnnot), "description" -> JsString("Annotated Parameter description")),
+ JsObject("name" -> JsString(paramOverlap), "description" -> JsString("Annotated And Bound"))))
+ val paramsBound = Map(paramBound -> JsString("Bound"), paramOverlap -> JsString("Bound And Annotated"))
+
+ assetHelper.withCleaner(wsk.action, nameBoundParams) { (action, _) =>
+ action.create(nameBoundParams, defaultAction, annotations = annots, parameters = paramsBound)
+ }
+ assetHelper.withCleaner(wsk.action, nameFinalParams) { (action, _) =>
+ action.create(nameFinalParams, defaultAction, annotations = annotsFinal, parameters = paramsBound)
+ }
+
+ val stdoutBound = wsk.action.get(nameBoundParams, summary = true).stdout
+ val stdoutFinal = wsk.action.get(nameFinalParams, summary = true).stdout
+
+ stdoutBound should include(s"(parameters: $paramAnnot, *$paramBound, *$paramOverlap)")
+ stdoutFinal should include(s"(parameters: $paramAnnot, **$paramBound, **$paramOverlap)")
+ }
+
+ it should "create, and get an action summary without a description and/or defined parameters" in withAssetCleaner(
+ wskprops) { (wp, assetHelper) =>
+ val actNameNoParams = "actionNoParams"
+ val actNameNoDesc = "actionNoDesc"
+ val actNameNoDescOrParams = "actionNoDescOrParams"
+ val desc = "Action description"
+ val descFromParamsResp = "Returns a result based on parameters"
+ val annotsNoParams = Map("description" -> JsString(desc))
+ val annotsNoDesc = Map(
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+
+ assetHelper.withCleaner(wsk.action, actNameNoDesc) { (action, _) =>
+ action.create(actNameNoDesc, defaultAction, annotations = annotsNoDesc)
+ }
+ assetHelper.withCleaner(wsk.action, actNameNoParams) { (action, _) =>
+ action.create(actNameNoParams, defaultAction, annotations = annotsNoParams)
+ }
+ assetHelper.withCleaner(wsk.action, actNameNoDescOrParams) { (action, _) =>
+ action.create(actNameNoDescOrParams, defaultAction)
+ }
+
+ val stdoutNoDesc = wsk.action.get(actNameNoDesc, summary = true).stdout
+ val stdoutNoParams = wsk.action.get(actNameNoParams, summary = true).stdout
+ val stdoutNoDescOrParams = wsk.action.get(actNameNoDescOrParams, summary = true).stdout
+ val namespace = wsk.namespace.whois()
+
+ stdoutNoDesc should include regex (s"(?i)action /${namespace}/${actNameNoDesc}: ${descFromParamsResp} paramName1 and paramName2\\s*\\(parameters: paramName1, paramName2\\)")
+ stdoutNoParams should include regex (s"(?i)action /${namespace}/${actNameNoParams}: ${desc}\\s*\\(parameters: none defined\\)")
+ stdoutNoDescOrParams should include regex (s"(?i)action /${namespace}/${actNameNoDescOrParams}\\s*\\(parameters: none defined\\)")
+ }
+
+ behavior of "Wsk packages"
+
+ it should "create, and delete a package" in {
+ val name = "createDeletePackage"
+ wsk.pkg.create(name).stdout should include(s"ok: created package $name")
+ wsk.pkg.delete(name).stdout should include(s"ok: deleted package $name")
+ }
+
+ 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 stdout = wsk.pkg.get(name).stdout
+ assert(stdout.startsWith(s"ok: got package $name\n"))
+
+ 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 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
+ assert(stdout.startsWith(s"ok: got package $name\n"))
+
+ 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 stdout = wsk.pkg.get(name).stdout
+ assert(stdout.startsWith(s"ok: got package $name\n"))
+
+ 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 "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).stderr should include(Messages.conformanceMessage)
+
+ wsk.pkg.bind(name, "bogus", expectedExitCode = CONFLICT).stderr should include(Messages.requestedBindingIsNotValid)
+
+ wsk.pkg.bind("bogus", "alsobogus", expectedExitCode = BAD_REQUEST).stderr should include(
+ Messages.bindingDoesNotExist)
+
+ }
+
+ it should "create, and get a package summary without a description and/or parameters" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val pkgNoDesc = "pkgNoDesc"
+ val pkgNoParams = "pkgNoParams"
+ val pkgNoDescOrParams = "pkgNoDescOrParams"
+ val pkgDesc = "Package description"
+ val descFromParams = "Returns a result based on parameters"
+ val namespace = wsk.namespace.whois()
+ val qualpkgNoDesc = s"/${namespace}/${pkgNoDesc}"
+ val qualpkgNoParams = s"/${namespace}/${pkgNoParams}"
+ val qualpkgNoDescOrParams = s"/${namespace}/${pkgNoDescOrParams}"
+
+ val pkgAnnotsNoParams = Map("description" -> JsString(pkgDesc))
+ val pkgAnnotsNoDesc = Map(
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+
+ assetHelper.withCleaner(wsk.pkg, pkgNoDesc) { (pkg, _) =>
+ pkg.create(pkgNoDesc, annotations = pkgAnnotsNoDesc)
+ }
+ assetHelper.withCleaner(wsk.pkg, pkgNoParams) { (pkg, _) =>
+ pkg.create(pkgNoParams, annotations = pkgAnnotsNoParams)
+ }
+ assetHelper.withCleaner(wsk.pkg, pkgNoDescOrParams) { (pkg, _) =>
+ pkg.create(pkgNoDescOrParams)
+ }
+
+ val stdoutNoDescPkg = wsk.pkg.get(pkgNoDesc, summary = true).stdout
+ val stdoutNoParamsPkg = wsk.pkg.get(pkgNoParams, summary = true).stdout
+ val stdoutNoDescOrParams = wsk.pkg.get(pkgNoDescOrParams, summary = true).stdout
+
+ stdoutNoDescPkg should include regex (s"(?i)package ${qualpkgNoDesc}: ${descFromParams} paramName1 and paramName2\\s*\\(parameters: paramName1, paramName2\\)")
+ stdoutNoParamsPkg should include regex (s"(?i)package ${qualpkgNoParams}: ${pkgDesc}\\s*\\(parameters: none defined\\)")
+ stdoutNoDescOrParams should include regex (s"(?i)package ${qualpkgNoDescOrParams}\\s*\\(parameters: none defined\\)")
+ }
+
+ it should "denote bound package parameters for package summaries" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val pkgBoundParams = "pkgBoundParams"
+ val pkgParamAnnot = "pkgParamAnnot"
+ val pkgParamOverlap = "pkgParamOverlap"
+ val pkgParamBound = "pkgParamBound"
+ val pkgAnnots = Map(
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString(pkgParamAnnot), "description" -> JsString("Annotated")),
+ JsObject("name" -> JsString(pkgParamOverlap), "description" -> JsString("Annotated And Bound"))))
+ val pkgParamsBound = Map(pkgParamBound -> JsString("Bound"), pkgParamOverlap -> JsString("Bound And Annotated"))
+
+ assetHelper.withCleaner(wsk.pkg, pkgBoundParams) { (pkg, _) =>
+ pkg.create(pkgBoundParams, annotations = pkgAnnots, parameters = pkgParamsBound)
+ }
+
+ val pkgStdoutBound = wsk.pkg.get(pkgBoundParams, summary = true).stdout
+
+ pkgStdoutBound should include(s"(parameters: $pkgParamAnnot, *$pkgParamBound, *$pkgParamOverlap)")
+ }
+
+ 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
+ assert(stdout.startsWith(s"ok: got trigger $name\n"))
+
+ 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
+ assert(stdout.startsWith(s"ok: got trigger $name\n"))
+
+ 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 "display a trigger summary when --summary flag is used with 'wsk trigger get'" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName = "mySummaryTrigger"
+ assetHelper.withCleaner(wsk.trigger, triggerName, confirmDelete = false) { (trigger, name) =>
+ trigger.create(name)
+ }
+
+ // Summary namespace should match one of the allowable namespaces (typically 'guest')
+ val ns = wsk.namespace.whois()
+ val stdout = wsk.trigger.get(triggerName, summary = true).stdout
+ stdout should include regex (s"(?i)trigger\\s+/$ns/$triggerName")
+ }
+
+ 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
+ assert(stdout.startsWith(s"ok: got trigger $name\n"))
+
+ 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 = NOT_FOUND)
+
+ trigger.create(name, feed = Some(s"bogus/feed"), expectedExitCode = ANY_ERROR_EXIT).exitCode should equal(
+ NOT_FOUND)
+ trigger.get(name, expectedExitCode = NOT_FOUND)
+ }
+ }
+
+ it should "denote bound trigger parameters for trigger summaries" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val trgBoundParams = "trgBoundParams"
+ val trgParamAnnot = "trgParamAnnot"
+ val trgParamOverlap = "trgParamOverlap"
+ val trgParamBound = "trgParamBound"
+ val trgAnnots = Map(
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString(trgParamAnnot), "description" -> JsString("Annotated")),
+ JsObject("name" -> JsString(trgParamOverlap), "description" -> JsString("Annotated And Bound"))))
+ val trgParamsBound = Map(trgParamBound -> JsString("Bound"), trgParamOverlap -> JsString("Bound And Annotated"))
+
+ assetHelper.withCleaner(wsk.trigger, trgBoundParams) { (trigger, _) =>
+ trigger.create(trgBoundParams, annotations = trgAnnots, parameters = trgParamsBound)
+ }
+
+ val trgStdoutBound = wsk.trigger.get(trgBoundParams, summary = true).stdout
+
+ trgStdoutBound should include(s"(parameters: $trgParamAnnot, *$trgParamBound, *$trgParamOverlap)")
+ }
+
+ it should "create, and get a trigger summary without a description and/or parameters" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val trgNoDesc = "trgNoDesc"
+ val trgNoParams = "trgNoParams"
+ val trgNoDescOrParams = "trgNoDescOrParams"
+ val trgDesc = "Package description"
+ val descFromParams = "Returns a result based on parameters"
+ val namespace = wsk.namespace.whois()
+ val qualtrgNoDesc = s"/${namespace}/${trgNoDesc}"
+ val qualtrgNoParams = s"/${namespace}/${trgNoParams}"
+ val qualtrgNoDescOrParams = s"/${namespace}/${trgNoDescOrParams}"
+
+ val trgAnnotsNoParams = Map("description" -> JsString(trgDesc))
+ val trgAnnotsNoDesc = Map(
+ "parameters" -> JsArray(
+ JsObject("name" -> JsString("paramName1"), "description" -> JsString("Parameter description 1")),
+ JsObject("name" -> JsString("paramName2"), "description" -> JsString("Parameter description 2"))))
+
+ assetHelper.withCleaner(wsk.trigger, trgNoDesc) { (trigger, _) =>
+ trigger.create(trgNoDesc, annotations = trgAnnotsNoDesc)
+ }
+ assetHelper.withCleaner(wsk.trigger, trgNoParams) { (trigger, _) =>
+ trigger.create(trgNoParams, annotations = trgAnnotsNoParams)
+ }
+ assetHelper.withCleaner(wsk.trigger, trgNoDescOrParams) { (trigger, _) =>
+ trigger.create(trgNoDescOrParams)
+ }
+
+ val stdoutNoDescPkg = wsk.trigger.get(trgNoDesc, summary = true).stdout
+ val stdoutNoParamsPkg = wsk.trigger.get(trgNoParams, summary = true).stdout
+ val stdoutNoDescOrParams = wsk.trigger.get(trgNoDescOrParams, summary = true).stdout
+
+ stdoutNoDescPkg should include regex (s"(?i)trigger ${qualtrgNoDesc}: ${descFromParams} paramName1 and paramName2\\s*\\(parameters: paramName1, paramName2\\)")
+ stdoutNoParamsPkg should include regex (s"(?i)trigger ${qualtrgNoParams}: ${trgDesc}\\s*\\(parameters: none defined\\)")
+ stdoutNoDescOrParams should include regex (s"(?i)trigger ${qualtrgNoDescOrParams}\\s*\\(parameters: none defined\\)")
+ }
+
+ behavior of "Wsk entity list formatting"
+
+ it should "create, and list a package with a long name" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "x" * 70
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.create(name)
+ }
+ retry({
+ wsk.pkg.list().stdout should include(s"$name private")
+ }, 5, Some(1 second))
+ }
+
+ it should "create, and list an action with a long name" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "x" * 70
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file)
+ }
+ retry({
+ wsk.action.list().stdout should include(s"$name private nodejs")
+ }, 5, Some(1 second))
+ }
+
+ it should "create, and list a trigger with a long name" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "x" * 70
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ trigger.create(name)
+ }
+ retry({
+ wsk.trigger.list().stdout should include(s"$name private")
+ }, 5, Some(1 second))
+ }
+
+ it should "create, and list a rule with a long name" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val ruleName = "x" * 70
+ val triggerName = "listRulesTrigger"
+ val actionName = "listRulesAction";
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+ retry({
+ wsk.rule.list().stdout should include(s"$ruleName private")
+ }, 5, Some(1 second))
+ }
+
+ it should "return a list of alphabetized actions" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ // Declare 4 actions, create them out of alphabetical order
+ val actionName = "actionAlphaTest"
+ for (i <- 1 to 3) {
+ val name = s"$actionName$i"
+ assetHelper.withCleaner(wsk.action, name) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ }
+ retry({
+ val original = wsk.action.list(nameSort = Some(true)).stdout
+ // Create list with action names in correct order
+ val scalaSorted = List(s"${actionName}1", s"${actionName}2", s"${actionName}3")
+ // Filter out everything not previously created
+ val regex = s"${actionName}[1-3]".r
+ // Retrieve action names into list as found in original
+ val list = (regex.findAllMatchIn(original)).toList
+ scalaSorted.toString shouldEqual list.toString
+ }, 5, Some(1 second))
+ }
+
+ it should "return an alphabetized list with default package actions on top" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ // Declare 4 actions, create them out of alphabetical order
+ val actionName = "actionPackageAlphaTest"
+ val packageName = "packageAlphaTest"
+ assetHelper.withCleaner(wsk.action, actionName) { (action, actionName) =>
+ action.create(actionName, defaultAction)
+ }
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, packageName) =>
+ pkg.create(packageName)
+ }
+ for (i <- 1 to 3) {
+ val name = s"${packageName}/${actionName}$i"
+ assetHelper.withCleaner(wsk.action, name) { (action, name) =>
+ action.create(name, defaultAction)
+>>>>>>> c10e681... Apply standard scala formatting. (#2650)
}
- }
-
- it should "reject commands that are executed with not enough param or annot arguments" in {
- val invalidParamMsg = "Arguments for '-p' must be a key/value pair"
- val invalidAnnotMsg = "Arguments for '-a' must be a key/value pair"
- val invalidParamFileMsg = "An argument must be provided for '-P'"
- val invalidAnnotFileMsg = "An argument must be provided for '-A'"
- val invalidArgs = Seq(
- (Seq("action", "create", "actionName", "-p"), invalidParamMsg),
- (Seq("action", "create", "actionName", "-p", "key"), invalidParamMsg),
- (Seq("action", "create", "actionName", "-P"), invalidParamFileMsg),
- (Seq("action", "update", "actionName", "-p"), invalidParamMsg),
- (Seq("action", "update", "actionName", "-p", "key"), invalidParamMsg),
- (Seq("action", "update", "actionName", "-P"), invalidParamFileMsg),
- (Seq("action", "invoke", "actionName", "-p"), invalidParamMsg),
- (Seq("action", "invoke", "actionName", "-p", "key"), invalidParamMsg),
- (Seq("action", "invoke", "actionName", "-P"), invalidParamFileMsg),
- (Seq("action", "create", "actionName", "-a"), invalidAnnotMsg),
- (Seq("action", "create", "actionName", "-a", "key"), invalidAnnotMsg),
- (Seq("action", "create", "actionName", "-A"), invalidAnnotFileMsg),
- (Seq("action", "update", "actionName", "-a"), invalidAnnotMsg),
- (Seq("action", "update", "actionName", "-a", "key"), invalidAnnotMsg),
- (Seq("action", "update", "actionName", "-A"), invalidAnnotFileMsg),
- (Seq("action", "invoke", "actionName", "-a"), invalidAnnotMsg),
- (Seq("action", "invoke", "actionName", "-a", "key"), invalidAnnotMsg),
- (Seq("action", "invoke", "actionName", "-A"), invalidAnnotFileMsg),
- (Seq("package", "create", "packageName", "-p"), invalidParamMsg),
- (Seq("package", "create", "packageName", "-p", "key"), invalidParamMsg),
- (Seq("package", "create", "packageName", "-P"), invalidParamFileMsg),
- (Seq("package", "update", "packageName", "-p"), invalidParamMsg),
- (Seq("package", "update", "packageName", "-p", "key"), invalidParamMsg),
- (Seq("package", "update", "packageName", "-P"), invalidParamFileMsg),
- (Seq("package", "bind", "packageName", "boundPackageName", "-p"), invalidParamMsg),
- (Seq("package", "bind", "packageName", "boundPackageName", "-p", "key"), invalidParamMsg),
- (Seq("package", "bind", "packageName", "boundPackageName", "-P"), invalidParamFileMsg),
- (Seq("package", "create", "packageName", "-a"), invalidAnnotMsg),
- (Seq("package", "create", "packageName", "-a", "key"), invalidAnnotMsg),
- (Seq("package", "create", "packageName", "-A"), invalidAnnotFileMsg),
- (Seq("package", "update", "packageName", "-a"), invalidAnnotMsg),
- (Seq("package", "update", "packageName", "-a", "key"), invalidAnnotMsg),
- (Seq("package", "update", "packageName", "-A"), invalidAnnotFileMsg),
- (Seq("package", "bind", "packageName", "boundPackageName", "-a"), invalidAnnotMsg),
- (Seq("package", "bind", "packageName", "boundPackageName", "-a", "key"), invalidAnnotMsg),
- (Seq("package", "bind", "packageName", "boundPackageName", "-A"), invalidAnnotFileMsg),
- (Seq("trigger", "create", "triggerName", "-p"), invalidParamMsg),
- (Seq("trigger", "create", "triggerName", "-p", "key"), invalidParamMsg),
- (Seq("trigger", "create", "triggerName", "-P"), invalidParamFileMsg),
- (Seq("trigger", "update", "triggerName", "-p"), invalidParamMsg),
- (Seq("trigger", "update", "triggerName", "-p", "key"), invalidParamMsg),
- (Seq("trigger", "update", "triggerName", "-P"), invalidParamFileMsg),
- (Seq("trigger", "fire", "triggerName", "-p"), invalidParamMsg),
- (Seq("trigger", "fire", "triggerName", "-p", "key"), invalidParamMsg),
- (Seq("trigger", "fire", "triggerName", "-P"), invalidParamFileMsg),
- (Seq("trigger", "create", "triggerName", "-a"), invalidAnnotMsg),
- (Seq("trigger", "create", "triggerName", "-a", "key"), invalidAnnotMsg),
- (Seq("trigger", "create", "triggerName", "-A"), invalidAnnotFileMsg),
- (Seq("trigger", "update", "triggerName", "-a"), invalidAnnotMsg),
- (Seq("trigger", "update", "triggerName", "-a", "key"), invalidAnnotMsg),
- (Seq("trigger", "update", "triggerName", "-A"), invalidAnnotFileMsg),
- (Seq("trigger", "fire", "triggerName", "-a"), invalidAnnotMsg),
- (Seq("trigger", "fire", "triggerName", "-a", "key"), invalidAnnotMsg),
- (Seq("trigger", "fire", "triggerName", "-A"), invalidAnnotFileMsg))
-
- invalidArgs foreach {
- case (cmd, err) =>
- val stderr = wsk.cli(cmd, expectedExitCode = ERROR_EXIT).stderr
- stderr should include(err)
- stderr should include("Run 'wsk --help' for usage.")
+ }
+ retry(
+ {
+ val original = wsk.action.list(nameSort = Some(true)).stdout
+ // Create list with action names in correct order
+ val scalaSorted = List(
+ s"$actionName",
+ s"${packageName}/${actionName}1",
+ s"${packageName}/${actionName}2",
+ s"${packageName}/${actionName}3")
+ // Filter out everything not previously created
+ val regexNoPackage = s"$actionName".r
+ val regexWithPackage = s"${packageName}/${actionName}[1-3]".r
+ // Retrieve action names into list as found in original
+ val list = regexNoPackage.findFirstIn(original).get :: (regexWithPackage.findAllMatchIn(original)).toList
+ scalaSorted.toString shouldEqual list.toString
+ },
+ 5,
+ Some(1 second))
+ }
+
+ it should "return a list of alphabetized packages" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ // Declare 3 packages, create them out of alphabetical order
+ val packageName = "pkgAlphaTest"
+ for (i <- 1 to 3) {
+ val name = s"$packageName$i"
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, name) =>
+ pkg.create(name)
+ }
+ }
+ retry({
+ val original = wsk.pkg.list(nameSort = Some(true)).stdout
+ // Create list with package names in correct order
+ val scalaSorted = List(s"${packageName}1", s"${packageName}2", s"${packageName}3")
+ // Filter out everything not previously created
+ val regex = s"${packageName}[1-3]".r
+ // Retrieve package names into list as found in original
+ val list = (regex.findAllMatchIn(original)).toList
+ scalaSorted.toString shouldEqual list.toString
+ }, 5, Some(1 second))
+ }
+
+ it should "return a list of alphabetized triggers" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ // Declare 4 triggers, create them out of alphabetical order
+ val triggerName = "triggerAlphaTest"
+ for (i <- 1 to 3) {
+ val name = s"$triggerName$i"
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, name) =>
+ trigger.create(name)
+ }
+ }
+ retry({
+ val original = wsk.trigger.list(nameSort = Some(true)).stdout
+ // Create list with trigger names in correct order
+ val scalaSorted = List(s"${triggerName}1", s"${triggerName}2", s"${triggerName}3")
+ // Filter out everything not previously created
+ val regex = s"${triggerName}[1-3]".r
+ // Retrieve trigger names into list as found in original
+ val list = (regex.findAllMatchIn(original)).toList
+ scalaSorted.toString shouldEqual list.toString
+ }, 5, Some(1 second))
+ }
+
+ it should "return a list of alphabetized rules" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ // Declare a trigger and an action for the purposes of creating rules
+ val triggerName = "listRulesTrigger"
+ val actionName = "listRulesAction"
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) { (trigger, name) =>
+ trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
+ action.create(name, defaultAction)
+ }
+ // Declare 3 rules, create them out of alphabetical order
+ val ruleName = "ruleAlphaTest"
+ for (i <- 1 to 3) {
+ val name = s"$ruleName$i"
+ assetHelper.withCleaner(wsk.rule, name) { (rule, name) =>
+ rule.create(name, trigger = triggerName, action = actionName)
+ }
+ }
+ retry({
+ val original = wsk.rule.list(nameSort = Some(true)).stdout
+ // Create list with rule names in correct order
+ val scalaSorted = List(s"${ruleName}1", s"${ruleName}2", s"${ruleName}3")
+ // Filter out everything not previously created
+ val regex = s"${ruleName}[1-3]".r
+ // Retrieve rule names into list as found in original
+ val list = (regex.findAllMatchIn(original)).toList
+ scalaSorted.toString shouldEqual list.toString
+ })
+ }
+
+ behavior of "Wsk params and annotations"
+
+ it should "reject commands that are executed with invalid JSON for annotations and parameters" in {
+ val invalidJSONInputs = getInvalidJSONInput
+ val invalidJSONFiles = Seq(
+ TestUtils.getTestActionFilename("malformed.js"),
+ TestUtils.getTestActionFilename("invalidInput1.json"),
+ TestUtils.getTestActionFilename("invalidInput2.json"),
+ TestUtils.getTestActionFilename("invalidInput3.json"),
+ TestUtils.getTestActionFilename("invalidInput4.json"))
+ val paramCmds = Seq(
+ Seq("action", "create", "actionName", TestUtils.getTestActionFilename("hello.js")),
+ Seq("action", "update", "actionName", TestUtils.getTestActionFilename("hello.js")),
+ Seq("action", "invoke", "actionName"),
+ Seq("package", "create", "packageName"),
+ Seq("package", "update", "packageName"),
+ Seq("package", "bind", "packageName", "boundPackageName"),
+ Seq("trigger", "create", "triggerName"),
+ Seq("trigger", "update", "triggerName"),
+ Seq("trigger", "fire", "triggerName"))
+ val annotCmds = Seq(
+ Seq("action", "create", "actionName", TestUtils.getTestActionFilename("hello.js")),
+ Seq("action", "update", "actionName", TestUtils.getTestActionFilename("hello.js")),
+ Seq("package", "create", "packageName"),
+ Seq("package", "update", "packageName"),
+ Seq("package", "bind", "packageName", "boundPackageName"),
+ Seq("trigger", "create", "triggerName"),
+ Seq("trigger", "update", "triggerName"))
+
+ for (cmd <- paramCmds) {
+ for (invalid <- invalidJSONInputs) {
+ wsk
+ .cli(cmd ++ Seq("-p", "key", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT)
+ .stderr should include("Invalid parameter argument")
+ }
+
+ for (invalid <- invalidJSONFiles) {
+ wsk.cli(cmd ++ Seq("-P", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT).stderr should include(
+ "Invalid parameter argument")
+
+ }
+ }
+
+ for (cmd <- annotCmds) {
+ for (invalid <- invalidJSONInputs) {
+ wsk
+ .cli(cmd ++ Seq("-a", "key", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT)
+ .stderr should include("Invalid annotation argument")
+ }
+
+ for (invalid <- invalidJSONFiles) {
+ wsk.cli(cmd ++ Seq("-A", invalid) ++ wskprops.overrides, expectedExitCode = ERROR_EXIT).stderr should include(
+ "Invalid annotation argument")
+ }
+ }
+ }
+
+ it should "reject commands that are executed with a missing or invalid parameter or annotation file" in {
+ val emptyFile = TestUtils.getTestActionFilename("emtpy.js")
+ val missingFile = "notafile"
+ val emptyFileMsg = s"File '$emptyFile' is not a valid file or it does not exist"
+ val missingFileMsg = s"File '$missingFile' is not a valid file or it does not exist"
+ val invalidArgs = Seq(
+ (
+ Seq("action", "create", "actionName", TestUtils.getTestActionFilename("hello.js"), "-P", emptyFile),
+ emptyFileMsg),
+ (
+ Seq("action", "update", "actionName", TestUtils.getTestActionFilename("hello.js"), "-P", emptyFile),
+ emptyFileMsg),
+ (Seq("action", "invoke", "actionName", "-P", emptyFile), emptyFileMsg),
+ (Seq("action", "create", "actionName", "-P", emptyFile), emptyFileMsg),
+ (Seq("action", "update", "actionName", "-P", emptyFile), emptyFileMsg),
+ (Seq("action", "invoke", "actionName", "-P", emptyFile), emptyFileMsg),
+ (Seq("package", "create", "packageName", "-P", emptyFile), emptyFileMsg),
+ (Seq("package", "update", "packageName", "-P", emptyFile), emptyFileMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-P", emptyFile), emptyFileMsg),
+ (Seq("package", "create", "packageName", "-P", emptyFile), emptyFileMsg),
+ (Seq("package", "update", "packageName", "-P", emptyFile), emptyFileMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-P", emptyFile), emptyFileMsg),
+ (Seq("trigger", "create", "triggerName", "-P", emptyFile), emptyFileMsg),
+ (Seq("trigger", "update", "triggerName", "-P", emptyFile), emptyFileMsg),
+ (Seq("trigger", "fire", "triggerName", "-P", emptyFile), emptyFileMsg),
+ (Seq("trigger", "create", "triggerName", "-P", emptyFile), emptyFileMsg),
+ (Seq("trigger", "update", "triggerName", "-P", emptyFile), emptyFileMsg),
+ (Seq("trigger", "fire", "triggerName", "-P", emptyFile), emptyFileMsg),
+ (
+ Seq("action", "create", "actionName", TestUtils.getTestActionFilename("hello.js"), "-A", missingFile),
+ missingFileMsg),
+ (
+ Seq("action", "update", "actionName", TestUtils.getTestActionFilename("hello.js"), "-A", missingFile),
+ missingFileMsg),
+ (Seq("action", "invoke", "actionName", "-A", missingFile), missingFileMsg),
+ (Seq("action", "create", "actionName", "-A", missingFile), missingFileMsg),
+ (Seq("action", "update", "actionName", "-A", missingFile), missingFileMsg),
+ (Seq("action", "invoke", "actionName", "-A", missingFile), missingFileMsg),
+ (Seq("package", "create", "packageName", "-A", missingFile), missingFileMsg),
+ (Seq("package", "update", "packageName", "-A", missingFile), missingFileMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-A", missingFile), missingFileMsg),
+ (Seq("package", "create", "packageName", "-A", missingFile), missingFileMsg),
+ (Seq("package", "update", "packageName", "-A", missingFile), missingFileMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-A", missingFile), missingFileMsg),
+ (Seq("trigger", "create", "triggerName", "-A", missingFile), missingFileMsg),
+ (Seq("trigger", "update", "triggerName", "-A", missingFile), missingFileMsg),
+ (Seq("trigger", "fire", "triggerName", "-A", missingFile), missingFileMsg),
+ (Seq("trigger", "create", "triggerName", "-A", missingFile), missingFileMsg),
+ (Seq("trigger", "update", "triggerName", "-A", missingFile), missingFileMsg),
+ (Seq("trigger", "fire", "triggerName", "-A", missingFile), missingFileMsg))
+
+ invalidArgs foreach {
+ case (cmd, err) =>
+ val stderr = wsk.cli(cmd, expectedExitCode = MISUSE_EXIT).stderr
+ stderr should include(err)
+ stderr should include("Run 'wsk --help' for usage.")
+ }
+ }
+
+ it should "reject commands that are executed with not enough param or annot arguments" in {
+ val invalidParamMsg = "Arguments for '-p' must be a key/value pair"
+ val invalidAnnotMsg = "Arguments for '-a' must be a key/value pair"
+ val invalidParamFileMsg = "An argument must be provided for '-P'"
+ val invalidAnnotFileMsg = "An argument must be provided for '-A'"
+ val invalidArgs = Seq(
+ (Seq("action", "create", "actionName", "-p"), invalidParamMsg),
+ (Seq("action", "create", "actionName", "-p", "key"), invalidParamMsg),
+ (Seq("action", "create", "actionName", "-P"), invalidParamFileMsg),
+ (Seq("action", "update", "actionName", "-p"), invalidParamMsg),
+ (Seq("action", "update", "actionName", "-p", "key"), invalidParamMsg),
+ (Seq("action", "update", "actionName", "-P"), invalidParamFileMsg),
+ (Seq("action", "invoke", "actionName", "-p"), invalidParamMsg),
+ (Seq("action", "invoke", "actionName", "-p", "key"), invalidParamMsg),
+ (Seq("action", "invoke", "actionName", "-P"), invalidParamFileMsg),
+ (Seq("action", "create", "actionName", "-a"), invalidAnnotMsg),
+ (Seq("action", "create", "actionName", "-a", "key"), invalidAnnotMsg),
+ (Seq("action", "create", "actionName", "-A"), invalidAnnotFileMsg),
+ (Seq("action", "update", "actionName", "-a"), invalidAnnotMsg),
+ (Seq("action", "update", "actionName", "-a", "key"), invalidAnnotMsg),
+ (Seq("action", "update", "actionName", "-A"), invalidAnnotFileMsg),
+ (Seq("action", "invoke", "actionName", "-a"), invalidAnnotMsg),
+ (Seq("action", "invoke", "actionName", "-a", "key"), invalidAnnotMsg),
+ (Seq("action", "invoke", "actionName", "-A"), invalidAnnotFileMsg),
+ (Seq("package", "create", "packageName", "-p"), invalidParamMsg),
+ (Seq("package", "create", "packageName", "-p", "key"), invalidParamMsg),
+ (Seq("package", "create", "packageName", "-P"), invalidParamFileMsg),
+ (Seq("package", "update", "packageName", "-p"), invalidParamMsg),
+ (Seq("package", "update", "packageName", "-p", "key"), invalidParamMsg),
+ (Seq("package", "update", "packageName", "-P"), invalidParamFileMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-p"), invalidParamMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-p", "key"), invalidParamMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-P"), invalidParamFileMsg),
+ (Seq("package", "create", "packageName", "-a"), invalidAnnotMsg),
+ (Seq("package", "create", "packageName", "-a", "key"), invalidAnnotMsg),
+ (Seq("package", "create", "packageName", "-A"), invalidAnnotFileMsg),
+ (Seq("package", "update", "packageName", "-a"), invalidAnnotMsg),
+ (Seq("package", "update", "packageName", "-a", "key"), invalidAnnotMsg),
+ (Seq("package", "update", "packageName", "-A"), invalidAnnotFileMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-a"), invalidAnnotMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-a", "key"), invalidAnnotMsg),
+ (Seq("package", "bind", "packageName", "boundPackageName", "-A"), invalidAnnotFileMsg),
+ (Seq("trigger", "create", "triggerName", "-p"), invalidParamMsg),
+ (Seq("trigger", "create", "triggerName", "-p", "key"), invalidParamMsg),
+ (Seq("trigger", "create", "triggerName", "-P"), invalidParamFileMsg),
+ (Seq("trigger", "update", "triggerName", "-p"), invalidParamMsg),
+ (Seq("trigger", "update", "triggerName", "-p", "key"), invalidParamMsg),
+ (Seq("trigger", "update", "triggerName", "-P"), invalidParamFileMsg),
+ (Seq("trigger", "fire", "triggerName", "-p"), invalidParamMsg),
+ (Seq("trigger", "fire", "triggerName", "-p", "key"), invalidParamMsg),
+ (Seq("trigger", "fire", "triggerName", "-P"), invalidParamFileMsg),
+ (Seq("trigger", "create", "triggerName", "-a"), invalidAnnotMsg),
+ (Seq("trigger", "create", "triggerName", "-a", "key"), invalidAnnotMsg),
+ (Seq("trigger", "create", "triggerName", "-A"), invalidAnnotFileMsg),
+ (Seq("trigger", "update", "triggerName", "-a"), invalidAnnotMsg),
+ (Seq("trigger", "update", "triggerName", "-a", "key"), invalidAnnotMsg),
+ (Seq("trigger", "update", "triggerName", "-A"), invalidAnnotFileMsg),
+ (Seq("trigger", "fire", "triggerName", "-a"), invalidAnnotMsg),
+ (Seq("trigger", "fire", "triggerName", "-a", "key"), invalidAnnotMsg),
+ (Seq("trigger", "fire", "triggerName", "-A"), invalidAnnotFileMsg))
+
+ invalidArgs foreach {
+ case (cmd, err) =>
+ val stderr = wsk.cli(cmd, expectedExitCode = ERROR_EXIT).stderr
+ stderr should include(err)
+ stderr should include("Run 'wsk --help' for usage.")
+ }
+ }
+
+ behavior of "Wsk invalid argument handling"
+
+ it should "reject commands that are executed with invalid arguments" in {
+ val invalidArgsMsg = "error: Invalid argument(s)"
+ val tooFewArgsMsg = invalidArgsMsg + "."
+ val tooManyArgsMsg = invalidArgsMsg + ": "
+ val actionNameActionReqMsg = "An action name and code artifact are required."
+ val actionNameReqMsg = "An action name is required."
+ val actionOptMsg = "A code artifact is optional."
+ val packageNameReqMsg = "A package name is required."
+ val packageNameBindingReqMsg = "A package name and binding name are required."
+ val ruleNameReqMsg = "A rule name is required."
+ val ruleTriggerActionReqMsg = "A rule, trigger and action name are required."
+ val activationIdReq = "An activation ID is required."
+ val triggerNameReqMsg = "A trigger name is required."
+ val optNamespaceMsg = "An optional namespace is the only valid argument."
+ val optPayloadMsg = "A payload is optional."
+ val noArgsReqMsg = "No arguments are required."
+ val invalidArg = "invalidArg"
+ val apiCreateReqMsg =
+ "Specify a swagger file or specify an API base path with an API path, an API verb, and an action name."
+ val apiGetReqMsg = "An API base path or API name is required."
+ val apiDeleteReqMsg =
+ "An API base path or API name is required. An optional API relative path and operation may also be provided."
+ val apiListReqMsg = "Optional parameters are: API base path (or API name), API relative path and operation."
+ val invalidShared = s"Cannot use value '$invalidArg' for shared"
+ val entityNameMsg =
+ s"An entity name, '$invalidArg', was provided instead of a namespace. Valid namespaces are of the following format: /NAMESPACE."
+ val invalidArgs = Seq(
+ (Seq("api", "create"), s"${tooFewArgsMsg} ${apiCreateReqMsg}"),
+ (
+ Seq("api", "create", "/basepath", "/path", "GET", "action", invalidArg),
+ s"${tooManyArgsMsg}${invalidArg}. ${apiCreateReqMsg}"),
+ (Seq("api", "get"), s"${tooFewArgsMsg} ${apiGetReqMsg}"),
+ (Seq("api", "get", "/basepath", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${apiGetReqMsg}"),
+ (Seq("api", "delete"), s"${tooFewArgsMsg} ${apiDeleteReqMsg}"),
+ (
+ Seq("api", "delete", "/basepath", "/path", "GET", invalidArg),
+ s"${tooManyArgsMsg}${invalidArg}. ${apiDeleteReqMsg}"),
+ (
+ Seq("api", "list", "/basepath", "/path", "GET", invalidArg),
+ s"${tooManyArgsMsg}${invalidArg}. ${apiListReqMsg}"),
+ (Seq("action", "create"), s"${tooFewArgsMsg} ${actionNameActionReqMsg}"),
+ (Seq("action", "create", "someAction"), s"${tooFewArgsMsg} ${actionNameActionReqMsg}"),
+ (Seq("action", "create", "actionName", "artifactName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("action", "update"), s"${tooFewArgsMsg} ${actionNameReqMsg} ${actionOptMsg}"),
+ (
+ Seq("action", "update", "actionName", "artifactName", invalidArg),
+ s"${tooManyArgsMsg}${invalidArg}. ${actionNameReqMsg} ${actionOptMsg}"),
+ (Seq("action", "delete"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
+ (Seq("action", "delete", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("action", "get"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
+ (Seq("action", "get", "actionName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("action", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("action", "invoke"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
+ (Seq("action", "invoke", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("activation", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("activation", "get"), s"${tooFewArgsMsg} ${activationIdReq}"),
+ (Seq("activation", "get", "activationID", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("activation", "logs"), s"${tooFewArgsMsg} ${activationIdReq}"),
+ (Seq("activation", "logs", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("activation", "result"), s"${tooFewArgsMsg} ${activationIdReq}"),
+ (Seq("activation", "result", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("activation", "poll", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("namespace", "list", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${noArgsReqMsg}"),
+ (Seq("namespace", "get", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("package", "create"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
+ (Seq("package", "create", "packageName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("package", "create", "packageName", "--shared", invalidArg), invalidShared),
+ (Seq("package", "update"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
+ (Seq("package", "update", "packageName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("package", "update", "packageName", "--shared", invalidArg), invalidShared),
+ (Seq("package", "get"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
+ (Seq("package", "get", "packageName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("package", "bind"), s"${tooFewArgsMsg} ${packageNameBindingReqMsg}"),
+ (Seq("package", "bind", "packageName"), s"${tooFewArgsMsg} ${packageNameBindingReqMsg}"),
+ (Seq("package", "bind", "packageName", "bindingName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("package", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("package", "list", invalidArg), entityNameMsg),
+ (Seq("package", "delete"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
+ (Seq("package", "delete", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("package", "refresh", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("package", "refresh", invalidArg), entityNameMsg),
+ (Seq("rule", "enable"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
+ (Seq("rule", "enable", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "disable"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
+ (Seq("rule", "disable", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "status"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
+ (Seq("rule", "status", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "create"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
+ (Seq("rule", "create", "ruleName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
+ (Seq("rule", "create", "ruleName", "triggerName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
+ (Seq("rule", "create", "ruleName", "triggerName", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "update"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
+ (Seq("rule", "update", "ruleName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
+ (Seq("rule", "update", "ruleName", "triggerName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
+ (Seq("rule", "update", "ruleName", "triggerName", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "get"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
+ (Seq("rule", "get", "ruleName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "delete"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
+ (Seq("rule", "delete", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("rule", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("rule", "list", invalidArg), entityNameMsg),
+ (Seq("trigger", "fire"), s"${tooFewArgsMsg} ${triggerNameReqMsg} ${optPayloadMsg}"),
+ (
+ Seq("trigger", "fire", "triggerName", "triggerPayload", invalidArg),
+ s"${tooManyArgsMsg}${invalidArg}. ${triggerNameReqMsg} ${optPayloadMsg}"),
+ (Seq("trigger", "create"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
+ (Seq("trigger", "create", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("trigger", "update"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
+ (Seq("trigger", "update", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("trigger", "get"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
+ (Seq("trigger", "get", "triggerName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("trigger", "delete"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
+ (Seq("trigger", "delete", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
+ (Seq("trigger", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
+ (Seq("trigger", "list", invalidArg), entityNameMsg))
+
+ invalidArgs foreach {
+ case (cmd, err) =>
+ withClue(cmd) {
+ val rr = wsk.cli(cmd ++ wskprops.overrides, expectedExitCode = ANY_ERROR_EXIT)
+ rr.exitCode should (be(ERROR_EXIT) or be(MISUSE_EXIT))
+ rr.stderr should include(err)
+ rr.stderr should include("Run 'wsk --help' for usage.")
}
}
-
- behavior of "Wsk invalid argument handling"
-
- it should "reject commands that are executed with invalid arguments" in {
- val invalidArgsMsg = "error: Invalid argument(s)"
- val tooFewArgsMsg = invalidArgsMsg + "."
- val tooManyArgsMsg = invalidArgsMsg + ": "
- val actionNameActionReqMsg = "An action name and code artifact are required."
- val actionNameReqMsg = "An action name is required."
- val actionOptMsg = "A code artifact is optional."
- val packageNameReqMsg = "A package name is required."
- val packageNameBindingReqMsg = "A package name and binding name are required."
- val ruleNameReqMsg = "A rule name is required."
- val ruleTriggerActionReqMsg = "A rule, trigger and action name are required."
- val activationIdReq = "An activation ID is required."
- val triggerNameReqMsg = "A trigger name is required."
- val optNamespaceMsg = "An optional namespace is the only valid argument."
- val optPayloadMsg = "A payload is optional."
- val noArgsReqMsg = "No arguments are required."
- val invalidArg = "invalidArg"
- val apiCreateReqMsg = "Specify a swagger file or specify an API base path with an API path, an API verb, and an action name."
- val apiGetReqMsg = "An API base path or API name is required."
- val apiDeleteReqMsg = "An API base path or API name is required. An optional API relative path and operation may also be provided."
- val apiListReqMsg = "Optional parameters are: API base path (or API name), API relative path and operation."
- val invalidShared = s"Cannot use value '$invalidArg' for shared"
- val entityNameMsg = s"An entity name, '$invalidArg', was provided instead of a namespace. Valid namespaces are of the following format: /NAMESPACE."
- val invalidArgs = Seq(
- (Seq("api", "create"), s"${tooFewArgsMsg} ${apiCreateReqMsg}"),
- (Seq("api", "create", "/basepath", "/path", "GET", "action", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${apiCreateReqMsg}"),
- (Seq("api", "get"), s"${tooFewArgsMsg} ${apiGetReqMsg}"),
- (Seq("api", "get", "/basepath", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${apiGetReqMsg}"),
- (Seq("api", "delete"), s"${tooFewArgsMsg} ${apiDeleteReqMsg}"),
- (Seq("api", "delete", "/basepath", "/path", "GET", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${apiDeleteReqMsg}"),
- (Seq("api", "list", "/basepath", "/path", "GET", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${apiListReqMsg}"),
- (Seq("action", "create"), s"${tooFewArgsMsg} ${actionNameActionReqMsg}"),
- (Seq("action", "create", "someAction"), s"${tooFewArgsMsg} ${actionNameActionReqMsg}"),
- (Seq("action", "create", "actionName", "artifactName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("action", "update"), s"${tooFewArgsMsg} ${actionNameReqMsg} ${actionOptMsg}"),
- (Seq("action", "update", "actionName", "artifactName", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${actionNameReqMsg} ${actionOptMsg}"),
- (Seq("action", "delete"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
- (Seq("action", "delete", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("action", "get"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
- (Seq("action", "get", "actionName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("action", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("action", "invoke"), s"${tooFewArgsMsg} ${actionNameReqMsg}"),
- (Seq("action", "invoke", "actionName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("activation", "list", "namespace", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("activation", "get"), s"${tooFewArgsMsg} ${activationIdReq}"),
- (Seq("activation", "get", "activationID", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("activation", "logs"), s"${tooFewArgsMsg} ${activationIdReq}"),
- (Seq("activation", "logs", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("activation", "result"), s"${tooFewArgsMsg} ${activationIdReq}"),
- (Seq("activation", "result", "activationID", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("activation", "poll", "activationID", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("namespace", "list", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${noArgsReqMsg}"),
- (Seq("namespace", "get", "namespace", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("package", "create"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
- (Seq("package", "create", "packageName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("package", "create", "packageName", "--shared", invalidArg), invalidShared),
- (Seq("package", "update"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
- (Seq("package", "update", "packageName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("package", "update", "packageName", "--shared", invalidArg), invalidShared),
- (Seq("package", "get"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
- (Seq("package", "get", "packageName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("package", "bind"), s"${tooFewArgsMsg} ${packageNameBindingReqMsg}"),
- (Seq("package", "bind", "packageName"), s"${tooFewArgsMsg} ${packageNameBindingReqMsg}"),
- (Seq("package", "bind", "packageName", "bindingName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("package", "list", "namespace", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("package", "list", invalidArg), entityNameMsg),
- (Seq("package", "delete"), s"${tooFewArgsMsg} ${packageNameReqMsg}"),
- (Seq("package", "delete", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("package", "refresh", "namespace", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("package", "refresh", invalidArg), entityNameMsg),
- (Seq("rule", "enable"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
- (Seq("rule", "enable", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "disable"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
- (Seq("rule", "disable", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "status"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
- (Seq("rule", "status", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "create"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
- (Seq("rule", "create", "ruleName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
- (Seq("rule", "create", "ruleName", "triggerName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
- (Seq("rule", "create", "ruleName", "triggerName", "actionName", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "update"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
- (Seq("rule", "update", "ruleName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
- (Seq("rule", "update", "ruleName", "triggerName"), s"${tooFewArgsMsg} ${ruleTriggerActionReqMsg}"),
- (Seq("rule", "update", "ruleName", "triggerName", "actionName", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "get"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
- (Seq("rule", "get", "ruleName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "delete"), s"${tooFewArgsMsg} ${ruleNameReqMsg}"),
- (Seq("rule", "delete", "ruleName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("rule", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("rule", "list", invalidArg), entityNameMsg),
- (Seq("trigger", "fire"), s"${tooFewArgsMsg} ${triggerNameReqMsg} ${optPayloadMsg}"),
- (Seq("trigger", "fire", "triggerName", "triggerPayload", invalidArg),
- s"${tooManyArgsMsg}${invalidArg}. ${triggerNameReqMsg} ${optPayloadMsg}"),
- (Seq("trigger", "create"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
- (Seq("trigger", "create", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("trigger", "update"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
- (Seq("trigger", "update", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("trigger", "get"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
- (Seq("trigger", "get", "triggerName", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("trigger", "delete"), s"${tooFewArgsMsg} ${triggerNameReqMsg}"),
- (Seq("trigger", "delete", "triggerName", invalidArg), s"${tooManyArgsMsg}${invalidArg}."),
- (Seq("trigger", "list", "namespace", invalidArg), s"${tooManyArgsMsg}${invalidArg}. ${optNamespaceMsg}"),
- (Seq("trigger", "list", invalidArg), entityNameMsg))
-
- invalidArgs foreach {
- case (cmd, err) =>
- withClue(cmd) {
- val rr = wsk.cli(cmd ++ wskprops.overrides, expectedExitCode = ANY_ERROR_EXIT)
- rr.exitCode should (be(ERROR_EXIT) or be(MISUSE_EXIT))
- rr.stderr should include(err)
- rr.stderr should include("Run 'wsk --help' for usage.")
- }
+ }
+
+ behavior of "Wsk action parameters"
+
+ it should "create an action with different permutations of limits" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+
+ def testLimit(timeout: Option[Duration] = None,
+ memory: Option[ByteSize] = None,
+ logs: Option[ByteSize] = None,
+ ec: Int = SUCCESS_EXIT) = {
+ // Limits to assert, standard values if CLI omits certain values
+ val limits = JsObject(
+ "timeout" -> timeout.getOrElse(STD_DURATION).toMillis.toJson,
+ "memory" -> memory.getOrElse(STD_MEMORY).toMB.toInt.toJson,
+ "logs" -> logs.getOrElse(STD_LOGSIZE).toMB.toInt.toJson)
+
+ val name = "ActionLimitTests" + Instant.now.toEpochMilli
+ val createResult = assetHelper.withCleaner(wsk.action, name, confirmDelete = (ec == SUCCESS_EXIT)) {
+ (action, _) =>
+ val result = action.create(
+ name,
+ file,
+ logsize = logs,
+ memory = memory,
+ timeout = timeout,
+ expectedExitCode = DONTCARE_EXIT)
+ withClue(s"create failed for parameters: timeout = $timeout, memory = $memory, logsize = $logs:") {
+ result.exitCode should be(ec)
+ }
+ result
}
- }
-
- behavior of "Wsk action parameters"
-
- it should "create an action with different permutations of limits" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
-
- def testLimit(timeout: Option[Duration] = None, memory: Option[ByteSize] = None, logs: Option[ByteSize] = None, ec: Int = SUCCESS_EXIT) = {
- // Limits to assert, standard values if CLI omits certain values
- val limits = JsObject(
- "timeout" -> timeout.getOrElse(STD_DURATION).toMillis.toJson,
- "memory" -> memory.getOrElse(STD_MEMORY).toMB.toInt.toJson,
- "logs" -> logs.getOrElse(STD_LOGSIZE).toMB.toInt.toJson)
-
- val name = "ActionLimitTests" + Instant.now.toEpochMilli
- val createResult = assetHelper.withCleaner(wsk.action, name, confirmDelete = (ec == SUCCESS_EXIT)) {
- (action, _) =>
- val result = action.create(name, file, logsize = logs, memory = memory, timeout = timeout, expectedExitCode = DONTCARE_EXIT)
- withClue(s"create failed for parameters: timeout = $timeout, memory = $memory, logsize = $logs:") {
- result.exitCode should be(ec)
- }
- result
- }
-
- if (ec == SUCCESS_EXIT) {
- val JsObject(parsedAction) = wsk.action.get(name).stdout.split("\n").tail.mkString.parseJson.asJsObject
- parsedAction("limits") shouldBe limits
- } else {
- createResult.stderr should include("allowed threshold")
- }
- }
- // Assert for valid permutations that the values are set correctly
- for {
- time <- Seq(None, Some(MIN_DURATION), Some(MAX_DURATION))
- mem <- Seq(None, Some(MIN_MEMORY), Some(MAX_MEMORY))
- log <- Seq(None, Some(MIN_LOGSIZE), Some(MAX_LOGSIZE))
- } testLimit(time, mem, log)
-
- // Assert that invalid permutation are rejected
- testLimit(Some(0.milliseconds), None, None, BAD_REQUEST)
- testLimit(Some(100.minutes), None, None, BAD_REQUEST)
- testLimit(None, Some(0.MB), None, BAD_REQUEST)
- testLimit(None, Some(32768.MB), None, BAD_REQUEST)
- testLimit(None, None, Some(32768.MB), BAD_REQUEST)
- }
+ if (ec == SUCCESS_EXIT) {
+ val JsObject(parsedAction) = wsk.action.get(name).stdout.split("\n").tail.mkString.parseJson.asJsObject
+ parsedAction("limits") shouldBe limits
+ } else {
+ createResult.stderr should include("allowed threshold")
+ }
+ }
+
+ // Assert for valid permutations that the values are set correctly
+ for {
+ time <- Seq(None, Some(MIN_DURATION), Some(MAX_DURATION))
+ mem <- Seq(None, Some(MIN_MEMORY), Some(MAX_MEMORY))
+ log <- Seq(None, Some(MIN_LOGSIZE), Some(MAX_LOGSIZE))
+ } testLimit(time, mem, log)
+
+ // Assert that invalid permutation are rejected
+ testLimit(Some(0.milliseconds), None, None, BAD_REQUEST)
+ testLimit(Some(100.minutes), None, None, BAD_REQUEST)
+ testLimit(None, Some(0.MB), None, BAD_REQUEST)
+ testLimit(None, Some(32768.MB), None, BAD_REQUEST)
+ testLimit(None, None, Some(32768.MB), BAD_REQUEST)
+ }
}
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskConfigTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskConfigTests.scala
index fbe1631..90b014d 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskConfigTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskConfigTests.scala
@@ -33,229 +33,287 @@ import common.WskProps
import common.WskTestHelpers
@RunWith(classOf[JUnitRunner])
-class WskConfigTests
- extends TestHelpers
- with WskTestHelpers {
+class WskConfigTests extends TestHelpers with WskTestHelpers {
- implicit val wskprops = WskProps()
- val wsk = new Wsk
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk
- behavior of "Wsk CLI config"
+ behavior of "Wsk CLI config"
- it should "fail to show api build when setting apihost to bogus value" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- wsk.cli(Seq("property", "set", "-i", "--apihost", "xxxx.yyyy"), env = env)
- val rr = wsk.cli(Seq("property", "get", "--apibuild", "-i"), env = env, expectedExitCode = ANY_ERROR_EXIT)
- rr.stdout should include regex ("""whisk API build\s*Unknown""")
- rr.stderr should include regex ("Unable to obtain API build information")
- } finally {
- tmpwskprops.delete()
- }
+ it should "fail to show api build when setting apihost to bogus value" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ wsk.cli(Seq("property", "set", "-i", "--apihost", "xxxx.yyyy"), env = env)
+ val rr = wsk.cli(Seq("property", "get", "--apibuild", "-i"), env = env, expectedExitCode = ANY_ERROR_EXIT)
+ rr.stdout should include regex ("""whisk API build\s*Unknown""")
+ rr.stderr should include regex ("Unable to obtain API build information")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "validate default property values" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val stdout = wsk.cli(Seq("property", "unset", "--auth", "--cert", "--key", "--apihost", "--apiversion", "--namespace"), env = env).stdout
- try {
- stdout should include regex ("ok: whisk auth unset")
- stdout should include regex ("ok: client cert unset")
- stdout should include regex ("ok: client key unset")
- stdout should include regex ("ok: whisk API host unset")
- stdout should include regex ("ok: whisk API version unset")
- stdout should include regex ("ok: whisk namespace unset")
+ it should "validate default property values" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val stdout = wsk
+ .cli(Seq("property", "unset", "--auth", "--cert", "--key", "--apihost", "--apiversion", "--namespace"), env = env)
+ .stdout
+ try {
+ stdout should include regex ("ok: whisk auth unset")
+ stdout should include regex ("ok: client cert unset")
+ stdout should include regex ("ok: client key unset")
+ stdout should include regex ("ok: whisk API host unset")
+ stdout should include regex ("ok: whisk API version unset")
+ stdout should include regex ("ok: whisk namespace unset")
- wsk.cli(Seq("property", "get", "--auth"), env = env).
- stdout should include regex ("""(?i)whisk auth\s*$""") // default = empty string
- wsk.cli(Seq("property", "get", "--cert"), env = env).
- stdout should include regex ("""(?i)client cert\s*$""") // default = empty string
- wsk.cli(Seq("property", "get", "--key"), env = env).
- stdout should include regex ("""(?i)client key\s*$""") // default = empty string
- wsk.cli(Seq("property", "get", "--apihost"), env = env).
- stdout should include regex ("""(?i)whisk API host\s*$""") // default = empty string
- wsk.cli(Seq("property", "get", "--namespace"), env = env).
- stdout should include regex ("""(?i)whisk namespace\s*_$""") // default = _
- } finally {
- tmpwskprops.delete()
- }
+ wsk
+ .cli(Seq("property", "get", "--auth"), env = env)
+ .stdout should include regex ("""(?i)whisk auth\s*$""") // default = empty string
+ wsk
+ .cli(Seq("property", "get", "--cert"), env = env)
+ .stdout should include regex ("""(?i)client cert\s*$""") // default = empty string
+ wsk
+ .cli(Seq("property", "get", "--key"), env = env)
+ .stdout should include regex ("""(?i)client key\s*$""") // default = empty string
+ wsk
+ .cli(Seq("property", "get", "--apihost"), env = env)
+ .stdout should include regex ("""(?i)whisk API host\s*$""") // default = empty string
+ wsk
+ .cli(Seq("property", "get", "--namespace"), env = env)
+ .stdout should include regex ("""(?i)whisk namespace\s*_$""") // default = _
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "reject authenticated command when no auth key is given" in {
- // override wsk props file in case it exists
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val stderr = wsk.cli(Seq("list") ++ wskprops.overrides, env = env, expectedExitCode = MISUSE_EXIT).stderr
- try {
- stderr should include regex (s"usage[:.]")
- stderr should include("--auth is required")
- } finally {
- tmpwskprops.delete()
- }
+ it should "reject authenticated command when no auth key is given" in {
+ // override wsk props file in case it exists
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val stderr = wsk.cli(Seq("list") ++ wskprops.overrides, env = env, expectedExitCode = MISUSE_EXIT).stderr
+ try {
+ stderr should include regex (s"usage[:.]")
+ stderr should include("--auth is required")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "reject a command when the API host is not set" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val stderr = wsk.cli(Seq("property", "get", "-i"), env = env, expectedExitCode = ERROR_EXIT).stderr
- stderr should include("The API host is not valid: An API host must be provided.")
- } finally {
- tmpwskprops.delete()
- }
+ it should "reject a command when the API host is not set" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val stderr = wsk.cli(Seq("property", "get", "-i"), env = env, expectedExitCode = ERROR_EXIT).stderr
+ stderr should include("The API host is not valid: An API host must be provided.")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "show api build details" in {
- val tmpProps = File.createTempFile("wskprops", ".tmp")
- try {
- val env = Map("WSK_CONFIG_FILE" -> tmpProps.getAbsolutePath())
- wsk.cli(Seq("property", "set", "-i") ++ wskprops.overrides, env = env)
- val rr = wsk.cli(Seq("property", "get", "--apibuild", "--apibuildno", "-i"), env = env)
- rr.stderr should not include ("https:///api/v1: http: no Host in request URL")
- rr.stdout should not include regex("Cannot determine API build")
- rr.stdout should include regex ("""(?i)whisk API build\s+201.*""")
- rr.stdout should include regex ("""(?i)whisk API build number\s+.*""")
- } finally {
- tmpProps.delete()
- }
+ it should "show api build details" in {
+ val tmpProps = File.createTempFile("wskprops", ".tmp")
+ try {
+ val env = Map("WSK_CONFIG_FILE" -> tmpProps.getAbsolutePath())
+ wsk.cli(Seq("property", "set", "-i") ++ wskprops.overrides, env = env)
+ val rr = wsk.cli(Seq("property", "get", "--apibuild", "--apibuildno", "-i"), env = env)
+ rr.stderr should not include ("https:///api/v1: http: no Host in request URL")
+ rr.stdout should not include regex("Cannot determine API build")
+ rr.stdout should include regex ("""(?i)whisk API build\s+201.*""")
+ rr.stdout should include regex ("""(?i)whisk API build number\s+.*""")
+ } finally {
+ tmpProps.delete()
}
+ }
- it should "set apihost, auth, and namespace" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val namespace = wsk.namespace.whois()
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val stdout = wsk.cli(Seq("property", "set", "-i",
- "--apihost", wskprops.apihost,
- "--auth", wskprops.authKey,
- "--namespace", namespace),
- env = env).stdout
- stdout should include(s"ok: whisk auth set")
- stdout should include(s"ok: whisk API host set to ${wskprops.apihost}")
- stdout should include(s"ok: whisk namespace set to ${namespace}")
- } finally {
- tmpwskprops.delete()
- }
+ it should "set apihost, auth, and namespace" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val namespace = wsk.namespace.whois()
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val stdout = wsk
+ .cli(
+ Seq(
+ "property",
+ "set",
+ "-i",
+ "--apihost",
+ wskprops.apihost,
+ "--auth",
+ wskprops.authKey,
+ "--namespace",
+ namespace),
+ env = env)
+ .stdout
+ stdout should include(s"ok: whisk auth set")
+ stdout should include(s"ok: whisk API host set to ${wskprops.apihost}")
+ stdout should include(s"ok: whisk namespace set to ${namespace}")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- // If client certificate verification is off, should ingore run below tests.
- if (!WhiskProperties.getProperty("whisk.ssl.client.verification").equals("off")) {
- it should "set valid cert key to get expected success result for client certificate verification" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val namespace = wsk.namespace.list().stdout.trim.split("\n").last
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- // Send request to https://<apihost>/api/v1/namespaces, wsk client passes client certificate to nginx, nginx will
- // verify it by client ca's openwhisk-client-ca-cert.pem
- val stdout = wsk.cli(Seq("property", "set", "-i", "--apihost", wskprops.apihost, "--auth", wskprops.authKey,
- "--cert", wskprops.cert, "--key", wskprops.key, "--namespace", namespace), env = env).stdout
- stdout should include(s"ok: client cert set")
- stdout should include(s"ok: client key set")
- stdout should include(s"ok: whisk auth set")
- stdout should include(s"ok: whisk API host set to ${wskprops.apihost}")
- stdout should include(s"ok: whisk namespace set to ${namespace}")
- } finally {
- tmpwskprops.delete()
- }
- }
+ // If client certificate verification is off, should ingore run below tests.
+ if (!WhiskProperties.getProperty("whisk.ssl.client.verification").equals("off")) {
+ it should "set valid cert key to get expected success result for client certificate verification" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val namespace = wsk.namespace.list().stdout.trim.split("\n").last
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ // Send request to https://<apihost>/api/v1/namespaces, wsk client passes client certificate to nginx, nginx will
+ // verify it by client ca's openwhisk-client-ca-cert.pem
+ val stdout = wsk
+ .cli(
+ Seq(
+ "property",
+ "set",
+ "-i",
+ "--apihost",
+ wskprops.apihost,
+ "--auth",
+ wskprops.authKey,
+ "--cert",
+ wskprops.cert,
+ "--key",
+ wskprops.key,
+ "--namespace",
+ namespace),
+ env = env)
+ .stdout
+ stdout should include(s"ok: client cert set")
+ stdout should include(s"ok: client key set")
+ stdout should include(s"ok: whisk auth set")
+ stdout should include(s"ok: whisk API host set to ${wskprops.apihost}")
+ stdout should include(s"ok: whisk namespace set to ${namespace}")
+ } finally {
+ tmpwskprops.delete()
+ }
+ }
- it should "set invalid cert key to get expected exception result for client certificate verification" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val namespace = wsk.namespace.list().stdout.trim.split("\n").last
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val thrown = the[Exception] thrownBy wsk.cli(Seq("property", "set", "-i", "--apihost", wskprops.apihost, "--auth", wskprops.authKey,
- "--cert", "invalid-cert.pem", "--key", "invalid-key.pem", "--namespace", namespace), env = env)
- thrown.getMessage should include("cannot validate certificate")
- } finally {
- tmpwskprops.delete()
- }
- }
+ it should "set invalid cert key to get expected exception result for client certificate verification" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val namespace = wsk.namespace.list().stdout.trim.split("\n").last
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val thrown = the[Exception] thrownBy wsk.cli(
+ Seq(
+ "property",
+ "set",
+ "-i",
+ "--apihost",
+ wskprops.apihost,
+ "--auth",
+ wskprops.authKey,
+ "--cert",
+ "invalid-cert.pem",
+ "--key",
+ "invalid-key.pem",
+ "--namespace",
+ namespace),
+ env = env)
+ thrown.getMessage should include("cannot validate certificate")
+ } finally {
+ tmpwskprops.delete()
+ }
}
+ }
- it should "ensure default namespace is used when a blank namespace is set" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val writer = new BufferedWriter(new FileWriter(tmpwskprops))
- writer.write(s"NAMESPACE=")
- writer.close()
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val stdout = wsk.cli(Seq("property", "get", "-i", "--namespace"), env = env).stdout
- stdout should include regex ("whisk namespace\\s+_")
- } finally {
- tmpwskprops.delete()
- }
+ it should "ensure default namespace is used when a blank namespace is set" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val writer = new BufferedWriter(new FileWriter(tmpwskprops))
+ writer.write(s"NAMESPACE=")
+ writer.close()
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val stdout = wsk.cli(Seq("property", "get", "-i", "--namespace"), env = env).stdout
+ stdout should include regex ("whisk namespace\\s+_")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "show api build version using property file" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- wsk.cli(Seq("property", "set", "-i") ++ wskprops.overrides, env = env)
- val stdout = wsk.cli(Seq("property", "get", "--apibuild", "-i"), env = env).stdout
- stdout should include regex ("""(?i)whisk API build\s+201.*""")
- } finally {
- tmpwskprops.delete()
- }
+ it should "show api build version using property file" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ wsk.cli(Seq("property", "set", "-i") ++ wskprops.overrides, env = env)
+ val stdout = wsk.cli(Seq("property", "get", "--apibuild", "-i"), env = env).stdout
+ stdout should include regex ("""(?i)whisk API build\s+201.*""")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "show api build using http apihost" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- try {
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val apihost = s"http://${WhiskProperties.getBaseControllerAddress()}"
- wsk.cli(Seq("property", "set", "--apihost", apihost), env = env)
- val rr = wsk.cli(Seq("property", "get", "--apibuild", "-i"), env = env)
- rr.stdout should not include regex("""whisk API build\s*Unknown""")
- rr.stderr should not include regex("Unable to obtain API build information")
- rr.stdout should include regex ("""(?i)whisk API build\s+201.*""")
- } finally {
- tmpwskprops.delete()
- }
+ it should "show api build using http apihost" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ try {
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val apihost = s"http://${WhiskProperties.getBaseControllerAddress()}"
+ wsk.cli(Seq("property", "set", "--apihost", apihost), env = env)
+ val rr = wsk.cli(Seq("property", "get", "--apibuild", "-i"), env = env)
+ rr.stdout should not include regex("""whisk API build\s*Unknown""")
+ rr.stderr should not include regex("Unable to obtain API build information")
+ rr.stdout should include regex ("""(?i)whisk API build\s+201.*""")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "set auth in property file" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- wsk.cli(Seq("property", "set", "--auth", "testKey"), env = env)
- try {
- val fileContent = FileUtils.readFileToString(tmpwskprops)
- fileContent should include("AUTH=testKey")
- } finally {
- tmpwskprops.delete()
- }
+ it should "set auth in property file" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ wsk.cli(Seq("property", "set", "--auth", "testKey"), env = env)
+ try {
+ val fileContent = FileUtils.readFileToString(tmpwskprops)
+ fileContent should include("AUTH=testKey")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "set multiple property values with single command" in {
- val tmpwskprops = File.createTempFile("wskprops", ".tmp")
- val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
- val stdout = wsk.cli(Seq("property", "set", "--auth", "testKey", "--cert", "cert.pem", "--key", "key.pem", "--apihost", "openwhisk.ng.bluemix.net", "--apiversion", "v1"), env = env).stdout
- try {
- stdout should include regex ("ok: whisk auth set")
- stdout should include regex ("ok: client cert set")
- stdout should include regex ("ok: client key set")
- stdout should include regex ("ok: whisk API host set")
- stdout should include regex ("ok: whisk API version set")
- val fileContent = FileUtils.readFileToString(tmpwskprops)
- fileContent should include("AUTH=testKey")
- fileContent should include("APIHOST=openwhisk.ng.bluemix.net")
- fileContent should include("APIVERSION=v1")
- } finally {
- tmpwskprops.delete()
- }
+ it should "set multiple property values with single command" in {
+ val tmpwskprops = File.createTempFile("wskprops", ".tmp")
+ val env = Map("WSK_CONFIG_FILE" -> tmpwskprops.getAbsolutePath())
+ val stdout = wsk
+ .cli(
+ Seq(
+ "property",
+ "set",
+ "--auth",
+ "testKey",
+ "--cert",
+ "cert.pem",
+ "--key",
+ "key.pem",
+ "--apihost",
+ "openwhisk.ng.bluemix.net",
+ "--apiversion",
+ "v1"),
+ env = env)
+ .stdout
+ try {
+ stdout should include regex ("ok: whisk auth set")
+ stdout should include regex ("ok: client cert set")
+ stdout should include regex ("ok: client key set")
+ stdout should include regex ("ok: whisk API host set")
+ stdout should include regex ("ok: whisk API version set")
+ val fileContent = FileUtils.readFileToString(tmpwskprops)
+ fileContent should include("AUTH=testKey")
+ fileContent should include("APIHOST=openwhisk.ng.bluemix.net")
+ fileContent should include("APIVERSION=v1")
+ } finally {
+ tmpwskprops.delete()
}
+ }
- it should "create a trigger using property file" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "listTriggers"
- val tmpProps = File.createTempFile("wskprops", ".tmp")
- val env = Map("WSK_CONFIG_FILE" -> tmpProps.getAbsolutePath())
- wsk.cli(Seq("property", "set", "--auth", wp.authKey) ++ wskprops.overrides, env = env)
- assetHelper.withCleaner(wsk.trigger, name) {
- (trigger, _) =>
- wsk.cli(Seq("-i", "trigger", "create", name), env = env)
- }
- tmpProps.delete()
+ it should "create a trigger using property file" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "listTriggers"
+ val tmpProps = File.createTempFile("wskprops", ".tmp")
+ val env = Map("WSK_CONFIG_FILE" -> tmpProps.getAbsolutePath())
+ wsk.cli(Seq("property", "set", "--auth", wp.authKey) ++ wskprops.overrides, env = env)
+ assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) =>
+ wsk.cli(Seq("-i", "trigger", "create", name), env = env)
}
+ tmpProps.delete()
+ }
}
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 7f83762..8140cb4 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskEntitlementTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskEntitlementTests.scala
@@ -35,326 +35,315 @@ import whisk.core.entity.Subject
import whisk.core.entity.WhiskPackage
@RunWith(classOf[JUnitRunner])
-class WskEntitlementTests
- extends TestHelpers
- with WskTestHelpers
- with BeforeAndAfterAll {
-
- val wsk = new Wsk
- lazy val defaultWskProps = WskProps()
- lazy val guestWskProps = getAdditionalTestSubject(Subject().asString)
-
- override def afterAll() = {
- disposeAdditionalTestSubject(guestWskProps.namespace)
+class WskEntitlementTests extends TestHelpers with WskTestHelpers with BeforeAndAfterAll {
+
+ val wsk = new Wsk
+ lazy val defaultWskProps = WskProps()
+ lazy val guestWskProps = getAdditionalTestSubject(Subject().asString)
+
+ override def afterAll() = {
+ disposeAdditionalTestSubject(guestWskProps.namespace)
+ }
+
+ val samplePackage = "samplePackage"
+ val sampleAction = "sampleAction"
+ val fullSampleActionName = s"$samplePackage/$sampleAction"
+ val guestNamespace = guestWskProps.namespace
+
+ behavior of "Wsk Package Entitlement"
+
+ it should "not allow unauthorized subject to operate on private action" in withAssetCleaner(guestWskProps) {
+ (wp, assetHelper) =>
+ val privateAction = "privateAction"
+
+ assetHelper.withCleaner(wsk.action, privateAction) { (action, name) =>
+ action.create(name, Some(TestUtils.getTestActionFilename("hello.js")))(wp)
+ }
+
+ val fullyQualifiedActionName = s"/$guestNamespace/$privateAction"
+ wsk.action.get(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps).stderr should include(
+ "not authorized")
+
+ withAssetCleaner(defaultWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, fullyQualifiedActionName, confirmDelete = false) { (action, name) =>
+ val rr = action.create(name, None, update = true, expectedExitCode = FORBIDDEN)(wp)
+ rr.stderr should include("not authorized")
+ rr
+ }
+ assetHelper.withCleaner(wsk.action, "unauthorized sequence", confirmDelete = false) { (action, name) =>
+ val rr = action.create(
+ name,
+ Some(fullyQualifiedActionName),
+ kind = Some("sequence"),
+ update = true,
+ expectedExitCode = FORBIDDEN)(wp)
+ rr.stderr should include("not authorized")
+ rr
+ }
+ }
+
+ wsk.action.delete(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps).stderr should include(
+ "not authorized")
+
+ wsk.action.invoke(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps).stderr should include(
+ "not authorized")
+ }
+
+ it should "reject deleting action in shared package not owned by authkey" in withAssetCleaner(guestWskProps) {
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(true))(wp)
+ }
+
+ assetHelper.withCleaner(wsk.action, fullSampleActionName) {
+ val file = Some(TestUtils.getTestActionFilename("empty.js"))
+ (action, _) =>
+ action.create(fullSampleActionName, file)(wp)
+ }
+
+ val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
+ wsk.action.get(fullyQualifiedActionName)(defaultWskProps)
+ wsk.action.delete(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps)
+ }
+
+ it should "reject create action in shared package not owned by authkey" in withAssetCleaner(guestWskProps) {
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, name) =>
+ pkg.create(name, shared = Some(true))(wp)
+ }
+
+ val fullyQualifiedActionName = s"/$guestNamespace/notallowed"
+ val file = Some(TestUtils.getTestActionFilename("empty.js"))
+
+ withAssetCleaner(defaultWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, fullyQualifiedActionName, confirmDelete = false) { (action, name) =>
+ action.create(name, file, expectedExitCode = FORBIDDEN)(wp)
+ }
+ }
+ }
+
+ it should "reject update action in shared package not owned by authkey" in withAssetCleaner(guestWskProps) {
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(true))(wp)
+ }
+
+ assetHelper.withCleaner(wsk.action, fullSampleActionName) {
+ val file = Some(TestUtils.getTestActionFilename("empty.js"))
+ (action, _) =>
+ action.create(fullSampleActionName, file)(wp)
+ }
+
+ val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
+ wsk.action.create(fullyQualifiedActionName, None, update = true, expectedExitCode = FORBIDDEN)(defaultWskProps)
+ }
+
+ behavior of "Wsk Package Listing"
+
+ it should "list shared packages" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(true))(wp)
}
- val samplePackage = "samplePackage"
- val sampleAction = "sampleAction"
- val fullSampleActionName = s"$samplePackage/$sampleAction"
- val guestNamespace = guestWskProps.namespace
-
- behavior of "Wsk Package Entitlement"
-
- it should "not allow unauthorized subject to operate on private action" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- val privateAction = "privateAction"
-
- assetHelper.withCleaner(wsk.action, privateAction) {
- (action, name) => action.create(name, Some(TestCLIUtils.getTestActionFilename("hello.js")))(wp)
- }
-
- val fullyQualifiedActionName = s"/$guestNamespace/$privateAction"
- wsk.action.get(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps).
- stderr should include("not authorized")
-
- withAssetCleaner(defaultWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.action, fullyQualifiedActionName, confirmDelete = false) {
- (action, name) =>
- val rr = action.create(name, None, update = true, expectedExitCode = FORBIDDEN)(wp)
- rr.stderr should include("not authorized")
- rr
- }
- assetHelper.withCleaner(wsk.action, "unauthorized sequence", confirmDelete = false) {
- (action, name) =>
- val rr = action.create(name, Some(fullyQualifiedActionName), kind = Some("sequence"), update = true, expectedExitCode = FORBIDDEN)(wp)
- rr.stderr should include("not authorized")
- rr
- }
- }
-
- wsk.action.delete(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps).
- stderr should include("not authorized")
-
- wsk.action.invoke(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps).
- stderr should include("not authorized")
- }
+ val fullyQualifiedPackageName = s"/$guestNamespace/$samplePackage"
+ val result = wsk.pkg.list(Some(s"/$guestNamespace"))(defaultWskProps).stdout
+ result should include regex (fullyQualifiedPackageName + """\s+shared""")
+ }
- it should "reject deleting action in shared package not owned by authkey" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(true))(wp)
- }
+ it should "not list private packages" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage)(wp)
+ }
- assetHelper.withCleaner(wsk.action, fullSampleActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("empty.js"))
- (action, _) => action.create(fullSampleActionName, file)(wp)
- }
+ val fullyQualifiedPackageName = s"/$guestNamespace/$samplePackage"
+ val result = wsk.pkg.list(Some(s"/$guestNamespace"))(defaultWskProps).stdout
+ result should not include regex(fullyQualifiedPackageName)
+ }
- val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
- wsk.action.get(fullyQualifiedActionName)(defaultWskProps)
- wsk.action.delete(fullyQualifiedActionName, expectedExitCode = FORBIDDEN)(defaultWskProps)
+ it should "list shared package actions" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(true))(wp)
}
- it should "reject create action in shared package not owned by authkey" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, name) => pkg.create(name, shared = Some(true))(wp)
- }
-
- val fullyQualifiedActionName = s"/$guestNamespace/notallowed"
- val file = Some(TestCLIUtils.getTestActionFilename("empty.js"))
-
- withAssetCleaner(defaultWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.action, fullyQualifiedActionName, confirmDelete = false) {
- (action, name) => action.create(name, file, expectedExitCode = FORBIDDEN)(wp)
- }
- }
+ assetHelper.withCleaner(wsk.action, fullSampleActionName) {
+ val file = Some(TestUtils.getTestActionFilename("empty.js"))
+ (action, _) =>
+ action.create(fullSampleActionName, file, kind = Some("nodejs:default"))(wp)
}
- it should "reject update action in shared package not owned by authkey" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(true))(wp)
- }
+ val fullyQualifiedPackageName = s"/$guestNamespace/$samplePackage"
+ val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
+ val result = wsk.action.list(Some(fullyQualifiedPackageName))(defaultWskProps).stdout
+ result should include regex (fullyQualifiedActionName)
+ }
- assetHelper.withCleaner(wsk.action, fullSampleActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("empty.js"))
- (action, _) => action.create(fullSampleActionName, file)(wp)
- }
+ behavior of "Wsk Package Binding"
- val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
- wsk.action.create(fullyQualifiedActionName, None, update = true, expectedExitCode = FORBIDDEN)(defaultWskProps)
+ it should "create a package binding" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(true))(wp)
}
- behavior of "Wsk Package Listing"
-
- it should "list shared packages" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(true))(wp)
- }
-
- val fullyQualifiedPackageName = s"/$guestNamespace/$samplePackage"
- val result = wsk.pkg.list(Some(s"/$guestNamespace"))(defaultWskProps).stdout
- result should include regex (fullyQualifiedPackageName + """\s+shared""")
+ val name = "bindPackage"
+ val annotations = Map("a" -> "A".toJson, WhiskPackage.bindingFieldName -> "xxx".toJson)
+ val provider = s"/$guestNamespace/$samplePackage"
+ withAssetCleaner(defaultWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) =>
+ pkg.bind(provider, name, annotations = annotations)(wp)
+ }
+
+ val stdout = wsk.pkg.get(name)(defaultWskProps).stdout
+ val annotationString = wsk.parseJsonString(stdout).fields("annotations").toString
+ annotationString should include regex (""""key":"a"""")
+ annotationString should include regex (""""value":"A"""")
+ annotationString should include regex (s""""key":"${WhiskPackage.bindingFieldName}"""")
+ annotationString should not include regex(""""key":"xxx"""")
+ annotationString should include regex (s""""name":"${samplePackage}"""")
}
+ }
- it should "not list private packages" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage)(wp)
- }
-
- val fullyQualifiedPackageName = s"/$guestNamespace/$samplePackage"
- val result = wsk.pkg.list(Some(s"/$guestNamespace"))(defaultWskProps).stdout
- result should not include regex(fullyQualifiedPackageName)
+ it should "not create a package binding for private package" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(false))(wp)
}
- it should "list shared package actions" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(true))(wp)
- }
-
- assetHelper.withCleaner(wsk.action, fullSampleActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("empty.js"))
- (action, _) => action.create(fullSampleActionName, file, kind = Some("nodejs:default"))(wp)
- }
-
- val fullyQualifiedPackageName = s"/$guestNamespace/$samplePackage"
- val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
- val result = wsk.action.list(Some(fullyQualifiedPackageName))(defaultWskProps).stdout
- result should include regex (fullyQualifiedActionName)
+ val name = "bindPackage"
+ val provider = s"/$guestNamespace/$samplePackage"
+ withAssetCleaner(defaultWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, name, confirmDelete = false) { (pkg, _) =>
+ pkg.bind(provider, name, expectedExitCode = FORBIDDEN)(wp)
+ }
}
+ }
+
+ behavior of "Wsk Package Action"
- behavior of "Wsk Package Binding"
-
- it should "create a package binding" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(true))(wp)
- }
-
- val name = "bindPackage"
- val annotations = Map("a" -> "A".toJson, WhiskPackage.bindingFieldName -> "xxx".toJson)
- val provider = s"/$guestNamespace/$samplePackage"
- withAssetCleaner(defaultWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, name) {
- (pkg, _) => pkg.bind(provider, name, annotations = annotations)(wp)
- }
-
- val stdout = wsk.pkg.get(name)(defaultWskProps).stdout
- val annotationString = wsk.parseJsonString(stdout).fields("annotations").toString
- annotationString should include regex (""""key":"a"""")
- annotationString should include regex (""""value":"A"""")
- annotationString should include regex (s""""key":"${WhiskPackage.bindingFieldName}"""")
- annotationString should not include regex(""""key":"xxx"""")
- annotationString should include regex (s""""name":"${samplePackage}"""")
- }
+ it should "get and invoke an action from package" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(wp)
}
- it should "not create a package binding for private package" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(false))(wp)
- }
-
- val name = "bindPackage"
- val provider = s"/$guestNamespace/$samplePackage"
- withAssetCleaner(defaultWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, name, confirmDelete = false) {
- (pkg, _) => pkg.bind(provider, name, expectedExitCode = FORBIDDEN)(wp)
- }
- }
+ assetHelper.withCleaner(wsk.action, fullSampleActionName) {
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ (action, _) =>
+ action.create(fullSampleActionName, file)(wp)
}
- behavior of "Wsk Package Action"
+ val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
+ val stdout = wsk.action.get(fullyQualifiedActionName)(defaultWskProps).stdout
+ stdout should include("name")
+ stdout should include("parameters")
+ stdout should include("limits")
+ stdout should include regex (""""key": "a"""")
+ stdout should include regex (""""value": "A"""")
- it should "get and invoke an action from package" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(wp)
- }
+ val run = wsk.action.invoke(fullyQualifiedActionName)(defaultWskProps)
- assetHelper.withCleaner(wsk.action, fullSampleActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- (action, _) => action.create(fullSampleActionName, file)(wp)
- }
+ withActivation(wsk.activation, run)({
+ _.response.success shouldBe true
+ })(defaultWskProps)
+ }
- val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
- val stdout = wsk.action.get(fullyQualifiedActionName)(defaultWskProps).stdout
- stdout should include("name")
- stdout should include("parameters")
- stdout should include("limits")
- stdout should include regex (""""key": "a"""")
- stdout should include regex (""""value": "A"""")
+ it should "invoke an action sequence from package" in withAssetCleaner(guestWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(wp)
+ }
+
+ assetHelper.withCleaner(wsk.action, fullSampleActionName) {
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ (action, _) =>
+ action.create(fullSampleActionName, file)(wp)
+ }
- val run = wsk.action.invoke(fullyQualifiedActionName)(defaultWskProps)
+ withAssetCleaner(defaultWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, "sequence") { (action, name) =>
+ val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
+ action.create(name, Some(fullyQualifiedActionName), kind = Some("sequence"), update = true)(wp)
+ }
- withActivation(wsk.activation, run)({
- _.response.success shouldBe true
- })(defaultWskProps)
+ val run = wsk.action.invoke("sequence")(defaultWskProps)
+ withActivation(wsk.activation, run)({
+ _.response.success shouldBe true
+ })(defaultWskProps)
+ }
+ }
+
+ it should "not allow invoke an action sequence with more than one component from package after entitlement change" in withAssetCleaner(
+ guestWskProps) { (guestwp, assetHelper) =>
+ val privateSamplePackage = samplePackage + "prv"
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(guestwp)
+ pkg.create(privateSamplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(guestwp)
}
- it should "invoke an action sequence from package" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(wp)
- }
-
- assetHelper.withCleaner(wsk.action, fullSampleActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- (action, _) => action.create(fullSampleActionName, file)(wp)
- }
-
- withAssetCleaner(defaultWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.action, "sequence") {
- (action, name) =>
- val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
- action.create(name, Some(fullyQualifiedActionName), kind = Some("sequence"), update = true)(wp)
- }
-
- val run = wsk.action.invoke("sequence")(defaultWskProps)
- withActivation(wsk.activation, run)({
- _.response.success shouldBe true
- })(defaultWskProps)
- }
+ assetHelper.withCleaner(wsk.action, fullSampleActionName) {
+ val file = Some(TestUtils.getTestActionFilename("hello.js"))
+ (action, _) =>
+ action.create(fullSampleActionName, file)(guestwp)
+ action.create(s"$privateSamplePackage/$sampleAction", file)(guestwp)
}
- it should "not allow invoke an action sequence with more than one component from package after entitlement change" in withAssetCleaner(guestWskProps) {
- (guestwp, assetHelper) =>
- val privateSamplePackage = samplePackage + "prv"
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) =>
- pkg.create(samplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(guestwp)
- pkg.create(privateSamplePackage, parameters = Map("a" -> "A".toJson), shared = Some(true))(guestwp)
- }
-
- assetHelper.withCleaner(wsk.action, fullSampleActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("hello.js"))
- (action, _) =>
- action.create(fullSampleActionName, file)(guestwp)
- action.create(s"$privateSamplePackage/$sampleAction", file)(guestwp)
- }
-
- withAssetCleaner(defaultWskProps) {
- (dwp, assetHelper) =>
- assetHelper.withCleaner(wsk.action, "sequence") {
- (action, name) =>
- val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
- val fullyQualifiedActionName2 = s"/$guestNamespace/$privateSamplePackage/$sampleAction"
- action.create(name, Some(s"$fullyQualifiedActionName,$fullyQualifiedActionName2"),
- kind = Some("sequence"))(dwp)
- }
-
- // change package visibility
- wsk.pkg.create(privateSamplePackage, update = true, shared = Some(false))(guestwp)
- wsk.action.invoke("sequence", expectedExitCode = FORBIDDEN)(defaultWskProps)
- }
+ withAssetCleaner(defaultWskProps) { (dwp, assetHelper) =>
+ assetHelper.withCleaner(wsk.action, "sequence") { (action, name) =>
+ val fullyQualifiedActionName = s"/$guestNamespace/$fullSampleActionName"
+ val fullyQualifiedActionName2 = s"/$guestNamespace/$privateSamplePackage/$sampleAction"
+ action.create(name, Some(s"$fullyQualifiedActionName,$fullyQualifiedActionName2"), kind = Some("sequence"))(dwp)
+ }
+
+ // change package visibility
+ wsk.pkg.create(privateSamplePackage, update = true, shared = Some(false))(guestwp)
+ wsk.action.invoke("sequence", expectedExitCode = FORBIDDEN)(defaultWskProps)
}
+ }
+
+ it should "invoke a packaged action not owned by the subject to get the subject's namespace" in withAssetCleaner(
+ guestWskProps) { (_, assetHelper) =>
+ val packageName = "namespacePackage"
+ val actionName = "namespaceAction"
+ val packagedActionName = s"$packageName/$actionName"
- it should "invoke a packaged action not owned by the subject to get the subject's namespace" in withAssetCleaner(guestWskProps) {
- (_, assetHelper) =>
- val packageName = "namespacePackage"
- val actionName = "namespaceAction"
- val packagedActionName = s"$packageName/$actionName"
-
- assetHelper.withCleaner(wsk.pkg, packageName) {
- (pkg, _) => pkg.create(packageName, shared = Some(true))(guestWskProps)
- }
-
- assetHelper.withCleaner(wsk.action, packagedActionName) {
- val file = Some(TestCLIUtils.getTestActionFilename("helloContext.js"))
- (action, _) => action.create(packagedActionName, file)(guestWskProps)
- }
-
- val fullyQualifiedActionName = s"/$guestNamespace/$packagedActionName"
- val run = wsk.action.invoke(fullyQualifiedActionName)(defaultWskProps)
-
- withActivation(wsk.activation, run)({ activation =>
- val namespace = wsk.namespace.whois()(defaultWskProps)
- activation.response.success shouldBe true
- activation.response.result.get.toString should include regex (s""""namespace":\\s*"$namespace"""")
- })(defaultWskProps)
+ assetHelper.withCleaner(wsk.pkg, packageName) { (pkg, _) =>
+ pkg.create(packageName, shared = Some(true))(guestWskProps)
}
- behavior of "Wsk Trigger Feed"
-
- it should "not create a trigger with timeout error when feed fails to initialize" in withAssetCleaner(guestWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.pkg, samplePackage) {
- (pkg, _) => pkg.create(samplePackage, shared = Some(true))(wp)
- }
-
- val sampleFeed = s"$samplePackage/sampleFeed"
- assetHelper.withCleaner(wsk.action, sampleFeed) {
- val file = Some(TestCLIUtils.getTestActionFilename("empty.js"))
- (action, _) => action.create(sampleFeed, file, kind = Some("nodejs:default"))(wp)
- }
-
- val fullyQualifiedFeedName = s"/$guestNamespace/$sampleFeed"
- withAssetCleaner(defaultWskProps) {
- (wp, assetHelper) =>
- assetHelper.withCleaner(wsk.trigger, "badfeed", confirmDelete = false) {
- (trigger, name) => trigger.create(name, feed = Some(fullyQualifiedFeedName), expectedExitCode = TIMEOUT)(wp)
- }
- wsk.trigger.get("badfeed", expectedExitCode = NOT_FOUND)(wp)
- }
+ assetHelper.withCleaner(wsk.action, packagedActionName) {
+ val file = Some(TestUtils.getTestActionFilename("helloContext.js"))
+ (action, _) =>
+ action.create(packagedActionName, file)(guestWskProps)
}
+ val fullyQualifiedActionName = s"/$guestNamespace/$packagedActionName"
+ val run = wsk.action.invoke(fullyQualifiedActionName)(defaultWskProps)
+
+ withActivation(wsk.activation, run)({ activation =>
+ val namespace = wsk.namespace.whois()(defaultWskProps)
+ activation.response.success shouldBe true
+ activation.response.result.get.toString should include regex (s""""namespace":\\s*"$namespace"""")
+ })(defaultWskProps)
+ }
+
+ behavior of "Wsk Trigger Feed"
+
+ it should "not create a trigger with timeout error when feed fails to initialize" in withAssetCleaner(guestWskProps) {
+ (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.pkg, samplePackage) { (pkg, _) =>
+ pkg.create(samplePackage, shared = Some(true))(wp)
+ }
+
+ val sampleFeed = s"$samplePackage/sampleFeed"
+ assetHelper.withCleaner(wsk.action, sampleFeed) {
+ val file = Some(TestUtils.getTestActionFilename("empty.js"))
+ (action, _) =>
+ action.create(sampleFeed, file, kind = Some("nodejs:default"))(wp)
+ }
+
+ val fullyQualifiedFeedName = s"/$guestNamespace/$sampleFeed"
+ withAssetCleaner(defaultWskProps) { (wp, assetHelper) =>
+ assetHelper.withCleaner(wsk.trigger, "badfeed", confirmDelete = false) { (trigger, name) =>
+ trigger.create(name, feed = Some(fullyQualifiedFeedName), expectedExitCode = TIMEOUT)(wp)
+ }
+ wsk.trigger.get("badfeed", expectedExitCode = NOT_FOUND)(wp)
+ }
+ }
+
}
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
index 8097ce9..c94c36e 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
@@ -49,295 +49,289 @@ import whisk.core.entity.Subject
*/
@RunWith(classOf[JUnitRunner])
class WskWebActionsTests extends TestHelpers with WskTestHelpers with RestUtil with BeforeAndAfterAll {
- val MAX_URL_LENGTH = 8192 // 8K matching nginx default
+ val MAX_URL_LENGTH = 8192 // 8K matching nginx default
- val wsk = new Wsk
- private implicit val wskprops = WskProps()
- val namespace = wsk.namespace.whois()
+ val wsk = new Wsk
+ private implicit val wskprops = WskProps()
+ val namespace = wsk.namespace.whois()
- protected val testRoutePath: String = "/api/v1/web"
+ protected val testRoutePath: String = "/api/v1/web"
- behavior of "Wsk Web Actions"
+ behavior of "Wsk Web Actions"
- /**
- * Tests web actions, plus max url limit.
- */
- it should "create a web action accessible via HTTPS" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
- val host = getServiceURL()
- val requestPath = host + s"$testRoutePath/$namespace/default/$name.text/a?a="
- val padAmount = MAX_URL_LENGTH - requestPath.length
+ /**
+ * Tests web actions, plus max url limit.
+ */
+ it should "create a web action accessible via HTTPS" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ val host = getServiceURL()
+ val requestPath = host + s"$testRoutePath/$namespace/default/$name.text/a?a="
+ val padAmount = MAX_URL_LENGTH - requestPath.length
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file, web = Some("true"))
- }
-
- Seq(("A", 200),
- ("A" * padAmount, 200),
- // ideally the bad case is just +1 but there's some differences
- // in how characters are counted i.e., whether these count "https://:443"
- // or not; it seems sufficient to test right around the boundary
- ("A" * (padAmount + 100), 414))
- .foreach {
- case (pad, code) =>
- val url = (requestPath + pad)
- val response = RestAssured.given().config(sslconfig).get(url)
- val responseCode = response.statusCode
-
- withClue(s"response code: $responseCode, url length: ${url.length}, pad amount: ${pad.length}, url: $url") {
- responseCode shouldBe code
- if (code == 200) {
- response.body.asString shouldBe pad
- } else {
- response.body.asString should include("414 Request-URI Too Large") // from nginx
- }
- }
- }
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"))
}
- /**
- * Tests web action requiring authentication.
- */
- it should "create a web action requiring authentication accessible via HTTPS" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
- val host = getServiceURL()
- val url = s"$host$testRoutePath/$namespace/default/$name.text/__ow_user"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"), annotations = Map("require-whisk-auth" -> true.toJson))
+ Seq(
+ ("A", 200),
+ ("A" * padAmount, 200),
+ // ideally the bad case is just +1 but there's some differences
+ // in how characters are counted i.e., whether these count "https://:443"
+ // or not; it seems sufficient to test right around the boundary
+ ("A" * (padAmount + 100), 414))
+ .foreach {
+ case (pad, code) =>
+ val url = (requestPath + pad)
+ val response = RestAssured.given().config(sslconfig).get(url)
+ val responseCode = response.statusCode
+
+ withClue(s"response code: $responseCode, url length: ${url.length}, pad amount: ${pad.length}, url: $url") {
+ responseCode shouldBe code
+ if (code == 200) {
+ response.body.asString shouldBe pad
+ } else {
+ response.body.asString should include("414 Request-URI Too Large") // from nginx
}
-
- val unauthorizedResponse = RestAssured.given().config(sslconfig).get(url)
- unauthorizedResponse.statusCode shouldBe 401
-
- val authorizedResponse = RestAssured
- .given()
- .config(sslconfig)
- .auth().preemptive().basic(wskprops.authKey.split(":")(0), wskprops.authKey.split(":")(1))
- .get(url)
-
- authorizedResponse.statusCode shouldBe 200
- authorizedResponse.body.asString shouldBe namespace
+ }
+ }
+ }
+
+ /**
+ * Tests web action requiring authentication.
+ */
+ it should "create a web action requiring authentication accessible via HTTPS" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ val host = getServiceURL()
+ val url = s"$host$testRoutePath/$namespace/default/$name.text/__ow_user"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"), annotations = Map("require-whisk-auth" -> true.toJson))
+ }
+
+ val unauthorizedResponse = RestAssured.given().config(sslconfig).get(url)
+ unauthorizedResponse.statusCode shouldBe 401
+
+ val authorizedResponse = RestAssured
+ .given()
+ .config(sslconfig)
+ .auth()
+ .preemptive()
+ .basic(wskprops.authKey.split(":")(0), wskprops.authKey.split(":")(1))
+ .get(url)
+
+ authorizedResponse.statusCode shouldBe 200
+ authorizedResponse.body.asString shouldBe namespace
+ }
+
+ it should "ensure that CORS header is preserved for custom options" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("corsHeaderMod.js"))
+ val host = getServiceURL()
+ val url = host + s"$testRoutePath/$namespace/default/$name.http"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"), annotations = Map("web-custom-options" -> true.toJson))
+ }
+
+ val response = RestAssured.given().config(sslconfig).options(url)
+
+ response.statusCode shouldBe 200
+ response.header("Access-Control-Allow-Origin") shouldBe "Origin set from Web Action"
+ response.header("Access-Control-Allow-Methods") shouldBe "Methods set from Web Action"
+ response.header("Access-Control-Allow-Headers") shouldBe "Headers set from Web Action"
+ response.header("Location") shouldBe "openwhisk.org"
+ response.header("Set-Cookie") shouldBe "cookie-cookie-cookie"
+ }
+
+ it should "ensure that default CORS header is preserved" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("corsHeaderMod.js"))
+ val host = getServiceURL()
+ val url = host + s"$testRoutePath/$namespace/default/$name"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"))
}
- it should "ensure that CORS header is preserved for custom options" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("corsHeaderMod.js"))
- val host = getServiceURL()
- val url = host + s"$testRoutePath/$namespace/default/$name.http"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"), annotations = Map("web-custom-options" -> true.toJson))
- }
-
- val response = RestAssured.given().config(sslconfig).options(url)
-
- response.statusCode shouldBe 200
- response.header("Access-Control-Allow-Origin") shouldBe "Origin set from Web Action"
- response.header("Access-Control-Allow-Methods") shouldBe "Methods set from Web Action"
- response.header("Access-Control-Allow-Headers") shouldBe "Headers set from Web Action"
- response.header("Location") shouldBe "openwhisk.org"
- response.header("Set-Cookie") shouldBe "cookie-cookie-cookie"
+ val responses = Seq(
+ RestAssured.given().config(sslconfig).options(s"$url.http"),
+ RestAssured.given().config(sslconfig).get(s"$url.json"))
+
+ responses.foreach { response =>
+ response.statusCode shouldBe 200
+ response.header("Access-Control-Allow-Origin") shouldBe "*"
+ response.header("Access-Control-Allow-Methods") shouldBe "OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH"
+ response.header("Access-Control-Allow-Headers") shouldBe "Authorization, Content-Type"
+ response.header("Location") shouldBe null
+ response.header("Set-Cookie") shouldBe null
}
-
- it should "ensure that default CORS header is preserved" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("corsHeaderMod.js"))
- val host = getServiceURL()
- val url = host + s"$testRoutePath/$namespace/default/$name"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file, web = Some("true"))
- }
-
- val responses = Seq(
- RestAssured.given().config(sslconfig).options(s"$url.http"),
- RestAssured.given().config(sslconfig).get(s"$url.json"))
-
- responses.foreach { response =>
- response.statusCode shouldBe 200
- response.header("Access-Control-Allow-Origin") shouldBe "*"
- response.header("Access-Control-Allow-Methods") shouldBe "OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH"
- response.header("Access-Control-Allow-Headers") shouldBe "Authorization, Content-Type"
- response.header("Location") shouldBe null
- response.header("Set-Cookie") shouldBe null
- }
+ }
+
+ it should "invoke web action to ensure the returned body argument is correct" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ val bodyContent = "This is the body"
+ val host = getServiceURL()
+ val url = s"$host$testRoutePath/$namespace/default/webaction.text/__ow_body"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"))
+ }
+
+ val paramRes = RestAssured.given().contentType("text/html").param("key", "value").config(sslconfig).post(url)
+ paramRes.statusCode shouldBe 200
+ new String(paramRes.body.asByteArray, StandardCharsets.UTF_8) shouldBe "key=value"
+
+ val bodyRes = RestAssured.given().contentType("text/html").body(bodyContent).config(sslconfig).post(url)
+ bodyRes.statusCode shouldBe 200
+ new String(bodyRes.body.asByteArray, StandardCharsets.UTF_8) shouldBe bodyContent
+ }
+
+ it should "reject invocation of web action with invalid accept header" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("textBody.js"))
+ val host = getServiceURL()
+ val url = host + s"$testRoutePath/$namespace/default/$name.http"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"))
+ }
+
+ val response = RestAssured.given().header("accept", "application/json").config(sslconfig).get(url)
+ response.statusCode shouldBe 406
+ response.body.asString should include("Resource representation is only available with these types:\\ntext/html")
+ }
+
+ it should "support multiple response header values" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "webaction"
+ val file = Some(TestUtils.getTestActionFilename("multipleHeaders.js"))
+ val host = getServiceURL()
+ val url = host + s"$testRoutePath/$namespace/default/$name.http"
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"), annotations = Map("web-custom-options" -> true.toJson))
}
- it should "invoke web action to ensure the returned body argument is correct" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("echo.js"))
- val bodyContent = "This is the body"
- val host = getServiceURL()
- val url = s"$host$testRoutePath/$namespace/default/webaction.text/__ow_body"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file, web = Some("true"))
- }
-
- val paramRes = RestAssured.given().contentType("text/html").param("key", "value").config(sslconfig).post(url)
- paramRes.statusCode shouldBe 200
- new String(paramRes.body.asByteArray, StandardCharsets.UTF_8) shouldBe "key=value"
-
- val bodyRes = RestAssured.given().contentType("text/html").body(bodyContent).config(sslconfig).post(url)
- bodyRes.statusCode shouldBe 200
- new String(bodyRes.body.asByteArray, StandardCharsets.UTF_8) shouldBe bodyContent
- }
+ val response = RestAssured.given().config(sslconfig).options(url)
- it should "reject invocation of web action with invalid accept header" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("textBody.js"))
- val host = getServiceURL()
- val url = host + s"$testRoutePath/$namespace/default/$name.http"
+ response.statusCode shouldBe 200
+ val cookieHeaders = response.headers.getList("Set-Cookie")
+ cookieHeaders should contain allOf (
+ new Header("Set-Cookie", "a=b"),
+ new Header("Set-Cookie", "c=d")
+ )
+ }
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) => action.create(name, file, web = Some("true"))
- }
+ it should "handle http web action with base64 encoded response" in withAssetCleaner(wskprops) { (wp, assetHelper) =>
+ val name = "base64Web"
+ val file = Some(TestUtils.getTestActionFilename("base64Web.js"))
+ val host = getServiceURL
+ val url = host + s"$testRoutePath/$namespace/default/$name.http"
- val response = RestAssured.given().header("accept", "application/json").config(sslconfig).get(url)
- response.statusCode shouldBe 406
- response.body.asString should include("Resource representation is only available with these types:\\ntext/html")
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("raw"))
}
- it should "support multiple response header values" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "webaction"
- val file = Some(TestCLIUtils.getTestActionFilename("multipleHeaders.js"))
- val host = getServiceURL()
- val url = host + s"$testRoutePath/$namespace/default/$name.http"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"), annotations = Map("web-custom-options" -> true.toJson))
- }
-
- val response = RestAssured.given().config(sslconfig).options(url)
-
- response.statusCode shouldBe 200
- val cookieHeaders = response.headers.getList("Set-Cookie")
- cookieHeaders should contain allOf (
- new Header("Set-Cookie", "a=b"),
- new Header("Set-Cookie", "c=d")
- )
- }
-
- it should "handle http web action with base64 encoded response" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "base64Web"
- val file = Some(TestCLIUtils.getTestActionFilename("base64Web.js"))
- val host = getServiceURL
- val url = host + s"$testRoutePath/$namespace/default/$name.http"
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("raw"))
- }
-
- val response = RestAssured.given().config(sslconfig).get(url)
-
- response.statusCode shouldBe 200
- response.header("Content-type") shouldBe "application/json"
- response.body.asString.parseJson.asJsObject shouldBe JsObject("status" -> "success".toJson)
- }
-
- it should "handle http web action with base64 encoded binary response" in withAssetCleaner(wskprops) {
- (wp, assetHelper) =>
- val name = "binaryWeb"
- val file = Some(TestUtils.getTestActionFilename("pngWeb.js"))
- val host = getServiceURL
- val url = host + s"$testRoutePath/$namespace/default/$name.http"
- val png = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAA/klEQVQYGWNgAAEHBxaG//+ZQMyyn581Pfas+cRQnf1LfF" +
- "Ljf+62smUgcUbt0FA2Zh7drf/ffMy9vLn3RurrW9e5hCU11i2azfD4zu1/DHz8TAy/foUxsXBrFzHzC7r8+M9S1vn1qxQT07dDjL" +
- "9fdemrqKxlYGT6z8AIMo6hgeUfA0PUvy9fGFh5GWK3z7vNxSWt++jX99+8SoyiGQwsW38w8PJEM7x5v5SJ8f+/xv8MDAzffv9hev" +
- "fkWjiXBGMpMx+j2awovjcMjFztDO8+7GF49LkbZDCDeXLTWnZO7qDfn1/+5jbw/8pjYWS4wZLztXnuEuYTk2M+MzIw/AcA36Vewa" +
- "D6fzsAAAAASUVORK5CYII="
-
- assetHelper.withCleaner(wsk.action, name) {
- (action, _) =>
- action.create(name, file, web = Some("true"))
- }
-
- val response = RestAssured.given().config(sslconfig).get(url)
-
- response.statusCode shouldBe 200
- response.header("Content-type") shouldBe "image/png"
- response.body.asByteArray shouldBe Base64.getDecoder().decode(png)
- }
-
- private val subdomainRegex = Seq.fill(WhiskProperties.getPartsInVanitySubdomain)("[a-zA-Z0-9]+").mkString("-")
-
- private val (vanitySubdomain, vanityNamespace, makeTestSubject) = {
- if (namespace.matches(subdomainRegex)) {
- (namespace, namespace, false)
- } else {
- val s = Subject().asString.toLowerCase // this will generate two confirming parts
- (s, s.replace("-", "_"), true)
- }
- }
-
- private val wskPropsForSubdomainTest = if (makeTestSubject) {
- getAdditionalTestSubject(vanityNamespace) // create new subject for the test
+ val response = RestAssured.given().config(sslconfig).get(url)
+
+ response.statusCode shouldBe 200
+ response.header("Content-type") shouldBe "application/json"
+ response.body.asString.parseJson.asJsObject shouldBe JsObject("status" -> "success".toJson)
+ }
+
+ it should "handle http web action with base64 encoded binary response" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val name = "binaryWeb"
+ val file = Some(TestUtils.getTestActionFilename("pngWeb.js"))
+ val host = getServiceURL
+ val url = host + s"$testRoutePath/$namespace/default/$name.http"
+ val png = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAGCAYAAAD68A/GAAAA/klEQVQYGWNgAAEHBxaG//+ZQMyyn581Pfas+cRQnf1LfF" +
+ "Ljf+62smUgcUbt0FA2Zh7drf/ffMy9vLn3RurrW9e5hCU11i2azfD4zu1/DHz8TAy/foUxsXBrFzHzC7r8+M9S1vn1qxQT07dDjL" +
+ "9fdemrqKxlYGT6z8AIMo6hgeUfA0PUvy9fGFh5GWK3z7vNxSWt++jX99+8SoyiGQwsW38w8PJEM7x5v5SJ8f+/xv8MDAzffv9hev" +
+ "fkWjiXBGMpMx+j2awovjcMjFztDO8+7GF49LkbZDCDeXLTWnZO7qDfn1/+5jbw/8pjYWS4wZLztXnuEuYTk2M+MzIw/AcA36Vewa" +
+ "D6fzsAAAAASUVORK5CYII="
+
+ assetHelper.withCleaner(wsk.action, name) { (action, _) =>
+ action.create(name, file, web = Some("true"))
+ }
+
+ val response = RestAssured.given().config(sslconfig).get(url)
+
+ response.statusCode shouldBe 200
+ response.header("Content-type") shouldBe "image/png"
+ response.body.asByteArray shouldBe Base64.getDecoder().decode(png)
+ }
+
+ private val subdomainRegex = Seq.fill(WhiskProperties.getPartsInVanitySubdomain)("[a-zA-Z0-9]+").mkString("-")
+
+ private val (vanitySubdomain, vanityNamespace, makeTestSubject) = {
+ if (namespace.matches(subdomainRegex)) {
+ (namespace, namespace, false)
} else {
- WskProps()
+ val s = Subject().asString.toLowerCase // this will generate two confirming parts
+ (s, s.replace("-", "_"), true)
}
+ }
- override def afterAll() = {
- if (makeTestSubject) {
- disposeAdditionalTestSubject(vanityNamespace)
- }
- }
+ private val wskPropsForSubdomainTest = if (makeTestSubject) {
+ getAdditionalTestSubject(vanityNamespace) // create new subject for the test
+ } else {
+ WskProps()
+ }
- "test subdomain" should "have conforming parts" in {
- vanitySubdomain should fullyMatch regex subdomainRegex.r
- vanitySubdomain.length should be <= 63
- }
-
- "vanity subdomain" should "access a web action via namespace subdomain" in withAssetCleaner(wskPropsForSubdomainTest) {
- (wp, assetHelper) =>
- val actionName = "webaction"
-
- val file = Some(TestUtils.getTestActionFilename("echo.js"))
- assetHelper.withCleaner(wsk.action, actionName) {
- (action, _) => action.create(actionName, file, web = Some(true.toString))(wp)
- }
-
- val url = getServiceApiHost(vanitySubdomain, true) + s"/default/$actionName.text/a?a=A"
- println(s"url: $url")
-
- // try the rest assured path first, failing that, try curl with explicit resolve
- Try {
- val response = RestAssured.given().config(sslconfig).get(url)
- val responseCode = response.statusCode
- responseCode shouldBe 200
- response.body.asString shouldBe "A"
- } match {
- case Failure(t) =>
- println(s"RestAssured path failed, trying curl: $t")
- implicit val tid = TransactionId.testing
- implicit val logger = new PrintStreamLogging(Console.out)
- val host = getServiceApiHost(vanitySubdomain, false)
- // if the edge host is a name, try to resolve it, otherwise, it should be an ip address already
- val edgehost = WhiskProperties.getEdgeHost
- val ip = Try(java.net.InetAddress.getByName(edgehost).getHostAddress) getOrElse "???"
- println(s"edge: $edgehost, ip: $ip")
- val cmd = Seq("curl", "-k", url, "--resolve", s"$host:$ip")
- val (stdout, stderr, exitCode) = SimpleExec.syncRunCmd(cmd)
- withClue(s"\n$stderr\n") {
- stdout shouldBe "A"
- exitCode shouldBe 0
- }
-
- case _ =>
- }
+ override def afterAll() = {
+ if (makeTestSubject) {
+ disposeAdditionalTestSubject(vanityNamespace)
}
+ }
+
+ "test subdomain" should "have conforming parts" in {
+ vanitySubdomain should fullyMatch regex subdomainRegex.r
+ vanitySubdomain.length should be <= 63
+ }
+
+ "vanity subdomain" should "access a web action via namespace subdomain" in withAssetCleaner(wskPropsForSubdomainTest) {
+ (wp, assetHelper) =>
+ val actionName = "webaction"
+
+ val file = Some(TestUtils.getTestActionFilename("echo.js"))
+ assetHelper.withCleaner(wsk.action, actionName) { (action, _) =>
+ action.create(actionName, file, web = Some(true.toString))(wp)
+ }
+
+ val url = getServiceApiHost(vanitySubdomain, true) + s"/default/$actionName.text/a?a=A"
+ println(s"url: $url")
+
+ // try the rest assured path first, failing that, try curl with explicit resolve
+ Try {
+ val response = RestAssured.given().config(sslconfig).get(url)
+ val responseCode = response.statusCode
+ responseCode shouldBe 200
+ response.body.asString shouldBe "A"
+ } match {
+ case Failure(t) =>
+ println(s"RestAssured path failed, trying curl: $t")
+ implicit val tid = TransactionId.testing
+ implicit val logger = new PrintStreamLogging(Console.out)
+ val host = getServiceApiHost(vanitySubdomain, false)
+ // if the edge host is a name, try to resolve it, otherwise, it should be an ip address already
+ val edgehost = WhiskProperties.getEdgeHost
+ val ip = Try(java.net.InetAddress.getByName(edgehost).getHostAddress) getOrElse "???"
+ println(s"edge: $edgehost, ip: $ip")
+ val cmd = Seq("curl", "-k", url, "--resolve", s"$host:$ip")
+ val (stdout, stderr, exitCode) = SimpleExec.syncRunCmd(cmd)
+ withClue(s"\n$stderr\n") {
+ stdout shouldBe "A"
+ exitCode shouldBe 0
+ }
+
+ case _ =>
+ }
+ }
}
--
To stop receiving notification emails like this one, please contact
"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>.