1 package net.pterodactylus.rhynodge.engine
3 import net.pterodactylus.rhynodge.Query
4 import net.pterodactylus.rhynodge.Reaction
5 import net.pterodactylus.rhynodge.State
6 import net.pterodactylus.rhynodge.actions.EmailAction
7 import net.pterodactylus.rhynodge.output.Output
8 import net.pterodactylus.rhynodge.states.AbstractState
9 import net.pterodactylus.rhynodge.states.FailedState
10 import net.pterodactylus.rhynodge.states.StateManager
11 import org.hamcrest.Description
12 import org.hamcrest.Matcher
13 import org.hamcrest.MatcherAssert.assertThat
14 import org.hamcrest.Matchers.equalTo
15 import org.hamcrest.TypeSafeDiagnosingMatcher
17 import java.util.Optional
18 import java.util.Optional.empty
19 import java.util.Optional.of
20 import java.util.concurrent.atomic.AtomicBoolean
21 import java.util.concurrent.atomic.AtomicReference
24 * Unit test for [ReactionRunner].
26 class ReactionRunnerTest {
29 fun `reaction runner sends email if reaction fails and there is no last state`() {
30 val emailSent = AtomicBoolean(false)
31 val errorEmailAction = createEmailAction { emailSent.set(true) }
32 val reactionRunner = ReactionRunner(failingReaction, reactionState, errorEmailAction)
34 assertThat(emailSent.get(), equalTo(true))
38 fun `reaction runner sends email if reaction fails and the last state is successful`() {
39 val reactionState = createReactionState({
40 of<State>(object : AbstractState(true) {
41 override fun plainText() = "success"
44 val emailSent = AtomicBoolean(false)
45 val errorEmailAction = createEmailAction { emailSent.set(true) }
46 val reactionRunner = ReactionRunner(failingReaction, reactionState, errorEmailAction)
48 assertThat(emailSent.get(), equalTo(true))
52 fun `reaction runner does not send email if reaction fails and the last state is failed`() {
53 val reactionState = createReactionState({ of<State>(FailedState()) })
54 val emailSent = AtomicBoolean(false)
55 val errorEmailAction = createEmailAction { emailSent.set(true) }
56 val reactionRunner = ReactionRunner(failingReaction, reactionState, errorEmailAction)
58 assertThat(emailSent.get(), equalTo(false))
62 fun `reaction runner saves state when unsuccessful and last state does not exist`() {
63 val savedState = AtomicReference<State>()
64 val reactionState = createReactionState(saveState = { _, state -> savedState.set(state) })
65 val reactionRunner = ReactionRunner(failingReaction, reactionState, nullEmailAction)
67 assertThat(savedState.get(), isState(success = equalTo(false), failCount = equalTo(1)))
71 fun `reaction runner saves state when unsuccessful and last state is failed`() {
72 val savedState = AtomicReference<State>()
73 val failedState = FailedState().apply { setFailCount(12) }
74 val reactionState = createReactionState(lastState = { of<State>(failedState) }, saveState = { _, state -> savedState.set(state) })
75 val reactionRunner = ReactionRunner(failingReaction, reactionState, nullEmailAction)
77 assertThat(savedState.get(), isState(success = equalTo(false), failCount = equalTo(13)))
80 private fun createReactionState(lastState: (reactionName: String) -> Optional<State> = { empty<State>() }, lastSuccessfulState: (reactionName: String) -> Optional<State> = { empty<State>() }, saveState: (reactionName: String, State) -> Unit = { reactionName, state -> }) =
81 ReactionState(object : StateManager(StateDirectory.of("")) {
82 override fun loadLastState(reactionName: String) = lastState(reactionName)
83 override fun loadLastSuccessfulState(reactionName: String) = lastSuccessfulState(reactionName)
84 override fun saveState(reactionName: String, state: State) = saveState(reactionName, state)
87 private fun createEmailAction(action: () -> Unit = {}) = object : EmailAction("test.test", "sender@test.test", "recipient@test.test") {
88 override fun execute(output: Output?) = action()
91 private val reactionState = createReactionState()
92 private val failingReaction = object : Reaction("Test", null, null, null) {
93 override fun query() = Query { FailedState() }
95 private val nullEmailAction = createEmailAction { }
99 private fun isState(success: Matcher<Boolean> = equalTo(true), failCount: Matcher<Int> = equalTo(0)) = object : TypeSafeDiagnosingMatcher<State>(State::class.java) {
100 override fun matchesSafely(item: State, mismatchDescription: Description): Boolean {
101 if (!success.matches(item.success())) {
102 mismatchDescription.appendText("success is ").appendValue(item.success())
105 if (!failCount.matches(item.failCount())) {
106 mismatchDescription.appendText("failCount is ").appendValue(item.failCount())
112 override fun describeTo(description: Description) {
113 description.appendText("state with success ").appendValue(success)
114 .appendText(" and failCount ").appendValue(failCount)