You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by cb...@apache.org on 2018/02/21 09:41:47 UTC

[incubator-openwhisk] branch master updated: Rework rule tests to not rely on views anymore. (#3309)

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

cbickel 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 a6fd056  Rework rule tests to not rely on views anymore. (#3309)
a6fd056 is described below

commit a6fd056c92683ce42b662c6cb24cd50d87f2cbc2
Author: Markus Thömmes <ma...@me.com>
AuthorDate: Wed Feb 21 10:41:43 2018 +0100

    Rework rule tests to not rely on views anymore. (#3309)
    
    Relying on views is brittle and will fail if the database is under high load. We don't need to rely on those anymore.
---
 tests/src/test/scala/common/WskTestHelpers.scala   |  40 +---
 .../src/test/scala/system/basic/WskRuleTests.scala | 230 +++++++++------------
 .../test/scala/system/basic/WskSequenceTests.scala |   8 +-
 3 files changed, 117 insertions(+), 161 deletions(-)

diff --git a/tests/src/test/scala/common/WskTestHelpers.scala b/tests/src/test/scala/common/WskTestHelpers.scala
index eb4bad6..5be175c 100644
--- a/tests/src/test/scala/common/WskTestHelpers.scala
+++ b/tests/src/test/scala/common/WskTestHelpers.scala
@@ -119,6 +119,12 @@ object ActivationResult extends DefaultJsonProtocol {
   }
 }
 
+/** The result of a rule-activation written into the trigger activation */
+case class RuleActivationResult(statusCode: Int, success: Boolean, activationId: String, action: String)
+object RuleActivationResult extends DefaultJsonProtocol {
+  implicit val serdes = jsonFormat4(RuleActivationResult.apply)
+}
+
 /**
  * Test fixture to ease cleaning of whisk entities created during testing.
  *
@@ -232,37 +238,9 @@ trait WskTestHelpers extends Matchers {
         }
     }
   }
-
-  /**
-   * Polls until it finds {@code N} activationIds from an entity. Asserts the count
-   * of the activationIds actually equal {@code N}. Takes a {@code since} parameter
-   * defining the oldest activationId to consider valid.
-   */
-  def withActivationsFromEntity(
-    wsk: BaseActivation,
-    entity: String,
-    N: Int = 1,
-    since: Option[Instant] = None,
-    pollPeriod: Duration = 1.second,
-    totalWait: Duration = 60.seconds)(check: Seq[ActivationResult] => Unit)(implicit wskprops: WskProps): Unit = {
-
-    val activationIds =
-      wsk.pollFor(N, Some(entity), since = since, retries = (totalWait / pollPeriod).toInt, pollPeriod = pollPeriod)
-    withClue(
-      s"expecting $N activations matching '$entity' name since $since but found ${activationIds.mkString(",")} instead") {
-      activationIds.length shouldBe N
-    }
-
-    val parsed = activationIds.map { id =>
-      wsk.parseJsonString(wsk.get(Some(id)).stdout).convertTo[ActivationResult]
-    }
-    try {
-      check(parsed)
-    } catch {
-      case error: Throwable =>
-        println(s"check failed for activations $activationIds: $parsed")
-        throw error
-    }
+  def withActivation(wsk: BaseActivation, activationId: String)(check: ActivationResult => Unit)(
+    implicit wskprops: WskProps): Unit = {
+    withActivation(wsk, activationId, 1.second, 1.second, 60.seconds)(check)
   }
 
   /**
diff --git a/tests/src/test/scala/system/basic/WskRuleTests.scala b/tests/src/test/scala/system/basic/WskRuleTests.scala
index 62890b7..bd30bc7 100644
--- a/tests/src/test/scala/system/basic/WskRuleTests.scala
+++ b/tests/src/test/scala/system/basic/WskRuleTests.scala
@@ -25,6 +25,7 @@ import common.TestUtils.RunResult
 import common.BaseWsk
 import common.WskProps
 import common.WskTestHelpers
+import common.RuleActivationResult
 import spray.json._
 import spray.json.DefaultJsonProtocol._
 import java.time.Instant
@@ -40,14 +41,6 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
   val testResult = JsObject("count" -> testString.split(" ").length.toJson)
 
   /**
-   * Invoker clock skew can sometimes make it appear as if an action was invoked
-   * _before_ the trigger was fired. The "fudge factor" below allows the test to look
-   * for action activations that occur starting at most this amount of time before
-   * the trigger was fired.
-   */
-  val activationTimeSkewFactorMs = 500
-
-  /**
    * Sets up trigger -> rule -> action triplets. Deduplicates triggers and rules
    * and links it all up.
    *
@@ -56,7 +49,7 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
    *   where the action name for the created action is allowed to differ from that used by the rule binding
    *   for cases that reference actions in a package binding.
    */
-  def ruleSetup(rules: Seq[(String, String, (String, String, String))], assetHelper: AssetCleaner) = {
+  def ruleSetup(rules: Seq[(String, String, (String, String, String))], assetHelper: AssetCleaner): Unit = {
     val triggers = rules.map(_._2).distinct
     val actions = rules.map(_._3).distinct
 
@@ -84,7 +77,7 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
   /**
    * Append the current timestamp in ms
    */
-  def withTimestamp(text: String) = s"${text}-${System.currentTimeMillis}"
+  def withTimestamp(text: String) = s"$text-${System.currentTimeMillis}"
 
   behavior of "Whisk rules"
 
@@ -112,8 +105,8 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
           .parseJson
           .asJsObject
           .fields
-          .get("status") shouldBe (status)
-        wsk.rule.get(ruleName).stdout.parseJson.asJsObject.fields.get("status") shouldBe (status)
+          .get("status") shouldBe status
+        wsk.rule.get(ruleName).stdout.parseJson.asJsObject.fields.get("status") shouldBe status
     }
   }
 
@@ -129,18 +122,16 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
 
     withActivation(wsk.activation, run) { triggerActivation =>
       triggerActivation.cause shouldBe None
-      triggerActivation.logs.get.size shouldBe (1)
-      val logs = triggerActivation.logs.get.mkString(" ")
-      logs should include(""""statusCode":0""")
-      logs should include(""""activationId":""")
-      logs should include(""""success":true""")
-
-      withActivationsFromEntity(
-        wsk.activation,
-        actionName,
-        since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activationList =>
-        activationList.head.response.result shouldBe Some(testResult)
-        activationList.head.cause shouldBe None
+
+      val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+      ruleActivations should have size 1
+      val ruleActivation = ruleActivations.head
+      ruleActivation.success shouldBe true
+      ruleActivation.statusCode shouldBe 0
+
+      withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+        actionActivation.response.result shouldBe Some(testResult)
+        actionActivation.cause shouldBe None
       }
     }
   }
@@ -164,17 +155,15 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
 
     withActivation(wsk.activation, run) { triggerActivation =>
       triggerActivation.cause shouldBe None
-      triggerActivation.logs.get.size shouldBe (1)
-      val logs = triggerActivation.logs.get.mkString(" ")
-      logs should include(""""statusCode":0""")
-      logs should include(""""activationId":""")
-      logs should include(""""success":true""")
-
-      withActivationsFromEntity(
-        wsk.activation,
-        pkgActionName,
-        since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
-        _.head.response.result shouldBe Some(testResult)
+
+      val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+      ruleActivations should have size 1
+      val ruleActivation = ruleActivations.head
+      ruleActivation.success shouldBe true
+      ruleActivation.statusCode shouldBe 0
+
+      withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+        actionActivation.response.result shouldBe Some(testResult)
       }
     }
   }
@@ -202,17 +191,16 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
 
     withActivation(wsk.activation, run) { triggerActivation =>
       triggerActivation.cause shouldBe None
-      triggerActivation.logs.get.size shouldBe (1)
-      val logs = triggerActivation.logs.get.mkString(" ")
-      logs should include(""""statusCode":0""")
-      logs should include(""""activationId":""")
-      logs should include(""""success":true""")
-
-      withActivationsFromEntity(
-        wsk.activation,
-        pkgActionName,
-        since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
-        _.head.response.result shouldBe Some(testResult)
+
+      val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+      ruleActivations should have size 1
+      val ruleActivation = ruleActivations.head
+      ruleActivation.success shouldBe true
+      ruleActivation.statusCode shouldBe 0
+
+      withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+        actionActivation.response.result shouldBe Some(testResult)
+        actionActivation.cause shouldBe None
       }
     }
   }
