public class ReactionLoader {
@Inject
- public ReactionLoader(@Named("smtpHostname") String smtpHostname, @Named("emailSender") String emailSender, @Named("emailRecipient") String emailRecipient) {
- this.smtpHostname = smtpHostname;
- this.emailSender = emailSender;
- this.emailRecipient = emailRecipient;
+ public ReactionLoader(@Named("email") Action emailAction) {
+ this.emailAction = emailAction;
}
/**
@SuppressWarnings("unchecked")
private <T> T createObject(String className, String packageName, List<String> parameters) throws LoaderException {
+ if (className.equals("EmailAction")) {
+ return (T) emailAction;
+ }
+
/* try to load class without package name. */
Class<?> objectClass = null;
try {
}
}
- var effectiveParameters = overrideParameters(className, parameters);
-
/* locate an eligible constructor. */
Constructor<?> wantedConstructor = null;
for (Constructor<?> constructor : objectClass.getConstructors()) {
Class<?>[] parameterTypes = constructor.getParameterTypes();
- if (parameterTypes.length != effectiveParameters.size()) {
+ if (parameterTypes.length != parameters.size()) {
continue;
}
boolean compatibleTypes = true;
}
try {
- return (T) wantedConstructor.newInstance(effectiveParameters.toArray());
+ return (T) wantedConstructor.newInstance(parameters.toArray());
} catch (IllegalArgumentException iae1) {
throw new LoaderException("Could not invoke constructor.", iae1);
} catch (InstantiationException ie1) {
}
- private List<String> overrideParameters(String className, List<String> parameters) {
- if (className.equals("EmailAction")) {
- return List.of(smtpHostname, emailSender, emailRecipient);
- }
- return parameters;
- }
-
- private final String smtpHostname;
- private final String emailSender;
- private final String emailRecipient;
+ private final Action emailAction;
}
package net.pterodactylus.rhynodge.loader
-import com.google.inject.Guice
+import com.fasterxml.jackson.databind.ObjectMapper
+import com.fasterxml.jackson.module.kotlin.readValue
+import com.google.inject.Guice.createInjector
+import net.pterodactylus.rhynodge.Action
+import net.pterodactylus.rhynodge.Filter
+import net.pterodactylus.rhynodge.Merger
+import net.pterodactylus.rhynodge.Query
+import net.pterodactylus.rhynodge.State
+import net.pterodactylus.rhynodge.Watcher
+import net.pterodactylus.rhynodge.actions.EmailAction
+import net.pterodactylus.rhynodge.output.Output
+import net.pterodactylus.rhynodge.states.AbstractState
import net.pterodactylus.util.inject.ObjectBinding
+import org.hamcrest.Matcher
+import org.hamcrest.MatcherAssert.assertThat
+import org.hamcrest.Matchers.contains
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.instanceOf
+import org.hamcrest.Matchers.sameInstance
import org.junit.jupiter.api.Test
+import org.junit.jupiter.api.assertThrows
+import java.util.concurrent.TimeUnit
class ReactionLoaderTest {
@Test
fun `reaction loader can be created by guice`() {
- val injector = Guice.createInjector(
- ObjectBinding.forClass(String::class.java).named("smtpHostname").`is`("host"),
- ObjectBinding.forClass(String::class.java).named("emailSender").`is`("sender"),
- ObjectBinding.forClass(String::class.java).named("emailRecipient").`is`("recipient"),
+ val injector = createInjector(
+ ObjectBinding.forClass(Action::class.java).named("email").shallBe(emailAction)
)
injector.getInstance(ReactionLoader::class.java)
}
+ @Test
+ fun `loader refuses to load disabled chains`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("disabled-chain.json")!!)
+ assertThrows<IllegalArgumentException> { reactionLoader.loadReaction(chain) }
+ }
+
+ @Test
+ fun `loader can load chain without watcher`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-without-watcher.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat(reaction.name(), equalTo("Test Reaction"))
+ assertThat(reaction.updateInterval(), equalTo(TimeUnit.SECONDS.toMillis(3600)))
+ assertThat(reaction.query(), instanceOf<TestQuery0>())
+ assertThat(reaction.filters(), contains(instanceOf<TestFilter0>()))
+ assertThat(reaction.merger(), instanceOf<TestMerger0>())
+ assertThat(reaction.action(), instanceOf<TestAction0>())
+ }
+
+ @Test
+ fun `loader can load chain with watcher`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-watcher.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat(reaction.name(), equalTo("Test Watcher"))
+ assertThat(reaction.updateInterval(), equalTo(TimeUnit.SECONDS.toMillis(7200)))
+ assertThat(reaction.query(), instanceOf<TestQuery0>())
+ assertThat(reaction.filters(), contains(instanceOf<TestFilter0>()))
+ assertThat(reaction.merger(), instanceOf<TestMerger0>())
+ assertThat(reaction.action(), instanceOf<TestAction0>())
+ }
+
+ @Test
+ fun `loader can instantiate query with multiple string parameters`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-query-parameters.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat((reaction.query() as TestQuery2).parameter1, equalTo("value1"))
+ assertThat((reaction.query() as TestQuery2).parameter2, equalTo("value2"))
+ }
+
+ @Test
+ fun `loader can instantiate filters with multiple string parameters`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-filter-parameters.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat(reaction.filters(), contains(instanceOf<TestFilter1>(), instanceOf<TestFilter2>()))
+ assertThat((reaction.filters().toList()[0] as TestFilter1).parameter, equalTo("value"))
+ assertThat((reaction.filters().toList()[1] as TestFilter2).first, equalTo("one"))
+ assertThat((reaction.filters().toList()[1] as TestFilter2).second, equalTo("two"))
+ }
+
+ @Test
+ fun `loader can instantiate merger with multiple parameters`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-merger-parameters.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat((reaction.merger() as TestMerger3).one, equalTo("1"))
+ assertThat((reaction.merger() as TestMerger3).two, equalTo("2"))
+ assertThat((reaction.merger() as TestMerger3).three, equalTo("3"))
+ }
+
+ @Test
+ fun `loader can instantiate action with multiple parameters`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-action-parameters.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat((reaction.action() as TestAction2).foo, equalTo("1"))
+ assertThat((reaction.action() as TestAction2).bar, equalTo("2"))
+ }
+
+ @Test
+ fun `loader can instantiate watcher with multiple parameters`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-watcher-parameters.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat((reaction.query() as TestQuery1).parameter1, equalTo("query"))
+ assertThat((reaction.filters().single() as TestFilter1).parameter, equalTo("filter"))
+ assertThat((reaction.merger() as TestMerger1).one, equalTo("merger"))
+ }
+
+ @Test
+ fun `loader replaces email actions with instance given in constructor`() {
+ val chain = objectMapper.readValue<Chain>(javaClass.getResourceAsStream("chain-with-email-action.json")!!)
+ val reaction = reactionLoader.loadReaction(chain)
+ assertThat(reaction.action(), sameInstance(emailAction))
+ }
+
+ private val emailAction = EmailAction("local.host", "send@r", "recipi@nt")
+ private val reactionLoader = ReactionLoader(emailAction)
+
+}
+
+private inline fun <reified T> instanceOf(): Matcher<Any> = instanceOf(T::class.java)
+
+open class TestQuery0 : Query {
+ override fun state(): State = object : AbstractState() {
+ override fun plainText() = ""
+ }
+}
+
+class TestQuery1(val parameter1: String) : TestQuery0()
+class TestQuery2(val parameter1: String, val parameter2: String) : TestQuery0()
+
+open class TestFilter0 : Filter {
+ override fun filter(state: State) = state
}
+
+class TestFilter1(val parameter: String): TestFilter0()
+class TestFilter2(val first: String, val second: String): TestFilter0()
+
+open class TestMerger0 : Merger {
+ override fun mergeStates(previousState: State, currentState: State) = currentState.apply { trigger() }
+}
+class TestMerger1(val one: String) : TestMerger0()
+class TestMerger3(val one: String, val two: String, val three: String) : TestMerger0()
+
+open class TestAction0 : Action {
+ override fun execute(output: Output) = Unit
+}
+
+class TestAction2(val foo: String, val bar: String) : TestAction0()
+
+class TestWatcher0 : Watcher {
+ override fun query() = TestQuery0()
+ override fun filters() = listOf(TestFilter0())
+ override fun merger() = TestMerger0()
+}
+
+class TestWatcher3(val one: String, val two: String, val three: String) : Watcher {
+ override fun query() = TestQuery1(one)
+ override fun filters() = listOf(TestFilter1(two))
+ override fun merger() = TestMerger1(three)
+}
+
+private val objectMapper = ObjectMapper()