summaryrefslogtreecommitdiff
path: root/platform-velocity
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-02-26 00:20:33 +0900
committerGitHub <noreply@github.com>2026-02-26 00:20:33 +0900
commit382ccbc765cd1855d688f60f1235f3f4bfaac9d8 (patch)
tree2bb41b90c19cfd3cfbe638f9f8fbd420ab96aa86 /platform-velocity
parent56ed04185036635dcfd2763391cd64b8be371b1a (diff)
parent70c785eee4c61601ba7fffa17cc8b992b4793491 (diff)
downloadLunaticChat-382ccbc765cd1855d688f60f1235f3f4bfaac9d8.tar.gz
LunaticChat-382ccbc765cd1855d688f60f1235f3f4bfaac9d8.tar.bz2
LunaticChat-382ccbc765cd1855d688f60f1235f3f4bfaac9d8.zip
Merge pull request #119 from m1sk9/feat/codecov-jacoco-integration
feat: add Codecov/Jacoco integration and expand test coverage
Diffstat (limited to 'platform-velocity')
-rw-r--r--platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/CrossServerChatRelayTest.kt136
-rw-r--r--platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt211
2 files changed, 347 insertions, 0 deletions
diff --git a/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/CrossServerChatRelayTest.kt b/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/CrossServerChatRelayTest.kt
new file mode 100644
index 0000000..50b0db2
--- /dev/null
+++ b/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/CrossServerChatRelayTest.kt
@@ -0,0 +1,136 @@
+package dev.m1sk9.lunaticChat.velocity.messaging
+
+import com.velocitypowered.api.proxy.ProxyServer
+import com.velocitypowered.api.proxy.messages.ChannelIdentifier
+import com.velocitypowered.api.proxy.server.RegisteredServer
+import dev.m1sk9.lunaticChat.engine.protocol.PluginMessage
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import org.slf4j.Logger
+import kotlin.test.Test
+
+class CrossServerChatRelayTest {
+ private fun createRelay(): Triple<CrossServerChatRelay, ProxyServer, Logger> {
+ val server = mockk<ProxyServer>(relaxed = true)
+ val logger = mockk<Logger>(relaxed = true)
+ val relay = CrossServerChatRelay(server, logger)
+ return Triple(relay, server, logger)
+ }
+
+ private fun createRegisteredServer(name: String): RegisteredServer {
+ val server = mockk<RegisteredServer>(relaxed = true)
+ val serverInfo = mockk<com.velocitypowered.api.proxy.server.ServerInfo>(relaxed = true)
+ every { serverInfo.name } returns name
+ every { server.serverInfo } returns serverInfo
+ return server
+ }
+
+ private fun createTestMessage(
+ messageId: String = "msg-1",
+ serverName: String = "lobby",
+ ): PluginMessage.GlobalChatMessage =
+ PluginMessage.GlobalChatMessage(
+ messageId = messageId,
+ serverName = serverName,
+ playerId = "00000001-0000-0000-0000-000000000000",
+ playerName = "TestPlayer",
+ message = "Hello!",
+ timestamp = 1000L,
+ )
+
+ @Test
+ fun `relayGlobalMessage should exclude source server`() {
+ val (relay, proxyServer, _) = createRelay()
+ val sourceServer = createRegisteredServer("lobby")
+ val targetServer = createRegisteredServer("survival")
+
+ every { proxyServer.allServers } returns listOf(sourceServer, targetServer)
+
+ relay.relayGlobalMessage(createTestMessage(), sourceServer)
+
+ verify(exactly = 1) { targetServer.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ verify(exactly = 0) { sourceServer.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+
+ @Test
+ fun `relayGlobalMessage should relay to multiple servers`() {
+ val (relay, proxyServer, _) = createRelay()
+ val sourceServer = createRegisteredServer("lobby")
+ val target1 = createRegisteredServer("survival")
+ val target2 = createRegisteredServer("creative")
+
+ every { proxyServer.allServers } returns listOf(sourceServer, target1, target2)
+
+ relay.relayGlobalMessage(createTestMessage(), sourceServer)
+
+ verify(exactly = 1) { target1.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ verify(exactly = 1) { target2.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+
+ @Test
+ fun `relayGlobalMessage with only source server should relay to zero`() {
+ val (relay, proxyServer, logger) = createRelay()
+ val sourceServer = createRegisteredServer("lobby")
+
+ every { proxyServer.allServers } returns listOf(sourceServer)
+
+ relay.relayGlobalMessage(createTestMessage(), sourceServer)
+
+ verify(exactly = 0) { sourceServer.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ verify { logger.info(match { it.contains("0 servers") }) }
+ }
+
+ @Test
+ fun `relayGlobalMessage should log with messageId and playerName`() {
+ val (relay, proxyServer, logger) = createRelay()
+ val sourceServer = createRegisteredServer("lobby")
+ val targetServer = createRegisteredServer("survival")
+
+ every { proxyServer.allServers } returns listOf(sourceServer, targetServer)
+
+ val message = createTestMessage(messageId = "test-msg-123")
+ relay.relayGlobalMessage(message, sourceServer)
+
+ verify {
+ logger.info(
+ match { msg ->
+ msg.contains("test-msg-123") && msg.contains("TestPlayer")
+ },
+ )
+ }
+ }
+
+ @Test
+ fun `relayGlobalMessage should log error on exception`() {
+ val (relay, proxyServer, logger) = createRelay()
+ val sourceServer = createRegisteredServer("lobby")
+ val targetServer = createRegisteredServer("survival")
+
+ every { proxyServer.allServers } returns listOf(sourceServer, targetServer)
+ every { targetServer.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) } throws
+ RuntimeException("connection failed")
+
+ relay.relayGlobalMessage(createTestMessage(), sourceServer)
+
+ verify { logger.error(match { it.contains("Failed to relay") }, any<Exception>()) }
+ }
+
+ @Test
+ fun `relayGlobalMessage should send encoded message data`() {
+ val (relay, proxyServer, _) = createRelay()
+ val sourceServer = createRegisteredServer("lobby")
+ val targetServer = createRegisteredServer("survival")
+
+ every { proxyServer.allServers } returns listOf(sourceServer, targetServer)
+
+ relay.relayGlobalMessage(createTestMessage(), sourceServer)
+
+ verify {
+ targetServer.sendPluginMessage(
+ any<ChannelIdentifier>(),
+ match<ByteArray> { it.isNotEmpty() },
+ )
+ }
+ }
+}
diff --git a/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt b/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt
new file mode 100644
index 0000000..876eec6
--- /dev/null
+++ b/platform-velocity/src/test/kotlin/dev/m1sk9/lunaticChat/velocity/messaging/PluginMessageHandlerTest.kt
@@ -0,0 +1,211 @@
+package dev.m1sk9.lunaticChat.velocity.messaging
+
+import com.velocitypowered.api.event.connection.PluginMessageEvent
+import com.velocitypowered.api.proxy.ProxyServer
+import com.velocitypowered.api.proxy.ServerConnection
+import com.velocitypowered.api.proxy.messages.ChannelIdentifier
+import com.velocitypowered.api.proxy.messages.ChannelMessageSink
+import com.velocitypowered.api.proxy.messages.ChannelMessageSource
+import com.velocitypowered.api.proxy.messages.MinecraftChannelIdentifier
+import com.velocitypowered.api.proxy.server.RegisteredServer
+import com.velocitypowered.api.proxy.server.ServerInfo
+import dev.m1sk9.lunaticChat.engine.protocol.PluginMessage
+import dev.m1sk9.lunaticChat.engine.protocol.PluginMessageCodec
+import dev.m1sk9.lunaticChat.engine.protocol.ProtocolVersion
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import org.slf4j.Logger
+import kotlin.test.Test
+
+class PluginMessageHandlerTest {
+ private val channel = MinecraftChannelIdentifier.create("lunaticchat", "main")
+
+ private fun createHandler(pluginVersion: String = "0.10.0"): Triple<PluginMessageHandler, ProxyServer, CrossServerChatRelay> {
+ val plugin = Any()
+ val server = mockk<ProxyServer>(relaxed = true)
+ val logger = mockk<Logger>(relaxed = true)
+ val relay = mockk<CrossServerChatRelay>(relaxed = true)
+
+ val handler = PluginMessageHandler(plugin, server, logger, pluginVersion, relay)
+ return Triple(handler, server, relay)
+ }
+
+ private fun createServerConnection(serverName: String = "lobby"): ServerConnection {
+ val connection = mockk<ServerConnection>(relaxed = true)
+ val serverInfo = mockk<ServerInfo>(relaxed = true)
+ val registeredServer = mockk<RegisteredServer>(relaxed = true)
+ every { serverInfo.name } returns serverName
+ every { connection.serverInfo } returns serverInfo
+ every { connection.server } returns registeredServer
+ return connection
+ }
+
+ private fun createPluginMessageEvent(
+ source: ChannelMessageSource,
+ target: ChannelMessageSink,
+ identifier: ChannelIdentifier,
+ data: ByteArray,
+ ): PluginMessageEvent = PluginMessageEvent(source, target, identifier, data)
+
+ @Test
+ fun `onPluginMessage should handle successful handshake`() {
+ val (handler, _, _) = createHandler(pluginVersion = "0.10.0")
+ val connection = createServerConnection()
+
+ val handshake =
+ PluginMessage.Handshake(
+ pluginVersion = "0.10.0",
+ protocolMajor = ProtocolVersion.MAJOR,
+ protocolMinor = ProtocolVersion.MINOR,
+ protocolPatch = ProtocolVersion.PATCH,
+ )
+ val data = PluginMessageCodec.encode(handshake)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify { connection.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+
+ @Test
+ fun `onPluginMessage should reject version mismatch handshake`() {
+ val (handler, _, _) = createHandler(pluginVersion = "0.10.0")
+ val connection = createServerConnection()
+
+ val handshake =
+ PluginMessage.Handshake(
+ pluginVersion = "0.9.0",
+ protocolMajor = ProtocolVersion.MAJOR,
+ protocolMinor = ProtocolVersion.MINOR,
+ protocolPatch = ProtocolVersion.PATCH,
+ )
+ val data = PluginMessageCodec.encode(handshake)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify { connection.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+
+ @Test
+ fun `onPluginMessage should reject protocol mismatch handshake`() {
+ val (handler, _, _) = createHandler(pluginVersion = "0.10.0")
+ val connection = createServerConnection()
+
+ val handshake =
+ PluginMessage.Handshake(
+ pluginVersion = "0.10.0",
+ protocolMajor = ProtocolVersion.MAJOR + 1,
+ protocolMinor = ProtocolVersion.MINOR,
+ protocolPatch = ProtocolVersion.PATCH,
+ )
+ val data = PluginMessageCodec.encode(handshake)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify { connection.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+
+ @Test
+ fun `onPluginMessage should handle status request`() {
+ val (handler, _, _) = createHandler()
+ val connection = createServerConnection()
+
+ val data = PluginMessageCodec.encode(PluginMessage.StatusRequest)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify { connection.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+
+ @Test
+ fun `onPluginMessage should relay global chat message`() {
+ val (handler, _, relay) = createHandler()
+ val connection = createServerConnection()
+
+ val chatMessage =
+ PluginMessage.GlobalChatMessage(
+ messageId = "msg-1",
+ serverName = "lobby",
+ playerId = "00000001-0000-0000-0000-000000000000",
+ playerName = "TestPlayer",
+ message = "Hello!",
+ timestamp = 1000L,
+ )
+ val data = PluginMessageCodec.encode(chatMessage)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify { relay.relayGlobalMessage(any<PluginMessage.GlobalChatMessage>(), any<RegisteredServer>()) }
+ }
+
+ @Test
+ fun `onPluginMessage should ignore messages from wrong channel`() {
+ val (handler, _, relay) = createHandler()
+ val connection = createServerConnection()
+
+ val wrongChannel = MinecraftChannelIdentifier.create("other", "channel")
+ val data = PluginMessageCodec.encode(PluginMessage.StatusRequest)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), wrongChannel, data)
+
+ handler.onPluginMessage(event)
+
+ verify(exactly = 0) { relay.relayGlobalMessage(any<PluginMessage.GlobalChatMessage>(), any<RegisteredServer>()) }
+ }
+
+ @Test
+ fun `onPluginMessage should ignore messages from non-server source`() {
+ val (handler, _, relay) = createHandler()
+ val source = mockk<ChannelMessageSource>(relaxed = true)
+
+ val data = PluginMessageCodec.encode(PluginMessage.StatusRequest)
+ val event = createPluginMessageEvent(source, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify(exactly = 0) { relay.relayGlobalMessage(any<PluginMessage.GlobalChatMessage>(), any<RegisteredServer>()) }
+ }
+
+ @Test
+ fun `initialize should register channel and listener`() {
+ val (handler, server, _) = createHandler()
+
+ handler.initialize()
+
+ verify { server.channelRegistrar.register(any<ChannelIdentifier>()) }
+ verify { server.eventManager.register(any(), handler) }
+ }
+
+ @Test
+ fun `shutdown should unregister listener`() {
+ val (handler, server, _) = createHandler()
+
+ handler.shutdown()
+
+ verify { server.eventManager.unregisterListener(any(), handler) }
+ }
+
+ @Test
+ fun `handshake with matching patch difference should succeed`() {
+ val (handler, _, _) = createHandler(pluginVersion = "0.10.0")
+ val connection = createServerConnection()
+
+ val handshake =
+ PluginMessage.Handshake(
+ pluginVersion = "0.10.0",
+ protocolMajor = ProtocolVersion.MAJOR,
+ protocolMinor = ProtocolVersion.MINOR,
+ protocolPatch = ProtocolVersion.PATCH + 5,
+ )
+ val data = PluginMessageCodec.encode(handshake)
+ val event = createPluginMessageEvent(connection, mockk(relaxed = true), channel, data)
+
+ handler.onPluginMessage(event)
+
+ verify { connection.sendPluginMessage(any<ChannelIdentifier>(), any<ByteArray>()) }
+ }
+}