@@ -229,24 +217,16 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
       assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
         action.create(name, Some(defaultAction))
       }
-      assetHelper.withCleaner(wsk.rule, ruleName, false) { (rule, name) =>
+      assetHelper.withCleaner(wsk.rule, ruleName, confirmDelete = false) { (rule, name) =>
         rule.create(name, triggerName, actionName)
       }
 
       val first = wsk.trigger.fire(triggerName, Map("payload" -> "bogus".toJson))
       wsk.rule.delete(ruleName)
-      wsk.trigger.fire(triggerName, Map("payload" -> "bogus2".toJson))
-
-      withActivation(wsk.activation, first) { activation =>
-        // tries to find 2 activations for the action, should only find 1
-        val activations = wsk.activation.pollFor(
-          2,
-          Some(actionName),
-          since = Some(activation.start.minusMillis(activationTimeSkewFactorMs)),
-          retries = 30)
+      val second = wsk.trigger.fire(triggerName, Map("payload" -> "bogus2".toJson))
 
-        activations.length shouldBe 1
-      }
+      withActivation(wsk.activation, first)(activation => activation.logs.get should have size 1)
+    // there won't be an activation for the second fire since there is no rule
   }
 
   it should "enable and disable a rule and check action is activated only when rule is enabled" in withAssetCleaner(
@@ -259,18 +239,27 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
 
     val first = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
     wsk.rule.disable(ruleName)
-    wsk.trigger.fire(triggerName, Map("payload" -> s"$testString with added words".toJson))
+    val second = wsk.trigger.fire(triggerName, Map("payload" -> s"$testString with added words".toJson))
     wsk.rule.enable(ruleName)
-    wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+    val third = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
 
     withActivation(wsk.activation, first) { triggerActivation =>
-      withActivationsFromEntity(
-        wsk.activation,
-        actionName,
-        N = 2,
-        since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
-        val results = activations.map(_.response.result)
-        results should contain theSameElementsAs Seq(Some(testResult), Some(testResult))
+      val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+      ruleActivations should have size 1
+      val ruleActivation = ruleActivations.head
+      withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+        actionActivation.response.result shouldBe Some(testResult)
+      }
+    }
+
+    // second fire will not write an activation
+
+    withActivation(wsk.activation, third) { triggerActivation =>
+      val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+      ruleActivations should have size 1
+      val ruleActivation = ruleActivations.head
+      withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+        actionActivation.response.result shouldBe Some(testResult)
       }
     }
   }
