summaryrefslogtreecommitdiff
path: root/platform-paper/src/test
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-01-24 02:50:57 +0900
committerSho Sakuma <me@m1sk9.dev>2026-01-24 02:52:05 +0900
commit59f947dfd8c19859cba6cb3939c859c1fa64e634 (patch)
tree4818f6b25297eadd6c11f00fb18f4a9025660746 /platform-paper/src/test
parenta822c76bdf264c4887ebe4cc354341337ca13f30 (diff)
downloadLunaticChat-59f947dfd8c19859cba6cb3939c859c1fa64e634.tar.gz
LunaticChat-59f947dfd8c19859cba6cb3939c859c1fa64e634.tar.bz2
LunaticChat-59f947dfd8c19859cba6cb3939c859c1fa64e634.zip
test: Add i18n test-case
Diffstat (limited to 'platform-paper/src/test')
-rw-r--r--platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageManagerTest.kt308
-rw-r--r--platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageTest.kt75
-rw-r--r--platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/MessageFormatterTest.kt181
-rw-r--r--platform-paper/src/test/resources/languages/en.yml18
-rw-r--r--platform-paper/src/test/resources/languages/ja.yml18
5 files changed, 600 insertions, 0 deletions
diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageManagerTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageManagerTest.kt
new file mode 100644
index 0000000..158d985
--- /dev/null
+++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageManagerTest.kt
@@ -0,0 +1,308 @@
+package dev.m1sk9.lunaticChat.paper.i18n
+
+import java.io.InputStream
+import java.util.logging.Logger
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+/**
+ * Test for LanguageManager using test resources.
+ */
+class LanguageManagerTest {
+ /**
+ * Mock Logger that collects log messages.
+ */
+ private class TestLogger : Logger("test", null) {
+ val infoMessages = mutableListOf<String>()
+ val warningMessages = mutableListOf<String>()
+
+ override fun info(msg: String) {
+ infoMessages.add(msg)
+ }
+
+ override fun warning(msg: String) {
+ warningMessages.add(msg)
+ }
+ }
+
+ /**
+ * Resource loader that loads files from test classpath.
+ */
+ private val testResourceLoader: (String) -> InputStream? = { path ->
+ this::class.java.classLoader.getResourceAsStream(path)
+ }
+
+ private fun createLanguageManager(language: Language): Pair<LanguageManager, TestLogger> {
+ val logger = TestLogger()
+ val manager =
+ LanguageManager(
+ plugin = null,
+ logger = logger,
+ selectedLanguage = language,
+ resourceLoader = testResourceLoader,
+ )
+ return Pair(manager, logger)
+ }
+
+ @Test
+ fun `initialize should load English language file`() {
+ val (manager, logger) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ assertTrue(logger.infoMessages.any { it.contains("en.yml") })
+ }
+
+ @Test
+ fun `initialize should load Japanese language file`() {
+ val (manager, logger) = createLanguageManager(Language.JA)
+ manager.initialize()
+
+ assertTrue(logger.infoMessages.any { it.contains("ja.yml") })
+ }
+
+ @Test
+ fun `initialize should load both language files`() {
+ val (manager, logger) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ // Both en.yml and ja.yml should be loaded
+ assertTrue(logger.infoMessages.any { it.contains("en.yml") })
+ assertTrue(logger.infoMessages.any { it.contains("ja.yml") })
+ }
+
+ @Test
+ fun `getMessage should return English message for EN language`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message = manager.getMessage("commandDescription.tell")
+ assertEquals("Send a direct message to another player", message)
+ }
+
+ @Test
+ fun `getMessage should return Japanese message for JA language`() {
+ val (manager, _) = createLanguageManager(Language.JA)
+ manager.initialize()
+
+ val message = manager.getMessage("commandDescription.tell")
+ assertEquals("他のプレイヤーにダイレクトメッセージを送信します", message)
+ }
+
+ @Test
+ fun `getMessage should handle nested keys with dot notation`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val onMessage = manager.getMessage("toggle.on")
+ val offMessage = manager.getMessage("toggle.off")
+
+ assertEquals("Enabled", onMessage)
+ assertEquals("Disabled", offMessage)
+ }
+
+ @Test
+ fun `getMessage should substitute placeholders`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message =
+ manager.getMessage(
+ "tellTargetOffline",
+ mapOf("target" to "Steve"),
+ )
+
+ assertEquals("Player 'Steve' is currently offline.", message)
+ }
+
+ @Test
+ fun `getMessage should substitute multiple placeholders`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message =
+ manager.getMessage(
+ "directMessageNoticeToggle",
+ mapOf("toggle" to "Enabled"),
+ )
+
+ assertEquals("Direct message notifications have been set to Enabled", message)
+ }
+
+ @Test
+ fun `getMessage should handle Japanese placeholders`() {
+ val (manager, _) = createLanguageManager(Language.JA)
+ manager.initialize()
+
+ val message =
+ manager.getMessage(
+ "tellTargetOffline",
+ mapOf("target" to "太郎"),
+ )
+
+ assertEquals("プレイヤー '太郎' は現在オフラインです。", message)
+ }
+
+ @Test
+ fun `getMessage should return key when message not found`() {
+ val (manager, logger) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message = manager.getMessage("nonexistent.key")
+
+ assertEquals("nonexistent.key", message)
+ assertTrue(logger.warningMessages.any { it.contains("Message key not found: nonexistent.key") })
+ }
+
+ @Test
+ fun `getMessage should handle empty placeholders map`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message = manager.getMessage("playerOnlyCommand", emptyMap())
+
+ assertEquals("This command can only be executed by players.", message)
+ }
+
+ @Test
+ fun `getToggleText should return 'on' text when enabled`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val text = manager.getToggleText(true)
+
+ assertEquals("Enabled", text)
+ }
+
+ @Test
+ fun `getToggleText should return 'off' text when disabled`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val text = manager.getToggleText(false)
+
+ assertEquals("Disabled", text)
+ }
+
+ @Test
+ fun `getToggleText should return Japanese text for JA language`() {
+ val (manager, _) = createLanguageManager(Language.JA)
+ manager.initialize()
+
+ val onText = manager.getToggleText(true)
+ val offText = manager.getToggleText(false)
+
+ assertEquals("有効", onText)
+ assertEquals("無効", offText)
+ }
+
+ @Test
+ fun `getMessage should retrieve all command descriptions`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val commands = listOf("tell", "reply", "jp", "notice")
+ commands.forEach { command ->
+ val message = manager.getMessage("commandDescription.$command")
+ assertNotNull(message)
+ assertTrue(message.isNotEmpty())
+ }
+ }
+
+ @Test
+ fun `getMessage should retrieve all standard messages`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val keys =
+ listOf(
+ "playerOnlyCommand",
+ "replyTargetNotFound",
+ "tellYourself",
+ )
+
+ keys.forEach { key ->
+ val message = manager.getMessage(key)
+ assertNotNull(message)
+ assertTrue(message.isNotEmpty())
+ }
+ }
+
+ @Test
+ fun `getMessage should handle placeholder not provided`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ // Get message with placeholder but don't provide value
+ val message = manager.getMessage("tellTargetOffline")
+
+ // Should still return message with placeholder intact
+ assertEquals("Player '{target}' is currently offline.", message)
+ }
+
+ @Test
+ fun `initialize should log number of loaded keys`() {
+ val (manager, logger) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ // Should log number of keys for each language file
+ assertTrue(logger.infoMessages.any { it.contains("keys)") })
+ }
+
+ @Test
+ fun `getMessage should be case sensitive for keys`() {
+ val (manager, logger) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message = manager.getMessage("PlayerOnlyCommand") // Wrong case
+
+ assertEquals("PlayerOnlyCommand", message) // Should return key as-is
+ assertTrue(logger.warningMessages.any { it.contains("Message key not found") })
+ }
+
+ @Test
+ fun `getMessage should handle special characters in placeholder values`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ val message =
+ manager.getMessage(
+ "tellTargetOffline",
+ mapOf("target" to "Player@123!"),
+ )
+
+ assertEquals("Player 'Player@123!' is currently offline.", message)
+ }
+
+ @Test
+ fun `getMessage should replace all occurrences of placeholder`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ // Create a message with the same placeholder multiple times (if such exists)
+ val message =
+ manager.getMessage(
+ "directMessageNoticeToggle",
+ mapOf("toggle" to "ON"),
+ )
+
+ // Should replace the placeholder
+ assertTrue(message.contains("ON"))
+ }
+
+ @Test
+ fun `flattened keys should handle nested structure correctly`() {
+ val (manager, _) = createLanguageManager(Language.EN)
+ manager.initialize()
+
+ // Verify that nested keys work
+ val commandTell = manager.getMessage("commandDescription.tell")
+ val toggleOn = manager.getMessage("toggle.on")
+
+ assertNotNull(commandTell)
+ assertNotNull(toggleOn)
+ assertTrue(commandTell.isNotEmpty())
+ assertTrue(toggleOn.isNotEmpty())
+ }
+}
diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageTest.kt
new file mode 100644
index 0000000..c8b2c3b
--- /dev/null
+++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/LanguageTest.kt
@@ -0,0 +1,75 @@
+package dev.m1sk9.lunaticChat.paper.i18n
+
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class LanguageTest {
+ @Test
+ fun `should have correct code for EN`() {
+ assertEquals("en", Language.EN.code)
+ }
+
+ @Test
+ fun `should have correct code for JA`() {
+ assertEquals("ja", Language.JA.code)
+ }
+
+ @Test
+ fun `should have correct fileName for EN`() {
+ assertEquals("en.yml", Language.EN.fileName)
+ }
+
+ @Test
+ fun `should have correct fileName for JA`() {
+ assertEquals("ja.yml", Language.JA.fileName)
+ }
+
+ @Test
+ fun `fromCode should return EN for 'en'`() {
+ assertEquals(Language.EN, Language.fromCode("en"))
+ }
+
+ @Test
+ fun `fromCode should return JA for 'ja'`() {
+ assertEquals(Language.JA, Language.fromCode("ja"))
+ }
+
+ @Test
+ fun `fromCode should be case insensitive for EN`() {
+ assertEquals(Language.EN, Language.fromCode("EN"))
+ assertEquals(Language.EN, Language.fromCode("En"))
+ assertEquals(Language.EN, Language.fromCode("eN"))
+ }
+
+ @Test
+ fun `fromCode should be case insensitive for JA`() {
+ assertEquals(Language.JA, Language.fromCode("JA"))
+ assertEquals(Language.JA, Language.fromCode("Ja"))
+ assertEquals(Language.JA, Language.fromCode("jA"))
+ }
+
+ @Test
+ fun `fromCode should fallback to EN for unknown code`() {
+ assertEquals(Language.EN, Language.fromCode("unknown"))
+ assertEquals(Language.EN, Language.fromCode("fr"))
+ assertEquals(Language.EN, Language.fromCode("de"))
+ assertEquals(Language.EN, Language.fromCode(""))
+ }
+
+ @Test
+ fun `fromCode should fallback to EN for empty string`() {
+ assertEquals(Language.EN, Language.fromCode(""))
+ }
+
+ @Test
+ fun `should have exactly two language entries`() {
+ assertEquals(2, Language.entries.size)
+ }
+
+ @Test
+ fun `entries should contain EN and JA`() {
+ val entries = Language.entries
+ assertEquals(true, entries.contains(Language.EN))
+ assertEquals(true, entries.contains(Language.JA))
+ }
+}
diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/MessageFormatterTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/MessageFormatterTest.kt
new file mode 100644
index 0000000..04ef02a
--- /dev/null
+++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/i18n/MessageFormatterTest.kt
@@ -0,0 +1,181 @@
+package dev.m1sk9.lunaticChat.paper.i18n
+
+import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class MessageFormatterTest {
+ private val plainSerializer = PlainTextComponentSerializer.plainText()
+
+ @Test
+ fun `format should add LC prefix`() {
+ val result = MessageFormatter.format("test message")
+ val plainText = plainSerializer.serialize(result)
+ assertTrue(plainText.startsWith("[LC] "))
+ }
+
+ @Test
+ fun `format should include message text`() {
+ val result = MessageFormatter.format("test message")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] test message", plainText)
+ }
+
+ @Test
+ fun `format should preserve placeholder braces in plain text`() {
+ val result = MessageFormatter.format("Hello {name}")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Hello {name}", plainText)
+ }
+
+ @Test
+ fun `format should handle multiple placeholders`() {
+ val result = MessageFormatter.format("Player {player} sent message to {target}")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Player {player} sent message to {target}", plainText)
+ }
+
+ @Test
+ fun `format should handle message without placeholders`() {
+ val result = MessageFormatter.format("Simple message", highlightPlaceholders = false)
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Simple message", plainText)
+ }
+
+ @Test
+ fun `formatError should add LC prefix`() {
+ val result = MessageFormatter.formatError("error message")
+ val plainText = plainSerializer.serialize(result)
+ assertTrue(plainText.startsWith("[LC] "))
+ }
+
+ @Test
+ fun `formatError should include error message text`() {
+ val result = MessageFormatter.formatError("Something went wrong")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Something went wrong", plainText)
+ }
+
+ @Test
+ fun `formatError should preserve placeholders in plain text`() {
+ val result = MessageFormatter.formatError("Player {player} not found")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Player {player} not found", plainText)
+ }
+
+ @Test
+ fun `formatSuccess should add LC prefix`() {
+ val result = MessageFormatter.formatSuccess("success message")
+ val plainText = plainSerializer.serialize(result)
+ assertTrue(plainText.startsWith("[LC] "))
+ }
+
+ @Test
+ fun `formatSuccess should include success message text`() {
+ val result = MessageFormatter.formatSuccess("Operation completed")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Operation completed", plainText)
+ }
+
+ @Test
+ fun `formatSuccess should preserve placeholders in plain text`() {
+ val result = MessageFormatter.formatSuccess("Setting {setting} enabled")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Setting {setting} enabled", plainText)
+ }
+
+ @Test
+ fun `format should handle empty message`() {
+ val result = MessageFormatter.format("")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] ", plainText)
+ }
+
+ @Test
+ fun `format should handle message with only placeholder`() {
+ val result = MessageFormatter.format("{value}")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] {value}", plainText)
+ }
+
+ @Test
+ fun `format should handle nested braces`() {
+ val result = MessageFormatter.format("{{nested}}")
+ val plainText = plainSerializer.serialize(result)
+ // Only {nested} should be treated as placeholder, outer braces are literal
+ assertTrue(plainText.contains("{nested}"))
+ }
+
+ @Test
+ fun `format should handle Japanese text`() {
+ val result = MessageFormatter.format("こんにちは {name}")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] こんにちは {name}", plainText)
+ }
+
+ @Test
+ fun `formatError should handle Japanese text`() {
+ val result = MessageFormatter.formatError("エラーが発生しました")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] エラーが発生しました", plainText)
+ }
+
+ @Test
+ fun `formatSuccess should handle Japanese text`() {
+ val result = MessageFormatter.formatSuccess("設定を有効にしました")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] 設定を有効にしました", plainText)
+ }
+
+ @Test
+ fun `format should handle very long message`() {
+ val longMessage = "a".repeat(1000)
+ val result = MessageFormatter.format(longMessage)
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] $longMessage", plainText)
+ }
+
+ @Test
+ fun `format should handle special characters`() {
+ val result = MessageFormatter.format("Special: !@#$%^&*()")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Special: !@#$%^&*()", plainText)
+ }
+
+ @Test
+ fun `format should handle message with newlines`() {
+ val result = MessageFormatter.format("Line 1\nLine 2")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Line 1\nLine 2", plainText)
+ }
+
+ @Test
+ fun `format should handle unclosed brace`() {
+ val result = MessageFormatter.format("Unclosed {placeholder")
+ val plainText = plainSerializer.serialize(result)
+ // Unclosed braces should not be treated as placeholders
+ assertEquals("[LC] Unclosed {placeholder", plainText)
+ }
+
+ @Test
+ fun `format should handle placeholder at start`() {
+ val result = MessageFormatter.format("{player} joined the game")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] {player} joined the game", plainText)
+ }
+
+ @Test
+ fun `format should handle placeholder at end`() {
+ val result = MessageFormatter.format("Welcome {player}")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] Welcome {player}", plainText)
+ }
+
+ @Test
+ fun `format should handle consecutive placeholders`() {
+ val result = MessageFormatter.format("{player1}{player2}")
+ val plainText = plainSerializer.serialize(result)
+ assertEquals("[LC] {player1}{player2}", plainText)
+ }
+}
diff --git a/platform-paper/src/test/resources/languages/en.yml b/platform-paper/src/test/resources/languages/en.yml
new file mode 100644
index 0000000..b7d2223
--- /dev/null
+++ b/platform-paper/src/test/resources/languages/en.yml
@@ -0,0 +1,18 @@
+commandDescription:
+ tell: "Send a direct message to another player"
+ reply: "Reply to the player who last sent/received a direct message"
+ jp: "Toggle romaji-to-kana conversion on/off"
+ notice: "Toggle direct message notifications on/off"
+
+directMessageNoticeStatus: "Direct message notifications are currently {toggle}"
+directMessageNoticeToggle: "Direct message notifications have been set to {toggle}"
+playerOnlyCommand: "This command can only be executed by players."
+replyTargetNotFound: "Reply target player not found."
+romajiConversionToggle: "Romaji-to-kana conversion has been set to {toggle}"
+romajiConversionStatus: "Romaji-to-kana conversion is currently {toggle}"
+tellTargetOffline: "Player '{target}' is currently offline."
+tellYourself: "You cannot send a message to yourself."
+
+toggle:
+ off: "Disabled"
+ on: "Enabled"
diff --git a/platform-paper/src/test/resources/languages/ja.yml b/platform-paper/src/test/resources/languages/ja.yml
new file mode 100644
index 0000000..9588375
--- /dev/null
+++ b/platform-paper/src/test/resources/languages/ja.yml
@@ -0,0 +1,18 @@
+commandDescription:
+ tell: "他のプレイヤーにダイレクトメッセージを送信します"
+ reply: "最後にダイレクトメッセージを送受信したプレイヤーに返信します"
+ jp: "ローマ字→かな変換のオン/オフを切り替えます"
+ notice: "ダイレクトメッセージ通知のオン/オフを切り替えます"
+
+directMessageNoticeStatus: "ダイレクトメッセージ通知は現在 {toggle} です"
+directMessageNoticeToggle: "ダイレクトメッセージ通知を {toggle} に設定しました"
+playerOnlyCommand: "このコマンドはプレイヤーのみが実行できます。"
+replyTargetNotFound: "返信先のプレイヤーが見つかりませんでした。"
+romajiConversionToggle: "ローマ字→かな変換を {toggle} に設定しました"
+romajiConversionStatus: "ローマ字→かな変換は現在 {toggle} です"
+tellTargetOffline: "プレイヤー '{target}' は現在オフラインです。"
+tellYourself: "自分自身にメッセージを送信することはできません。"
+
+toggle:
+ off: "無効"
+ on: "有効"