Rewriting CLIRuleTests in Scala, adding a new testhelper
Added a new testhelper to facilitate tests that rely on activations on a specified entity rather than an activationId.
diff --git a/tests/src/common/WskTestHelpers.scala b/tests/src/common/WskTestHelpers.scala
index 13c6b25..4349928 100644
--- a/tests/src/common/WskTestHelpers.scala
+++ b/tests/src/common/WskTestHelpers.scala
@@ -27,6 +27,7 @@
import common.TestUtils.RunResult
import spray.json._
+import java.time.Instant
/**
* Test fixture to ease cleaning of whisk entities created during testing.
@@ -43,7 +44,7 @@
* in given collection.
*
*/
- private class AssetCleaner(assetsToDeleteAfterTest: Assets, wskprops: WskProps) {
+ class AssetCleaner(assetsToDeleteAfterTest: Assets, wskprops: WskProps) {
def withCleaner[T <: DeleteFromCollection](cli: T, name: String, confirmDelete: Boolean = true)(
cmd: (T, String) => RunResult): RunResult = {
cli.sanitize(name)(wskprops) // sanitize (delete) if asset exists
@@ -134,6 +135,38 @@
}
/**
+ * 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: WskActivation,
+ entity: String,
+ N: Int = 1,
+ since: Option[Instant] = None,
+ pollPeriod: Duration = 1 second,
+ totalWait: Duration = 30 seconds)(
+ check: Seq[CliActivation] => Unit)(
+ implicit wskprops: WskProps): Unit = {
+
+ val activationIds = wsk.pollFor(N, Some(entity), since = since)
+ withClue(s"did not find $N activations for $entity since $since") {
+ activationIds.length shouldBe N
+ }
+
+ val parsed = activationIds.map { id =>
+ wsk.parseJsonString(wsk.get(id).stdout).convertTo[CliActivation]
+ }
+ try {
+ check(parsed)
+ } catch {
+ case error: Throwable =>
+ println(s"check failed for activations $activationIds: ${parsed}")
+ throw error
+ }
+ }
+
+ /**
* In the case that test throws an exception, print stderr and stdout
* from the provided RunResult.
*/
diff --git a/tests/src/system/basic/CLIRuleTests.java b/tests/src/system/basic/CLIRuleTests.java
deleted file mode 100644
index 8aaed20..0000000
--- a/tests/src/system/basic/CLIRuleTests.java
+++ /dev/null
@@ -1,456 +0,0 @@
-/*
- * Copyright 2015-2016 IBM Corporation
- *
- * Licensed 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 system.basic;
-
-import static common.WskCli.Item.Action;
-import static common.WskCli.Item.Activation;
-import static common.WskCli.Item.Rule;
-import static common.WskCli.Item.Trigger;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ThreadLocalRandom;
-
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import com.google.code.tempusfugit.concurrency.ParallelRunner;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParser;
-
-import common.TestUtils;
-import common.WskCli;
-
-/**
- * Tests for rules using command line interface
- */
-@RunWith(ParallelRunner.class)
-public class CLIRuleTests {
- private static final Boolean usePythonCLI = false;
- private static final WskCli wsk = new WskCli(usePythonCLI);
- private static final int RULE_DELAY = 30;
- private static final int DELAY = 90;
- // NEGATIVE_DELAY is used for tests when checking that something doesn't
- // show up in activations
- private static final int NEGATIVE_DELAY = 30;
-
- @BeforeClass
- public static void setUp() throws Exception {
- }
-
- /**
- * rule test with one trigger and one action
- */
- @Test
- public void rule1to1() throws Exception {
- try {
- wsk.sanitize(Rule, "R_121");
- wsk.sanitize(Action, "A_121");
- wsk.sanitize(Trigger, "T_121");
-
- wsk.createAction("A_121", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createTrigger("T_121");
- wsk.createRule("R_121", "T_121", "A_121");
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T_121", "bob 121");
-
- String expected = "The message 'bob 121' has";
- List<String> activationIds = wsk.waitForActivations("A_121", 1, beforeTrigger, DELAY);
- assertTrue("Not enough activation ids found", activationIds != null);
- // the most recent id
- String activationId = activationIds.get(0);
- assertTrue("Expected message not found: " + expected, wsk.logsForActivationContain(activationId, expected, DELAY));
- } finally {
- wsk.delete(Rule, "R_121");
- wsk.delete(Action, "A_121");
- wsk.delete(Trigger, "T_121");
- }
- }
-
- /**
- * rule test with one trigger and one action (action has space in name)
- */
- @Test
- public void rule1to1WithSpaceInActionName() throws Exception {
- try {
- wsk.sanitize(Rule, "R_121s");
- wsk.sanitize(Action, "A_121s A_121s");
- wsk.sanitize(Trigger, "T_121s");
-
- wsk.createAction("A_121s A_121s", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createTrigger("T_121s");
- wsk.createRule("R_121s", "T_121s", "A_121s A_121s");
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T_121s", "bob 121");
-
- String expected = "The message 'bob 121' has";
- List<String> activationIds = wsk.waitForActivations("A_121s A_121s", 1, beforeTrigger, DELAY);
- assertTrue("Not enough activation ids found", activationIds != null);
- // the most recent id
- String activationId = activationIds.get(0);
- assertTrue("Expected message not found: " + expected, wsk.logsForActivationContain(activationId, expected, DELAY));
- } finally {
- wsk.delete(Rule, "R_121s");
- wsk.delete(Action, "A_121s A_121s");
- wsk.delete(Trigger, "T_121s");
- }
- }
-
- /**
- * rule test with two triggers and one action
- */
- @Test
- public void rule2to1() throws Exception {
- try {
- wsk.sanitize(Action, "A_221");
- wsk.sanitize(Trigger, "T1_221");
- wsk.sanitize(Trigger, "T2_221");
- wsk.sanitize(Rule, "R1_221");
- wsk.sanitize(Rule, "R2_221");
-
- wsk.createAction("A_221", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createTrigger("T1_221");
- wsk.createTrigger("T2_221");
- wsk.createRule("R1_221", "T1_221", "A_221");
- wsk.createRule("R2_221", "T2_221", "A_221");
-
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T2_221", "i'll be back");
- wsk.trigger("T1_221", "terminator");
-
- String expected1 = "The message 'terminator' has";
- assertTrue("Expected message not found: " + expected1, wsk.logsForActionContain("A_221", expected1, beforeTrigger, DELAY));
-
- String expected2 = "The message 'i'll be back' has";
- assertTrue("Expected message not found: " + expected1, wsk.logsForActionContain("A_221", expected2, beforeTrigger, DELAY));
-
- } finally {
- wsk.delete(Action, "A_221");
- wsk.delete(Trigger, "T1_221");
- wsk.delete(Trigger, "T2_221");
- wsk.delete(Rule, "R1_221");
- wsk.delete(Rule, "R2_221");
- }
- }
-
- /**
- * rule test with one trigger and two actions
- */
- @Test
- public void rule1to2() throws Exception {
- try {
- wsk.sanitize(Action, "A1_122");
- wsk.sanitize(Action, "A2_122");
- wsk.sanitize(Trigger, "T1_122");
- wsk.sanitize(Rule, "R1_122");
- wsk.sanitize(Rule, "R2_122");
-
- wsk.createAction("A1_122", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createAction("A2_122", TestUtils.getCatalogFilename("samples/hello.js"));
- wsk.createTrigger("T1_122");
- wsk.createRule("R1_122", "T1_122", "A1_122");
- wsk.createRule("R2_122", "T1_122", "A2_122");
-
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T1_122", "put a fork in it");
-
- String expected1 = "The message 'put a fork in it' has";
- assertTrue("Expected message not found: " + expected1, wsk.logsForActionContain("A1_122", expected1, beforeTrigger, DELAY));
-
- String expected2 = "hello put a fork in it";
- assertTrue("Expected message not found: " + expected2, wsk.logsForActionContain("A2_122", expected2, beforeTrigger, DELAY));
-
- } finally {
- wsk.delete(Action, "A1_122");
- wsk.delete(Action, "A2_122");
- wsk.delete(Trigger, "T1_122");
- wsk.delete(Rule, "R1_122");
- wsk.delete(Rule, "R2_122");
- }
- }
-
- /**
- * rule test with two triggers and two actions
- */
- @Test
- public void rule2to2() throws Exception {
- long startMilli = System.currentTimeMillis();
- try {
- wsk.sanitize(Action, "A1_222");
- wsk.sanitize(Action, "A2_222");
- wsk.sanitize(Trigger, "T1_222");
- wsk.sanitize(Trigger, "T2_222");
- wsk.sanitize(Rule, "Alpha");
- wsk.sanitize(Rule, "Beta");
- wsk.sanitize(Rule, "Gamma");
- wsk.sanitize(Rule, "Delta");
- long endMilli = System.currentTimeMillis();
- System.out.format("rule2to2: %.1f seconds to sanitize\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
-
- wsk.createAction("A1_222", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createAction("A2_222", TestUtils.getCatalogFilename("samples/hello.js"));
- wsk.createTrigger("T1_222");
- wsk.createTrigger("T2_222");
- wsk.createRule("Alpha", "T1_222", "A1_222");
- wsk.createRule("Beta", "T1_222", "A2_222");
- wsk.createRule("Gamma", "T2_222", "A1_222");
- wsk.createRule("Delta", "T2_222", "A2_222");
- endMilli = System.currentTimeMillis();
- System.out.format("rule2to2: %.1f seconds to create actions and rules\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
-
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T1_222", "XXX");
- wsk.trigger("T2_222", "YYY");
- endMilli = System.currentTimeMillis();
- System.out.format("rule2to2: %.1f seconds to trigger\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
-
- List<String> activations = wsk.waitForActivations("A1_222", 2, beforeTrigger, DELAY);
- assertTrue("Not enough activation ids found", activations != null);
-
- String expected1 = "The message 'XXX' has";
- assertTrue("Expected message not found: " + expected1, wsk.logsForActionContain("A1_222", expected1, beforeTrigger, DELAY));
- String expected2 = "The message 'YYY' has";
- assertTrue("Expected message not found: " + expected2, wsk.logsForActionContain("A1_222", expected2, beforeTrigger, DELAY));
- endMilli = System.currentTimeMillis();
- System.out.format("rule2to2: %.1f seconds for first check\n", (endMilli - startMilli) / 1000.0);
-
- startMilli = endMilli;
-
- expected1 = "hello XXX";
- expected2 = "hello YYY";
- assertTrue("Expected message not found: " + expected1, wsk.logsForActionContain("A2_222", expected1, beforeTrigger, DELAY));
- assertTrue("Expected message not found: " + expected2, wsk.logsForActionContain("A2_222", expected2, beforeTrigger, DELAY));
- endMilli = System.currentTimeMillis();
- System.out.format("rule2to2: %.1f seconds for second check\n", (endMilli - startMilli) / 1000.0);
-
- startMilli = endMilli;
- } finally {
- wsk.delete(Action, "A1_222");
- wsk.delete(Action, "A2_222");
- wsk.delete(Trigger, "T1_222");
- wsk.delete(Trigger, "T2_222");
- wsk.delete(Rule, "Alpha");
- wsk.delete(Rule, "Beta");
- wsk.delete(Rule, "Gamma");
- wsk.delete(Rule, "Delta");
- long endMilli = System.currentTimeMillis();
- System.out.format("rule2to2: %.1f seconds for cleanup\n", (endMilli - startMilli) / 1000.0);
- }
- }
-
- /**
- * rule test to make sure deleting a rule also disables it.
- */
- @Test
- public void ruleDisable() throws Exception {
- long startMilli = System.currentTimeMillis();
- try {
- wsk.sanitize(Action, "A_321");
- wsk.sanitize(Trigger, "T_321");
- wsk.sanitize(Rule, "R_321");
- long endMilli = System.currentTimeMillis();
- System.out.format("ruleDisable: %.1f seconds to sanitize\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
-
- wsk.createAction("A_321", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createTrigger("T_321");
- wsk.createRule("R_321", "T_321", "A_321");
- wsk.delete(Rule, "R_321");
- endMilli = System.currentTimeMillis();
- System.out.format("ruleDisable: %.1f seconds to create and delete rule/action\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
- wsk.trigger("T_321", "ralph");
- endMilli = System.currentTimeMillis();
- System.out.format("ruleDisable: %.1f seconds to trigger\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
-
- // retrieve activation ids; wait for at least one
- List<String> activations = wsk.waitForActivations("A_321", 1, startMilli, NEGATIVE_DELAY);
- assertTrue("Unexpected activation id found: ", activations == null || activations.size() == 0);
- endMilli = System.currentTimeMillis();
- System.out.format("ruleDisable: %.1f seconds to get activation\n", (endMilli - startMilli) / 1000.0);
- startMilli = endMilli;
- } finally {
- wsk.delete(Action, "A_321");
- wsk.delete(Trigger, "T_321");
- wsk.sanitize(Rule, "R_321");
- long endMilli = System.currentTimeMillis();
- System.out.format("ruleDisable: %.1f seconds to cleanup\n", (endMilli - startMilli) / 1000.0);
- }
- }
-
- /**
- * rule test to check if a rule can be deleted and recreated with the same
- * name.
- */
- @Test
- public void ruleRecreate() throws Exception {
- try {
- wsk.sanitize(Action, "A_421");
- wsk.sanitize(Trigger, "T_421");
- wsk.sanitize(Trigger, "T_422");
- wsk.sanitize(Rule, "R_421");
-
- wsk.createAction("A_421", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createTrigger("T_421");
- wsk.createRule("R_421", "T_421", "A_421");
- wsk.delete(Rule, "R_421");
- wsk.createTrigger("T_422");
- wsk.createRule("R_421", "T_422", "A_421");
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T_422", "david");
-
- List<String> activations = wsk.waitForActivations("A_421", 1, beforeTrigger, NEGATIVE_DELAY);
- if (activations == null || activations.size() == 0) {
- assertFalse("Did not find any activations for A_421", true);
- return;
- }
- String activationId = activations.get(0);
- String expected = "The message 'david' has";
- assertTrue("Expected message found: " + expected, wsk.logsForActivationContain(activationId, expected, DELAY));
- } finally {
- wsk.delete(Action, "A_421");
- wsk.delete(Trigger, "T_421");
- wsk.delete(Trigger, "T_422");
- wsk.sanitize(Rule, "R_421");
- }
- }
-
- /**
- * rule test to check if a rule can be disable and enabled.
- */
- @Test
- public void ruleDisableEnable() throws Exception {
- try {
- wsk.sanitize(Action, "A_621");
- wsk.sanitize(Trigger, "T_621");
- wsk.sanitize(Rule, "R_621");
-
- wsk.createAction("A_621", TestUtils.getCatalogFilename("samples/wc.js"));
- wsk.createTrigger("T_621");
- wsk.createRule("R_621", "T_621", "A_621");
-
- wsk.disableRule("R_621", RULE_DELAY);
- wsk.trigger("T_621", "batman");
-
- wsk.enableRule("R_621", RULE_DELAY);
- long beforeTrigger = System.currentTimeMillis();
- wsk.trigger("T_621", "bruce wayne");
-
- List<String> activations = wsk.waitForActivations("A_621", 1, beforeTrigger, DELAY);
- assertTrue("Not enough activation ids found", activations != null);
-
- String activationId = activations.get(0);
- String expected = "The message 'bruce wayne' has";
- assertTrue("Expected message not found: " + expected, wsk.logsForActivationContain(activationId, expected, DELAY));
-
- String unexpected = "The message 'batman' has";
- assertFalse("Unexpected message found: " + unexpected, wsk.logsForActivationContain(activationId, unexpected, NEGATIVE_DELAY));
- } finally {
- wsk.delete(Action, "A_621");
- wsk.delete(Trigger, "T_621");
- wsk.sanitize(Rule, "R_621");
- }
- }
-
- /**
- * Test for presence of activation records for trigger, rule, and action.
- */
- @Test
- public void activations() throws Exception {
- int nameSuffix = ThreadLocalRandom.current().nextInt(Integer.MAX_VALUE);
- String action = "ACT_A_" + nameSuffix;
- String rule = "ACT_R_" + nameSuffix;
- String trigger = "ACT_T_" + nameSuffix;
-
- final class ActivationInfo {
- public String activationId;
- public String cause;
-
- public ActivationInfo(String activationId, String cause) {
- this.activationId = activationId;
- this.cause = cause;
- }
-
- @Override
- public String toString() {
- return "ActivationInfo [activationId=" + activationId + ", cause=" + cause + "]";
- }
- }
-
- try {
- wsk.sanitize(Action, action);
- wsk.sanitize(Trigger, trigger);
- wsk.sanitize(Rule, rule);
-
- wsk.createAction(action, TestUtils.getCatalogFilename("utils/date.js"));
- wsk.createTrigger(trigger);
- wsk.createRule(rule, trigger, action);
- wsk.trigger(trigger, "bobby 121");
-
- // Get activations
- Set<String> entities = new HashSet<String>(Arrays.asList(new String[] { trigger, action, rule }));
- Map<String, ActivationInfo> activations = TestUtils.waitfor(() -> {
- String list = wsk.list(Activation);
- String[] lines = list.split("\\r?\\n");
- Map<String, ActivationInfo> infos = new HashMap<String, ActivationInfo>();
- for (String line : lines) {
- String[] words = line.split("\\s+");
- if (words.length == 2) {
- String entityName = words[1];
- String activationId = words[0];
- if (entities.contains(entityName)) {
- String activation = wsk.get(Activation, activationId);
- // remove "ok" line in stdout; leaving json
- activation = activation.substring(activation.indexOf(System.getProperty("line.separator")) + 1);
- JsonObject json = new JsonParser().parse(activation).getAsJsonObject();
- String cause = json.get("cause") != null ? json.get("cause").getAsString() : "";
- infos.put(entityName, new ActivationInfo(activationId, cause));
- }
- }
- }
- return infos.size() == 3 ? infos : null;
- } , 8, 1, DELAY);
-
- // Check that activations exist.
- assertTrue("Activation not found for " + trigger, activations.containsKey(trigger));
- assertTrue("Activation not found for " + rule, activations.containsKey(rule));
- assertTrue("Activation not found for " + action, activations.containsKey(action));
-
- // Check that activation cause is correct.
- assertTrue("Wrong cause found for " + trigger, activations.get(trigger).cause.equals(""));
- assertTrue("Wrong cause found for " + rule, activations.get(rule).cause.equals(activations.get(trigger).activationId));
- // assertTrue("Wrong cause found for " + action, activations.get(action).cause.equals(activations.get(rule).activationId));
-
- } finally {
- wsk.delete(Rule, rule);
- wsk.delete(Action, action);
- wsk.delete(Trigger, trigger);
- }
- }
-
-}
diff --git a/tests/src/system/basic/WskRuleTests.scala b/tests/src/system/basic/WskRuleTests.scala
new file mode 100644
index 0000000..d7a6f4d
--- /dev/null
+++ b/tests/src/system/basic/WskRuleTests.scala
@@ -0,0 +1,290 @@
+/*
+ * Copyright 2015-2016 IBM Corporation
+ *
+ * Licensed 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 system.basic
+
+import org.junit.runner.RunWith
+import org.scalatest.junit.JUnitRunner
+
+import common.JsHelpers
+import common.TestHelpers
+import common.TestUtils
+import common.Wsk
+import common.WskProps
+import common.WskTestHelpers
+import spray.json._
+import spray.json.DefaultJsonProtocol._
+import java.time.Instant
+
+@RunWith(classOf[JUnitRunner])
+class WskRuleTests
+ extends TestHelpers
+ with JsHelpers
+ with WskTestHelpers {
+
+ implicit val wskprops = WskProps()
+ val wsk = new Wsk(usePythonCLI = false)
+ val defaultAction = TestUtils.getCatalogFilename("samples/wc.js")
+ val secondAction = TestUtils.getCatalogFilename("samples/hello.js")
+
+ val testString = "this is a test"
+ val testResult = JsObject("count" -> testString.split(" ").length.toJson)
+
+ /**
+ * Sets up trigger -> rule -> action triplets. Deduplicates triggers and rules
+ * and links it all up.
+ *
+ * @param rules Tuple3s containing (rule, trigger, (actionName, actionFile))
+ */
+ def ruleSetup(rules: Seq[(String, String, (String, String))], assetHelper: AssetCleaner) = {
+ val triggers = rules.map(_._2).distinct
+ val actions = rules.map(_._3).distinct
+
+ triggers.foreach { trigger =>
+ assetHelper.withCleaner(wsk.trigger, trigger) {
+ (trigger, name) => trigger.create(name)
+ }
+ }
+
+ actions.foreach {
+ case (actionName, file) =>
+ assetHelper.withCleaner(wsk.action, actionName) {
+ (action, name) => action.create(name, Some(file))
+ }
+ }
+
+ rules.foreach {
+ case (ruleName, triggerName, action) =>
+ assetHelper.withCleaner(wsk.rule, ruleName) {
+ (rule, name) => rule.create(name, triggerName, action._1)
+ }
+ }
+ }
+
+ behavior of "Whisk rules"
+
+ it should "invoke the action attached on trigger fire, creating an activation for each entity including the cause" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = "r1to1"
+ val triggerName = "t1to1"
+ val actionName = "a1 to 1" // spaces in name intended for greater test coverage
+
+ ruleSetup(Seq(
+ (ruleName, triggerName, (actionName, defaultAction))),
+ assetHelper)
+
+ val now = Instant.now
+ val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+
+ withActivation(wsk.activation, run) {
+ triggerActivation =>
+ triggerActivation.cause shouldBe None
+
+ withActivationsFromEntity(wsk.activation, ruleName, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ _.head.cause shouldBe Some(triggerActivation.activationId)
+ }
+
+ withActivationsFromEntity(wsk.activation, actionName, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
+ }
+ }
+
+ it should "not activate an action if the rule is deleted when the trigger is fired" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = "ruleDelete"
+ val triggerName = "ruleDeleteTrigger"
+ val actionName = "ruleDeleteAction"
+
+ assetHelper.withCleaner(wsk.trigger, triggerName) {
+ (trigger, name) => trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) {
+ (action, name) => action.create(name, Some(defaultAction))
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName, false) {
+ (rule, name) => rule.create(name, triggerName, actionName)
+ }
+
+ val first = wsk.trigger.fire(triggerName, Map("payload" -> "bogus".toJson))
+ wsk.rule.delete(ruleName)
+ wsk.trigger.fire(triggerName, Map("payload" -> "bogus2".toJson))
+
+ withActivation(wsk.activation, first) {
+ activation =>
+ // tries to find 2 activations for the action, should only find 1
+ val activations = wsk.activation.pollFor(2, Some(actionName), since = Some(Instant.ofEpochMilli(activation.start)), retries = 30)
+
+ activations.length shouldBe 1
+ }
+ }
+
+ it should "enable and disable a rule and check action is activated only when rule is enabled" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = "ruleDisable"
+ val triggerName = "ruleDisableTrigger"
+ val actionName = "ruleDisableAction"
+
+ ruleSetup(Seq(
+ (ruleName, triggerName, (actionName, defaultAction))),
+ assetHelper)
+
+ val first = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+ wsk.rule.disableRule(ruleName)
+ wsk.trigger.fire(triggerName, Map("payload" -> s"$testString with added words".toJson))
+ wsk.rule.enableRule(ruleName)
+ wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+
+ withActivation(wsk.activation, first) {
+ triggerActivation =>
+ withActivationsFromEntity(wsk.activation, actionName, N = 2, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ activations =>
+ val results = activations.map(_.response.result)
+ results should contain theSameElementsAs Seq(Some(testResult), Some(testResult))
+ }
+ }
+ }
+
+ it should "be able to recreate a rule with the same name and match it successfully" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val ruleName = "ruleRecreate"
+ val triggerName1 = "ruleRecreateTrigger1"
+ val triggerName2 = "ruleRecreateTrigger2"
+ val actionName = "ruleRecreateAction"
+
+ assetHelper.withCleaner(wsk.trigger, triggerName1) {
+ (trigger, name) => trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.action, actionName) {
+ (action, name) => action.create(name, Some(defaultAction))
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName, false) {
+ (rule, name) => rule.create(name, triggerName1, actionName)
+ }
+
+ wsk.rule.delete(ruleName)
+
+ assetHelper.withCleaner(wsk.trigger, triggerName2) {
+ (trigger, name) => trigger.create(name)
+ }
+ assetHelper.withCleaner(wsk.rule, ruleName) {
+ (rule, name) => rule.create(name, triggerName2, actionName)
+ }
+
+ val first = wsk.trigger.fire(triggerName2, Map("payload" -> testString.toJson))
+ withActivation(wsk.activation, first) {
+ triggerActivation =>
+ withActivationsFromEntity(wsk.activation, actionName, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
+ }
+ }
+
+ it should "connect two triggers via rules to one action and activate it accordingly" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName1 = "t2to1a"
+ val triggerName2 = "t2to1b"
+ val actionName = "a2to1"
+
+ ruleSetup(Seq(
+ ("r2to1a", triggerName1, (actionName, defaultAction)),
+ ("r2to1b", triggerName2, (actionName, defaultAction))),
+ assetHelper)
+
+ val testPayloads = Seq("got three words", "got four words, period")
+
+ val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
+ wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
+
+ withActivation(wsk.activation, run) {
+ triggerActivation =>
+ withActivationsFromEntity(wsk.activation, actionName, N = 2, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ activations =>
+ val results = activations.map(_.response.result)
+ val expectedResults = testPayloads.map { payload =>
+ Some(JsObject("count" -> payload.split(" ").length.toJson))
+ }
+
+ results should contain theSameElementsAs expectedResults
+ }
+ }
+ }
+
+ it should "connect one trigger to two different actions, invoking them both eventually" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName = "t1to2"
+ val actionName1 = "a1to2a"
+ val actionName2 = "a1to2b"
+
+ ruleSetup(Seq(
+ ("r1to2a", triggerName, (actionName1, defaultAction)),
+ ("r1to2b", triggerName, (actionName2, secondAction))),
+ assetHelper)
+
+ val run = wsk.trigger.fire(triggerName, Map("payload" -> testString.toJson))
+
+ withActivation(wsk.activation, run) {
+ triggerActivation =>
+ withActivationsFromEntity(wsk.activation, actionName1, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ _.head.response.result shouldBe Some(testResult)
+ }
+ withActivationsFromEntity(wsk.activation, actionName2, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ _.head.logs.get.mkString(" ") should include(s"hello $testString")
+ }
+ }
+ }
+
+ it should "connect two triggers to two different actions, invoking them both eventually" in withAssetCleaner(wskprops) {
+ (wp, assetHelper) =>
+ val triggerName1 = "t1to2a"
+ val triggerName2 = "t1to2b"
+ val actionName1 = "a1to2a"
+ val actionName2 = "a1to2b"
+
+ ruleSetup(Seq(
+ ("r2to2a", triggerName1, (actionName1, defaultAction)),
+ ("r2to2b", triggerName1, (actionName2, secondAction)),
+ ("r2to2c", triggerName2, (actionName1, defaultAction)),
+ ("r2to2d", triggerName2, (actionName2, secondAction))),
+ assetHelper)
+
+ val testPayloads = Seq("got three words", "got four words, period")
+ val run = wsk.trigger.fire(triggerName1, Map("payload" -> testPayloads(0).toJson))
+ wsk.trigger.fire(triggerName2, Map("payload" -> testPayloads(1).toJson))
+
+ withActivation(wsk.activation, run) {
+ triggerActivation =>
+ withActivationsFromEntity(wsk.activation, actionName1, N = 2, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ activations =>
+ val results = activations.map(_.response.result)
+ val expectedResults = testPayloads.map { payload =>
+ Some(JsObject("count" -> payload.split(" ").length.toJson))
+ }
+
+ results should contain theSameElementsAs expectedResults
+ }
+ withActivationsFromEntity(wsk.activation, actionName2, N = 2, since = Some(Instant.ofEpochMilli(triggerActivation.start))) {
+ 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
+ }
+ }
+ }
+
+}