@@ -288,7 +277,7 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
       assetHelper.withCleaner(wsk.action, actionName) { (action, name) =>
         action.create(name, Some(defaultAction))
       }
-      assetHelper.withCleaner(wsk.rule, ruleName, false) { (rule, name) =>
+      assetHelper.withCleaner(wsk.rule, ruleName, confirmDelete = false) { (rule, name) =>
         rule.create(name, triggerName1, actionName)
       }
 
@@ -303,11 +292,11 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
 
       val first = wsk.trigger.fire(triggerName2, Map("payload" -> testString.toJson))
       withActivation(wsk.activation, first) { triggerActivation =>
-        withActivationsFromEntity(
-          wsk.activation,
-          actionName,
-          since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
-          _.head.response.result shouldBe Some(testResult)
+        val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+        ruleActivations should have size 1
+        val ruleActivation = ruleActivations.head
+        withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+          actionActivation.response.result shouldBe Some(testResult)
         }
       }
   }
@@ -325,23 +314,18 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
         assetHelper)
 
       val testPayloads = Seq("got three words", "got four words, period")
-
-      val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
-      wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
-
-      withActivation(wsk.activation, run) { triggerActivation =>
-        withActivationsFromEntity(
-          wsk.activation,
-          actionName,
-          N = 2,
-          since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
-          val results = activations.map(_.response.result)
-          val expectedResults = testPayloads.map { payload =>
-            Some(JsObject("count" -> payload.split(" ").length.toJson))
+      val runs = testPayloads.map(payload => wsk.trigger.fire(triggerName1, Map("payload" -> payload.toJson)))
+
+      runs.zip(testPayloads).foreach {
+        case (run, payload) =>
+          withActivation(wsk.activation, run) { triggerActivation =>
+            val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+            ruleActivations should have size 1
+            val ruleActivation = ruleActivations.head
+            withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+              actionActivation.response.result shouldBe Some(JsObject("count" -> payload.split(" ").length.toJson))
+            }
           }
-
-          results should contain theSameElementsAs expectedResults
-        }
       }
   }
 
@@ -360,17 +344,17 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
       val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
 
       withActivation(wsk.activation, run) { triggerActivation =>
-        withActivationsFromEntity(
-          wsk.activation,
-          actionName1,
-          since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
-          _.head.response.result shouldBe Some(testResult)
+        val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+        ruleActivations should have size 2
+
+        val action1Result = ruleActivations.find(_.action.contains(actionName1)).get
+        val action2Result = ruleActivations.find(_.action.contains(actionName2)).get
+
+        withActivation(wsk.activation, action1Result.activationId) { actionActivation =>
+          actionActivation.response.result shouldBe Some(testResult)
         }
-        withActivationsFromEntity(
-          wsk.activation,
-          actionName2,
-          since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) {
-          _.head.logs.get.mkString(" ") should include(s"hello, $testString")
+        withActivation(wsk.activation, action2Result.activationId) { actionActivation =>
+          actionActivation.logs.get.mkString(" ") should include(s"hello, $testString")
         }
       }
   }
@@ -391,35 +375,27 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
         assetHelper)
 
       val testPayloads = Seq("got three words", "got four words, period")
