Apply ktlint standard formatting
This commit is contained in:
+4
-4
@@ -13,10 +13,10 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
implementation ("io.ktor:ktor-client-core:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-client-cio:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-network:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-network-tls:${ktorVersion}")
|
||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
||||
implementation("io.ktor:ktor-network:$ktorVersion")
|
||||
implementation("io.ktor:ktor-network-tls:$ktorVersion")
|
||||
implementation("io.github.oshai:kotlin-logging-jvm:8.0.4")
|
||||
implementation("ch.qos.logback:logback-classic:1.6.1")
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0"
|
||||
}
|
||||
rootProject.name = "ferretirc-client"
|
||||
rootProject.name = "ferretirc-client"
|
||||
|
||||
@@ -2,8 +2,8 @@ package com.shr4pnel.ferretirc
|
||||
|
||||
import com.shr4pnel.ferretirc.irc.Server
|
||||
import com.shr4pnel.ferretirc.irc.util.Helpers
|
||||
import com.shr4pnel.ferretirc.net.messages.ClientMessage
|
||||
import com.shr4pnel.ferretirc.net.Connection
|
||||
import com.shr4pnel.ferretirc.net.messages.ClientMessage
|
||||
import com.shr4pnel.ferretirc.net.messages.ServerMessage
|
||||
import io.github.oshai.kotlinlogging.KotlinLoggingConfiguration
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -64,15 +64,23 @@ class IrcClient(hostname: String, port: Int, enableLogging: Boolean = true) {
|
||||
suspend inline fun <reified T : ServerMessage> waitForNext(noinline predicate: (T) -> Boolean = { true }): T =
|
||||
connection.reader.waitForNext(T::class, predicate)
|
||||
|
||||
suspend fun register(nick: String, realName: String? = null, password: String? = null) {
|
||||
val messages = buildList {
|
||||
if (!password.isNullOrEmpty()) add(ClientMessage.Pass(password))
|
||||
add(ClientMessage.Cap.LS())
|
||||
add(ClientMessage.Nick(nick))
|
||||
if (!realName.isNullOrEmpty()) add(ClientMessage.User(nick, realName))
|
||||
else add(ClientMessage.User(nick, nick))
|
||||
add(ClientMessage.Cap.END())
|
||||
}
|
||||
suspend fun register(
|
||||
nick: String,
|
||||
realName: String? = null,
|
||||
password: String? = null,
|
||||
) {
|
||||
val messages =
|
||||
buildList {
|
||||
if (!password.isNullOrEmpty()) add(ClientMessage.Pass(password))
|
||||
add(ClientMessage.Cap.LS())
|
||||
add(ClientMessage.Nick(nick))
|
||||
if (!realName.isNullOrEmpty()) {
|
||||
add(ClientMessage.User(nick, realName))
|
||||
} else {
|
||||
add(ClientMessage.User(nick, nick))
|
||||
}
|
||||
add(ClientMessage.Cap.END())
|
||||
}
|
||||
queueMessages(*messages.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
package com.shr4pnel.ferretirc.irc
|
||||
|
||||
class IRCChannel(val chanName: String, val clientCount: Int, val topic: String) {
|
||||
|
||||
}
|
||||
class IRCChannel(val chanName: String, val clientCount: Int, val topic: String)
|
||||
|
||||
@@ -20,4 +20,4 @@ sealed class Feature(key: String) {
|
||||
|
||||
// Indicates the maximum length of a nickname that a client may use. Other clients on the network may have nicknames longer than this.
|
||||
data class MAXNICKLEN(val max: Int) : Feature("MAXNICKLEN")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,11 @@ import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.onSubscription
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
|
||||
class Server(private val msgBuffer: SharedFlow<ServerMessage>, private val outgoingMessages: Channel<ClientMessage>, private val scope: CoroutineScope) {
|
||||
class Server(
|
||||
private val msgBuffer: SharedFlow<ServerMessage>,
|
||||
private val outgoingMessages: Channel<ClientMessage>,
|
||||
private val scope: CoroutineScope,
|
||||
) {
|
||||
lateinit var features: ISupportFeatures
|
||||
private set
|
||||
|
||||
@@ -31,6 +35,5 @@ class Server(private val msgBuffer: SharedFlow<ServerMessage>, private val outgo
|
||||
}
|
||||
|
||||
fun fetchFeatures() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,4 +4,4 @@ class Helpers {
|
||||
companion object LoggingConfig {
|
||||
var enableLogging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,4 @@ class Connection(val hostname: String, val port: Int, val scope: CoroutineScope)
|
||||
writer.start()
|
||||
return Server(reader.sharedMessageBuffer, writer.outgoingMessages, scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class MessageParser(val incoming: Channel<String>) {
|
||||
|
||||
abstract class Command(open val command: String, open val parameters: List<String>) {
|
||||
abstract fun toServerMessage(): ServerMessage
|
||||
|
||||
fun getTrailingParameterIndex() = parameters.indexOfFirst { it.startsWith(":") }
|
||||
|
||||
fun getTrailingParameterString() =
|
||||
@@ -35,41 +36,47 @@ class MessageParser(val incoming: Channel<String>) {
|
||||
fun getNonTrailingParameters() = getNonTrailingParameterString().split(" ")
|
||||
|
||||
class NamedCommand(override val command: String, override val parameters: List<String>) : Command(command, parameters) {
|
||||
override fun toServerMessage() = when (command.uppercase()) {
|
||||
"PONG" -> ServerMessage.Pong(getTrailingParameterString().removePrefix(":"))
|
||||
"MODE" -> ServerMessage.Mode(
|
||||
parameters.first(),
|
||||
parameters.last().removePrefix(":")
|
||||
) // TODO MODE, CHANMODE, LOCALMODE
|
||||
override fun toServerMessage() =
|
||||
when (command.uppercase()) {
|
||||
"PONG" -> ServerMessage.Pong(getTrailingParameterString().removePrefix(":"))
|
||||
"MODE" ->
|
||||
ServerMessage.Mode(
|
||||
parameters.first(),
|
||||
parameters.last().removePrefix(":"),
|
||||
) // TODO MODE, CHANMODE, LOCALMODE
|
||||
|
||||
"NOTICE" -> ServerMessage.Notice(
|
||||
getNonTrailingParameters(),
|
||||
getTrailingParameterString()
|
||||
)
|
||||
"NOTICE" ->
|
||||
ServerMessage.Notice(
|
||||
getNonTrailingParameters(),
|
||||
getTrailingParameterString(),
|
||||
)
|
||||
|
||||
"PRIVMSG" -> ServerMessage.PrivMsg(
|
||||
getNonTrailingParameters(),
|
||||
getTrailingParameterString()
|
||||
)
|
||||
"PRIVMSG" ->
|
||||
ServerMessage.PrivMsg(
|
||||
getNonTrailingParameters(),
|
||||
getTrailingParameterString(),
|
||||
)
|
||||
|
||||
else -> {
|
||||
ServerMessage.UNIMPLEMENTED("$command ${parameters.joinToString(" ")}")
|
||||
else -> {
|
||||
ServerMessage.UNIMPLEMENTED("$command ${parameters.joinToString(" ")}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class NumericCommand(override val command: String, override val parameters: List<String>) : Command(command, parameters) {
|
||||
override fun toServerMessage() = when (command.toInt()) {
|
||||
5 -> ServerMessage.Numeric.RPL_ISUPPORT(getNonTrailingParameterString())
|
||||
321 -> ServerMessage.Numeric.RPL_LISTSTART()
|
||||
322 -> ServerMessage.Numeric.RPL_LIST(
|
||||
parameters[1],
|
||||
parameters[2].toInt(),
|
||||
getTrailingParameterString()
|
||||
)
|
||||
323 -> ServerMessage.Numeric.RPL_LISTEND()
|
||||
else -> ServerMessage.UNIMPLEMENTED(parameters.toString())
|
||||
}
|
||||
override fun toServerMessage() =
|
||||
when (command.toInt()) {
|
||||
5 -> ServerMessage.Numeric.RPL_ISUPPORT(getNonTrailingParameterString())
|
||||
321 -> ServerMessage.Numeric.RPL_LISTSTART()
|
||||
322 ->
|
||||
ServerMessage.Numeric.RPL_LIST(
|
||||
parameters[1],
|
||||
parameters[2].toInt(),
|
||||
getTrailingParameterString(),
|
||||
)
|
||||
323 -> ServerMessage.Numeric.RPL_LISTEND()
|
||||
else -> ServerMessage.UNIMPLEMENTED(parameters.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,21 +106,31 @@ class MessageParser(val incoming: Channel<String>) {
|
||||
val tokens = commandList.toMutableList()
|
||||
|
||||
// remove tags from strlist if present
|
||||
tags = if (tokens.first().startsWith("@")) {
|
||||
tokens.removeFirst()
|
||||
} else null
|
||||
tags =
|
||||
if (tokens.first().startsWith("@")) {
|
||||
tokens.removeFirst()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// remove client prefix (nick&opt hostname) if present
|
||||
prefix = if (tokens.first().startsWith(":")) {
|
||||
tokens.removeFirst()
|
||||
} else null
|
||||
prefix =
|
||||
if (tokens.first().startsWith(":")) {
|
||||
tokens.removeFirst()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// get command e.g. PING
|
||||
val commandStr = tokens.removeFirst()
|
||||
|
||||
// Check if command is a numeric (RPL/ERR) or named
|
||||
command = if (commandStr.toIntOrNull() == null) Command.NamedCommand(commandStr, tokens)
|
||||
else Command.NumericCommand(commandStr, tokens)
|
||||
command =
|
||||
if (commandStr.toIntOrNull() == null) {
|
||||
Command.NamedCommand(commandStr, tokens)
|
||||
} else {
|
||||
Command.NumericCommand(commandStr, tokens)
|
||||
}
|
||||
|
||||
return command.toServerMessage()
|
||||
}
|
||||
@@ -122,11 +139,10 @@ class MessageParser(val incoming: Channel<String>) {
|
||||
}
|
||||
|
||||
suspend fun start() {
|
||||
|
||||
val builder = MessageBuilder()
|
||||
for (msg in incoming) {
|
||||
logger.trace { "Receiving: $msg" }
|
||||
incomingParsedMessages.send(builder.build(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ class MessageReader(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
|
||||
val parser = MessageParser(incomingMessages)
|
||||
private val logger = if (Helpers.enableLogging) KotlinLogging.logger {} else KotlinLogging.logger(NOPLogger.NOP_LOGGER)
|
||||
|
||||
|
||||
init {
|
||||
scope.launch {
|
||||
parser.start()
|
||||
@@ -49,7 +48,10 @@ class MessageReader(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun <T : ServerMessage> waitForNext(kClass: KClass<T>, predicate: (T) -> Boolean = { true }): T {
|
||||
suspend fun <T : ServerMessage> waitForNext(
|
||||
kClass: KClass<T>,
|
||||
predicate: (T) -> Boolean = { true },
|
||||
): T {
|
||||
return messageBuffer
|
||||
.filter {
|
||||
kClass.isInstance(it)
|
||||
@@ -57,4 +59,4 @@ class MessageReader(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
|
||||
kClass.cast(it)
|
||||
}.first(predicate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@ class MessageWriter(socket: Socket, scope: CoroutineScope) : MessageIO(socket, s
|
||||
private lateinit var send: ByteWriteChannel
|
||||
private val logger = if (Helpers.enableLogging) KotlinLogging.logger {} else KotlinLogging.logger(NOPLogger.NOP_LOGGER)
|
||||
|
||||
override fun start() = scope.launch {
|
||||
send = socket.openWriteChannel()
|
||||
override fun start() =
|
||||
scope.launch {
|
||||
send = socket.openWriteChannel()
|
||||
|
||||
for (msg in outgoingMessages) {
|
||||
logger.debug { "Sending: ${msg.toWireIntermediate()}" }
|
||||
send.writeFully(msg.toWire())
|
||||
send.flush()
|
||||
for (msg in outgoingMessages) {
|
||||
logger.debug { "Sending: ${msg.toWireIntermediate()}" }
|
||||
send.writeFully(msg.toWire())
|
||||
send.flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,9 @@ sealed class ClientMessage(val strName: String) {
|
||||
class Nick(val nickname: String) : ClientMessage("NICK") {
|
||||
init {
|
||||
require(nickname.length < 10) { "\"$nickname\" exceeds IRCs maximum nickname length of 9" }
|
||||
require(!nickname.startsWith(":") && !nickname.startsWith("#")) { "\"$nickname\" may not begin with : or #" } // TODO THIS SHOULD BLACKLIST ALL PREFIXES NAMED IN CHANTYPES PARAMETER
|
||||
require(!nickname.startsWith(":") && !nickname.startsWith("#")) {
|
||||
"\"$nickname\" may not begin with : or #"
|
||||
} // TODO THIS SHOULD BLACKLIST ALL PREFIXES NAMED IN CHANTYPES PARAMETER
|
||||
require(!nickname.contains(" ")) { "$nickname may not contain a space" }
|
||||
}
|
||||
|
||||
@@ -97,9 +99,12 @@ sealed class ClientMessage(val strName: String) {
|
||||
|
||||
class PrivMsg(val target: IRCChannel, val message: String) : ClientMessage("PRIVMSG") {
|
||||
override val params: kotlin.collections.List<String>
|
||||
get() = listOf(target.chanName) // TODO RPL_ISUPPORT REQUIREMENTS IN FUN EG LINELEN STATUSMSG - ALSO ADD FUCKING STUPID STATUSMSG & SUPPORT FOR MASKS. FUCK ME
|
||||
get() =
|
||||
listOf(
|
||||
target.chanName,
|
||||
) // TODO RPL_ISUPPORT REQUIREMENTS IN FUN EG LINELEN STATUSMSG - ALSO ADD FUCKING STUPID STATUSMSG & SUPPORT FOR MASKS. FUCK ME
|
||||
|
||||
override val trailing: String
|
||||
get() = message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,5 +7,6 @@ import kotlinx.coroutines.channels.Channel
|
||||
abstract class MessageIO(val socket: Socket, val scope: CoroutineScope) {
|
||||
val incomingMessages = Channel<String>(Channel.BUFFERED)
|
||||
val outgoingMessages = Channel<ClientMessage>(Channel.BUFFERED)
|
||||
|
||||
abstract fun start(): Any
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,16 +5,24 @@ package com.shr4pnel.ferretirc.net.messages
|
||||
*/
|
||||
sealed class ServerMessage {
|
||||
data class Cap(val placeholder: String? = null) : ServerMessage()
|
||||
|
||||
data class Pong(val token: String? = null) : ServerMessage()
|
||||
|
||||
data class Mode(val operatorName: String, val pMask: String) : ServerMessage()
|
||||
|
||||
data class PrivMsg(val targets: List<String>, val message: String) : ServerMessage()
|
||||
|
||||
data class Notice(val targets: List<String>, val message: String) : ServerMessage()
|
||||
|
||||
data class UNIMPLEMENTED(val msg: String) : ServerMessage()
|
||||
|
||||
sealed class Numeric(val number: Short) : ServerMessage() {
|
||||
data class RPL_ISUPPORT(val keypairs: String): Numeric(5)
|
||||
data class RPL_ISUPPORT(val keypairs: String) : Numeric(5)
|
||||
|
||||
class RPL_LISTSTART : Numeric(321)
|
||||
|
||||
data class RPL_LIST(val chanName: String, val clientCount: Int, val topic: String) : Numeric(322)
|
||||
|
||||
class RPL_LISTEND : Numeric(323)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import com.shr4pnel.ferretirc.net.messages.ClientMessage as Message
|
||||
import org.junit.jupiter.api.AfterAll
|
||||
import org.junit.jupiter.api.BeforeAll
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -14,6 +13,7 @@ import java.lang.ProcessBuilder
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import com.shr4pnel.ferretirc.net.messages.ClientMessage as Message
|
||||
|
||||
class IrcClientTest {
|
||||
private val logger = KotlinLogging.logger {}
|
||||
@@ -25,87 +25,95 @@ class IrcClientTest {
|
||||
|
||||
@JvmStatic
|
||||
@BeforeAll
|
||||
fun setup() = runBlocking {
|
||||
val logFile = File("src/test/resources/logs/ngircd.log")
|
||||
val ngircdConfigPath = javaClass.classLoader.getResource("ngircd.conf")!!.path
|
||||
logFile.createNewFile()
|
||||
processes = ProcessBuilder.startPipeline(
|
||||
listOf<ProcessBuilder>(
|
||||
// Run IRC server in [-d] debug log mode, with [-n] no daemon, using config [-f] file at path
|
||||
ProcessBuilder("ngircd", "-dnf", ngircdConfigPath).redirectErrorStream(true),
|
||||
// Remove unhelpful default ngircd info [PID. relative time, GID?]
|
||||
ProcessBuilder("sed", "-ue", "s/\\[[^][]*\\]//g"),
|
||||
// Add relative timestamp
|
||||
ProcessBuilder("ts", "-s", "%H:%M:%.S").redirectOutput(logFile)
|
||||
)
|
||||
)
|
||||
client.connect()
|
||||
client.register("shr4p", "Tyler D", "password")
|
||||
}
|
||||
fun setup() =
|
||||
runBlocking {
|
||||
val logFile = File("src/test/resources/logs/ngircd.log")
|
||||
val ngircdConfigPath = javaClass.classLoader.getResource("ngircd.conf")!!.path
|
||||
logFile.createNewFile()
|
||||
processes =
|
||||
ProcessBuilder.startPipeline(
|
||||
listOf<ProcessBuilder>(
|
||||
// Run IRC server in [-d] debug log mode, with [-n] no daemon, using config [-f] file at path
|
||||
ProcessBuilder("ngircd", "-dnf", ngircdConfigPath).redirectErrorStream(true),
|
||||
// Remove unhelpful default ngircd info [PID. relative time, GID?]
|
||||
ProcessBuilder("sed", "-ue", "s/\\[[^][]*\\]//g"),
|
||||
// Add relative timestamp
|
||||
ProcessBuilder("ts", "-s", "%H:%M:%.S").redirectOutput(logFile),
|
||||
),
|
||||
)
|
||||
client.connect()
|
||||
client.register("shr4p", "Tyler D", "password")
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@AfterAll
|
||||
fun shutdown() = runBlocking {
|
||||
logger.info { "Closing server." }
|
||||
client.queueMessage(Message.Die())
|
||||
client.waitForNext<ServerMessage.Notice>()
|
||||
processes.forEach { it.destroy() }
|
||||
}
|
||||
fun shutdown() =
|
||||
runBlocking {
|
||||
logger.info { "Closing server." }
|
||||
client.queueMessage(Message.Die())
|
||||
client.waitForNext<ServerMessage.Notice>()
|
||||
processes.forEach { it.destroy() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sample() {
|
||||
client.scope.launch {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pingGetsPong() = runBlocking {
|
||||
val token = "ACK"
|
||||
logger.info { "Sending PING $token" }
|
||||
client.queueMessage(Message.Ping(token))
|
||||
fun pingGetsPong() =
|
||||
runBlocking {
|
||||
val token = "ACK"
|
||||
logger.info { "Sending PING $token" }
|
||||
client.queueMessage(Message.Ping(token))
|
||||
|
||||
logger.info { "Waiting to receive PONG" }
|
||||
val pong = withTimeoutOrNull(1.seconds) {
|
||||
client.waitForNext<ServerMessage.Pong> { it.token.equals(token) }
|
||||
logger.info { "Waiting to receive PONG" }
|
||||
val pong =
|
||||
withTimeoutOrNull(1.seconds) {
|
||||
client.waitForNext<ServerMessage.Pong> { it.token.equals(token) }
|
||||
}
|
||||
assertNotNull(pong, "Reached timeout while waiting for PONG")
|
||||
assertEquals(token, pong.token, "Token in PING did not match PONG")
|
||||
logger.info { "Received PING" }
|
||||
}
|
||||
assertNotNull(pong, "Reached timeout while waiting for PONG")
|
||||
assertEquals(token, pong.token, "Token in PING did not match PONG")
|
||||
logger.info { "Received PING" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oper() = runBlocking {
|
||||
client.queueMessage(Message.Oper("shr4p", "password"))
|
||||
val mode = withTimeoutOrNull(1.seconds) {
|
||||
client.waitForNext<ServerMessage.Mode>()
|
||||
fun oper() =
|
||||
runBlocking {
|
||||
client.queueMessage(Message.Oper("shr4p", "password"))
|
||||
val mode =
|
||||
withTimeoutOrNull(1.seconds) {
|
||||
client.waitForNext<ServerMessage.Mode>()
|
||||
}
|
||||
assertNotNull(mode, "Timed out waiting for OPER MODE response")
|
||||
assertEquals("shr4p", mode.operatorName, "Received incorrect operator name in MODE")
|
||||
assertEquals("+o", mode.pMask, "Received unexpected mask in MODE")
|
||||
logger.info { "Received MODE with mask ${mode.pMask}" }
|
||||
}
|
||||
assertNotNull(mode, "Timed out waiting for OPER MODE response")
|
||||
assertEquals("shr4p", mode.operatorName, "Received incorrect operator name in MODE")
|
||||
assertEquals("+o", mode.pMask, "Received unexpected mask in MODE")
|
||||
logger.info { "Received MODE with mask ${mode.pMask}" }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun listChannels() = runBlocking {
|
||||
val channels = client.server.fetchChannels()
|
||||
assert(channels.isNotEmpty())
|
||||
client.queueMessage(Message.Join(channels.first()))
|
||||
}
|
||||
fun listChannels() =
|
||||
runBlocking {
|
||||
val channels = client.server.fetchChannels()
|
||||
assert(channels.isNotEmpty())
|
||||
client.queueMessage(Message.Join(channels.first()))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun messageLands() = runBlocking {
|
||||
val guestUser = IrcClient("localhost", 6667, false)
|
||||
guestUser.connect()
|
||||
guestUser.register("guest", "guest", "pass")
|
||||
val channels = client.server.fetchChannels()
|
||||
val message = Message.PrivMsg(channels.first(), "bring me to life")
|
||||
guestUser.queueMessage(message)
|
||||
val receivedMessage = client.waitForNext<ServerMessage.PrivMsg>()
|
||||
logger.debug { "Message: $message, Received: $receivedMessage" }
|
||||
assertEquals(message.message, receivedMessage.message)
|
||||
assertEquals(message.target.chanName, receivedMessage.targets.first())
|
||||
guestUser.close()
|
||||
}
|
||||
fun messageLands() =
|
||||
runBlocking {
|
||||
val guestUser = IrcClient("localhost", 6667, false)
|
||||
guestUser.connect()
|
||||
guestUser.register("guest", "guest", "pass")
|
||||
val channels = client.server.fetchChannels()
|
||||
val message = Message.PrivMsg(channels.first(), "bring me to life")
|
||||
guestUser.queueMessage(message)
|
||||
val receivedMessage = client.waitForNext<ServerMessage.PrivMsg>()
|
||||
logger.debug { "Message: $message, Received: $receivedMessage" }
|
||||
assertEquals(message.message, receivedMessage.message)
|
||||
assertEquals(message.target.chanName, receivedMessage.targets.first())
|
||||
guestUser.close()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user