diff --git a/core/actionProxy/actionproxy.py b/core/actionProxy/actionproxy.py index 4266f5c777a782eff236823606326ba4a08f0bf7..3c3c84005ff679fe206c9bfd81bcfcb6d03ec400 100644 --- a/core/actionProxy/actionproxy.py +++ b/core/actionProxy/actionproxy.py @@ -91,13 +91,14 @@ class ActionRunner: return (os.path.isfile(self.binary) and os.access(self.binary, os.X_OK)) # constructs an environment for the action to run in - # @param message is a JSON object received from invoker (should contain 'value' and 'authKey') + # @param message is a JSON object received from invoker (should contain 'value' and 'api_key' and other metadata) # @return an environment dictionary for the action process def env(self, message): # make sure to include all the env vars passed in by the invoker env = os.environ - if 'authKey' in message: - env['AUTH_KEY'] = message['authKey'] + for p in [ 'api_key', 'namespace', 'action_name', 'activation_id', 'deadline' ]: + if p in message: + env['__OW_%s' % p.upper()] = message[p] return env # runs the action, called iff self.verify() is True. diff --git a/core/nodejsAction/Dockerfile b/core/nodejsAction/Dockerfile index 6af9760be46dbc7e84950f50abf240ed1ef5c0d1..1abd89e974eb4db4858bc578779f220b4ae4956b 100644 --- a/core/nodejsAction/Dockerfile +++ b/core/nodejsAction/Dockerfile @@ -34,6 +34,7 @@ mustache@2.1.3 \ nano@5.10.0 \ node-uuid@1.4.2 \ oauth2-server@2.4.0 \ +openwhisk@2.6.0 \ process@0.11.0 \ request@2.79.0 \ rimraf@2.5.1 \ diff --git a/tests/src/actionContainers/ActionProxyContainerTests.scala b/tests/src/actionContainers/ActionProxyContainerTests.scala index 110b77b3fad097d1463bcd72358cb4faaecafcd5..8584b85ea3d3de2ef72bb002f1dd6f450b9ba85a 100644 --- a/tests/src/actionContainers/ActionProxyContainerTests.scala +++ b/tests/src/actionContainers/ActionProxyContainerTests.scala @@ -26,12 +26,8 @@ import org.scalatest.junit.JUnitRunner import ActionContainer.withContainer import common.TestUtils import common.WskActorSystem -import spray.json.JsArray -import spray.json.JsNull -import spray.json.JsNumber -import spray.json.JsObject -import spray.json.JsString -import spray.json.JsBoolean +import spray.json.DefaultJsonProtocol._ +import spray.json._ @RunWith(classOf[JUnitRunner]) class ActionProxyContainerTests extends BasicActionRunnerTests with WskActorSystem { @@ -81,20 +77,31 @@ class ActionProxyContainerTests extends BasicActionRunnerTests with WskActorSyst val stdEnvSamples = { val bash = """ |#!/bin/bash - |echo "{ \"auth\": \"$AUTH_KEY\", \"edge\": \"$EDGE_HOST\" }" + |echo "{ \ + |\"api_host\": \"$__OW_API_HOST\", \"api_key\": \"$__OW_API_KEY\", \ + |\"namespace\": \"$__OW_NAMESPACE\", \"action_name\": \"$__OW_ACTION_NAME\", \ + |\"activation_id\": \"$__OW_ACTIVATION_ID\", \"deadline\": \"$__OW_DEADLINE\" }" """.stripMargin.trim val python = """ |#!/usr/bin/env python |import os - |print '{ "auth": "%s", "edge": "%s" }' % (os.environ['AUTH_KEY'], os.environ['EDGE_HOST']) + | + |print '{ "api_host": "%s", "api_key": "%s", "namespace": "%s", "action_name" : "%s", "activation_id": "%s", "deadline": "%s" }' % ( + | os.environ['__OW_API_HOST'], os.environ['__OW_API_KEY'], + | os.environ['__OW_NAMESPACE'], os.environ['__OW_ACTION_NAME'], + | os.environ['__OW_ACTIVATION_ID'], os.environ['__OW_DEADLINE']) """.stripMargin.trim val perl = """ |#!/usr/bin/env perl - |$a = $ENV{'AUTH_KEY'}; - |$e = $ENV{'EDGE_HOST'}; - |print "{ \"auth\": \"$a\", \"edge\": \"$e\" }"; + |$a = $ENV{'__OW_API_HOST'}; + |$b = $ENV{'__OW_API_KEY'}; + |$c = $ENV{'__OW_NAMESPACE'}; + |$d = $ENV{'__OW_ACTION_NAME'}; + |$e = $ENV{'__OW_ACTIVATION_ID'}; + |$f = $ENV{'__OW_DEADLINE'}; + |print "{ \"api_host\": \"$a\", \"api_key\": \"$b\", \"namespace\": \"$c\", \"action_name\": \"$d\", \"activation_id\": \"$e\", \"deadline\": \"$f\" }"; """.stripMargin.trim // excluding perl as it not installed in alpine based image @@ -244,28 +251,37 @@ trait BasicActionRunnerTests extends ActionProxyContainerTestUtils { } /** Runs tests for code samples which are expected to return the expected standard environment {auth, edge}. */ - def testEnv(stdEnvSamples: Seq[(String, String)], enforceEmptyOutputStream: Boolean = true) = { + def testEnv(stdEnvSamples: Seq[(String, String)], enforceEmptyOutputStream: Boolean = true, enforceEmptyErrorStream: Boolean = true) = { for (s <- stdEnvSamples) { it should s"run a ${s._1} script and confirm expected environment variables" in { - val auth = JsString("abc") - val edge = "xyz" - val env = Map("EDGE_HOST" -> edge) - - val (out, err) = withActionContainer(env) { c => + val props = Seq( + "api_host" -> "xyz", + "api_key" -> "abc", + "namespace" -> "zzz", + "action_name" -> "xxx", + "activation_id" -> "iii", + "deadline" -> "123") + val env = props.map { case (k, v) => s"__OW_${k.toUpperCase()}" -> v } + + val (out, err) = withActionContainer(env.take(1).toMap) { c => val (initCode, _) = c.init(initPayload(s._2)) initCode should be(200) - val (runCode, out) = c.run(runPayload(JsObject(), Some(JsObject("authKey" -> auth)))) + val (runCode, out) = c.run(runPayload(JsObject(), Some(props.toMap.toJson.asJsObject))) runCode should be(200) out shouldBe defined - out.get.fields("auth") shouldBe auth - out.get.fields("edge") shouldBe JsString(edge) + props.map { + case (k, v) => withClue(k) { + out.get.fields(k) shouldBe JsString(v) + } + + } } checkStreams(out, err, { case (o, e) => if (enforceEmptyOutputStream) o shouldBe empty - e shouldBe empty + if (enforceEmptyErrorStream) e shouldBe empty }) } } diff --git a/tests/src/actionContainers/JavaActionContainerTests.scala b/tests/src/actionContainers/JavaActionContainerTests.scala index 77bfff7a1efe2f40eca1713195502e026c9c7287..876f0b4ba28b163b5e755bcd47f8452382a7cb21 100644 --- a/tests/src/actionContainers/JavaActionContainerTests.scala +++ b/tests/src/actionContainers/JavaActionContainerTests.scala @@ -20,6 +20,7 @@ import org.junit.runner.RunWith import org.scalatest.FlatSpec import org.scalatest.Matchers import org.scalatest.junit.JUnitRunner +import spray.json.DefaultJsonProtocol._ import spray.json._ import ActionContainer.withContainer @@ -28,19 +29,62 @@ import ResourceHelpers.JarBuilder import common.WskActorSystem @RunWith(classOf[JUnitRunner]) -class JavaActionContainerTests extends FlatSpec with Matchers with WskActorSystem { +class JavaActionContainerTests extends FlatSpec with Matchers with WskActorSystem with ActionProxyContainerTestUtils { // Helpers specific to javaaction - def withJavaContainer(code: ActionContainer => Unit) = withContainer("javaaction")(code) + def withJavaContainer(code: ActionContainer => Unit, env: Map[String, String] = Map.empty) = withContainer("javaaction", env)(code) def initPayload(mainClass: String, jar64: String) = JsObject( "value" -> JsObject( "name" -> JsString("dummyAction"), "main" -> JsString(mainClass), "jar" -> JsString(jar64))) - def runPayload(args: JsValue) = JsObject("value" -> args) behavior of "Java action" + it should s"run a java snippet and confirm expected environment variables" in { + val props = Seq("api_host" -> "xyz", + "api_key" -> "abc", + "namespace" -> "zzz", + "action_name" -> "xxx", + "activation_id" -> "iii", + "deadline" -> "123") + val env = props.map { case (k, v) => s"__OW_${k.toUpperCase}" -> v } + val (out, err) = withJavaContainer({ c => + val jar = JarBuilder.mkBase64Jar( + Seq("example", "HelloWhisk.java") -> """ + | package example; + | + | import com.google.gson.JsonObject; + | + | public class HelloWhisk { + | public static JsonObject main(JsonObject args) { + | JsonObject response = new JsonObject(); + | response.addProperty("api_host", System.getenv("__OW_API_HOST")); + | response.addProperty("api_key", System.getenv("__OW_API_KEY")); + | response.addProperty("namespace", System.getenv("__OW_NAMESPACE")); + | response.addProperty("action_name", System.getenv("__OW_ACTION_NAME")); + | response.addProperty("activation_id", System.getenv("__OW_ACTIVATION_ID")); + | response.addProperty("deadline", System.getenv("__OW_DEADLINE")); + | return response; + | } + | } + """.stripMargin.trim) + + val (initCode, _) = c.init(initPayload("example.HelloWhisk", jar)) + initCode should be(200) + + val (runCode, out) = c.run(runPayload(JsObject(), Some(props.toMap.toJson.asJsObject))) + runCode should be(200) + props.map { + case (k, v) => out.get.fields(k) shouldBe JsString(v) + + } + }, env.take(1).toMap) + + out.trim shouldBe empty + err.trim shouldBe empty + } + it should "support valid flows" in { val (out, err) = withJavaContainer { c => val jar = JarBuilder.mkBase64Jar( diff --git a/tests/src/actionContainers/NodeJsActionContainerTests.scala b/tests/src/actionContainers/NodeJsActionContainerTests.scala index 395449dabd802f395e2920357e820fe8c4c5d7d9..f33e3e26c21103407b22e78cc7b2b96dd079574d 100644 --- a/tests/src/actionContainers/NodeJsActionContainerTests.scala +++ b/tests/src/actionContainers/NodeJsActionContainerTests.scala @@ -31,6 +31,8 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys lazy val nodejsContainerImageName = "nodejsaction" + val hasDeprecationWarnings = true + override def withActionContainer(env: Map[String, String] = Map.empty)(code: ActionContainer => Unit) = { withContainer(nodejsContainerImageName, env)(code) } @@ -68,6 +70,21 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys """.stripMargin) }) + testEnv(Seq { + ("node", """ + |function main(args) { + | return { + | "api_host": process.env['__OW_API_HOST'], + | "api_key": process.env['__OW_API_KEY'], + | "namespace": process.env['__OW_NAMESPACE'], + | "action_name": process.env['__OW_ACTION_NAME'], + | "activation_id": process.env['__OW_ACTIVATION_ID'], + | "deadline": process.env['__OW_DEADLINE'] + | } + |} + """.stripMargin.trim) + }) + it should "fail to initialize with bad code" in { val (out, err) = withNodeJsContainer { c => val code = """ @@ -158,7 +175,7 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys checkStreams(out, err, { case (o, e) => o shouldBe empty - e shouldBe empty + if (!hasDeprecationWarnings) e shouldBe empty }) } @@ -180,7 +197,58 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys checkStreams(out, err, { case (o, e) => o shouldBe empty - e shouldBe empty + if (!hasDeprecationWarnings) e shouldBe empty + }) + } + + it should "warn when using deprecated whisk object methods" in { + val (out, err) = withNodeJsContainer { c => + val code = """ + | function main(args) { + | whisk.getAuthKey(whisk.setAuthKey('xxx')); + | try { whisk.invoke(); } catch (e) {} + | try { whisk.trigger(); } catch (e) {} + | setTimeout(function () { whisk.done(); }, 1000); + | return whisk.async(); + | } + """.stripMargin + + c.init(initPayload(code))._1 should be(200) + + val (runCode, runRes) = c.run(runPayload(JsObject())) + runCode should be(200) + } + + checkStreams(out, err, { + case (o, e) => + o shouldBe empty + e should not be empty + val lines = e.split("\n") + lines.filter { l => l.startsWith("[WARN] \"whisk.") && l.contains("deprecated") }.length shouldBe 8 + }) + } + + it should "warn when using deprecated whisk.error" in { + val (out, err) = withNodeJsContainer { c => + val code = """ + | function main(args) { + | whisk.error("{warnme: true}"); + | } + """.stripMargin + + c.init(initPayload(code))._1 should be(200) + + val (runCode, runRes) = c.run(runPayload(JsObject())) + runCode should be(200) + } + + checkStreams(out, err, { + case (o, e) => + o shouldBe empty + e should not be empty + val lines = e.split("\n") + lines.length shouldBe 1 + lines.forall { l => l.startsWith("[WARN] \"whisk.") && l.contains("deprecated") } }) } @@ -205,7 +273,7 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys checkStreams(out, err, { case (o, e) => o should include("more than once") - e shouldBe empty + if (!hasDeprecationWarnings) e shouldBe empty }) } @@ -243,7 +311,7 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys checkStreams(out, err, { case (o, e) => o shouldBe empty - e shouldBe empty + if (!hasDeprecationWarnings) e shouldBe empty }, 3) } @@ -268,7 +336,7 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys checkStreams(out, err, { case (o, e) => o shouldBe empty - e shouldBe empty + if (!hasDeprecationWarnings) e shouldBe empty }) } @@ -302,7 +370,7 @@ class NodeJsActionContainerTests extends BasicActionRunnerTests with WskActorSys checkStreams(out, err, { case (o, e) => o shouldBe empty - e shouldBe empty + if (!hasDeprecationWarnings) e shouldBe empty }, 2) } diff --git a/tests/src/actionContainers/PythonActionContainerTests.scala b/tests/src/actionContainers/PythonActionContainerTests.scala index fe17476c12a8f8b9db4600938035e90c4ce75c0d..41968bf26e7a2189b9785989ca3204b0b9a3ca51 100644 --- a/tests/src/actionContainers/PythonActionContainerTests.scala +++ b/tests/src/actionContainers/PythonActionContainerTests.scala @@ -53,7 +53,14 @@ class PythonActionContainerTests extends BasicActionRunnerTests with WskActorSys ("python", """ |import os |def main(dict): - | return { "auth": os.environ['AUTH_KEY'], "edge": os.environ['EDGE_HOST'] } + | return { + | "api_host": os.environ['__OW_API_HOST'], + | "api_key": os.environ['__OW_API_KEY'], + | "namespace": os.environ['__OW_NAMESPACE'], + | "action_name": os.environ['__OW_ACTION_NAME'], + | "activation_id": os.environ['__OW_ACTIVATION_ID'], + | "deadline": os.environ['__OW_DEADLINE'] + | } """.stripMargin.trim) }) diff --git a/tests/src/actionContainers/Swift3ActionContainerTests.scala b/tests/src/actionContainers/Swift3ActionContainerTests.scala index f1f5bbde75d08fdbea22ca4aa008ed59754a050d..a294bea65b344d39eb9f859264d1adb2d9d65a83 100644 --- a/tests/src/actionContainers/Swift3ActionContainerTests.scala +++ b/tests/src/actionContainers/Swift3ActionContainerTests.scala @@ -27,20 +27,8 @@ class Swift3ActionContainerTests extends SwiftActionContainerTests { override val enforceEmptyOutputStream = false override lazy val swiftContainerImageName = "swift3action" - override lazy val envCode = """ - |func main(args: [String: Any]) -> [String: Any] { - | let env = ProcessInfo.processInfo.environment - | var auth = "???" - | var edge = "???" - | if let authKey : String = env["AUTH_KEY"] { - | auth = "\(authKey)" - | } - | if let edgeHost : String = env["EDGE_HOST"] { - | edge = "\(edgeHost)" - | } - | return ["auth": auth, "edge": edge] - |} - """.stripMargin + override lazy val envCode = makeEnvCode("ProcessInfo.processInfo") + override lazy val errorCode = """ | // You need an indirection, or swiftc detects the div/0 | // at compile-time. Smart. diff --git a/tests/src/actionContainers/SwiftActionContainerTests.scala b/tests/src/actionContainers/SwiftActionContainerTests.scala index 44d965e26eb5d2b7841d9f5fbb0fe073c7dc2b51..cfe3ce2a81c96b92a82f5935f066cd564461a399 100644 --- a/tests/src/actionContainers/SwiftActionContainerTests.scala +++ b/tests/src/actionContainers/SwiftActionContainerTests.scala @@ -32,21 +32,38 @@ class SwiftActionContainerTests extends BasicActionRunnerTests with WskActorSyst // prints status messages and there doesn't seem to be a way to quiet them val enforceEmptyOutputStream = true lazy val swiftContainerImageName = "swiftaction" + lazy val envCode = makeEnvCode("NSProcessInfo.processInfo()") - lazy val envCode = """ + def makeEnvCode(processInfo: String) = (""" |func main(args: [String: Any]) -> [String: Any] { - | let env = NSProcessInfo.processInfo().environment - | var auth = "???" - | var edge = "???" - | if let authKey : String = env["AUTH_KEY"] { - | auth = "\(authKey)" + | let env = """+processInfo+""".environment + | var a = "???" + | var b = "???" + | var c = "???" + | var d = "???" + | var e = "???" + | var f = "???" + | if let v : String = env["__OW_API_HOST"] { + | a = "\(v)" | } - | if let edgeHost : String = env["EDGE_HOST"] { - | edge = "\(edgeHost)" + | if let v : String = env["__OW_API_KEY"] { + | b = "\(v)" | } - | return ["auth": auth, "edge": edge] + | if let v : String = env["__OW_NAMESPACE"] { + | c = "\(v)" + | } + | if let v : String = env["__OW_ACTION_NAME"] { + | d = "\(v)" + | } + | if let v : String = env["__OW_ACTIVATION_ID"] { + | e = "\(v)" + | } + | if let v : String = env["__OW_DEADLINE"] { + | f = "\(v)" + | } + | return ["api_host": a, "api_key": b, "namespace": c, "action_name": d, "activation_id": e, "deadline": f] |} - """.stripMargin + """).stripMargin lazy val errorCode = """ | // You need an indirection, or swiftc detects the div/0 diff --git a/tests/src/system/basic/CLIPythonTests.scala b/tests/src/system/basic/CLIPythonTests.scala index 2f00ca645254fdc5f307e5baf92b8dc96f5872eb..891745bc0e7b04db7f571101a60147c8854bb0d3 100644 --- a/tests/src/system/basic/CLIPythonTests.scala +++ b/tests/src/system/basic/CLIPythonTests.scala @@ -72,7 +72,7 @@ class CLIPythonTests it should "invoke an invalid action and get error back" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "basicInvoke" + val name = "bad code" assetHelper.withCleaner(wsk.action, name) { (action, _) => action.create(name, Some(TestUtils.getTestActionFilename("malformed.py"))) } diff --git a/tests/src/system/basic/WskActionTests.scala b/tests/src/system/basic/WskActionTests.scala index 1cc8e8e0baca054d0eefac85f6d302cc8d274481..524eb20605db9331aaa78b0fa7c8d1f228cee55a 100644 --- a/tests/src/system/basic/WskActionTests.scala +++ b/tests/src/system/basic/WskActionTests.scala @@ -45,6 +45,22 @@ class WskActionTests 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" @@ -169,7 +185,7 @@ class WskActionTests wsk.parseJsonString(rr.stdout).getFieldPath("exec", "code") shouldBe Some(JsString("")) } - it should "blocking invoke nested blocking actions" in withAssetCleaner(wskprops) { + it should "blocking invoke of nested blocking actions" in withAssetCleaner(wskprops) { (wp, assetHelper) => val name = "nestedBlockingAction" val child = "wc" diff --git a/tests/src/system/basic/WskBasicNodeTests.scala b/tests/src/system/basic/WskBasicNodeTests.scala index 4e5fdca130bc5a53c43e0e58470d2de948757cfa..d78c7d620a99f8e993f138442b4c90db9f8ea090 100644 --- a/tests/src/system/basic/WskBasicNodeTests.scala +++ b/tests/src/system/basic/WskBasicNodeTests.scala @@ -113,6 +113,7 @@ class WskBasicNodeTests } } + // TODO: remove this tests and its assets when "whisk.js" is removed entirely as it is no longer necessary it should "Ensure that whisk.invoke() returns a promise" in withAssetCleaner(wskprops) { val expectedDuration = 3.seconds @@ -165,6 +166,7 @@ class WskBasicNodeTests } } + // TODO: remove this tests and its assets when "whisk.js" is removed entirely as it is no longer necessary it should "Ensure that whisk.invoke() still uses a callback when provided one" in withAssetCleaner(wskprops) { val expectedDuration = 3.seconds @@ -216,6 +218,7 @@ class WskBasicNodeTests } } + // TODO: remove this tests and its assets when "whisk.js" is removed entirely as it is no longer necessary it should "Ensure that whisk.trigger() still uses a callback when provided one" in withAssetCleaner(wskprops) { (wp, assetHelper) => // this action supplies a 'next' callback to whisk.trigger() @@ -251,6 +254,7 @@ class WskBasicNodeTests } } + // TODO: remove this tests and its assets when "whisk.js" is removed entirely as it is no longer necessary it should "Ensure that whisk.trigger() returns a promise" in withAssetCleaner(wskprops) { (wp, assetHelper) => // this action supplies a 'next' callback to whisk.trigger() diff --git a/tests/src/system/basic/WskBasicTests.scala b/tests/src/system/basic/WskBasicTests.scala index 36c661dcb32bea4a4861d4cc699c3db3d30d8b50..1acfa4087fcaa55aa41e59580ab4434e13bd7a43 100644 --- a/tests/src/system/basic/WskBasicTests.scala +++ b/tests/src/system/basic/WskBasicTests.scala @@ -16,19 +16,15 @@ package system.basic -import java.time.Instant import java.io.File +import java.time.Instant import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner import common.TestHelpers import common.TestUtils -import common.TestUtils.CONFLICT -import common.TestUtils.SUCCESS_EXIT -import common.TestUtils.UNAUTHORIZED -import common.TestUtils.FORBIDDEN -import common.TestUtils.ERROR_EXIT +import common.TestUtils._ import common.Wsk import common.WskProps import common.WskTestHelpers @@ -361,15 +357,17 @@ class WskBasicTests } } - it should "create and invoke a blocking action resulting in an error response object" in withAssetCleaner(wskprops) { + 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"))) } - wsk.action.invoke(name, blocking = true, expectedExitCode = 246) - .stderr should include regex (""""error": "name '!' contains illegal characters \(code \d+\)"""") + val stderr = wsk.action.invoke(name, blocking = true, expectedExitCode = 246).stderr + CliActivation.serdes.read(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) { diff --git a/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala b/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala index e832c112c3212f721113c6db5f4e56a1a618adaf..0bf4198b744236be510f2a5074042d1e6887f2db 100644 --- a/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala +++ b/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala @@ -45,6 +45,8 @@ import whisk.core.entity.size.SizeInt import whisk.utils.retry import JsonArgsForTests._ import whisk.http.Messages +import common.WskAdmin +import java.time.Clock /** * Tests for basic CLI usage. Some of these tests require a deployed backend. @@ -452,6 +454,63 @@ class WskBasicUsageTests } } + 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 user = WskAdmin.getUser(wskprops.authKey) + 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.getEdgeHost + ":" + WhiskProperties.getEdgeHostApiPort + fields("api_key") shouldBe wskprops.authKey + fields("namespace") shouldBe user + fields("action_name") shouldBe s"/$user/$name" + fields("activation_id") shouldBe activation.activationId + fields("deadline").toLong should be >= start + } + } + + it should s"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 start = Instant.now(Clock.systemUTC()).toEpochMilli + 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)) + } + } + behavior of "Wsk packages" it should "create, and get a package to verify parameter and annotation parsing" in withAssetCleaner(wskprops) {