Browse Source

Rudimentary capabilities support

tags/v0.1.0
Chris Smith 5 years ago
parent
commit
a1a3c66c5b

+ 3
- 3
src/main/kotlin/com/dmdirc/ktirc/IrcClient.kt View File

@@ -60,7 +60,7 @@ class IrcClientImpl(private val server: Server, private val profile: Profile) :
60 60
             with(socketFactory(server.host, server.port)) {
61 61
                 socket = this
62 62
                 connect()
63
-                // TODO: CAP LS
63
+                sendLine("CAP LS 302")
64 64
                 server.password?.let { pass -> sendLine(passwordMessage(pass)) }
65 65
                 sendLine(nickMessage(profile.initialNick))
66 66
                 // TODO: Send correct host
@@ -81,11 +81,11 @@ class IrcClientImpl(private val server: Server, private val profile: Profile) :
81 81
             }
82 82
 
83 83
             runBlocking {
84
-                val client = IrcClientImpl(Server("uk.quakenet.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
84
+                val client = IrcClientImpl(Server("testnet.inspircd.org", 6667), Profile("KtIrc", "Kotlin!", "kotlin"))
85 85
                 client.eventHandler = object : EventHandler {
86 86
                     override suspend fun processEvent(client: IrcClient, event: IrcEvent) {
87 87
                         when (event) {
88
-                            is ServerWelcome -> client.send(joinMessage("#mdbot"))
88
+                            is ServerWelcome -> client.send(joinMessage("#ktirc"))
89 89
                             is MessageReceived -> if (event.message == "!test") client.send(privmsgMessage(event.target, "Test successful!"))
90 90
                         }
91 91
                     }

+ 49
- 0
src/main/kotlin/com/dmdirc/ktirc/events/CapabilitiesHandler.kt View File

@@ -0,0 +1,49 @@
1
+package com.dmdirc.ktirc.events
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.messages.capabilityEndMessage
5
+import com.dmdirc.ktirc.messages.capabilityRequestMessage
6
+import com.dmdirc.ktirc.model.CapabilitiesNegotiationState
7
+import com.dmdirc.ktirc.model.CapabilitiesState
8
+import com.dmdirc.ktirc.model.Capability
9
+import com.dmdirc.ktirc.util.logger
10
+
11
+class CapabilitiesHandler : EventHandler {
12
+
13
+    private val log by logger()
14
+
15
+    override suspend fun processEvent(client: IrcClient, event: IrcEvent) {
16
+        when (event) {
17
+            is ServerCapabilitiesReceived -> handleCapabilitiesReceived(client.serverState.capabilities, event.capabilities)
18
+            is ServerCapabilitiesFinished -> handleCapabilitiesFinished(client)
19
+            is ServerCapabilitiesAcknowledged -> handleCapabilitiesAcknowledged(client, event.capabilities)
20
+        }
21
+    }
22
+
23
+    private fun handleCapabilitiesReceived(state: CapabilitiesState, capabilities: Map<Capability, String>) {
24
+        state.advertisedCapabilities.putAll(capabilities)
25
+    }
26
+
27
+    private suspend fun handleCapabilitiesFinished(client: IrcClient) {
28
+        // TODO: We probably need to split the outgoing REQ lines if there are lots of caps
29
+        // TODO: For caps with values we'll need to decide which value to use/whether to enable them/etc
30
+        with (client.serverState.capabilities) {
31
+            negotiationState = CapabilitiesNegotiationState.AWAITING_ACK
32
+            advertisedCapabilities.keys.map { it.name }.let {
33
+                log.info { "Requesting capabilities: ${it.toList()}" }
34
+                client.send(capabilityRequestMessage(it))
35
+            }
36
+        }
37
+    }
38
+
39
+    private suspend fun handleCapabilitiesAcknowledged(client: IrcClient, capabilities: Map<Capability, String>) {
40
+        // TODO: Check if everything we wanted is enabled
41
+        with (client.serverState.capabilities) {
42
+            log.info { "Acknowledged capabilities: ${capabilities.keys.map { it.name }.toList()}" }
43
+            negotiationState = CapabilitiesNegotiationState.FINISHED
44
+            enabledCapabilities.putAll(capabilities)
45
+            client.send(capabilityEndMessage())
46
+        }
47
+    }
48
+
49
+}

+ 1
- 0
src/main/kotlin/com/dmdirc/ktirc/events/EventHandler.kt View File

@@ -9,6 +9,7 @@ interface EventHandler {
9 9
 }
10 10
 
11 11
 val eventHandlers = setOf(
12
+        CapabilitiesHandler(),
12 13
         ChannelStateHandler(),
13 14
         PingHandler(),
14 15
         ServerStateHandler()

+ 10
- 0
src/main/kotlin/com/dmdirc/ktirc/events/Events.kt View File

@@ -2,6 +2,7 @@
2 2
 
3 3
 package com.dmdirc.ktirc.events
4 4
 
5
+import com.dmdirc.ktirc.model.Capability
5 6
 import com.dmdirc.ktirc.model.ServerFeatureMap
6 7
 import com.dmdirc.ktirc.model.User
7 8
 
@@ -37,3 +38,12 @@ data class MessageReceived(val user: User, val target: String, val message: Stri
37 38
 
38 39
 /** Raised when a user quits. */
39 40
 data class UserQuit(val user: User, val reason: String = "") : IrcEvent()
41
+
42
+/** Raised when available server capabilities are received. More batches may follow. */
43
+data class ServerCapabilitiesReceived(val capabilities: Map<Capability, String>): IrcEvent()
44
+
45
+/** Raised when our requested capabilities are acknowledged. More batches may follow. */
46
+data class ServerCapabilitiesAcknowledged(val capabilities: Map<Capability, String>): IrcEvent()
47
+
48
+/** Raised when the server has finished sending us capabilities. */
49
+object ServerCapabilitiesFinished : IrcEvent()

+ 47
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/CapabilityProcessor.kt View File

@@ -0,0 +1,47 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.ServerCapabilitiesAcknowledged
4
+import com.dmdirc.ktirc.events.ServerCapabilitiesFinished
5
+import com.dmdirc.ktirc.events.ServerCapabilitiesReceived
6
+import com.dmdirc.ktirc.io.IrcMessage
7
+import com.dmdirc.ktirc.model.capabilities
8
+import com.dmdirc.ktirc.util.logger
9
+
10
+class CapabilityProcessor : MessageProcessor {
11
+
12
+    private val log by logger()
13
+
14
+    override val commands = arrayOf("CAP")
15
+
16
+    override fun process(message: IrcMessage) = when (message.subCommand) {
17
+        "LS" -> handleList(message.subCommandArguments)
18
+        "ACK" -> listOf(ServerCapabilitiesAcknowledged(message.params.capabilities))
19
+        else -> emptyList()
20
+    }
21
+
22
+    private fun handleList(lsParams: List<ByteArray>) = sequence {
23
+        yield(ServerCapabilitiesReceived(lsParams.capabilities))
24
+        if (lsParams.size < 2 || String(lsParams[0]) != "*") {
25
+            yield(ServerCapabilitiesFinished)
26
+        }
27
+    }.toList()
28
+
29
+    private val IrcMessage.subCommand
30
+        get() = String(params[1])
31
+
32
+    private val IrcMessage.subCommandArguments
33
+        get() = params.slice(1 until params.size)
34
+
35
+    private val List<ByteArray>.capabilities
36
+        get() = String(last()).split(' ').toCapabilities()
37
+
38
+    private fun List<String>.toCapabilities() = sequence {
39
+        forEach { cap ->
40
+            val index = cap.indexOf('=')
41
+            val name = if (index == -1) cap else cap.substring(0 until index)
42
+            val value = if (index == -1) "" else cap.substring(index + 1)
43
+            capabilities[name]?.let { yield(Pair(it, value)) } ?: log.info { "Unknown capability: $name (value: $value)" }
44
+        }
45
+    }.toMap()
46
+
47
+}

+ 2
- 0
src/main/kotlin/com/dmdirc/ktirc/messages/MessageBuilders.kt View File

@@ -1,5 +1,7 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3
+fun capabilityEndMessage() = "CAP END"
4
+fun capabilityRequestMessage(capabilities: List<String>) = "CAP REQ :${capabilities.joinToString(" ")}"
3 5
 fun joinMessage(channel: String) = "JOIN :$channel"
4 6
 fun nickMessage(nick: String) = "NICK :$nick"
5 7
 fun passwordMessage(password: String) = "PASS :$password"

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

@@ -18,6 +18,7 @@ interface MessageProcessor {
18 18
 }
19 19
 
20 20
 val messageProcessors = setOf(
21
+        CapabilityProcessor(),
21 22
         ISupportProcessor(),
22 23
         JoinProcessor(),
23 24
         NamesProcessor(),

+ 41
- 0
src/main/kotlin/com/dmdirc/ktirc/model/CapabilitiesState.kt View File

@@ -0,0 +1,41 @@
1
+package com.dmdirc.ktirc.model
2
+
3
+class CapabilitiesState {
4
+
5
+    var negotiationState: CapabilitiesNegotiationState = CapabilitiesNegotiationState.AWAITING_LIST
6
+
7
+    val advertisedCapabilities = HashMap<Capability, String>()
8
+    val enabledCapabilities = HashMap<Capability, String>()
9
+
10
+}
11
+
12
+enum class CapabilitiesNegotiationState {
13
+
14
+    AWAITING_LIST,
15
+    AWAITING_ACK,
16
+    FINISHED
17
+
18
+}
19
+
20
+sealed class Capability(val name: String) {
21
+    // Capabilities that enable more information in message tags:
22
+    object ServerTimeMessageTag : Capability("server-time") // TODO: Parse this and expose time in events
23
+    object UserAccountMessageTag : Capability("account-tag") // TODO: Add accounts to user info
24
+
25
+    // Capabilities that extend existing commands to supply extra information:
26
+    object HostsInNamesReply : Capability("userhost-in-names") // TODO: Parse these hosts
27
+    object MultipleUserModePrefixes : Capability("multi-prefix")
28
+    object AccountAndRealNameInJoinMessages : Capability("extended-join") // TODO: Parse this
29
+
30
+    // Capabilities that affect how messages are sent/received:
31
+    object EchoMessages : Capability("echo-message")
32
+
33
+    // Capabilities that notify us of changes to other clients:
34
+    object AccountChangeMessages : Capability("account-notify")
35
+    object AwayStateMessages : Capability("away-notify")
36
+    object HostChangeMessages : Capability("chghost")
37
+}
38
+
39
+val capabilities: Map<String, Capability> by lazy {
40
+    Capability::class.nestedClasses.map { it.objectInstance as Capability }.associateBy { it.name }
41
+}

+ 2
- 1
src/main/kotlin/com/dmdirc/ktirc/model/ServerState.kt View File

@@ -7,6 +7,7 @@ class ServerState(initialNickname: String) {
7 7
 
8 8
     var localNickname: String = initialNickname
9 9
     val features = ServerFeatureMap()
10
+    val capabilities = CapabilitiesState()
10 11
 
11 12
 }
12 13
 
@@ -38,7 +39,7 @@ data class ModePrefixMapping(val modes: String, val prefixes: String) {
38 39
 sealed class ServerFeature<T : Any>(val name: String, val type: KClass<T>, val default: T? = null) {
39 40
     object ServerCaseMapping : ServerFeature<CaseMapping>("CASEMAPPING", CaseMapping::class, CaseMapping.Rfc)
40 41
     object ModePrefixes : ServerFeature<ModePrefixMapping>("PREFIX", ModePrefixMapping::class, ModePrefixMapping("ov", "@+"))
41
-    object MaximumChannels : ServerFeature<Int>("CHANLIMIT", Int::class)
42
+    object MaximumChannels : ServerFeature<Int>("MAXCHANNELS", Int::class) // TODO: CHANLIMIT also exists
42 43
     object ChannelModes : ServerFeature<String>("CHANMODES", String::class)
43 44
     object MaximumChannelNameLength : ServerFeature<Int>("CHANNELLEN", Int::class, 200)
44 45
     object WhoxSupport : ServerFeature<Boolean>("WHOX", Boolean::class, false)

+ 2
- 0
src/test/kotlin/com/dmdirc/ktirc/IrcClientTest.kt View File

@@ -79,6 +79,7 @@ internal class IrcClientImplTest {
79 79
             client.connect()
80 80
 
81 81
             with(inOrder(mockSocket).verify(mockSocket)) {
82
+                sendLine("CAP LS 302")
82 83
                 sendLine("NICK :$NICK")
83 84
                 sendLine("USER $USER_NAME localhost $HOST :$REAL_NAME")
84 85
             }
@@ -95,6 +96,7 @@ internal class IrcClientImplTest {
95 96
             client.connect()
96 97
 
97 98
             with(inOrder(mockSocket).verify(mockSocket)) {
99
+                sendLine("CAP LS 302")
98 100
                 sendLine("PASS :$PASSWORD")
99 101
                 sendLine("NICK :$NICK")
100 102
             }

+ 96
- 0
src/test/kotlin/com/dmdirc/ktirc/events/CapabilitiesHandlerTest.kt View File

@@ -0,0 +1,96 @@
1
+package com.dmdirc.ktirc.events
2
+
3
+import com.dmdirc.ktirc.IrcClient
4
+import com.dmdirc.ktirc.model.CapabilitiesNegotiationState
5
+import com.dmdirc.ktirc.model.Capability
6
+import com.dmdirc.ktirc.model.ServerState
7
+import com.nhaarman.mockitokotlin2.argThat
8
+import com.nhaarman.mockitokotlin2.doReturn
9
+import com.nhaarman.mockitokotlin2.mock
10
+import com.nhaarman.mockitokotlin2.verify
11
+import kotlinx.coroutines.runBlocking
12
+import org.junit.jupiter.api.Assertions.assertEquals
13
+import org.junit.jupiter.api.Test
14
+
15
+internal class CapabilitiesHandlerTest {
16
+
17
+    private val handler = CapabilitiesHandler()
18
+    private val serverState = ServerState("")
19
+    private val ircClient = mock<IrcClient> {
20
+        on { serverState } doReturn serverState
21
+    }
22
+
23
+    @Test
24
+    fun `CapabilitiesHandler adds new capabilities to the state`() {
25
+        runBlocking {
26
+            handler.processEvent(ircClient, ServerCapabilitiesReceived(hashMapOf(
27
+                    Capability.EchoMessages to "",
28
+                    Capability.HostsInNamesReply to "123"
29
+            )))
30
+
31
+            assertEquals(2, serverState.capabilities.advertisedCapabilities.size)
32
+            assertEquals("", serverState.capabilities.advertisedCapabilities[Capability.EchoMessages])
33
+            assertEquals("123", serverState.capabilities.advertisedCapabilities[Capability.HostsInNamesReply])
34
+        }
35
+    }
36
+
37
+    @Test
38
+    fun `CapabilitiesHandler updates negotiation state when capabilities finished`() {
39
+        runBlocking {
40
+            handler.processEvent(ircClient, ServerCapabilitiesFinished)
41
+
42
+            assertEquals(CapabilitiesNegotiationState.AWAITING_ACK, serverState.capabilities.negotiationState)
43
+        }
44
+    }
45
+
46
+    @Test
47
+    fun `CapabilitiesHandler sends REQ when capabilities received`() {
48
+        runBlocking {
49
+            serverState.capabilities.advertisedCapabilities[Capability.EchoMessages] = ""
50
+            serverState.capabilities.advertisedCapabilities[Capability.AccountChangeMessages] = ""
51
+
52
+            handler.processEvent(ircClient, ServerCapabilitiesFinished)
53
+
54
+            verify(ircClient).send(argThat { equals("CAP REQ :echo-message account-notify") || equals("CAP REQ :account-notify echo-message") })
55
+        }
56
+    }
57
+
58
+    @Test
59
+    fun `CapabilitiesHandler sends END when capabilities acknowledged`() {
60
+        runBlocking {
61
+            handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(hashMapOf(
62
+                    Capability.EchoMessages to "",
63
+                    Capability.HostsInNamesReply to "123"
64
+            )))
65
+
66
+            verify(ircClient).send("CAP END")
67
+        }
68
+    }
69
+
70
+    @Test
71
+    fun `CapabilitiesHandler updates negotiation state when capabilities acknowledged`() {
72
+        runBlocking {
73
+            handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(hashMapOf(
74
+                    Capability.EchoMessages to "",
75
+                    Capability.HostsInNamesReply to "123"
76
+            )))
77
+
78
+            assertEquals(CapabilitiesNegotiationState.FINISHED, serverState.capabilities.negotiationState)
79
+        }
80
+    }
81
+
82
+    @Test
83
+    fun `CapabilitiesHandler stores enabled caps when capabilities acknowledged`() {
84
+        runBlocking {
85
+            handler.processEvent(ircClient, ServerCapabilitiesAcknowledged(hashMapOf(
86
+                    Capability.EchoMessages to "",
87
+                    Capability.HostsInNamesReply to "123"
88
+            )))
89
+
90
+            assertEquals(2, serverState.capabilities.enabledCapabilities.size)
91
+            assertEquals("", serverState.capabilities.enabledCapabilities[Capability.EchoMessages])
92
+            assertEquals("123", serverState.capabilities.enabledCapabilities[Capability.HostsInNamesReply])
93
+        }
94
+    }
95
+
96
+}

+ 81
- 0
src/test/kotlin/com/dmdirc/ktirc/messages/CapabilityProcessorTest.kt View File

@@ -0,0 +1,81 @@
1
+package com.dmdirc.ktirc.messages
2
+
3
+import com.dmdirc.ktirc.events.ServerCapabilitiesAcknowledged
4
+import com.dmdirc.ktirc.events.ServerCapabilitiesFinished
5
+import com.dmdirc.ktirc.events.ServerCapabilitiesReceived
6
+import com.dmdirc.ktirc.io.IrcMessage
7
+import com.dmdirc.ktirc.model.Capability
8
+import org.junit.jupiter.api.Assertions.assertEquals
9
+import org.junit.jupiter.api.Assertions.assertTrue
10
+import org.junit.jupiter.api.Test
11
+
12
+internal class CapabilityProcessorTest {
13
+
14
+    private val processor = CapabilityProcessor()
15
+
16
+    @Test
17
+    fun `CapabilityProcessor does nothing for unknown subcommand`() {
18
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*".toByteArray(), "FOO".toByteArray())))
19
+
20
+        assertTrue(events.isEmpty())
21
+    }
22
+
23
+    @Test
24
+    fun `CapabilityProcessor raises ServerCapabilitiesReceived event with known capabilities`() {
25
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost extended-join invalid").map { it.toByteArray() }))
26
+
27
+        val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
28
+        assertEquals(2, receivedEvent.capabilities.size)
29
+        assertTrue(Capability.HostChangeMessages in receivedEvent.capabilities)
30
+        assertTrue(Capability.AccountAndRealNameInJoinMessages in receivedEvent.capabilities)
31
+    }
32
+
33
+    @Test
34
+    fun `CapabilityProcessor raises ServerCapabilitiesReceived event with values for capabilities`() {
35
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost=test123 extended-join=abc=def invalid").map { it.toByteArray() }))
36
+
37
+        val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
38
+        assertEquals(2, receivedEvent.capabilities.size)
39
+        assertEquals("test123", receivedEvent.capabilities[Capability.HostChangeMessages])
40
+        assertEquals("abc=def", receivedEvent.capabilities[Capability.AccountAndRealNameInJoinMessages])
41
+    }
42
+
43
+    @Test
44
+    fun `CapabilityProcessor overwrites earlier values with later ones for identical capabilities`() {
45
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost=test123 chghost chghost=456").map { it.toByteArray() }))
46
+
47
+        val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
48
+        assertEquals(1, receivedEvent.capabilities.size)
49
+        assertEquals("456", receivedEvent.capabilities[Capability.HostChangeMessages])
50
+    }
51
+
52
+
53
+    @Test
54
+    fun `CapabilityProcessor raises ServerCapabilitiesReceived event with known capabilities for multi-line responses`() {
55
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "*", "chghost extended-join invalid").map { it.toByteArray() }))
56
+
57
+        val receivedEvent = events.filterIsInstance<ServerCapabilitiesReceived>()[0]
58
+        assertEquals(2, receivedEvent.capabilities.size)
59
+        assertTrue(Capability.HostChangeMessages in receivedEvent.capabilities)
60
+        assertTrue(Capability.AccountAndRealNameInJoinMessages in receivedEvent.capabilities)
61
+    }
62
+
63
+    @Test
64
+    fun `CapabilityProcessor raises ServerCapabilitiesFinished event for final LS responses`() {
65
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "LS", "chghost extended-join invalid").map { it.toByteArray() }))
66
+
67
+        assertEquals(2, events.size)
68
+        assertTrue(events[1] is ServerCapabilitiesFinished)
69
+    }
70
+
71
+    @Test
72
+    fun `CapabilityProcessor raises ServerCapabilitiesAcknowledged event`() {
73
+        val events = processor.process(IrcMessage(null, "the.gibson".toByteArray(), "CAP", listOf("*", "ACK", "chghost=test123 extended-join=abc=def invalid").map { it.toByteArray() }))
74
+
75
+        val receivedEvent = events.filterIsInstance<ServerCapabilitiesAcknowledged>()[0]
76
+        assertEquals(2, receivedEvent.capabilities.size)
77
+        assertEquals("test123", receivedEvent.capabilities[Capability.HostChangeMessages])
78
+        assertEquals("abc=def", receivedEvent.capabilities[Capability.AccountAndRealNameInJoinMessages])
79
+    }
80
+
81
+}

+ 1
- 1
src/test/kotlin/com/dmdirc/ktirc/messages/ISupportProcessorTest.kt View File

@@ -20,7 +20,7 @@ internal class ISupportProcessorTest {
20 20
     @Test
21 21
     fun `ISupportProcessor handles multiple numeric arguments`() {
22 22
         val events = processor.process(IrcMessage(null, "server.com".toByteArray(), "005",
23
-                listOf("nickname", "CHANLIMIT=123", "CHANNELLEN=456", "are supported blah blah").map { it.toByteArray() }))
23
+                listOf("nickname", "MAXCHANNELS=123", "CHANNELLEN=456", "are supported blah blah").map { it.toByteArray() }))
24 24
 
25 25
         assertEquals(123, events[0].serverFeatures[ServerFeature.MaximumChannels])
26 26
         assertEquals(456, events[0].serverFeatures[ServerFeature.MaximumChannelNameLength])

+ 10
- 1
src/test/kotlin/com/dmdirc/ktirc/messages/MessageBuildersTest.kt View File

@@ -1,10 +1,16 @@
1 1
 package com.dmdirc.ktirc.messages
2 2
 
3
-import org.junit.jupiter.api.Assertions.*
3
+import org.junit.jupiter.api.Assertions.assertEquals
4 4
 import org.junit.jupiter.api.Test
5 5
 
6 6
 internal class MessageBuildersTest {
7 7
 
8
+    @Test
9
+    fun `CapabilityRequestMessage creates CAP REQ message with single argument`() = assertEquals("CAP REQ :a", capabilityRequestMessage(listOf("a")))
10
+
11
+    @Test
12
+    fun `CapabilityRequestMessage creates CAP REQ message with multiple args`() = assertEquals("CAP REQ :a b c", capabilityRequestMessage(listOf("a b c")))
13
+
8 14
     @Test
9 15
     fun `JoinMessage creates correct JOIN message`() = assertEquals("JOIN :#Test123", joinMessage("#Test123"))
10 16
 
@@ -17,6 +23,9 @@ internal class MessageBuildersTest {
17 23
     @Test
18 24
     fun `PongMessage creates correct PONG message`() = assertEquals("PONG :abcdef", pongMessage("abcdef".toByteArray()))
19 25
 
26
+    @Test
27
+    fun `PrivmsgMessage creates correct PRIVMSG message`() = assertEquals("PRIVMSG acidBurn :Hack the planet!", privmsgMessage("acidBurn", "Hack the planet!"))
28
+
20 29
     @Test
21 30
     fun `UserMessage creates correct USER message`() = assertEquals("USER AcidBurn localhost gibson :Kate", userMessage("AcidBurn", "localhost", "gibson", "Kate"))
22 31
 

+ 15
- 0
src/test/kotlin/com/dmdirc/ktirc/model/CapabilitiesStateTest.kt View File

@@ -0,0 +1,15 @@
1
+package com.dmdirc.ktirc.model
2
+
3
+import org.junit.jupiter.api.Assertions.assertEquals
4
+import org.junit.jupiter.api.Test
5
+
6
+internal class CapabilitiesStateTest {
7
+
8
+    @Test
9
+    fun `CapabilitiesState defaults negotiation state to awaiting list`() {
10
+        val capabilitiesState = CapabilitiesState()
11
+
12
+        assertEquals(CapabilitiesNegotiationState.AWAITING_LIST, capabilitiesState.negotiationState)
13
+    }
14
+
15
+}

Loading…
Cancel
Save