Browse Source

Event mutators

tags/v0.9.0
Chris Smith 5 years ago
parent
commit
0117d981a2
22 changed files with 435 additions and 264 deletions
  1. 4
    0
      CHANGELOG
  2. 5
    5
      src/main/kotlin/com/dmdirc/ktirc/IrcClientImpl.kt
  3. 2
    3
      src/main/kotlin/com/dmdirc/ktirc/events/handlers/CapabilitiesHandler.kt
  4. 11
    16
      src/main/kotlin/com/dmdirc/ktirc/events/handlers/ChannelStateHandler.kt
  5. 2
    2
      src/main/kotlin/com/dmdirc/ktirc/events/handlers/EventHandler.kt
  6. 2
    3
      src/main/kotlin/com/dmdirc/ktirc/events/handlers/PingHandler.kt
  7. 3
    20
      src/main/kotlin/com/dmdirc/ktirc/events/handlers/ServerStateHandler.kt
  8. 2
    3
      src/main/kotlin/com/dmdirc/ktirc/events/handlers/UserStateHandler.kt
  9. 36
    0
      src/main/kotlin/com/dmdirc/ktirc/events/mutators/ChannelFanOutMutator.kt
  10. 16
    0
      src/main/kotlin/com/dmdirc/ktirc/events/mutators/EventMutator.kt
  11. 36
    0
      src/main/kotlin/com/dmdirc/ktirc/events/mutators/ServerReadyMutator.kt
  12. 15
    9
      src/main/kotlin/com/dmdirc/ktirc/io/MessageHandler.kt
  13. 1
    1
      src/main/kotlin/com/dmdirc/ktirc/messages/MessageProcessor.kt
  14. 1
    1
      src/test/kotlin/com/dmdirc/ktirc/events/handlers/CapabilitiesHandlerTest.kt
  15. 6
    73
      src/test/kotlin/com/dmdirc/ktirc/events/handlers/ChannelStateHandlerTest.kt
  16. 1
    1
      src/test/kotlin/com/dmdirc/ktirc/events/handlers/PingHandlerTest.kt
  17. 79
    0
      src/test/kotlin/com/dmdirc/ktirc/events/handlers/ServerStateHandlerTest.kt
  18. 1
    1
      src/test/kotlin/com/dmdirc/ktirc/events/handlers/UserStateHandlerTest.kt
  19. 102
    0
      src/test/kotlin/com/dmdirc/ktirc/events/mutators/ChannelFanOutMutatorTest.kt
  20. 46
    0
      src/test/kotlin/com/dmdirc/ktirc/events/mutators/ServerReadyMutatorTest.kt
  21. 0
    109
      src/test/kotlin/com/dmdirc/ktirc/handlers/ServerStateHandlerTest.kt
  22. 64
    17
      src/test/kotlin/com/dmdirc/ktirc/io/MessageHandlerTest.kt

+ 4
- 0
CHANGELOG View File

@@ -4,6 +4,10 @@ vNEXT (in development)
4 4
    e.g. IrcClient { server("irc.example.com", 6667) }
5 5
  * Add behaviour options
6 6
    * requestModesOnJoin - automatically sends a MODE request when joining a channel
7
+ * (Internal) Introduced event mutators
8
+   * Event mutators are now responsible for handling changing events in response to state
9
+     e.g. ChannelFanOutMutator creates Channel* events for global quits/nick changes/etc
10
+   * Event handlers now just handle events, and don't return anything
7 11
 
8 12
 v0.8.0
9 13
 

+ 5
- 5
src/main/kotlin/com/dmdirc/ktirc/IrcClientImpl.kt View File

@@ -1,8 +1,9 @@
1 1
 package com.dmdirc.ktirc
2 2
 
3 3
 import com.dmdirc.ktirc.events.*
4
-import com.dmdirc.ktirc.handlers.EventHandler
5
-import com.dmdirc.ktirc.handlers.eventHandlers
4
+import com.dmdirc.ktirc.events.handlers.EventHandler
5
+import com.dmdirc.ktirc.events.handlers.eventHandlers
6
+import com.dmdirc.ktirc.events.mutators.eventMutators
6 7
 import com.dmdirc.ktirc.io.KtorLineBufferedSocket
7 8
 import com.dmdirc.ktirc.io.LineBufferedSocket
8 9
 import com.dmdirc.ktirc.io.MessageHandler
@@ -42,7 +43,7 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : IrcClient, C
42 43
     override val channelState = ChannelStateMap { caseMapping }
43 44
     override val userState = UserState { caseMapping }
44 45
 
45
-    private val messageHandler = MessageHandler(messageProcessors.toList(), eventHandlers.toMutableList())
46
+    private val messageHandler = MessageHandler(messageProcessors, eventMutators, eventHandlers.toMutableList())
46 47
 
47 48
     private val parser = MessageParser()
48 49
     private var socket: LineBufferedSocket? = null
@@ -87,9 +88,8 @@ internal class IrcClientImpl(private val config: IrcClientConfig) : IrcClient, C
87 88
 
88 89
     override fun onEvent(handler: (IrcEvent) -> Unit) {
89 90
         messageHandler.handlers.add(object : EventHandler {
90
-            override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
91
+            override fun processEvent(client: IrcClient, event: IrcEvent) {
91 92
                 handler(event)
92
-                return emptyList()
93 93
             }
94 94
         })
95 95
     }

