You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by du...@apache.org on 2017/09/21 13:32:43 UTC

[incubator-openwhisk] branch master updated: Add BaseWsk for and base entities for Wsk CLI and REST to extend or create (#2593)

This is an automated email from the ASF dual-hosted git repository.

dubeejw pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new 0199a07  Add BaseWsk for and base entities for Wsk CLI and REST to extend or create (#2593)
0199a07 is described below

commit 0199a07deeaa442716ae25e3d1a0a67efe928b07
Author: Vincent <sh...@us.ibm.com>
AuthorDate: Thu Sep 21 09:32:39 2017 -0400

    Add BaseWsk for and base entities for Wsk CLI and REST to extend or create (#2593)
    
    Currently, Wsk class is dedicated to CLI only and all the entities created
    inside, like WskAction, WskTrigger, WskActivation, etc, are implementations
    of issuing CLI commands to access openwhisk services. All the test cases
    invoke Wsk and Wsk entities for CLI. In order to have REST implementations,
    we need to create a base Wsk and base entities for both Wsk CLI and Wsk REST
    to extend. All the public methods of Wsk CLI should be put in the abstract
    trait, so that all the test cases need to create the base Wsk, and call
    different implementations based on different test requirements.
    
    This PR abstracts a basewsk and some base entities for Wsk and Wsk entities
    to extend. This will facilitate further development of REST invocation.
---
 tests/src/test/scala/common/BaseWsk.scala        | 366 +++++++++++++++++++++
 tests/src/test/scala/common/Wsk.scala            | 396 +++++++++--------------
 tests/src/test/scala/common/WskTestHelpers.scala |  12 +-
 3 files changed, 520 insertions(+), 254 deletions(-)

diff --git a/tests/src/test/scala/common/BaseWsk.scala b/tests/src/test/scala/common/BaseWsk.scala
new file mode 100644
index 0000000..701e7ba
--- /dev/null
+++ b/tests/src/test/scala/common/BaseWsk.scala
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package common
+
+import java.io.BufferedWriter
+import java.io.File
+import java.io.FileWriter
+import java.time.Instant
+
+import scala.concurrent.duration.DurationInt
+import scala.collection.mutable.Buffer
+import scala.concurrent.duration.Duration
+import scala.language.postfixOps
+import org.scalatest.Matchers
+
+import TestUtils._
+import spray.json.JsObject
+import spray.json.JsValue
+import spray.json.pimpString
+import whisk.core.entity.ByteSize
+
+case class WskProps(
+  authKey: String = WhiskProperties.readAuthKey(WhiskProperties.getAuthFileForTesting),
+  cert: String =
+    WhiskProperties.getFileRelativeToWhiskHome("ansible/roles/nginx/files/openwhisk-client-cert.pem").getAbsolutePath,
+  key: String =
+    WhiskProperties.getFileRelativeToWhiskHome("ansible/roles/nginx/files/openwhisk-client-key.pem").getAbsolutePath,
+  namespace: String = "_",
+  apiversion: String = "v1",
+  apihost: String = WhiskProperties.getEdgeHost,
+  token: String = "") {
+  def overrides = Seq("-i", "--apihost", apihost, "--apiversion", apiversion)
+  def writeFile(propsfile: File) = {
+    val propsStr = s"""NAMESPACE=$namespace
+                  |APIVERSION=$apiversion
+                  |AUTH=$authKey
+                  |APIHOST=$apihost
+                  |APIGW_ACCESS_TOKEN=$token""".stripMargin
+    val bw = new BufferedWriter(new FileWriter(propsfile))
+    try {
+      bw.write(propsStr)
+    } finally {
+      bw.close()
+    }
+  }
+}
+
+trait WaitFor {
+
+  /**
+   * Waits up to totalWait seconds for a 'step' to return value.
+   * Often tests call this routine immediately after starting work.
+   * Performs an initial wait before entering poll loop.
+   */
+  def waitfor[T](step: () => T,
+                 initialWait: Duration = 1 second,
+                 pollPeriod: Duration = 1 second,
+                 totalWait: Duration = 30 seconds): T = {
+    Thread.sleep(initialWait.toMillis)
+    val endTime = System.currentTimeMillis() + totalWait.toMillis
+    while (System.currentTimeMillis() < endTime) {
+      val predicate = step()
+      predicate match {
+        case (t: Boolean) if t =>
+          return predicate
+        case (t: Any) if t != null && !t.isInstanceOf[Boolean] =>
+          return predicate
+        case _ if System.currentTimeMillis() >= endTime =>
+          return predicate
+        case _ =>
+          Thread.sleep(pollPeriod.toMillis)
+      }
+    }
+    null.asInstanceOf[T]
+  }
+}
+
+trait BaseWsk extends BaseRunWsk {
+  val action: BaseAction
+  val trigger: BaseTrigger
+  val rule: BaseRule
+  val activation: BaseActivation
+  val pkg: BasePackage
+  val namespace: BaseNamespace
+  val api: BaseApi
+}
+
+trait FullyQualifiedNames {
+
+  /**
+   * Fully qualifies the name of an entity with its namespace.
+   * If the name already starts with the PATHSEP character, then
+   * it already is fully qualified. Otherwise (package name or
+   * basic entity name) it is prefixed with the namespace. The
+   * namespace is derived from the implicit whisk properties.
+   *
+   * @param name to fully qualify iff it is not already fully qualified
+   * @param wp whisk properties
+   * @return name if it is fully qualified else a name fully qualified for a namespace
+   */
+  def fqn(name: String)(implicit wp: WskProps) = {
+    val sep = "/" // Namespace.PATHSEP
+    if (name.startsWith(sep) || name.count(_ == sep(0)) == 2) name
+    else s"$sep${wp.namespace}$sep$name"
+  }
+
+  /**
+   * Resolves a namespace. If argument is defined, it takes precedence.
+   * else resolve to namespace in implicit WskProps.
+   *
+   * @param namespace an optional namespace
+   * @param wp whisk properties
+   * @return resolved namespace
+   */
+  def resolve(namespace: Option[String])(implicit wp: WskProps) = {
+    val sep = "/" // Namespace.PATHSEP
+    namespace getOrElse s"$sep${wp.namespace}"
+  }
+}
+
+trait BaseListOrGetFromCollection extends FullyQualifiedNames {
+
+  protected val noun: String
+
+  /**
+   * List entities in collection.
+   *
+   * @param namespace (optional) if specified must be  fully qualified namespace
+   * @param expectedExitCode (optional) the expected exit code for the command
+   * if the code is anything but DONTCARE_EXIT, assert the code is as expected
+   */
+  def list(namespace: Option[String] = None,
+           limit: Option[Int] = None,
+           nameSort: Option[Boolean] = None,
+           expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  /**
+   * Gets entity from collection.
+   *
+   * @param name either a fully qualified name or a simple entity name
+   * @param expectedExitCode (optional) the expected exit code for the command
+   * if the code is anything but DONTCARE_EXIT, assert the code is as expected
+   */
+  def get(name: String,
+          expectedExitCode: Int = SUCCESS_EXIT,
+          summary: Boolean = false,
+          fieldFilter: Option[String] = None,
+          url: Option[Boolean] = None)(implicit wp: WskProps): RunResult
+}
+
+trait BaseDeleteFromCollection extends FullyQualifiedNames {
+
+  protected val noun: String
+
+  /**
+   * Deletes entity from collection.
+   *
+   * @param name either a fully qualified name or a simple entity name
+   * @param expectedExitCode (optional) the expected exit code for the command
+   * if the code is anything but DONTCARE_EXIT, assert the code is as expected
+   */
+  def delete(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  /**
+   * Deletes entity from collection but does not assert that the command succeeds.
+   * Use this if deleting an entity that may not exist and it is OK if it does not.
+   *
+   * @param name either a fully qualified name or a simple entity name
+   */
+  def sanitize(name: String)(implicit wp: WskProps): RunResult
+}
+
+trait BaseAction extends BaseRunWsk with BaseDeleteFromCollection with BaseListOrGetFromCollection {
+
+  def create(name: String,
+             artifact: Option[String],
+             kind: Option[String] = None,
+             main: Option[String] = None,
+             docker: Option[String] = None,
+             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,
+             shared: Option[Boolean] = None,
+             update: Boolean = false,
+             web: Option[String] = None,
+             expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  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
+}
+
+trait BasePackage extends BaseRunWsk with BaseDeleteFromCollection with BaseListOrGetFromCollection {
+
+  def create(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)(implicit wp: WskProps): RunResult
+
+  def bind(provider: String,
+           name: String,
+           parameters: Map[String, JsValue] = Map(),
+           annotations: Map[String, JsValue] = Map(),
+           expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+}
+
+trait BaseTrigger extends BaseRunWsk with BaseDeleteFromCollection with BaseListOrGetFromCollection {
+
+  def create(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,
+             expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  def fire(name: String,
+           parameters: Map[String, JsValue] = Map(),
+           parameterFile: Option[String] = None,
+           expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+}
+
+trait BaseRule extends BaseRunWsk with BaseDeleteFromCollection with BaseListOrGetFromCollection {
+
+  def create(name: String,
+             trigger: String,
+             action: String,
+             annotations: Map[String, JsValue] = Map(),
+             shared: Option[Boolean] = None,
+             update: Boolean = false,
+             expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  def enable(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  def disable(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+
+  def state(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult
+}
+
+trait BaseActivation extends BaseRunWsk {
+
+  def extractActivationId(result: RunResult): Option[String]
+
+  def pollFor(N: Int,
+              entity: Option[String],
+              limit: Option[Int] = None,
+              since: Option[Instant],
+              retries: Int,
+              pollPeriod: Duration = 1.second)(implicit wp: WskProps): Seq[String]
+
+  def waitForActivation(activationId: String, initialWait: Duration, pollPeriod: Duration, totalWait: Duration)(
+    implicit wp: WskProps): Either[String, JsObject]
+
+  def get(activationId: Option[String] = None,
+          expectedExitCode: Int = SUCCESS_EXIT,
+          fieldFilter: Option[String] = None,
+          last: Option[Boolean] = None)(implicit wp: WskProps): RunResult
+
+  def console(duration: Duration, since: Option[Duration] = None, expectedExitCode: Int = SUCCESS_EXIT)(
+    implicit wp: WskProps): RunResult
+
+  def logs(activationId: Option[String] = None, expectedExitCode: Int = SUCCESS_EXIT, last: Option[Boolean] = None)(
+    implicit wp: WskProps): RunResult
+
+  def result(activationId: Option[String] = None, expectedExitCode: Int = SUCCESS_EXIT, last: Option[Boolean] = None)(
+    implicit wp: WskProps): RunResult
+}
+
+trait BaseNamespace extends BaseRunWsk {
+
+  def list(expectedExitCode: Int = SUCCESS_EXIT, nameSort: Option[Boolean] = None)(implicit wp: WskProps): RunResult
+
+  def whois()(implicit wskprops: WskProps): String
+
+  def get(namespace: Option[String] = None, expectedExitCode: Int, nameSort: Option[Boolean] = None)(
+    implicit wp: WskProps): RunResult
+}
+
+trait BaseApi extends BaseRunWsk {
+
+  def create(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] = None)(implicit wp: WskProps): RunResult
+
+  def list(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] = None)(implicit wp: WskProps): RunResult
+
+  def get(basepathOrApiName: Option[String] = None,
+          full: Option[Boolean] = None,
+          expectedExitCode: Int = SUCCESS_EXIT,
+          cliCfgFile: Option[String] = None,
+          format: Option[String] = None)(implicit wp: WskProps): RunResult
+
+  def delete(basepathOrApiName: String,
+             relpath: Option[String] = None,
+             operation: Option[String] = None,
+             expectedExitCode: Int = SUCCESS_EXIT,
+             cliCfgFile: Option[String] = None)(implicit wp: WskProps): RunResult
+}
+
+trait BaseRunWsk extends Matchers {
+
+  // Takes a string and a list of sensitive strings. Any sensistive string found in
+  // the target string will be replaced with "XXXXX", returning the processed string
+  def hideStr(str: String, hideThese: Seq[String]): String = {
+    // Iterate through each string to hide, replacing it in the target string (str)
+    hideThese.fold(str)((updatedStr, replaceThis) => updatedStr.replace(replaceThis, "XXXXX"))
+  }
+
+  /*
+   * Utility function to return a JSON object from the CLI output that returns
+   * an optional a status line following by the JSON data
+   */
+  def parseJsonString(jsonStr: String): JsObject = {
+    jsonStr.substring(jsonStr.indexOf("\n") + 1).parseJson.asJsObject // Skip optional status line before parsing
+  }
+
+  def reportFailure(args: Buffer[String], ec: Integer, rr: RunResult) = {
+    val s = new StringBuilder()
+    s.append(args.mkString(" ") + "\n")
+    if (rr.stdout.nonEmpty) s.append(rr.stdout + "\n")
+    if (rr.stderr.nonEmpty) s.append(rr.stderr)
+    s.append("exit code:")
+  }
+}
diff --git a/tests/src/test/scala/common/Wsk.scala b/tests/src/test/scala/common/Wsk.scala
index 8bef369..65149be 100644
--- a/tests/src/test/scala/common/Wsk.scala
+++ b/tests/src/test/scala/common/Wsk.scala
@@ -17,9 +17,7 @@
 
 package common
 
-import java.io.BufferedWriter
 import java.io.File
-import java.io.FileWriter
 import java.time.Instant
 
 import scala.Left
@@ -33,13 +31,9 @@ import scala.util.Failure
 import scala.util.Success
 import scala.util.Try
 
-import org.scalatest.Matchers
-
 import TestUtils._
-import common.TestUtils.RunResult
 import spray.json.JsObject
 import spray.json.JsValue
-import spray.json.pimpString
 import whisk.core.entity.ByteSize
 import whisk.utils.retry
 
@@ -68,29 +62,6 @@ import whisk.utils.retry
  * It also sets the apihost and apiversion explicitly to avoid ambiguity with
  * a local property file if it exists.
  */
-case class WskProps(
-  authKey: String = WhiskProperties.readAuthKey(WhiskProperties.getAuthFileForTesting),
-  cert: String =
-    WhiskProperties.getFileRelativeToWhiskHome("ansible/roles/nginx/files/openwhisk-client-cert.pem").getAbsolutePath,
-  key: String =
-    WhiskProperties.getFileRelativeToWhiskHome("ansible/roles/nginx/files/openwhisk-client-key.pem").getAbsolutePath,
-  namespace: String = "_",
-  apiversion: String = "v1",
-  apihost: String = WhiskProperties.getEdgeHost,
-  token: String = "") {
-  def overrides = Seq("-i", "--apihost", apihost, "--apiversion", apiversion)
-  def writeFile(propsfile: File) = {
-    val propsStr =
-      s"NAMESPACE=$namespace\nAPIVERSION=$apiversion\nAUTH=$authKey\nAPIHOST=$apihost\nAPIGW_ACCESS_TOKEN=$token\n"
-    val bw = new BufferedWriter(new FileWriter(propsfile))
-    try {
-      bw.write(propsStr)
-    } finally {
-      bw.close()
-    }
-  }
-}
-
 class Wsk() extends RunWskCmd {
   implicit val action = new WskAction
   implicit val trigger = new WskTrigger
@@ -101,44 +72,9 @@ class Wsk() extends RunWskCmd {
   implicit val api = new WskApi
 }
 
-trait FullyQualifiedNames {
-
-  /**
-   * Fully qualifies the name of an entity with its namespace.
-   * If the name already starts with the PATHSEP character, then
-   * it already is fully qualified. Otherwise (package name or
-   * basic entity name) it is prefixed with the namespace. The
-   * namespace is derived from the implicit whisk properties.
-   *
-   * @param name to fully qualify iff it is not already fully qualified
-   * @param wp whisk properties
-   * @return name if it is fully qualified else a name fully qualified for a namespace
-   */
-  def fqn(name: String)(implicit wp: WskProps) = {
-    val sep = "/" // Namespace.PATHSEP
-    if (name.startsWith(sep) || name.count(_ == sep(0)) == 2) name
-    else s"$sep${wp.namespace}$sep$name"
-  }
-
-  /**
-   * Resolves a namespace. If argument is defined, it takes precedence.
-   * else resolve to namespace in implicit WskProps.
-   *
-   * @param namespace an optional namespace
-   * @param wp whisk properties
-   * @return resolved namespace
-   */
-  def resolve(namespace: Option[String])(implicit wp: WskProps) = {
-    val sep = "/" // Namespace.PATHSEP
-    namespace getOrElse s"$sep${wp.namespace}"
-  }
-}
-
-trait ListOrGetFromCollection extends FullyQualifiedNames {
+trait ListOrGetFromCollectionCLI extends BaseListOrGetFromCollection {
   self: RunWskCmd =>
 
-  protected val noun: String
-
   /**
    * List entities in collection.
    *
@@ -146,10 +82,10 @@ trait ListOrGetFromCollection extends FullyQualifiedNames {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def list(namespace: Option[String] = None,
-           limit: Option[Int] = None,
-           nameSort: Option[Boolean] = None,
-           expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def list(namespace: Option[String] = None,
+                    limit: Option[Int] = None,
+                    nameSort: Option[Boolean] = None,
+                    expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "list", resolve(namespace), "--auth", wp.authKey) ++ {
       limit map { l =>
         Seq("--limit", l.toString)
@@ -169,11 +105,11 @@ trait ListOrGetFromCollection extends FullyQualifiedNames {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def get(name: String,
-          expectedExitCode: Int = SUCCESS_EXIT,
-          summary: Boolean = false,
-          fieldFilter: Option[String] = None,
-          url: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
+  override def get(name: String,
+                   expectedExitCode: Int = SUCCESS_EXIT,
+                   summary: Boolean = false,
+                   fieldFilter: Option[String] = None,
+                   url: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "get", "--auth", wp.authKey) ++
       Seq(fqn(name)) ++ { if (summary) Seq("--summary") else Seq() } ++ {
       fieldFilter map { f =>
@@ -189,11 +125,9 @@ trait ListOrGetFromCollection extends FullyQualifiedNames {
   }
 }
 
-trait DeleteFromCollection extends FullyQualifiedNames {
+trait DeleteFromCollectionCLI extends BaseDeleteFromCollection {
   self: RunWskCmd =>
 
-  protected val noun: String
-
   /**
    * Deletes entity from collection.
    *
@@ -201,7 +135,7 @@ trait DeleteFromCollection extends FullyQualifiedNames {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def delete(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def delete(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     cli(wp.overrides ++ Seq(noun, "delete", "--auth", wp.authKey, fqn(name)), expectedExitCode)
   }
 
@@ -211,7 +145,7 @@ trait DeleteFromCollection extends FullyQualifiedNames {
    *
    * @param name either a fully qualified name or a simple entity name
    */
-  def sanitize(name: String)(implicit wp: WskProps): RunResult = {
+  override def sanitize(name: String)(implicit wp: WskProps): RunResult = {
     delete(name, DONTCARE_EXIT)
   }
 }
@@ -272,7 +206,12 @@ trait HasActivation {
   }
 }
 
-class WskAction() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCollection with HasActivation {
+class WskAction()
+    extends RunWskCmd
+    with ListOrGetFromCollectionCLI
+    with DeleteFromCollectionCLI
+    with HasActivation
+    with BaseAction {
 
   override protected val noun = "action"
 
@@ -283,22 +222,23 @@ class WskAction() extends RunWskCmd with ListOrGetFromCollection with DeleteFrom
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def create(name: String,
-             artifact: Option[String],
-             kind: Option[String] = None, // one of docker, copy, sequence or none for autoselect else an explicit type
-             main: Option[String] = None,
-             docker: Option[String] = None,
-             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,
-             shared: Option[Boolean] = None,
-             update: Boolean = false,
-             web: Option[String] = None,
-             expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def create(
+    name: String,
+    artifact: Option[String],
+    kind: Option[String] = None, // one of docker, copy, sequence or none for autoselect else an explicit type
+    main: Option[String] = None,
+    docker: Option[String] = None,
+    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,
+    shared: Option[Boolean] = None,
+    update: Boolean = false,
+    web: Option[String] = None,
+    expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, if (!update) "create" else "update", "--auth", wp.authKey, fqn(name)) ++ {
       artifact map { Seq(_) } getOrElse Seq()
     } ++ {
@@ -361,12 +301,12 @@ class WskAction() extends RunWskCmd with ListOrGetFromCollection with DeleteFrom
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def 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 = {
+  override 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)
@@ -380,7 +320,12 @@ class WskAction() extends RunWskCmd with ListOrGetFromCollection with DeleteFrom
   }
 }
 
-class WskTrigger() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCollection with HasActivation {
+class WskTrigger()
+    extends RunWskCmd
+    with ListOrGetFromCollectionCLI
+    with DeleteFromCollectionCLI
+    with HasActivation
+    with BaseTrigger {
 
   override protected val noun = "trigger"
 
@@ -391,15 +336,15 @@ class WskTrigger() extends RunWskCmd with ListOrGetFromCollection with DeleteFro
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def create(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,
-             expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def create(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,
+                      expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, if (!update) "create" else "update", "--auth", wp.authKey, fqn(name)) ++ {
       feed map { f =>
         Seq("--feed", fqn(f))
@@ -435,10 +380,10 @@ class WskTrigger() extends RunWskCmd with ListOrGetFromCollection with DeleteFro
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def fire(name: String,
-           parameters: Map[String, JsValue] = Map(),
-           parameterFile: Option[String] = None,
-           expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override 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)
@@ -452,7 +397,12 @@ class WskTrigger() extends RunWskCmd with ListOrGetFromCollection with DeleteFro
   }
 }
 
-class WskRule() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCollection with WaitFor {
+class WskRule()
+    extends RunWskCmd
+    with ListOrGetFromCollectionCLI
+    with DeleteFromCollectionCLI
+    with WaitFor
+    with BaseRule {
 
   override protected val noun = "rule"
 
@@ -465,13 +415,13 @@ class WskRule() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCo
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def create(name: String,
-             trigger: String,
-             action: String,
-             annotations: Map[String, JsValue] = Map(),
-             shared: Option[Boolean] = None,
-             update: Boolean = false,
-             expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def create(name: String,
+                      trigger: String,
+                      action: String,
+                      annotations: Map[String, JsValue] = Map(),
+                      shared: Option[Boolean] = None,
+                      update: Boolean = false,
+                      expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, if (!update) "create" else "update", "--auth", wp.authKey, fqn(name), (trigger), (action)) ++ {
       annotations flatMap { p =>
         Seq("-a", p._1, p._2.compactPrint)
@@ -502,7 +452,7 @@ class WskRule() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCo
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def enable(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def enable(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     cli(wp.overrides ++ Seq(noun, "enable", "--auth", wp.authKey, fqn(name)), expectedExitCode)
   }
 
@@ -513,7 +463,7 @@ class WskRule() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCo
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def disable(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def disable(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     cli(wp.overrides ++ Seq(noun, "disable", "--auth", wp.authKey, fqn(name)), expectedExitCode)
   }
 
@@ -524,12 +474,12 @@ class WskRule() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCo
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def state(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def state(name: String, expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     cli(wp.overrides ++ Seq(noun, "status", "--auth", wp.authKey, fqn(name)), expectedExitCode)
   }
 }
 
-class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
+class WskActivation() extends RunWskCmd with HasActivation with WaitFor with BaseActivation {
 
   protected val noun = "activation"
 
@@ -539,7 +489,7 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
    * @param duration exits console after duration
    * @param since (optional) time travels back to activation since given duration
    */
-  def console(duration: Duration, since: Option[Duration] = None, expectedExitCode: Int = SUCCESS_EXIT)(
+  override def console(duration: Duration, since: Option[Duration] = None, expectedExitCode: Int = SUCCESS_EXIT)(
     implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "poll", "--auth", wp.authKey, "--exit", duration.toSeconds.toString) ++ {
       since map { s =>
@@ -599,10 +549,10 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    * @param last retrieves latest acitvation
    */
-  def get(activationId: Option[String] = None,
-          expectedExitCode: Int = SUCCESS_EXIT,
-          fieldFilter: Option[String] = None,
-          last: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
+  override def get(activationId: Option[String] = None,
+                   expectedExitCode: Int = SUCCESS_EXIT,
+                   fieldFilter: Option[String] = None,
+                   last: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
     val params = {
       activationId map { a =>
         Seq(a)
@@ -627,8 +577,9 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    * @param last retrieves latest acitvation
    */
-  def logs(activationId: Option[String] = None, expectedExitCode: Int = SUCCESS_EXIT, last: Option[Boolean] = None)(
-    implicit wp: WskProps): RunResult = {
+  override def logs(activationId: Option[String] = None,
+                    expectedExitCode: Int = SUCCESS_EXIT,
+                    last: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
     val params = {
       activationId map { a =>
         Seq(a)
@@ -649,8 +600,9 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    * @param last retrieves latest acitvation
    */
-  def result(activationId: Option[String] = None, expectedExitCode: Int = SUCCESS_EXIT, last: Option[Boolean] = None)(
-    implicit wp: WskProps): RunResult = {
+  override def result(activationId: Option[String] = None,
+                      expectedExitCode: Int = SUCCESS_EXIT,
+                      last: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
     val params = {
       activationId map { a =>
         Seq(a)
@@ -677,12 +629,12 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
    * @param retries the maximum retries (total timeout is retries + 1 seconds)
    * @return activation ids found, caller must check length of sequence
    */
-  def pollFor(N: Int,
-              entity: Option[String],
-              limit: Option[Int] = None,
-              since: Option[Instant] = None,
-              retries: Int = 10,
-              pollPeriod: Duration = 1.second)(implicit wp: WskProps): Seq[String] = {
+  override def pollFor(N: Int,
+                       entity: Option[String],
+                       limit: Option[Int] = None,
+                       since: Option[Instant] = None,
+                       retries: Int = 10,
+                       pollPeriod: Duration = 1.second)(implicit wp: WskProps): Seq[String] = {
     Try {
       retry({
         val result = ids(list(filter = entity, limit = limit, since = since))
@@ -701,10 +653,10 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
    *
    * @return either Left(error message) or Right(activation as JsObject)
    */
-  def waitForActivation(activationId: String,
-                        initialWait: Duration = 1 second,
-                        pollPeriod: Duration = 1 second,
-                        totalWait: Duration = 30 seconds)(implicit wp: WskProps): Either[String, JsObject] = {
+  override def waitForActivation(activationId: String,
+                                 initialWait: Duration = 1 second,
+                                 pollPeriod: Duration = 1 second,
+                                 totalWait: Duration = 30 seconds)(implicit wp: WskProps): Either[String, JsObject] = {
     val activation = waitfor(
       () => {
         val result =
@@ -736,7 +688,7 @@ class WskActivation() extends RunWskCmd with HasActivation with WaitFor {
   private case class PartialResult(ids: Seq[String]) extends Throwable
 }
 
-class WskNamespace() extends RunWskCmd with FullyQualifiedNames {
+class WskNamespace() extends RunWskCmd with FullyQualifiedNames with BaseNamespace {
 
   protected val noun = "namespace"
 
@@ -746,7 +698,8 @@ class WskNamespace() extends RunWskCmd with FullyQualifiedNames {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def list(expectedExitCode: Int = SUCCESS_EXIT, nameSort: Option[Boolean] = None)(implicit wp: WskProps): RunResult = {
+  override def list(expectedExitCode: Int = SUCCESS_EXIT, nameSort: Option[Boolean] = None)(
+    implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "list", "--auth", wp.authKey) ++ {
       nameSort map { n =>
         Seq("--name-sort")
@@ -761,7 +714,7 @@ class WskNamespace() extends RunWskCmd with FullyQualifiedNames {
    * @param wskprops instance of WskProps with an auth key to lookup
    * @return namespace as string
    */
-  def whois()(implicit wskprops: WskProps): String = {
+  override def whois()(implicit wskprops: WskProps): String = {
     // the invariant that list() returns a conforming result is enforced in a test in WskBasicTests
     val ns = list().stdout.lines.toSeq.last.trim
     assert(ns != "_") // this is not permitted
@@ -775,7 +728,7 @@ class WskNamespace() extends RunWskCmd with FullyQualifiedNames {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def get(namespace: Option[String] = None, expectedExitCode: Int, nameSort: Option[Boolean] = None)(
+  override def get(namespace: Option[String] = None, expectedExitCode: Int, nameSort: Option[Boolean] = None)(
     implicit wp: WskProps): RunResult = {
     val params = {
       nameSort map { n =>
@@ -786,7 +739,7 @@ class WskNamespace() extends RunWskCmd with FullyQualifiedNames {
   }
 }
 
-class WskPackage() extends RunWskCmd with ListOrGetFromCollection with DeleteFromCollection {
+class WskPackage() extends RunWskCmd with ListOrGetFromCollectionCLI with DeleteFromCollectionCLI with BasePackage {
   override protected val noun = "package"
 
   /**
@@ -796,14 +749,14 @@ class WskPackage() extends RunWskCmd with ListOrGetFromCollection with DeleteFro
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def create(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)(implicit wp: WskProps): RunResult = {
+  override def create(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)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, if (!update) "create" else "update", "--auth", wp.authKey, fqn(name)) ++ {
       parameters flatMap { p =>
         Seq("-p", p._1, p._2.compactPrint)
@@ -835,11 +788,11 @@ class WskPackage() extends RunWskCmd with ListOrGetFromCollection with DeleteFro
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def bind(provider: String,
-           name: String,
-           parameters: Map[String, JsValue] = Map(),
-           annotations: Map[String, JsValue] = Map(),
-           expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
+  override def bind(provider: String,
+                    name: String,
+                    parameters: Map[String, JsValue] = Map(),
+                    annotations: Map[String, JsValue] = Map(),
+                    expectedExitCode: Int = SUCCESS_EXIT)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "bind", "--auth", wp.authKey, fqn(provider), fqn(name)) ++ {
       parameters flatMap { p =>
         Seq("-p", p._1, p._2.compactPrint)
@@ -853,7 +806,7 @@ class WskPackage() extends RunWskCmd with ListOrGetFromCollection with DeleteFro
   }
 }
 
-class WskApi() extends RunWskCmd {
+class WskApi() extends RunWskCmd with BaseApi {
   protected val noun = "api"
 
   /**
@@ -862,15 +815,15 @@ class WskApi() extends RunWskCmd {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def create(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] = None)(implicit wp: WskProps): RunResult = {
+  override def create(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] = None)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "create", "--auth", wp.authKey) ++ {
       basepath map { b =>
         Seq(b)
@@ -913,15 +866,15 @@ class WskApi() extends RunWskCmd {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def list(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] = None)(implicit wp: WskProps): RunResult = {
+  override def list(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] = None)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "list", "--auth", wp.authKey) ++ {
       basepathOrApiName map { b =>
         Seq(b)
@@ -965,11 +918,11 @@ class WskApi() extends RunWskCmd {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def get(basepathOrApiName: Option[String] = None,
-          full: Option[Boolean] = None,
-          expectedExitCode: Int = SUCCESS_EXIT,
-          cliCfgFile: Option[String] = None,
-          format: Option[String] = None)(implicit wp: WskProps): RunResult = {
+  override def get(basepathOrApiName: Option[String] = None,
+                   full: Option[Boolean] = None,
+                   expectedExitCode: Int = SUCCESS_EXIT,
+                   cliCfgFile: Option[String] = None,
+                   format: Option[String] = None)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "get", "--auth", wp.authKey) ++ {
       basepathOrApiName map { b =>
         Seq(b)
@@ -996,11 +949,11 @@ class WskApi() extends RunWskCmd {
    * @param expectedExitCode (optional) the expected exit code for the command
    * if the code is anything but DONTCARE_EXIT, assert the code is as expected
    */
-  def delete(basepathOrApiName: String,
-             relpath: Option[String] = None,
-             operation: Option[String] = None,
-             expectedExitCode: Int = SUCCESS_EXIT,
-             cliCfgFile: Option[String] = None)(implicit wp: WskProps): RunResult = {
+  override def delete(basepathOrApiName: String,
+                      relpath: Option[String] = None,
+                      operation: Option[String] = None,
+                      expectedExitCode: Int = SUCCESS_EXIT,
+                      cliCfgFile: Option[String] = None)(implicit wp: WskProps): RunResult = {
     val params = Seq(noun, "delete", "--auth", wp.authKey, basepathOrApiName) ++ {
       relpath map { r =>
         Seq(r)
@@ -1018,36 +971,6 @@ class WskApi() extends RunWskCmd {
   }
 }
 
-trait WaitFor {
-
-  /**
-   * Waits up to totalWait seconds for a 'step' to return value.
-   * Often tests call this routine immediately after starting work.
-   * Performs an initial wait before entering poll loop.
-   */
-  def waitfor[T](step: () => T,
-                 initialWait: Duration = 1 second,
-                 pollPeriod: Duration = 1 second,
-                 totalWait: Duration = 30 seconds): T = {
-    Thread.sleep(initialWait.toMillis)
-    val endTime = System.currentTimeMillis() + totalWait.toMillis
-    while (System.currentTimeMillis() < endTime) {
-      val predicate = step()
-      predicate match {
-        case (t: Boolean) if t =>
-          return predicate
-        case (t: Any) if t != null && !t.isInstanceOf[Boolean] =>
-          return predicate
-        case _ if System.currentTimeMillis() >= endTime =>
-          return predicate
-        case _ =>
-          Thread.sleep(pollPeriod.toMillis)
-      }
-    }
-    null.asInstanceOf[T]
-  }
-}
-
 object Wsk {
   private val binaryName = "wsk"
   private val cliPath = if (WhiskProperties.useCLIDownload) getDownloadedGoCLIPath else WhiskProperties.getCLIPath
@@ -1062,7 +985,7 @@ object Wsk {
   def baseCommand = Buffer(cliPath)
 }
 
-trait RunWskCmd extends Matchers {
+trait RunWskCmd extends BaseRunWsk {
 
   /**
    * The base command to run.
@@ -1104,29 +1027,6 @@ trait RunWskCmd extends Matchers {
 
     rr
   }
-
-  // Takes a string and a list of sensitive strings. Any sensistive string found in
-  // the target string will be replaced with "XXXXX", returning the processed string
-  def hideStr(str: String, hideThese: Seq[String]): String = {
-    // Iterate through each string to hide, replacing it in the target string (str)
-    hideThese.fold(str)((updatedStr, replaceThis) => updatedStr.replace(replaceThis, "XXXXX"))
-  }
-
-  /*
-   * Utility function to return a JSON object from the CLI output that returns
-   * an optional a status line following by the JSON data
-   */
-  def parseJsonString(jsonStr: String): JsObject = {
-    jsonStr.substring(jsonStr.indexOf("\n") + 1).parseJson.asJsObject // Skip optional status line before parsing
-  }
-
-  private def reportFailure(args: Buffer[String], ec: Integer, rr: RunResult) = {
-    val s = new StringBuilder()
-    s.append(args.mkString(" ") + "\n")
-    if (rr.stdout.nonEmpty) s.append(rr.stdout + "\n")
-    if (rr.stderr.nonEmpty) s.append(rr.stderr)
-    s.append("exit code:")
-  }
 }
 
 object WskAdmin {
diff --git a/tests/src/test/scala/common/WskTestHelpers.scala b/tests/src/test/scala/common/WskTestHelpers.scala
index 3996cf7..23d6b87 100644
--- a/tests/src/test/scala/common/WskTestHelpers.scala
+++ b/tests/src/test/scala/common/WskTestHelpers.scala
@@ -129,7 +129,7 @@ object ActivationResult extends DefaultJsonProtocol {
  * completed, will delete them all.
  */
 trait WskTestHelpers extends Matchers {
-  type Assets = ListBuffer[(DeleteFromCollection, String, Boolean)]
+  type Assets = ListBuffer[(BaseDeleteFromCollection, String, Boolean)]
 
   /**
    * Helper to register an entity to delete once a test completes.
@@ -138,7 +138,7 @@ trait WskTestHelpers extends Matchers {
    *
    */
   class AssetCleaner(assetsToDeleteAfterTest: Assets, wskprops: WskProps) {
-    def withCleaner[T <: DeleteFromCollection](cli: T, name: String, confirmDelete: Boolean = true)(
+    def withCleaner[T <: BaseDeleteFromCollection](cli: T, name: String, confirmDelete: Boolean = true)(
       cmd: (T, String) => RunResult): RunResult = {
       // sanitize (delete) if asset exists
       cli.sanitize(name)(wskprops)
@@ -170,7 +170,7 @@ trait WskTestHelpers extends Matchers {
         case ((cli, n, delete)) =>
           n -> Try {
             cli match {
-              case _: WskPackage if delete =>
+              case _: BasePackage if delete =>
                 val rr = cli.delete(n)(wskprops)
                 rr.exitCode match {
                   case CONFLICT => whisk.utils.retry(cli.delete(n)(wskprops), 5, Some(1.second))
@@ -196,7 +196,7 @@ trait WskTestHelpers extends Matchers {
    * the activation to the post processor which then check for expected values.
    */
   def withActivation(
-    wsk: WskActivation,
+    wsk: BaseActivation,
     run: RunResult,
     initialWait: Duration = 1.second,
     pollPeriod: Duration = 1.second,
@@ -214,7 +214,7 @@ trait WskTestHelpers extends Matchers {
    * Polls activations until one matching id is found. If found, pass
    * the activation to the post processor which then check for expected values.
    */
-  def withActivation(wsk: WskActivation,
+  def withActivation(wsk: BaseActivation,
                      activationId: String,
                      initialWait: Duration,
                      pollPeriod: Duration,
@@ -239,7 +239,7 @@ trait WskTestHelpers extends Matchers {
    * defining the oldest activationId to consider valid.
    */
   def withActivationsFromEntity(
-    wsk: WskActivation,
+    wsk: BaseActivation,
     entity: String,
     N: Int = 1,
     since: Option[Instant] = None,

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].