--- /dev/null
+package net.pterodactylus.rhynodge.engine
+
+import net.pterodactylus.rhynodge.Query
+import net.pterodactylus.rhynodge.Reaction
+import net.pterodactylus.rhynodge.State
+import net.pterodactylus.rhynodge.actions.EmailAction
+import net.pterodactylus.rhynodge.output.Output
+import net.pterodactylus.rhynodge.states.AbstractState
+import net.pterodactylus.rhynodge.states.FailedState
+import net.pterodactylus.rhynodge.states.StateManager
+import org.hamcrest.Description
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.TypeSafeDiagnosingMatcher
+import org.junit.Test
+import java.util.Optional
+import java.util.Optional.empty
+import java.util.Optional.of
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicReference
+
+/**
+ * Unit test for [ReactionRunner].
+ */
+class ReactionRunnerTest {
+
+ @Test
+ fun `reaction runner sends email if reaction fails and there is no last state`() {
+ val emailSent = AtomicBoolean(false)
+ val errorEmailAction = createEmailAction { emailSent.set(true) }
+ val reactionRunner = ReactionRunner(failingReaction, reactionState, errorEmailAction)
+ reactionRunner.run()
+ assertThat(emailSent.get(), equalTo(true))
+ }
+
+ @Test
+ fun `reaction runner sends email if reaction fails and the last state is successful`() {
+ val reactionState = createReactionState({
+ of<State>(object : AbstractState(true) {
+ override fun plainText() = "success"
+ })
+ })
+ val emailSent = AtomicBoolean(false)
+ val errorEmailAction = createEmailAction { emailSent.set(true) }
+ val reactionRunner = ReactionRunner(failingReaction, reactionState, errorEmailAction)
+ reactionRunner.run()
+ assertThat(emailSent.get(), equalTo(true))
+ }
+
+ @Test
+ fun `reaction runner does not send email if reaction fails and the last state is failed`() {
+ val reactionState = createReactionState({ of<State>(FailedState()) })
+ val emailSent = AtomicBoolean(false)
+ val errorEmailAction = createEmailAction { emailSent.set(true) }
+ val reactionRunner = ReactionRunner(failingReaction, reactionState, errorEmailAction)
+ reactionRunner.run()
+ assertThat(emailSent.get(), equalTo(false))
+ }
+
+ @Test
+ fun `reaction runner saves state when unsuccessful and last state does not exist`() {
+ val savedState = AtomicReference<State>()
+ val reactionState = createReactionState(saveState = { _, state -> savedState.set(state) })
+ val reactionRunner = ReactionRunner(failingReaction, reactionState, nullEmailAction)
+ reactionRunner.run()
+ assertThat(savedState.get(), isState(success = equalTo(false), failCount = equalTo(1)))
+ }
+
+ @Test
+ fun `reaction runner saves state when unsuccessful and last state is failed`() {
+ val savedState = AtomicReference<State>()
+ val failedState = FailedState().apply { setFailCount(12) }
+ val reactionState = createReactionState(lastState = { of<State>(failedState) }, saveState = { _, state -> savedState.set(state) })
+ val reactionRunner = ReactionRunner(failingReaction, reactionState, nullEmailAction)
+ reactionRunner.run()
+ assertThat(savedState.get(), isState(success = equalTo(false), failCount = equalTo(13)))
+ }
+
+ private fun createReactionState(lastState: (reactionName: String) -> Optional<State> = { empty<State>() }, lastSuccessfulState: (reactionName: String) -> Optional<State> = { empty<State>() }, saveState: (reactionName: String, State) -> Unit = { reactionName, state -> }) =
+ ReactionState(object : StateManager(StateDirectory.of("")) {
+ override fun loadLastState(reactionName: String) = lastState(reactionName)
+ override fun loadLastSuccessfulState(reactionName: String) = lastSuccessfulState(reactionName)
+ override fun saveState(reactionName: String, state: State) = saveState(reactionName, state)
+ }, "Test Reaction")
+
+ private fun createEmailAction(action: () -> Unit = {}) = object : EmailAction("test.test", "sender@test.test", "recipient@test.test") {
+ override fun execute(output: Output?) = action()
+ }
+
+ private val reactionState = createReactionState()
+ private val failingReaction = object : Reaction("Test", null, null, null) {
+ override fun query() = Query { FailedState() }
+ }
+ private val nullEmailAction = createEmailAction { }
+
+}
+
+private fun isState(success: Matcher<Boolean> = equalTo(true), failCount: Matcher<Int> = equalTo(0)) = object : TypeSafeDiagnosingMatcher<State>(State::class.java) {
+ override fun matchesSafely(item: State, mismatchDescription: Description): Boolean {
+ if (!success.matches(item.success())) {
+ mismatchDescription.appendText("success is ").appendValue(item.success())
+ return false
+ }
+ if (!failCount.matches(item.failCount())) {
+ mismatchDescription.appendText("failCount is ").appendValue(item.failCount())
+ return false
+ }
+ return true
+ }
+
+ override fun describeTo(description: Description) {
+ description.appendText("state with success ").appendValue(success)
+ .appendText(" and failCount ").appendValue(failCount)
+ }
+}