src/main/kotlin/com/dmdirc/ktirc/handlers/CapabilitiesHandler.kt → src/main/kotlin/com/dmdirc/ktirc/events/handlers/CapabilitiesHandler.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.*
@@ -15,7 +15,7 @@ internal class CapabilitiesHandler : EventHandler {
15 15
 
16 16
     private val log by logger()
17 17
 
18
-    override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
18
+    override fun processEvent(client: IrcClient, event: IrcEvent) {
19 19
         when (event) {
20 20
             is ServerCapabilitiesReceived -> handleCapabilitiesReceived(client.serverState.capabilities, event.capabilities)
21 21
             is ServerCapabilitiesFinished -> handleCapabilitiesFinished(client)
@@ -24,7 +24,6 @@ internal class CapabilitiesHandler : EventHandler {
24 24
             is SaslMechanismNotAvailableError -> handleSaslMechanismChange(client, event.mechanisms)
25 25
             is SaslFinished -> handleSaslFinished(client)
26 26
         }
27
-        return emptyList()
28 27
     }
29 28
 
30 29
     private fun handleCapabilitiesReceived(state: CapabilitiesState, capabilities: Map<Capability, String>) {

src/main/kotlin/com/dmdirc/ktirc/handlers/ChannelStateHandler.kt → src/main/kotlin/com/dmdirc/ktirc/events/handlers/ChannelStateHandler.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.*
@@ -12,7 +12,7 @@ internal class ChannelStateHandler : EventHandler {
12 12
 
13 13
     private val log by logger()
14 14
 
15
-    override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
15
+    override fun processEvent(client: IrcClient, event: IrcEvent) {
16 16
         when (event) {
17 17
             is ChannelJoined -> handleJoin(client, event)
18 18
             is ChannelParted -> handlePart(client, event)
@@ -22,11 +22,10 @@ internal class ChannelStateHandler : EventHandler {
22 22
             is ChannelTopicDiscovered -> handleTopicDiscovered(client, event)
23 23
             is ChannelTopicMetadataDiscovered -> handleTopicMetadata(client, event)
24 24
             is ChannelTopicChanged -> handleTopicChanged(client, event)
25
+            is ChannelQuit -> handleQuit(client, event)
26
+            is ChannelNickChanged -> handleNickChanged(client, event)
25 27
             is ModeChanged -> handleModeChanged(client, event)
26
-            is UserQuit -> return handleQuit(client, event)
27
-            is UserNickChanged -> return handleNickChanged(client, event)
28 28
         }
29
-        return emptyList()
30 29
     }
31 30
 
32 31
     private fun handleJoin(client: IrcClient, event: ChannelJoined) {
@@ -158,22 +157,18 @@ internal class ChannelStateHandler : EventHandler {
158 157
         return 1
159 158
     }
160 159
 
161
-    private fun handleQuit(client: IrcClient, event: UserQuit) = sequence {
162
-        client.channelState.forEach {
163
-            if (it.users.contains(event.user.nickname)) {
164
-                it.users -= event.user.nickname
165
-                yield(ChannelQuit(event.time, event.user, it.name, event.reason))
166
-            }
160
+    private fun handleQuit(client: IrcClient, event: ChannelQuit) {
161
+        client.channelState[event.channel]?.let {
162
+            it.users -= event.user.nickname
167 163
         }
168
-    }.toList()
164
+    }
169 165
 
170
-    private fun handleNickChanged(client: IrcClient, event: UserNickChanged) = sequence {
171
-        client.channelState.forEach {
166
+    private fun handleNickChanged(client: IrcClient, event: ChannelNickChanged) {
167
+        client.channelState[event.channel]?.let {
172 168
             it.users[event.user.nickname]?.let { chanUser ->
173 169
                 chanUser.nickname = event.newNick
174
-                yield(ChannelNickChanged(event.time, event.user, it.name, event.newNick))
175 170
             }
176 171
         }
177
-    }.toList()
172
+    }
178 173
 
179 174
 }

src/main/kotlin/com/dmdirc/ktirc/handlers/EventHandler.kt → src/main/kotlin/com/dmdirc/ktirc/events/handlers/EventHandler.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.IrcEvent
@@ -6,7 +6,7 @@ import com.dmdirc.ktirc.events.IrcEvent
6 6
 @FunctionalInterface
7 7
 internal interface EventHandler {
8 8
 
9
-    fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent>
9
+    fun processEvent(client: IrcClient, event: IrcEvent)
10 10
 
11 11
 }
12 12
 

src/main/kotlin/com/dmdirc/ktirc/handlers/PingHandler.kt → src/main/kotlin/com/dmdirc/ktirc/events/handlers/PingHandler.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.IrcEvent
@@ -7,11 +7,10 @@ import com.dmdirc.ktirc.messages.sendPong
7 7
 
8 8
 internal class PingHandler : EventHandler {
9 9
 
10
-    override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
10
+    override fun processEvent(client: IrcClient, event: IrcEvent) {
11 11
         when (event) {
12 12
             is PingReceived -> client.sendPong(event.nonce)
13 13
         }
14
-        return emptyList()
15 14
     }
16 15
 
17 16
 }

src/main/kotlin/com/dmdirc/ktirc/handlers/ServerStateHandler.kt → src/main/kotlin/com/dmdirc/ktirc/events/handlers/ServerStateHandler.kt View File

@@ -1,30 +1,21 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.*
5 5
 import com.dmdirc.ktirc.model.ServerState
6 6
 import com.dmdirc.ktirc.model.ServerStatus
7
-import java.time.LocalDateTime
8 7
 
9 8
 internal class ServerStateHandler : EventHandler {
10 9
 
11
-    override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
10
+    override fun processEvent(client: IrcClient, event: IrcEvent) {
12 11
         when (event) {
13 12
             is ServerConnecting -> client.serverState.status = ServerStatus.Connecting
14 13
             is ServerConnected -> client.serverState.status = ServerStatus.Negotiating
15 14
             is ServerDisconnected -> client.serverState.status = ServerStatus.Disconnected
15
+            is ServerReady -> client.serverState.status = ServerStatus.Ready
16 16
             is ServerWelcome -> handleWelcome(client.serverState, event.server, event.localNick)
17 17
             is ServerFeaturesUpdated -> client.serverState.features.setAll(event.serverFeatures)
18
-
19
-            // Events that won't trigger a server ready event
20
-            is PingReceived -> Unit
21
-            is ServerCapabilitiesReceived -> Unit
22
-            is ServerCapabilitiesAcknowledged -> Unit
23
-            is ServerCapabilitiesFinished -> Unit
24
-
25
-            else -> return checkReadyState(client, event.time)
26 18
         }
27
-        return emptyList()
28 19
     }
29 20
 
30 21
     private fun handleWelcome(serverState: ServerState, server: String, localNick: String) {
@@ -33,12 +24,4 @@ internal class ServerStateHandler : EventHandler {
33 24
         serverState.localNickname = localNick
34 25
     }
35 26
 
36
-    private fun checkReadyState(client: IrcClient, time: LocalDateTime): List<IrcEvent> {
37
-        if (client.serverState.receivedWelcome && client.serverState.status == ServerStatus.Negotiating) {
38
-            client.serverState.status = ServerStatus.Ready
39
-            return listOf(ServerReady(time))
40
-        }
41
-        return emptyList()
42
-    }
43
-
44 27
 }

src/main/kotlin/com/dmdirc/ktirc/handlers/UserStateHandler.kt → src/main/kotlin/com/dmdirc/ktirc/events/handlers/UserStateHandler.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.events.*
@@ -6,7 +6,7 @@ import com.dmdirc.ktirc.model.UserState
6 6
 
7 7
 internal class UserStateHandler : EventHandler {
8 8
 
9
-    override fun processEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> {
9
+    override fun processEvent(client: IrcClient, event: IrcEvent) {
10 10
         when (event) {
11 11
             is ChannelJoined -> handleJoin(client.userState, event)
12 12
             is ChannelParted -> handlePart(client, event)
@@ -16,7 +16,6 @@ internal class UserStateHandler : EventHandler {
16 16
             is UserNickChanged -> handleNickChanged(client, event)
17 17
             is UserQuit -> handleQuit(client.userState, event)
18 18
         }
19
-        return emptyList()
20 19
     }
21 20
 
22 21
     private fun handleJoin(state: UserState, event: ChannelJoined) {

+ 36
- 0
src/main/kotlin/com/dmdirc/ktirc/events/mutators/ChannelFanOutMutator.kt View File

@@ -0,0 +1,36 @@
1
+package com.dmdirc.ktirc.events.mutators
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.events.*
5
+
6
+/**
7
+ * "Fans out" global events such as quits and nick changes to each channel a user is in.
8
+ */
9
+internal class ChannelFanOutMutator : EventMutator {
10
+
11
+    override fun mutateEvent(client: IrcClient, event: IrcEvent) = sequence<IrcEvent> {
12
+        yield(event)
13
+        when (event) {
14
+            is UserQuit -> handleQuit(client, event)
15
+            is UserNickChanged -> handleNickChanged(client, event)
16
+        }
17
+    }.toList()
18
+
19
+    private suspend fun SequenceScope<IrcEvent>.handleQuit(client: IrcClient, event: UserQuit) {
20
+        client.channelState.forEach {
21
+            if (it.users.contains(event.user.nickname)) {
22
+                yield(ChannelQuit(event.time, event.user, it.name, event.reason))
23
+            }
24
+        }
25
+    }
26
+
27
+    private suspend fun SequenceScope<IrcEvent>.handleNickChanged(client: IrcClient, event: UserNickChanged) {
28
+        client.channelState.forEach {
29
+            it.users[event.user.nickname]?.let { chanUser ->
30
+                chanUser.nickname = event.newNick
31
+                yield(ChannelNickChanged(event.time, event.user, it.name, event.newNick))
32
+            }
33
+        }
34
+    }
35
+
36
+}

+ 16
- 0
src/main/kotlin/com/dmdirc/ktirc/events/mutators/EventMutator.kt View File

@@ -0,0 +1,16 @@
1
+package com.dmdirc.ktirc.events.mutators
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.events.IrcEvent
5
+
6
+@FunctionalInterface
7
+internal interface EventMutator {
8
+
9
+    fun mutateEvent(client: IrcClient, event: IrcEvent): List<IrcEvent>
10
+
11
+}
12
+
13
+internal val eventMutators = listOf(
14
+        ServerReadyMutator(),
15
+        ChannelFanOutMutator()
16
+)

+ 36
- 0
src/main/kotlin/com/dmdirc/ktirc/events/mutators/ServerReadyMutator.kt View File

@@ -0,0 +1,36 @@
1
+package com.dmdirc.ktirc.events.mutators
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.events.*
5
+import com.dmdirc.ktirc.model.ServerStatus
6
+
7
+/**
8
+ * Sends a [ServerReady] event once the first line has been received post 001/005/etc.
9
+ */
10
+internal class ServerReadyMutator : EventMutator {
11
+
12
+    /** Events that won't trigger a 'server ready' event to be sent. */
13
+    private val excludedEvents = listOf(
14
+            ServerConnecting::class,
15
+            ServerConnected::class,
16
+            ServerDisconnected::class,
17
+            ServerWelcome::class,
18
+            ServerReady::class,
19
+            ServerFeaturesUpdated::class,
20
+
21
+            PingReceived::class,
22
+            ServerCapabilitiesReceived::class,
23
+            ServerCapabilitiesAcknowledged::class,
24
+            ServerCapabilitiesFinished::class
25
+    )
26
+
27
+    override fun mutateEvent(client: IrcClient, event: IrcEvent): List<IrcEvent> = sequence {
28
+        if (client.serverState.receivedWelcome
29
+                && client.serverState.status == ServerStatus.Negotiating
30
+                && event::class !in excludedEvents) {
31
+            yield(ServerReady(event.time))
32
+        }
33
+        yield(event)
34
+    }.toList()
35
+
36
+}

+ 15
- 9
src/main/kotlin/com/dmdirc/ktirc/io/MessageHandler.kt View File

@@ -1,29 +1,35 @@
1 1
 package com.dmdirc.ktirc.io
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4
-import com.dmdirc.ktirc.handlers.EventHandler
5 4
 import com.dmdirc.ktirc.events.IrcEvent
5
+import com.dmdirc.ktirc.events.handlers.EventHandler
6
+import com.dmdirc.ktirc.events.mutators.EventMutator
6 7
 import com.dmdirc.ktirc.messages.MessageProcessor
7 8
 import com.dmdirc.ktirc.model.IrcMessage
8 9
 import com.dmdirc.ktirc.util.logger
9 10
 import kotlinx.coroutines.channels.ReceiveChannel
10 11
 
11
-internal class MessageHandler(private val processors: List<MessageProcessor>, val handlers: MutableList<EventHandler>) {
12
+internal class MessageHandler(
13
+        private val processors: List<MessageProcessor>,
14
+        private val mutators: List<EventMutator>,
15
+        val handlers: MutableList<EventHandler>) {
12 16
 
13 17
     private val log by logger()
14 18
 
15 19
     suspend fun processMessages(ircClient: IrcClient, messages: ReceiveChannel<IrcMessage>) {
16 20
         for (message in messages) {
17
-            message.toEvents().forEach { event -> emitEvent(ircClient, event) }
21
+            emitEvents(ircClient, message.toEvents())
18 22
         }
19 23
     }
20 24
 
21
-    fun emitEvent(ircClient: IrcClient, ircEvent: IrcEvent) {
22
-        log.fine { "Dispatching event of type ${ircEvent::class}" }
23
-        handlers.forEach { handler ->
24
-            handler.processEvent(ircClient, ircEvent).forEach {
25
-                emitEvent(ircClient, it)
26
-            }
25
+    fun emitEvent(ircClient: IrcClient, ircEvent: IrcEvent) = emitEvents(ircClient, listOf(ircEvent))
26
+
27
+    fun emitEvents(ircClient: IrcClient, ircEvents: List<IrcEvent>) {
28
+        mutators.fold(ircEvents) { events, mutator ->
29
+            events.flatMap { mutator.mutateEvent(ircClient, it) }
30
+        }.forEach { event ->
31
+            log.fine { "Dispatching event of type ${event::class}" }
32
+            handlers.forEach { it.processEvent(ircClient, event) }
27 33
         }
28 34
     }
29 35
 

+ 1
- 1
src/main/kotlin/com/dmdirc/ktirc/messages/MessageProcessor.kt View File

@@ -17,7 +17,7 @@ internal interface MessageProcessor {
17 17
 
18 18
 }
19 19
 
20
-internal val messageProcessors = setOf(
20
+internal val messageProcessors = listOf(
21 21
         AccountProcessor(),
22 22
         AuthenticationProcessor(),
23 23
         CapabilityProcessor(),

src/test/kotlin/com/dmdirc/ktirc/handlers/CapabilitiesHandlerTest.kt → src/test/kotlin/com/dmdirc/ktirc/events/handlers/CapabilitiesHandlerTest.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.TestConstants

src/test/kotlin/com/dmdirc/ktirc/handlers/ChannelStateHandlerTest.kt → src/test/kotlin/com/dmdirc/ktirc/events/handlers/ChannelStateHandlerTest.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.BehaviourConfig
4 4
 import com.dmdirc.ktirc.IrcClient
@@ -216,7 +216,7 @@ internal class ChannelStateHandlerTest {
216 216
     }
217 217
 
218 218
     @Test
219
-    fun `removes user from all channel member lists for quits`() {
219
+    fun `removes user from channel member lists for quits`() {
220 220
         with (ChannelState("#thegibson") { CaseMapping.Rfc }) {
221 221
             users += ChannelUser("ZeroCool")
222 222
             channelStateMap += this
@@ -232,7 +232,8 @@ internal class ChannelStateHandlerTest {
232 232
             channelStateMap += this
233 233
         }
234 234
 
235
-        handler.processEvent(ircClient, UserQuit(TestConstants.time, User("zerocool", "dade", "root.localhost")))
235
+        handler.processEvent(ircClient, ChannelQuit(TestConstants.time, User("zerocool", "dade", "root.localhost"), "#thegibson"))
236
+        handler.processEvent(ircClient, ChannelQuit(TestConstants.time, User("zerocool", "dade", "root.localhost"), "#dumpsterdiving"))
236 237
 
237 238
         assertFalse("zerocool" in channelStateMap["#thegibson"]!!.users)
238 239
         assertFalse("zerocool" in channelStateMap["#dumpsterdiving"]!!.users)
@@ -240,88 +241,20 @@ internal class ChannelStateHandlerTest {
240 241
         assertTrue("acidburn" in channelStateMap["#chat"]!!.users)
241 242
     }
242 243
 
243
-
244
-    @Test
245
-    fun `raises ChannelQuit event for each channel a user quits from`() {
246
-        with (ChannelState("#thegibson") { CaseMapping.Rfc }) {
247
-            users += ChannelUser("ZeroCool")
248
-            channelStateMap += this
249
-        }
250
-
251
-        with (ChannelState("#dumpsterdiving") { CaseMapping.Rfc }) {
252
-            users += ChannelUser("ZeroCool")
253
-            channelStateMap += this
254
-        }
255
-
256
-        with (ChannelState("#chat") { CaseMapping.Rfc }) {
257
-            users += ChannelUser("AcidBurn")
258
-            channelStateMap += this
259
-        }
260
-
261
-        val events = handler.processEvent(ircClient, UserQuit(TestConstants.time, User("zerocool", "dade", "root.localhost"), "Hack the planet!"))
262
-
263
-        val names = mutableListOf<String>()
264
-        assertEquals(2, events.size)
265
-        events.forEach { event ->
266
-            (event as ChannelQuit).let {
267
-                assertEquals(TestConstants.time, it.time)
268
-                assertEquals("zerocool", it.user.nickname)
269
-                assertEquals("Hack the planet!", it.reason)
270
-                names.add(it.channel)
271
-            }
272
-        }
273
-
274
-        assertTrue("#thegibson" in names)
275
-        assertTrue("#dumpsterdiving" in names)
276
-    }
277
-
278 244
     @Test
279 245
     fun `renames user in channel member list for nick changes`() {
280 246
         val channel = ChannelState("#thegibson") { CaseMapping.Rfc }
281 247
         channel.users += ChannelUser("acidBurn")
282 248
         channelStateMap += channel
283 249
 
284
-        handler.processEvent(ircClient, UserNickChanged(TestConstants.time, User("acidburn", "libby", "root.localhost"), "acidB"))
250
+        handler.processEvent(ircClient, ChannelNickChanged(TestConstants.time, User("acidburn", "libby", "root.localhost"), "#thegibson", "acidB"))
251
+        handler.processEvent(ircClient, ChannelNickChanged(TestConstants.time, User("acidburn", "libby", "root.localhost"), "#dumpsterdiving", "acidB"))
285 252
 
286 253
         assertFalse("acidBurn" in channel.users)
287 254
         assertTrue("acidB" in channel.users)
288 255
         assertEquals("acidB", channel.users["acidB"]?.nickname)
289 256
     }
290 257
 
291
-    @Test
292
-    fun `raises ChannelNickChanged event for each channel a user changes nicks in`() {
293
-        with (ChannelState("#thegibson") { CaseMapping.Rfc }) {
294
-            users += ChannelUser("ZeroCool")
295
-            channelStateMap += this
296
-        }
297
-
298
-        with (ChannelState("#dumpsterdiving") { CaseMapping.Rfc }) {
299
-            users += ChannelUser("ZeroCool")
300
-            channelStateMap += this
301
-        }
302
-
303
-        with (ChannelState("#chat") { CaseMapping.Rfc }) {
304
-            users += ChannelUser("AcidBurn")
305
-            channelStateMap += this
306
-        }
307
-
308
-        val events = handler.processEvent(ircClient, UserNickChanged(TestConstants.time, User("zerocool", "dade", "root.localhost"), "zer0c00l"))
309
-
310
-        val names = mutableListOf<String>()
311
-        assertEquals(2, events.size)
312
-        events.forEach { event ->
313
-            (event as ChannelNickChanged).let {
314
-                assertEquals(TestConstants.time, it.time)
315
-                assertEquals("zerocool", it.user.nickname)
316
-                assertEquals("zer0c00l", it.newNick)
317
-                names.add(it.channel)
318
-            }
319
-        }
320
-
321
-        assertTrue("#thegibson" in names)
322
-        assertTrue("#dumpsterdiving" in names)
323
-    }
324
-
325 258
     @Test
326 259
     fun `sets mode discovered flag when discovered mode event received`() {
327 260
         val channel = ChannelState("#thegibson") { CaseMapping.Rfc }

src/test/kotlin/com/dmdirc/ktirc/handlers/PingHandlerTest.kt → src/test/kotlin/com/dmdirc/ktirc/events/handlers/PingHandlerTest.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.TestConstants

+ 79
- 0
src/test/kotlin/com/dmdirc/ktirc/events/handlers/ServerStateHandlerTest.kt View File

@@ -0,0 +1,79 @@
1
+package com.dmdirc.ktirc.events.handlers
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.TestConstants
5
+import com.dmdirc.ktirc.events.*
6
+import com.dmdirc.ktirc.model.ServerFeature
7
+import com.dmdirc.ktirc.model.ServerFeatureMap
8
+import com.dmdirc.ktirc.model.ServerState
9
+import com.dmdirc.ktirc.model.ServerStatus
10
+import com.nhaarman.mockitokotlin2.doReturn
11
+import com.nhaarman.mockitokotlin2.mock
12
+import org.junit.jupiter.api.Assertions.*
13
+import org.junit.jupiter.api.Test
14
+
15
+internal class ServerStateHandlerTest {
16
+
17
+    private val serverState = ServerState("", "")
18
+    private val ircClient = mock<IrcClient> {
19
+        on { serverState } doReturn serverState
20
+    }
21
+
22
+    private val handler = ServerStateHandler()
23
+
24
+    @Test
25
+    fun `sets local nickname on welcome event`() {
26
+        handler.processEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
27
+        assertEquals("acidBurn", serverState.localNickname)
28
+    }
29
+
30
+    @Test
31
+    fun `sets server name on welcome event`() {
32
+        handler.processEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
33
+        assertEquals("the.gibson", serverState.serverName)
34
+    }
35
+
36
+    @Test
37
+    fun `sets receivedWelcome on welcome event`() {
38
+        handler.processEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
39
+        assertTrue(serverState.receivedWelcome)
40
+    }
41
+
42
+    @Test
43
+    fun `sets state to connecting on event`() {
44
+        handler.processEvent(ircClient, ServerConnecting(TestConstants.time))
45
+        assertEquals(ServerStatus.Connecting, serverState.status)
46
+    }
47
+
48
+    @Test
49
+    fun `sets state to disconnected on event`() {
50
+        serverState.status = ServerStatus.Ready
51
+        handler.processEvent(ircClient, ServerDisconnected(TestConstants.time))
52
+        assertEquals(ServerStatus.Disconnected, serverState.status)
53
+    }
54
+
55
+    @Test
56
+    fun `sets state to negotiating on connected`() {
57
+        handler.processEvent(ircClient, ServerConnected(TestConstants.time))
58
+        assertEquals(ServerStatus.Negotiating, serverState.status)
59
+    }
60
+
61
+    @Test
62
+    fun `sets state to ready on ServerReady`() {
63
+        handler.processEvent(ircClient, ServerReady(TestConstants.time))
64
+        assertEquals(ServerStatus.Ready, serverState.status)
65
+    }
66
+
67
+    @Test
68
+    fun `updates features on features event`() {
69
+        val features = ServerFeatureMap()
70
+        features[ServerFeature.ChannelModes] = arrayOf("abc", "def")
71
+        features[ServerFeature.WhoxSupport] = true
72
+
73
+        handler.processEvent(ircClient, ServerFeaturesUpdated(TestConstants.time, features))
74
+
75
+        assertArrayEquals(arrayOf("abc", "def"), serverState.features[ServerFeature.ChannelModes])
76
+        assertEquals(true, serverState.features[ServerFeature.WhoxSupport])
77
+    }
78
+
79
+}

src/test/kotlin/com/dmdirc/ktirc/handlers/UserStateHandlerTest.kt → src/test/kotlin/com/dmdirc/ktirc/events/handlers/UserStateHandlerTest.kt View File

@@ -1,4 +1,4 @@
1
-package com.dmdirc.ktirc.handlers
1
+package com.dmdirc.ktirc.events.handlers
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.TestConstants

+ 102
- 0
src/test/kotlin/com/dmdirc/ktirc/events/mutators/ChannelFanOutMutatorTest.kt View File

@@ -0,0 +1,102 @@
1
+package com.dmdirc.ktirc.events.mutators
2
+
3
+import com.dmdirc.ktirc.BehaviourConfig
4
+import com.dmdirc.ktirc.IrcClient
5
+import com.dmdirc.ktirc.TestConstants
6
+import com.dmdirc.ktirc.events.ChannelNickChanged
7
+import com.dmdirc.ktirc.events.ChannelQuit
8
+import com.dmdirc.ktirc.events.UserNickChanged
9
+import com.dmdirc.ktirc.events.UserQuit
10
+import com.dmdirc.ktirc.io.CaseMapping
11
+import com.dmdirc.ktirc.model.*
12
+import com.nhaarman.mockitokotlin2.doReturn
13
+import com.nhaarman.mockitokotlin2.mock
14
+import org.junit.jupiter.api.Assertions
15
+import org.junit.jupiter.api.Test
16
+
17
+internal class ChannelFanOutMutatorTest {
18
+
19
+    private val mutator = ChannelFanOutMutator()
20
+    private val channelStateMap = ChannelStateMap { CaseMapping.Rfc }
21
+    private val serverState = ServerState("", "")
22
+    private val behaviour = BehaviourConfig()
23
+    private val ircClient = mock<IrcClient> {
24
+        on { serverState } doReturn serverState
25
+        on { channelState } doReturn channelStateMap
26
+        on { behaviour } doReturn behaviour
27
+        on { isLocalUser(User("acidburn", "libby", "root.localhost")) } doReturn true
28
+        on { isLocalUser("acidburn") } doReturn  true
29
+    }
30
+
31
+    @Test
32
+    fun `raises ChannelQuit event for each channel a user quits from`() {
33
+        with (ChannelState("#thegibson") { CaseMapping.Rfc }) {
34
+            users += ChannelUser("ZeroCool")
35
+            channelStateMap += this
36
+        }
37
+
38
+        with (ChannelState("#dumpsterdiving") { CaseMapping.Rfc }) {
39
+            users += ChannelUser("ZeroCool")
40
+            channelStateMap += this
41
+        }
42
+
43
+        with (ChannelState("#chat") { CaseMapping.Rfc }) {
44
+            users += ChannelUser("AcidBurn")
45
+            channelStateMap += this
46
+        }
47
+
48
+        val quitEvent = UserQuit(TestConstants.time, User("zerocool", "dade", "root.localhost"), "Hack the planet!")
49
+        val events = mutator.mutateEvent(ircClient, quitEvent)
50
+
51
+        val names = mutableListOf<String>()
52
+        Assertions.assertEquals(3, events.size)
53
+        Assertions.assertSame(quitEvent, events[0])
54
+        events.subList(1, events.size).forEach { event ->
55
+            (event as ChannelQuit).let {
56
+                Assertions.assertEquals(TestConstants.time, it.time)
57
+                Assertions.assertEquals("zerocool", it.user.nickname)
58
+                Assertions.assertEquals("Hack the planet!", it.reason)
59
+                names.add(it.channel)
60
+            }
61
+        }
62
+
63
+        Assertions.assertTrue("#thegibson" in names)
64
+        Assertions.assertTrue("#dumpsterdiving" in names)
65
+    }
66
+
67
+    @Test
68
+    fun `raises ChannelNickChanged event for each channel a user changes nicks in`() {
69
+        with (ChannelState("#thegibson") { CaseMapping.Rfc }) {
70
+            users += ChannelUser("ZeroCool")
71
+            channelStateMap += this
72
+        }
73
+
74
+        with (ChannelState("#dumpsterdiving") { CaseMapping.Rfc }) {
75
+            users += ChannelUser("ZeroCool")
76
+            channelStateMap += this
77
+        }
78
+
79
+        with (ChannelState("#chat") { CaseMapping.Rfc }) {
80
+            users += ChannelUser("AcidBurn")
81
+            channelStateMap += this
82
+        }
83
+
84
+        val nickEvent = UserNickChanged(TestConstants.time, User("zerocool", "dade", "root.localhost"), "zer0c00l")
85
+        val events = mutator.mutateEvent(ircClient, nickEvent)
86
+
87
+        val names = mutableListOf<String>()
88
+        Assertions.assertEquals(3, events.size)
89
+        Assertions.assertSame(nickEvent, events[0])
90
+        events.subList(1, events.size).forEach { event ->
91
+            (event as ChannelNickChanged).let {
92
+                Assertions.assertEquals(TestConstants.time, it.time)
93
+                Assertions.assertEquals("zerocool", it.user.nickname)
94
+                Assertions.assertEquals("zer0c00l", it.newNick)
95
+                names.add(it.channel)
96
+            }
97
+        }
98
+
99
+        Assertions.assertTrue("#thegibson" in names)
100
+        Assertions.assertTrue("#dumpsterdiving" in names)
101
+    }
102
+}

+ 46
- 0
src/test/kotlin/com/dmdirc/ktirc/events/mutators/ServerReadyMutatorTest.kt View File

@@ -0,0 +1,46 @@
1
+package com.dmdirc.ktirc.events.mutators
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.TestConstants
5
+import com.dmdirc.ktirc.events.*
6
+import com.dmdirc.ktirc.model.ServerState
7
+import com.dmdirc.ktirc.model.ServerStatus
8
+import com.dmdirc.ktirc.model.User
9
+import com.nhaarman.mockitokotlin2.doReturn
10
+import com.nhaarman.mockitokotlin2.mock
11
+import org.junit.jupiter.api.Assertions.*
12
+import org.junit.jupiter.api.Test
13
+
14
+internal class ServerReadyMutatorTest {
15
+
16
+    private val serverState = ServerState("", "")
17
+    private val ircClient = mock<IrcClient> {
18
+        on { serverState } doReturn serverState
19
+    }
20
+
21
+    private val mutator = ServerReadyMutator()
22
+
23
+
24
+    @Test
25
+    fun `emits event on receiving post-005 line`() {
26
+        ircClient.serverState.receivedWelcome = true
27
+        ircClient.serverState.status = ServerStatus.Negotiating
28
+
29
+        listOf(
30
+                ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"),
31
+                PingReceived(TestConstants.time, "1234".toByteArray()),
32
+                ServerCapabilitiesReceived(TestConstants.time, emptyMap()),
33
+                ServerCapabilitiesAcknowledged(TestConstants.time, emptyMap()),
34
+                ServerCapabilitiesFinished(TestConstants.time)
35
+        ).forEach {
36
+            assertEquals(1, mutator.mutateEvent(ircClient, it).size)
37
+        }
38
+
39
+        val event = MessageReceived(TestConstants.time, User("zeroCool"), "acidBurn", "Welcome!")
40
+        val events = mutator.mutateEvent(ircClient, event)
41
+        assertEquals(2, events.size)
42
+        assertSame(event, events[1])
43
+        assertTrue(events[0] is ServerReady)
44
+    }
45
+
46
+}

+ 0
- 109
src/test/kotlin/com/dmdirc/ktirc/handlers/ServerStateHandlerTest.kt View File

@@ -1,109 +0,0 @@
1
-package com.dmdirc.ktirc.handlers
2
-
3
-import com.dmdirc.ktirc.IrcClient
4
-import com.dmdirc.ktirc.TestConstants
5
-import com.dmdirc.ktirc.events.*
6
-import com.dmdirc.ktirc.model.*
7
-import com.nhaarman.mockitokotlin2.doReturn
8
-import com.nhaarman.mockitokotlin2.mock
9
-import kotlinx.coroutines.runBlocking
10
-import org.junit.jupiter.api.Assertions.*
11
-import org.junit.jupiter.api.Test
12
-
13
-internal class ServerStateHandlerTest {
14
-
15
-    private val serverState = ServerState("", "")
16
-    private val ircClient = mock<IrcClient> {
17
-        on { serverState } doReturn serverState
18
-    }
19
-
20
-    private val handler = ServerStateHandler()
21
-
22
-    @Test
23
-    fun `ServerStateHandler sets local nickname on welcome event`() = runBlocking {
24
-        handler.processEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
25
-        assertEquals("acidBurn", serverState.localNickname)
26
-    }
27
-
28
-    @Test
29
-    fun `ServerStateHandler sets server name on welcome event`() = runBlocking {
30
-        handler.processEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
31
-        assertEquals("the.gibson", serverState.serverName)
32
-    }
33
-
34
-    @Test
35
-    fun `ServerStateHandler sets receivedWelcome on welcome event`() = runBlocking {
36
-        handler.processEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
37
-        assertTrue(serverState.receivedWelcome)
38
-    }
39
-
40
-    @Test
41
-    fun `ServerStateHandler sets state to connecting on event`() = runBlocking {
42
-        handler.processEvent(ircClient, ServerConnecting(TestConstants.time))
43
-        assertEquals(ServerStatus.Connecting, serverState.status)
44
-    }
45
-
46
-    @Test
47
-    fun `ServerStateHandler sets state to disconnected on event`() = runBlocking {
48
-        serverState.status = ServerStatus.Ready
49
-        handler.processEvent(ircClient, ServerDisconnected(TestConstants.time))
50
-        assertEquals(ServerStatus.Disconnected, serverState.status)
51
-    }
52
-
53
-    @Test
54
-    fun `ServerStateHandler sets state to negotiating on connected`() = runBlocking {
55
-        handler.processEvent(ircClient, ServerConnected(TestConstants.time))
56
-        assertEquals(ServerStatus.Negotiating, serverState.status)
57
-    }
58
-
59
-    @Test
60
-    fun `ServerStateHandler sets server state to ready on receiving post-005 line`() = runBlocking {
61
-        ircClient.serverState.status = ServerStatus.Negotiating
62
-
63
-        listOf(
64
-                ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"),
65
-                PingReceived(TestConstants.time, "1234".toByteArray()),
66
-                ServerCapabilitiesReceived(TestConstants.time, emptyMap()),
67
-                ServerCapabilitiesAcknowledged(TestConstants.time, emptyMap()),
68
-                ServerCapabilitiesFinished(TestConstants.time),
69
-                MessageReceived(TestConstants.time, User("zeroCool"), "acidBurn", "Welcome!")
70
-        ).forEach {
71
-            assertEquals(ServerStatus.Negotiating, serverState.status)
72
-            handler.processEvent(ircClient, it)
73
-        }
74
-
75
-        assertEquals(ServerStatus.Ready, serverState.status)
76
-    }
77
-
78
-    @Test
79
-    fun `ServerStateHandler emits event on receiving post-005 line`() = runBlocking {
80
-        ircClient.serverState.status = ServerStatus.Negotiating
81
-
82
-        listOf(
83
-                ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"),
84
-                PingReceived(TestConstants.time, "1234".toByteArray()),
85
-                ServerCapabilitiesReceived(TestConstants.time, emptyMap()),
86
-                ServerCapabilitiesAcknowledged(TestConstants.time, emptyMap()),
87
-                ServerCapabilitiesFinished(TestConstants.time)
88
-        ).forEach {
89
-            assertTrue(handler.processEvent(ircClient, it).isEmpty())
90
-        }
91
-
92
-        val events = handler.processEvent(ircClient, MessageReceived(TestConstants.time, User("zeroCool"), "acidBurn", "Welcome!"))
93
-        assertEquals(1, events.size)
94
-        assertTrue(events[0] is ServerReady)
95
-    }
96
-
97
-    @Test
98
-    fun `ServerStateHandler updates features on features event`() = runBlocking {
99
-        val features = ServerFeatureMap()
100
-        features[ServerFeature.ChannelModes] = arrayOf("abc", "def")
101
-        features[ServerFeature.WhoxSupport] = true
102
-
103
-        handler.processEvent(ircClient, ServerFeaturesUpdated(TestConstants.time, features))
104
-
105
-        assertArrayEquals(arrayOf("abc", "def"), serverState.features[ServerFeature.ChannelModes])
106
-        assertEquals(true, serverState.features[ServerFeature.WhoxSupport])
107
-    }
108
-
109
-}

+ 64
- 17
src/test/kotlin/com/dmdirc/ktirc/io/MessageHandlerTest.kt View File

@@ -2,10 +2,11 @@ package com.dmdirc.ktirc.io
2 2
 
3 3
 import com.dmdirc.ktirc.IrcClient
4 4
 import com.dmdirc.ktirc.TestConstants
5
-import com.dmdirc.ktirc.handlers.EventHandler
6 5
 import com.dmdirc.ktirc.events.ServerConnected
7 6
 import com.dmdirc.ktirc.events.ServerReady
8 7
 import com.dmdirc.ktirc.events.ServerWelcome
8
+import com.dmdirc.ktirc.events.handlers.EventHandler
9
+import com.dmdirc.ktirc.events.mutators.EventMutator
9 10
 import com.dmdirc.ktirc.messages.MessageProcessor
10 11
 import com.dmdirc.ktirc.model.IrcMessage
11 12
 import com.nhaarman.mockitokotlin2.*
@@ -26,8 +27,8 @@ internal class MessageHandlerTest {
26 27
     }
27 28
 
28 29
     @Test
29
-    fun `MessageHandler passes message on to correct processor`() = runBlocking<Unit> {
30
-        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf())
30
+    fun `passes message on to correct processor`() = runBlocking<Unit> {
31
+        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), emptyList(), mutableListOf())
31 32
         val message = IrcMessage(emptyMap(), null, "JOIN", emptyList())
32 33
 
33 34
         with(Channel<IrcMessage>(1)) {
@@ -40,8 +41,8 @@ internal class MessageHandlerTest {
40 41
     }
41 42
 
42 43
     @Test
43
-    fun `MessageHandler reads multiple messages`() = runBlocking<Unit> {
44
-        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf())
44
+    fun `reads multiple messages`() = runBlocking<Unit> {
45
+        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), emptyList(), mutableListOf())
45 46
         val joinMessage = IrcMessage(emptyMap(), null, "JOIN", emptyList())
46 47
         val nickMessage = IrcMessage(emptyMap(), null, "NICK", emptyList())
47 48
         val otherMessage = IrcMessage(emptyMap(), null, "OTHER", emptyList())
@@ -61,10 +62,10 @@ internal class MessageHandlerTest {
61 62
     }
62 63
 
63 64
     @Test
64
-    fun `MessageHandler invokes all event handler with all returned events`() = runBlocking<Unit> {
65
+    fun `invokes all event handler with all returned events`() = runBlocking {
65 66
         val eventHandler1 = mock<EventHandler>()
66 67
         val eventHandler2 = mock<EventHandler>()
67
-        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), mutableListOf(eventHandler1, eventHandler2))
68
+        val handler = MessageHandler(listOf(joinProcessor, nickProcessor), emptyList(), mutableListOf(eventHandler1, eventHandler2))
68 69
         val joinMessage = IrcMessage(emptyMap(), null, "JOIN", emptyList())
69 70
         whenever(joinProcessor.process(any())).thenReturn(listOf(ServerConnected(TestConstants.time), ServerWelcome(TestConstants.time, "the.gibson", "acidBurn")))
70 71
 
@@ -81,10 +82,10 @@ internal class MessageHandlerTest {
81 82
     }
82 83
 
83 84
     @Test
84
-    fun `MessageHandler emits custom events to all handlers`() = runBlocking<Unit> {
85
+    fun `emits custom events to all handlers`() {
85 86
         val eventHandler1 = mock<EventHandler>()
86 87
         val eventHandler2 = mock<EventHandler>()
87
-        val handler = MessageHandler(emptyList(), mutableListOf(eventHandler1, eventHandler2))
88
+        val handler = MessageHandler(emptyList(), emptyList(), mutableListOf(eventHandler1, eventHandler2))
88 89
         handler.emitEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
89 90
 
90 91
         verify(eventHandler1).processEvent(same(ircClient), isA<ServerWelcome>())
@@ -92,16 +93,62 @@ internal class MessageHandlerTest {
92 93
     }
93 94
 
94 95
     @Test
95
-    fun `MessageHandler emits events returned from handler`() = runBlocking<Unit> {
96
-        val eventHandler1 = mock<EventHandler> {
97
-            on { processEvent(any(), isA<ServerWelcome>()) } doReturn listOf(ServerReady(TestConstants.time))
96
+    fun `mutates events in order`() {
97
+        val eventMutator1 = mock<EventMutator> {
98
+            on { mutateEvent(any(), isA<ServerWelcome>()) } doReturn listOf(ServerReady(TestConstants.time))
98 99
         }
99
-        val eventHandler2 = mock<EventHandler>()
100
-        val handler = MessageHandler(emptyList(), mutableListOf(eventHandler1, eventHandler2))
100
+        val eventMutator2 = mock<EventMutator> {
101
+            on { mutateEvent(any(), isA<ServerReady>()) } doReturn listOf(ServerConnected(TestConstants.time))
102
+        }
103
+        val eventHandler = mock<EventHandler>()
104
+
105
+        val handler = MessageHandler(emptyList(), listOf(eventMutator1, eventMutator2), mutableListOf(eventHandler))
106
+        handler.emitEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
107
+
108
+        verify(eventMutator1).mutateEvent(same(ircClient), isA<ServerWelcome>())
109
+        verify(eventMutator2).mutateEvent(same(ircClient), isA<ServerReady>())
110
+        verify(eventHandler).processEvent(same(ircClient), isA<ServerConnected>())
111
+        verifyNoMoreInteractions(eventHandler)
112
+    }
113
+
114
+    @Test
115
+    fun `allows mutators to fan out events`() {
116
+        val eventMutator1 = mock<EventMutator> {
117
+            on { mutateEvent(any(), isA<ServerWelcome>()) } doReturn listOf(
118
+                    ServerReady(TestConstants.time),
119
+                    ServerConnected(TestConstants.time)
120
+            )
121
+        }
122
+        val eventMutator2 = mock<EventMutator> {
123
+            on { mutateEvent(any(), isA<ServerReady>()) } doReturn listOf(ServerReady(TestConstants.time))
124
+            on { mutateEvent(any(), isA<ServerConnected>()) } doReturn listOf(ServerConnected(TestConstants.time))
125
+        }
126
+        val eventHandler = mock<EventHandler>()
127
+
128
+        val handler = MessageHandler(emptyList(), listOf(eventMutator1, eventMutator2), mutableListOf(eventHandler))
129
+        handler.emitEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
130
+
131
+        with (inOrder(eventMutator2, eventHandler)) {
132
+            verify(eventMutator2).mutateEvent(same(ircClient), isA<ServerReady>())
133
+            verify(eventMutator2).mutateEvent(same(ircClient), isA<ServerConnected>())
134
+            verify(eventHandler).processEvent(same(ircClient), isA<ServerReady>())
135
+            verify(eventHandler).processEvent(same(ircClient), isA<ServerConnected>())
136
+        }
137
+    }
138
+
139
+    @Test
140
+    fun `allows mutators to suppress events`() {
141
+        val eventMutator1 = mock<EventMutator> {
142
+            on { mutateEvent(any(), isA<ServerWelcome>()) } doReturn emptyList()
143
+        }
144
+        val eventMutator2 = mock<EventMutator>()
145
+        val eventHandler = mock<EventHandler>()
146
+
147
+        val handler = MessageHandler(emptyList(), listOf(eventMutator1, eventMutator2), mutableListOf(eventHandler))
101 148
         handler.emitEvent(ircClient, ServerWelcome(TestConstants.time, "the.gibson", "acidBurn"))
102 149
 
103
-        verify(eventHandler1).processEvent(same(ircClient), isA<ServerReady>())
104
-        verify(eventHandler2).processEvent(same(ircClient), isA<ServerReady>())
150
+        verify(eventMutator2, never()).mutateEvent(any(), any())
151
+        verify(eventHandler, never()).processEvent(any(), any())
105 152
     }
106 153
 
107
-}
154
+}

Loading…
Cancel
Save