🎨 Reduce dependency on Node’s fields
[Sone.git] / src / test / kotlin / net / pterodactylus / sone / main / SonePluginTest.kt
1 package net.pterodactylus.sone.main
2
3 import com.google.common.eventbus.*
4 import com.google.inject.*
5 import freenet.client.async.*
6 import freenet.l10n.BaseL10n.LANGUAGE.*
7 import freenet.node.*
8 import freenet.pluginmanager.*
9 import net.pterodactylus.sone.core.*
10 import net.pterodactylus.sone.core.event.*
11 import net.pterodactylus.sone.fcp.*
12 import net.pterodactylus.sone.freenet.wot.*
13 import net.pterodactylus.sone.test.*
14 import net.pterodactylus.sone.web.*
15 import net.pterodactylus.sone.web.notification.*
16 import org.hamcrest.MatcherAssert.*
17 import org.hamcrest.Matchers.*
18 import org.junit.experimental.categories.*
19 import org.mockito.Mockito.*
20 import java.io.*
21 import java.util.concurrent.atomic.*
22 import kotlin.test.*
23
24 /**
25  * Unit test for [SonePlugin].
26  */
27 @Dirty
28 @Category(NotParallel::class)
29 class SonePluginTest {
30
31         private var sonePlugin = SonePlugin { injector }
32         private val pluginRespirator = deepMock<PluginRespirator>()
33         private val node = deepMock<Node>()
34         private val clientCore = deepMock<NodeClientCore>()
35         private val uskManager = deepMock<USKManager>()
36
37         init {
38                 setField(node, "clientCore", clientCore)
39                 whenever(pluginRespirator.node).thenReturn(node)
40                 setField(clientCore, "uskManager", uskManager)
41                 setField(clientCore, "clientContext", mock<ClientContext>())
42         }
43
44         @Test
45         fun `sone plugin can be started`() {
46                 sonePlugin.setLanguage(ENGLISH)
47                 sonePlugin.runPlugin(pluginRespirator)
48         }
49
50         @Test
51         fun `core can be created`() {
52                 val injector: Injector = runSonePluginWithRealInjector()
53                 assertThat(injector.getInstance<Core>(), notNullValue())
54         }
55
56         @Test
57         fun `fcp interface can be created`() {
58                 val injector: Injector = runSonePluginWithRealInjector()
59                 assertThat(injector.getInstance<FcpInterface>(), notNullValue())
60         }
61
62         @Test
63         fun `web interface can be created`() {
64                 val injector: Injector = runSonePluginWithRealInjector()
65                 assertThat(injector.getInstance<WebInterface>(), notNullValue())
66         }
67
68         @Test
69         fun `web of trust connector can be created`() {
70                 val injector: Injector = runSonePluginWithRealInjector()
71                 assertThat(injector.getInstance<WebOfTrustConnector>(), notNullValue())
72         }
73
74         @Test
75         fun `notification handler can be created`() {
76                 val injector: Injector = runSonePluginWithRealInjector()
77                 assertThat(injector.getInstance<NotificationHandler>(), notNullValue())
78         }
79
80         private fun runSonePluginWithRealInjector(injectorConsumer: (Injector) -> Unit = {}): Injector {
81                 lateinit var injector: Injector
82                 sonePlugin = SonePlugin {
83                         Guice.createInjector(*it).also {
84                                 injector = it
85                                 injectorConsumer(it)
86                         }
87                 }
88                 sonePlugin.setLanguage(ENGLISH)
89                 sonePlugin.runPlugin(pluginRespirator)
90                 return injector
91         }
92
93         @Test
94         fun `core is being started`() {
95                 sonePlugin.runPlugin(pluginRespirator)
96                 val core = injector.getInstance<Core>()
97                 verify(core).start()
98         }
99
100         @Test
101         fun `notification handler is being requested`() {
102                 sonePlugin.runPlugin(pluginRespirator)
103                 assertThat(getInjected(NotificationHandler::class.java), notNullValue())
104         }
105
106         @Test
107         fun `ticker shutdown is being requested`() {
108                 sonePlugin.runPlugin(pluginRespirator)
109                 assertThat(getInjected(TickerShutdown::class.java), notNullValue())
110         }
111
112         private class FirstStartListener(private val firstStartReceived: AtomicBoolean) {
113                 @Subscribe
114                 fun firstStart(@Suppress("UNUSED_PARAMETER") firstStart: FirstStart) {
115                         firstStartReceived.set(true)
116                 }
117         }
118
119         @Test
120         fun `first-start event is sent to event bus when first start is true`() {
121                 File("sone.properties").delete()
122                 val firstStartReceived = AtomicBoolean()
123                 runSonePluginWithRealInjector {
124                         val eventBus = it.getInstance(EventBus::class.java)
125                         eventBus.register(FirstStartListener(firstStartReceived))
126                 }
127                 assertThat(firstStartReceived.get(), equalTo(true))
128         }
129
130         @Test
131         fun `first-start event is not sent to event bus when first start is false`() {
132                 File("sone.properties").deleteAfter {
133                         writeText("# empty")
134                         val firstStartReceived = AtomicBoolean()
135                         runSonePluginWithRealInjector {
136                                 val eventBus = it.getInstance(EventBus::class.java)
137                                 eventBus.register(FirstStartListener(firstStartReceived))
138                         }
139                         assertThat(firstStartReceived.get(), equalTo(false))
140                 }
141         }
142
143         private class ConfigNotReadListener(private val configNotReadReceiver: AtomicBoolean) {
144                 @Subscribe
145                 fun configNotRead(@Suppress("UNUSED_PARAMETER") configNotRead: ConfigNotRead) {
146                         configNotReadReceiver.set(true)
147                 }
148         }
149
150         @Test
151         fun `config-not-read event is sent to event bus when config file is invalid`() {
152                 File("sone.properties").deleteAfter {
153                         writeText("Invalid")
154                         val configNotReadReceived = AtomicBoolean()
155                         runSonePluginWithRealInjector {
156                                 val eventBus = it.getInstance(EventBus::class.java)
157                                 eventBus.register(ConfigNotReadListener(configNotReadReceived))
158                         }
159                         assertThat(configNotReadReceived.get(), equalTo(true))
160                 }
161         }
162
163         @Test
164         fun `config-not-read event is not sent to event bus when config file does not exist`() {
165                 File("sone.properties").delete()
166                 val configNotReadReceived = AtomicBoolean()
167                 runSonePluginWithRealInjector {
168                         val eventBus = it.getInstance(EventBus::class.java)
169                         eventBus.register(ConfigNotReadListener(configNotReadReceived))
170                 }
171                 assertThat(configNotReadReceived.get(), equalTo(false))
172         }
173
174         @Test
175         fun `config-not-read event is not sent to event bus when config file is valid`() {
176                 File("sone.properties").deleteAfter {
177                         writeText("# comment")
178                         val configNotReadReceived = AtomicBoolean()
179                         runSonePluginWithRealInjector {
180                                 val eventBus = it.getInstance(EventBus::class.java)
181                                 eventBus.register(ConfigNotReadListener(configNotReadReceived))
182                         }
183                         assertThat(configNotReadReceived.get(), equalTo(false))
184                 }
185         }
186
187         private class StartupListener(private val startupReceived: () -> Unit) {
188                 @Subscribe
189                 fun startup(@Suppress("UNUSED_PARAMETER") startup: Startup) {
190                         startupReceived()
191                 }
192         }
193
194         @Test
195         fun `startup event is sent to event bus`() {
196                 val startupReceived = AtomicBoolean()
197                 runSonePluginWithRealInjector {
198                         val eventBus = it.getInstance(EventBus::class.java)
199                         eventBus.register(StartupListener { startupReceived.set(true) })
200                 }
201                 assertThat(startupReceived.get(), equalTo(true))
202         }
203
204         private class ShutdownListener(private val shutdownReceived: () -> Unit) {
205                 @Subscribe
206                 fun shutdown(@Suppress("UNUSED_PARAMETER") shutdown: Shutdown) {
207                         shutdownReceived()
208                 }
209         }
210
211         @Test
212         fun `shutdown event is sent to event bus on terminate`() {
213                 val shutdownReceived = AtomicBoolean()
214                 runSonePluginWithRealInjector {
215                         val eventBus = it.getInstance(EventBus::class.java)
216                         eventBus.register(ShutdownListener { shutdownReceived.set(true) })
217                 }
218                 sonePlugin.terminate()
219                 assertThat(shutdownReceived.get(), equalTo(true))
220         }
221
222         private fun <T> getInjected(clazz: Class<T>, annotation: Annotation? = null): T? =
223                         injected[TypeLiteral.get(clazz) to annotation] as? T
224
225         private val injected =
226                         mutableMapOf<Pair<TypeLiteral<*>, Annotation?>, Any>()
227
228         private val injector = mock<Injector>().apply {
229                 fun mockValue(clazz: Class<*>) = false.takeIf { clazz.name == java.lang.Boolean::class.java.name } ?: mock(clazz)
230                 whenever(getInstance(any<Key<*>>())).then {
231                         injected.getOrPut((it.getArgument(0) as Key<*>).let { it.typeLiteral to it.annotation }) {
232                                 it.getArgument<Key<*>>(0).typeLiteral.type.typeName.toClass().let(::mockValue)
233                         }
234                 }
235                 whenever(getInstance(any<Class<*>>())).then {
236                         injected.getOrPut(TypeLiteral.get(it.getArgument(0) as Class<*>) to null) {
237                                 it.getArgument<Class<*>>(0).let(::mockValue)
238                         }
239                 }
240         }
241
242 }
243
244 private fun String.toClass(): Class<*> = SonePlugin::class.java.classLoader.loadClass(this)
245
246 private fun File.deleteAfter(action: File.() -> Unit) = try {
247         action(this)
248 } finally {
249         this.delete()
250 }