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(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(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() 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() val failedState = FailedState().apply { setFailCount(12) } val reactionState = createReactionState(lastState = { of(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 = { empty() }, lastSuccessfulState: (reactionName: String) -> Optional = { empty() }, 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 = equalTo(true), failCount: Matcher = equalTo(0)) = object : TypeSafeDiagnosingMatcher(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) } }