-      val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
-      wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
+      val runs = Seq(triggerName1, triggerName2).zip(testPayloads).map {
+        case (trigger, payload) =>
+          payload -> wsk.trigger.fire(trigger, Map("payload" -> payload.toJson))
+      }
 
-      withActivation(wsk.activation, run) { triggerActivation =>
-        withActivationsFromEntity(
-          wsk.activation,
-          actionName1,
-          N = 2,
-          since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
-          val results = activations.map(_.response.result)
-          val expectedResults = testPayloads.map { payload =>
-            Some(JsObject("count" -> payload.split(" ").length.toJson))
+      runs.foreach {
+        case (payload, run) =>
+          withActivation(wsk.activation, run) { triggerActivation =>
+            val ruleActivations = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult])
+            ruleActivations should have size 2 // each trigger has 2 actions attached
+
+            val action1Result = ruleActivations.find(_.action.contains(actionName1)).get
+            val action2Result = ruleActivations.find(_.action.contains(actionName2)).get
+
+            withActivation(wsk.activation, action1Result.activationId) { actionActivation =>
+              actionActivation.response.result shouldBe Some(JsObject("count" -> payload.split(" ").length.toJson))
+            }
+            withActivation(wsk.activation, action2Result.activationId) { actionActivation =>
+              actionActivation.logs.get.mkString(" ") should include(s"hello, $payload")
+            }
           }
-
-          results should contain theSameElementsAs expectedResults
-        }
-        withActivationsFromEntity(
-          wsk.activation,
-          actionName2,
-          N = 2,
-          since = Some(triggerActivation.start.minusMillis(activationTimeSkewFactorMs))) { activations =>
-          // drops the leftmost 39 characters (timestamp + streamname)
-          val logs = activations.map(_.logs.get.map(_.drop(39))).flatten
-          val expectedLogs = testPayloads.map { payload =>
-            s"hello, $payload!"
-          }
-
-          logs should contain theSameElementsAs expectedLogs
-        }
       }
   }
 
@@ -446,6 +422,6 @@ abstract class WskRuleTests extends TestHelpers with WskTestHelpers {
     val listOutput = ruleList.lines
     listOutput.find(_.contains(ruleNameEnable)).get should (include(ruleNameEnable) and include("active"))
     listOutput.find(_.contains(ruleName)).get should (include(ruleName) and include("inactive"))
-    ruleList should not include ("Unknown")
+    ruleList should not include "Unknown"
   }
 }
diff --git a/tests/src/test/scala/system/basic/WskSequenceTests.scala b/tests/src/test/scala/system/basic/WskSequenceTests.scala
index 99fa8fa..f7dc39d 100644
--- a/tests/src/test/scala/system/basic/WskSequenceTests.scala
+++ b/tests/src/test/scala/system/basic/WskSequenceTests.scala
@@ -34,6 +34,7 @@ import common.TestUtils
 import common.TestUtils._
 import common.BaseWsk
 import common.WskProps
+import common.RuleActivationResult
 import common.WskTestHelpers
 
 import akka.http.scaladsl.testkit.ScalatestRouteTest
@@ -472,9 +473,10 @@ abstract class WskSequenceTests extends TestHelpers with ScalatestRouteTest with
    */
   private def checkEchoSeqRuleResult(triggerFireRun: RunResult, seqName: String, triggerPayload: JsObject) = {
     withActivation(wsk.activation, triggerFireRun) { triggerActivation =>
-      withActivationsFromEntity(wsk.activation, seqName, since = Some(triggerActivation.start)) { activationList =>
-        activationList.head.response.result shouldBe Some(triggerPayload)
-        activationList.head.cause shouldBe None
+      val ruleActivation = triggerActivation.logs.get.map(_.parseJson.convertTo[RuleActivationResult]).head
+      withActivation(wsk.activation, ruleActivation.activationId) { actionActivation =>
+        actionActivation.response.result shouldBe Some(triggerPayload)
+        actionActivation.cause shouldBe None
       }
     }
   }

-- 
To stop receiving notification emails like this one, please contact
cbickel@apache.org.