From ea6dce4578221a1f94a52fab20b72a718be3fa46 Mon Sep 17 00:00:00 2001 From: James Dubee <jwdubee@us.ibm.com> Date: Wed, 12 Oct 2016 14:33:50 -0400 Subject: [PATCH] Allow JSON to be Input from a File (#1175) - Pass JSON files to parameters and annotations - Refactor parameter and annotation handling - Use KeyValueArr data type to store annotations and parameters - Refactor summaries for get commands - Use KeyValueArr data type to get annotations and parameters - Refactor parameter and annotation tests - Order of params and annots are not guaranteed in Go map data structures - Fixes: #426 --- tests/src/common/Wsk.scala | 18 +- tests/src/system/basic/WskBasicTests.scala | 41 +++ .../core/cli/test/JsonArgsForTests.scala | 106 ++++-- .../core/cli/test/WskBasicUsageTests.scala | 325 +++++++++++++----- 4 files changed, 381 insertions(+), 109 deletions(-) diff --git a/tests/src/common/Wsk.scala b/tests/src/common/Wsk.scala index ede51dba..c125a71b 100644 --- a/tests/src/common/Wsk.scala +++ b/tests/src/common/Wsk.scala @@ -225,6 +225,8 @@ class WskAction() kind: Option[String] = None, // one of docker, copy, sequence or none for autoselect else an explicit type parameters: Map[String, JsValue] = Map(), annotations: Map[String, JsValue] = Map(), + parameterFile: Option[String] = None, + annotationFile: Option[String] = None, timeout: Option[Duration] = None, memory: Option[ByteSize] = None, logsize: Option[ByteSize] = None, @@ -242,6 +244,8 @@ class WskAction() } ++ { parameters flatMap { p => Seq("-p", p._1, p._2.compactPrint) } } ++ { annotations flatMap { p => Seq("-a", p._1, p._2.compactPrint) } } ++ + { parameterFile map { pf => Seq("-P", pf) } getOrElse Seq() } ++ + { annotationFile map { af => Seq("-A", af) } getOrElse Seq() } ++ { timeout map { t => Seq("-t", t.toMillis.toString) } getOrElse Seq() } ++ { memory map { m => Seq("-m", m.toMB.toString) } getOrElse Seq() } ++ { logsize map { l => Seq("-l", l.toMB.toString) } getOrElse Seq() } ++ @@ -259,12 +263,14 @@ class WskAction() def invoke( name: String, parameters: Map[String, JsValue] = Map(), + parameterFile: Option[String] = None, blocking: Boolean = false, result: Boolean = false, expectedExitCode: Int = SUCCESS_EXIT)( implicit wp: WskProps): RunResult = { val params = Seq(noun, "invoke", "--auth", wp.authKey, fqn(name)) ++ { parameters flatMap { p => Seq("-p", p._1, p._2.compactPrint) } } ++ + { parameterFile map { pf => Seq("-P", pf) } getOrElse Seq() } ++ { if (blocking) Seq("--blocking") else Seq() } ++ { if (result) Seq("--result") else Seq() } cli(wp.overrides ++ params, expectedExitCode) @@ -290,6 +296,8 @@ class WskTrigger() name: String, parameters: Map[String, JsValue] = Map(), annotations: Map[String, JsValue] = Map(), + parameterFile: Option[String] = None, + annotationFile: Option[String] = None, feed: Option[String] = None, shared: Option[Boolean] = None, update: Boolean = false, @@ -299,6 +307,8 @@ class WskTrigger() { feed map { f => Seq("--feed", fqn(f)) } getOrElse Seq() } ++ { parameters flatMap { p => Seq("-p", p._1, p._2.compactPrint) } } ++ { annotations flatMap { p => Seq("-a", p._1, p._2.compactPrint) } } ++ + { parameterFile map { pf => Seq("-P", pf) } getOrElse Seq() } ++ + { annotationFile map { af => Seq("-A", af) } getOrElse Seq() } ++ { shared map { s => Seq("--shared", if (s) "yes" else "no") } getOrElse Seq() } cli(wp.overrides ++ params, expectedExitCode) } @@ -313,10 +323,12 @@ class WskTrigger() def fire( name: String, parameters: Map[String, JsValue] = Map(), + parameterFile: Option[String] = None, expectedExitCode: Int = SUCCESS_EXIT)( implicit wp: WskProps): RunResult = { val params = Seq(noun, "fire", "--auth", wp.authKey, fqn(name)) ++ - { parameters flatMap { p => Seq("-p", p._1, p._2.compactPrint) } } + { parameters flatMap { p => Seq("-p", p._1, p._2.compactPrint) } } ++ + { parameterFile map { pf => Seq("-P", pf) } getOrElse Seq() } cli(wp.overrides ++ params, expectedExitCode) } } @@ -650,6 +662,8 @@ class WskPackage() name: String, parameters: Map[String, JsValue] = Map(), annotations: Map[String, JsValue] = Map(), + parameterFile: Option[String] = None, + annotationFile: Option[String] = None, shared: Option[Boolean] = None, update: Boolean = false, expectedExitCode: Int = SUCCESS_EXIT)( @@ -657,6 +671,8 @@ class WskPackage() val params = Seq(noun, if (!update) "create" else "update", "--auth", wp.authKey, fqn(name)) ++ { parameters flatMap { p => Seq("-p", p._1, p._2.compactPrint) } } ++ { annotations flatMap { p => Seq("-a", p._1, p._2.compactPrint) } } ++ + { parameterFile map { pf => Seq("-P", pf) } getOrElse Seq() } ++ + { annotationFile map { af => Seq("-A", af) } getOrElse Seq() } ++ { shared map { s => Seq("--shared", if (s) "yes" else "no") } getOrElse Seq() } cli(wp.overrides ++ params, expectedExitCode) } diff --git a/tests/src/system/basic/WskBasicTests.scala b/tests/src/system/basic/WskBasicTests.scala index 36886e9c..24492e96 100644 --- a/tests/src/system/basic/WskBasicTests.scala +++ b/tests/src/system/basic/WskBasicTests.scala @@ -238,6 +238,26 @@ class WskBasicTests } } + 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) + } + } + /** * 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 @@ -394,6 +414,27 @@ class WskBasicTests 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) + } + } + behavior of "Wsk Rule CLI" it should "create rule, get rule, update rule and list rule" in withAssetCleaner(wskprops) { diff --git a/tests/src/whisk/core/cli/test/JsonArgsForTests.scala b/tests/src/whisk/core/cli/test/JsonArgsForTests.scala index e2cf23ba..4e41b856 100644 --- a/tests/src/whisk/core/cli/test/JsonArgsForTests.scala +++ b/tests/src/whisk/core/cli/test/JsonArgsForTests.scala @@ -24,41 +24,85 @@ import spray.json.JsBoolean object JsonArgsForTests { - def getEscapedJSONTestArgInput(parameters: Boolean = true) = Seq( - if (parameters) "-p" else "-a", - "\"key\"with\\escapes", // key: key"with\escapes (will be converted to JSON string "key\"with\\escapes") - "{\"valid\": \"JSON\"}", // value: {"valid":"JSON"} - if (parameters) "-p" else "-a", - "another\"escape\"", // key: another"escape" (will be converted to JSON string "another\"escape\"") - "{\"valid\": \"\\nJ\\rO\\tS\\bN\\f\"}", // value: {"valid":"\nJ\rO\tS\bN\f"} JSON strings can escape: \n, \r, \t, \b, \f - // NOTE: When uncommentting these tests, be sure to include the expected response in getEscapedJSONTestArgOutput() - // if (parameters) "-p" else "-a", - // "escape\\again", // key: escape\again (will be converted to JSON string "escape\\again") - // "{\"valid\": \"JS\\u2312ON\"}", // value: {"valid":"JS\u2312ON"} JSON strings can have escaped 4 digit unicode - // if (parameters) "-p" else "-a", - // "mykey", // key: mykey (will be converted to JSON string "key") - // "{\"valid\": \"JS\\/ON\"}", // value: {"valid":"JS\/ON"} JSON strings can have escaped \/ - if (parameters) "-p" else "-a", - "key1", // key: key (will be converted to JSON string "key") - "{\"nonascii\": \"日本語\"}", // value: {"nonascii":"日本語"} JSON strings can have non-ascii - if (parameters) "-p" else "-a", - "key2", // key: key (will be converted to JSON string "key") - "{\"valid\": \"J\\\\SO\\\"N\"}" // value: {"valid":"J\\SO\"N"} JSON strings can have escaped \\ and \" + def getInvalidJSONInput = Seq( + "{\"invalid1\": }", + "{\"invalid2\": bogus}", + "{\"invalid1\": \"aKey\"", + "invalid \"string\"", + "{\"invalid1\": [1, 2, \"invalid\"\"arr\"]}" ) - def getEscapedJSONTestArgOutput() = JsArray( + def getJSONFileOutput() = JsArray( JsObject( - "key" -> JsString("\"key\"with\\escapes"), + "key" -> JsString("a key"), + "value" -> JsString("a value") + ), + JsObject( + "key" -> JsString("a bool"), + "value" -> JsBoolean(true) + ), + JsObject( + "key" -> JsString("objKey"), "value" -> JsObject( - "valid" -> JsString("JSON") + "b" -> JsString("c") ) ), JsObject( - "key" -> JsString("another\"escape\""), + "key" -> JsString("objKey2"), + "value" -> JsObject( + "another object" -> JsObject( + "some string" -> JsString("1111") + ) + ) + ), + JsObject( + "key" -> JsString("objKey3"), "value" -> JsObject( - "valid" -> JsString("\nJ\rO\tS\bN\f") + "json object" -> JsObject( + "some int" -> JsNumber(1111) + ) + ) + ), + JsObject( + "key" -> JsString("a number arr"), + "value" -> JsArray( + JsNumber(1), JsNumber(2), JsNumber(3) + ) + ), + JsObject( + "key" -> JsString("a string arr"), + "value" -> JsArray( + JsString("1"), JsString("2"), JsString("3") + ) + ), + JsObject( + "key" -> JsString("a bool arr"), + "value" -> JsArray( + JsBoolean(true), JsBoolean(false), JsBoolean(true) ) ), + JsObject( + "key" -> JsString("strThatLooksLikeJSON"), + "value" -> JsString("{\"someKey\": \"someValue\"}") + ) + ) + + def getEscapedJSONTestArgInput() = Map( + "key1" -> JsObject( + "nonascii" -> JsString("日本語") + ), + "key2" -> JsObject( + "valid" -> JsString("J\\SO\"N") + ), + "\"key\"with\\escapes" -> JsObject( + "valid" -> JsString("JSON") + ), + "another\"escape\"" -> JsObject( + "valid" -> JsString("\\nJ\\rO\\tS\\bN\\f") + ) + ) + + def getEscapedJSONTestArgOutput() = JsArray( JsObject( "key" -> JsString("key1"), "value" -> JsObject( @@ -70,6 +114,18 @@ object JsonArgsForTests { "value" -> JsObject( "valid" -> JsString("J\\SO\"N") ) + ), + JsObject( + "key" -> JsString("\"key\"with\\escapes"), + "value" -> JsObject( + "valid" -> JsString("JSON") + ) + ), + JsObject( + "key" -> JsString("another\"escape\""), + "value" -> JsObject( + "valid" -> JsString("\\nJ\\rO\\tS\\bN\\f") + ) ) ) diff --git a/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala b/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala index 5e6f5f77..f2aa9592 100644 --- a/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala +++ b/tests/src/whisk/core/cli/test/WskBasicUsageTests.scala @@ -261,68 +261,76 @@ class WskBasicUsageTests wsk.action.create("updateMissingFile", Some("notfound"), update = true, expectedExitCode = MISUSE_EXIT) } - it should "create, and get an action to verify annotation parsing" in withAssetCleaner(wskprops) { + 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) + action.create(name, file, annotations = getValidJSONTestArgInput, + parameters = getValidJSONTestArgInput) } val stdout = wsk.action.get(name).stdout assert(stdout.startsWith(s"ok: got action $name\n")) - wsk.parseJsonString(stdout).fields("annotations") shouldBe getValidJSONTestArgOutput + 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 parameter parsing" in withAssetCleaner(wskprops) { + it should "create, and get an action to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "actionParameters" - + 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, parameters = getValidJSONTestArgInput) + action.create(name, file, annotationFile = argInput, parameterFile = argInput) } val stdout = wsk.action.get(name).stdout assert(stdout.startsWith(s"ok: got action $name\n")) - wsk.parseJsonString(stdout).fields("parameters") shouldBe getValidJSONTestArgOutput - } + 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 - it should "create an action with the proper parameter escapes" in withAssetCleaner(wskprops) { - (wp, assetHelper) => - val name = "actionName" - val file = TestUtils.getTestActionFilename("hello.js") - assetHelper.withCleaner(wsk.action, name) { - (action, _) => - wsk.cli(wskprops.overrides ++ Seq("action", "create", wsk.action.fqn(name), file, "--auth", wp.authKey) ++ - getEscapedJSONTestArgInput()) + for (expectedItem <- escapedJSONArr) { + receivedParams should contain(expectedItem) + receivedAnnots should contain(expectedItem) } - - val stdout = wsk.action.get(name).stdout - assert(stdout.startsWith(s"ok: got action $name\n")) - - wsk.parseJsonString(stdout).fields("parameters") shouldBe getEscapedJSONTestArgOutput } - it should "create an action with the proper annotation escapes" in withAssetCleaner(wskprops) { + it should "create an action with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "actionName" - val file = TestUtils.getTestActionFilename("hello.js") + val name = "actionEscapes" + val file = Some(TestUtils.getTestActionFilename("hello.js")) + assetHelper.withCleaner(wsk.action, name) { (action, _) => - wsk.cli(wskprops.overrides ++ Seq("action", "create", wsk.action.fqn(name), file, "--auth", wp.authKey) ++ - getEscapedJSONTestArgInput(false)) + action.create(name, file, parameters = getEscapedJSONTestArgInput, + annotations = getEscapedJSONTestArgInput) } val stdout = wsk.action.get(name).stdout assert(stdout.startsWith(s"ok: got action $name\n")) - wsk.parseJsonString(stdout).fields("annotations") shouldBe getEscapedJSONTestArgOutput + 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 init and get appropriate error" in withAssetCleaner(wskprops) { @@ -443,96 +451,121 @@ class WskBasicUsageTests behavior of "Wsk packages" - it should "create, and get a package to verify annotation parsing" in withAssetCleaner(wskprops) { + it should "create, and get a package to verify parameter and annotation parsing" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "packageAnnotations" + val name = "packageAnnotAndParamParsing" assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) => - pkg.create(name, annotations = getValidJSONTestArgInput) + pkg.create(name, annotations = getValidJSONTestArgInput, parameters = getValidJSONTestArgInput) } val stdout = wsk.pkg.get(name).stdout assert(stdout.startsWith(s"ok: got package $name\n")) - wsk.parseJsonString(stdout).fields("annotations") shouldBe getValidJSONTestArgOutput + 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 parameter parsing" in withAssetCleaner(wskprops) { + it should "create, and get a package to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "packageParameters" + 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, parameters = getValidJSONTestArgInput) + pkg.create(name, annotationFile = argInput, parameterFile = argInput) } val stdout = wsk.pkg.get(name).stdout assert(stdout.startsWith(s"ok: got package $name\n")) - wsk.parseJsonString(stdout).fields("parameters") shouldBe getValidJSONTestArgOutput - } + 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 - it should "create a package with the proper parameter escapes" in withAssetCleaner(wskprops) { - (wp, assetHelper) => - val name = "packageName" - assetHelper.withCleaner(wsk.pkg, name) { - (pkg, _) => - wsk.cli(wskprops.overrides ++ Seq("package", "create", wsk.pkg.fqn(name), "--auth", wp.authKey) ++ - getEscapedJSONTestArgInput()) + for (expectedItem <- escapedJSONArr) { + receivedParams should contain(expectedItem) + receivedAnnots should contain(expectedItem) } - - val stdout = wsk.pkg.get(name).stdout - assert(stdout.startsWith(s"ok: got package $name\n")) - - wsk.parseJsonString(stdout).fields("parameters") shouldBe getEscapedJSONTestArgOutput } - it should "create an package with the proper annotation escapes" in withAssetCleaner(wskprops) { + it should "create a package with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "packageName" + val name = "packageEscapses" + assetHelper.withCleaner(wsk.pkg, name) { (pkg, _) => - wsk.cli(wskprops.overrides ++ Seq("package", "create", wsk.pkg.fqn(name), "--auth", wp.authKey) ++ - getEscapedJSONTestArgInput(false)) + pkg.create(name, parameters = getEscapedJSONTestArgInput, + annotations = getEscapedJSONTestArgInput) } val stdout = wsk.pkg.get(name).stdout assert(stdout.startsWith(s"ok: got package $name\n")) - wsk.parseJsonString(stdout).fields("annotations") shouldBe getEscapedJSONTestArgOutput + 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) + } } behavior of "Wsk triggers" - it should "create, and get a trigger to verify annotation parsing" in withAssetCleaner(wskprops) { + it should "create, and get a trigger to verify parameter and annotation parsing" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "triggerAnnotations" + val name = "triggerAnnotAndParamParsing" assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) => - trigger.create(name, annotations = getValidJSONTestArgInput) + trigger.create(name, annotations = getValidJSONTestArgInput, parameters = getValidJSONTestArgInput) } val stdout = wsk.trigger.get(name).stdout assert(stdout.startsWith(s"ok: got trigger $name\n")) - wsk.parseJsonString(stdout).fields("annotations") shouldBe getValidJSONTestArgOutput + 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 parameter parsing" in withAssetCleaner(wskprops) { + it should "create, and get a trigger to verify file parameter and annotation parsing" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "triggerParameters" + 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, parameters = getValidJSONTestArgInput) + trigger.create(name, annotationFile = argInput, parameterFile = argInput) } val stdout = wsk.trigger.get(name).stdout assert(stdout.startsWith(s"ok: got trigger $name\n")) - wsk.parseJsonString(stdout).fields("parameters") shouldBe getValidJSONTestArgOutput + 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) { @@ -548,34 +581,27 @@ class WskBasicUsageTests stdout should include regex (s"(?i)trigger\\s+/${ns_regex_list}/${triggerName}") } - it should "create a trigger with the proper parameter escapes" in withAssetCleaner(wskprops) { + it should "create a trigger with the proper parameter and annotation escapes" in withAssetCleaner(wskprops) { (wp, assetHelper) => - val name = "triggerName" + val name = "triggerEscapes" + assetHelper.withCleaner(wsk.trigger, name) { (trigger, _) => - wsk.cli(wskprops.overrides ++ Seq("trigger", "create", wsk.trigger.fqn(name), "--auth", wp.authKey) ++ - getEscapedJSONTestArgInput()) + trigger.create(name, parameters = getEscapedJSONTestArgInput, + annotations = getEscapedJSONTestArgInput) } val stdout = wsk.trigger.get(name).stdout assert(stdout.startsWith(s"ok: got trigger $name\n")) - wsk.parseJsonString(stdout).fields("parameters") shouldBe getEscapedJSONTestArgOutput - } + 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 - it should "create a trigger with the proper annotation escapes" in withAssetCleaner(wskprops) { - (wp, assetHelper) => - val name = "triggerName" - assetHelper.withCleaner(wsk.trigger, name) { - (trigger, _) => - wsk.cli(wskprops.overrides ++ Seq("trigger", "create", wsk.trigger.fqn(name), "--auth", wp.authKey) ++ - getEscapedJSONTestArgInput(false)) + for (expectedItem <- escapedJSONArr) { + receivedParams should contain(expectedItem) + receivedAnnots should contain(expectedItem) } - - val stdout = wsk.trigger.get(name).stdout - assert(stdout.startsWith(s"ok: got trigger $name\n")) - - wsk.parseJsonString(stdout).fields("annotations") shouldBe getEscapedJSONTestArgOutput } it should "not create a trigger when feed fails to initialize" in withAssetCleaner(wskprops) { @@ -653,46 +679,179 @@ class WskBasicUsageTests behavior of "Wsk params and annotations" - it should "reject commands that are executed with params or annot that are not key/value pairs" in { + 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), expectedExitCode = ERROR_EXIT) + .stderr should include("Invalid parameter argument") + } + + for (invalid <- invalidJSONFiles) { + wsk.cli(cmd ++ Seq("-P", invalid), expectedExitCode = ERROR_EXIT) + .stderr should include("Invalid parameter argument") + + } + } + + for (cmd <- annotCmds) { + for (invalid <- invalidJSONInputs) { + wsk.cli(cmd ++ Seq("-a", "key", invalid), expectedExitCode = ERROR_EXIT) + .stderr should include("Invalid annotation argument") + } + + for (invalid <- invalidJSONFiles) { + wsk.cli(cmd ++ Seq("-A", invalid), 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", "key"), invalidAnnotMsg), + (Seq("trigger", "fire", "triggerName", "-A"), invalidAnnotFileMsg) + ) invalidArgs foreach { case (cmd, err) => -- GitLab