diff options
| author | Sho Sakuma <me@m1sk9.dev> | 2026-01-27 22:09:22 +0900 |
|---|---|---|
| committer | Sho Sakuma <me@m1sk9.dev> | 2026-01-27 22:33:43 +0900 |
| commit | 5171dc41dc633e51470ea2e9f92b3434547a465c (patch) | |
| tree | 046ac63c25077673bec55697c0cfb46857f77cb0 /platform-paper/src/test | |
| parent | 21d17ca948193a9f4b7ef080d2b949240e64e538 (diff) | |
| download | LunaticChat-5171dc41dc633e51470ea2e9f92b3434547a465c.tar.gz LunaticChat-5171dc41dc633e51470ea2e9f92b3434547a465c.tar.bz2 LunaticChat-5171dc41dc633e51470ea2e9f92b3434547a465c.zip | |
test: Add more test-case
Diffstat (limited to 'platform-paper/src/test')
4 files changed, 853 insertions, 0 deletions
diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/TestUtils.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/TestUtils.kt new file mode 100644 index 0000000..66fdbd4 --- /dev/null +++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/TestUtils.kt @@ -0,0 +1,208 @@ +package dev.m1sk9.lunaticChat.paper + +import dev.m1sk9.lunaticChat.engine.chat.channel.Channel +import dev.m1sk9.lunaticChat.engine.chat.channel.ChannelMember +import dev.m1sk9.lunaticChat.engine.chat.channel.ChannelRole +import dev.m1sk9.lunaticChat.engine.settings.PlayerChatSettings +import dev.m1sk9.lunaticChat.paper.config.LunaticChatConfiguration +import dev.m1sk9.lunaticChat.paper.config.key.ChannelChatFeatureConfig +import dev.m1sk9.lunaticChat.paper.config.key.FeaturesConfig +import dev.m1sk9.lunaticChat.paper.config.key.JapaneseConversionFeatureConfig +import dev.m1sk9.lunaticChat.paper.config.key.MessageFormatConfig +import dev.m1sk9.lunaticChat.paper.config.key.QuickRepliesFeatureConfig +import dev.m1sk9.lunaticChat.paper.i18n.Language +import io.mockk.mockk +import org.bukkit.entity.Player +import org.bukkit.plugin.java.JavaPlugin +import java.util.UUID +import java.util.logging.Logger + +/** + * Common test utilities for LunaticChat tests. + * Provides mock factories, test data builders, and assertion helpers. + */ +object TestUtils { + /** + * Creates a test logger that collects log messages for verification. + */ + class TestLogger : Logger("test", null) { + val infoMessages = mutableListOf<String>() + val warningMessages = mutableListOf<String>() + val severeMessages = mutableListOf<String>() + + override fun info(msg: String) { + infoMessages.add(msg) + } + + override fun warning(msg: String) { + warningMessages.add(msg) + } + + override fun severe(msg: String) { + severeMessages.add(msg) + } + + fun clear() { + infoMessages.clear() + warningMessages.clear() + severeMessages.clear() + } + } + + /** + * Creates a default test configuration with sensible defaults. + */ + fun createTestConfiguration( + quickRepliesEnabled: Boolean = true, + japaneseConversionEnabled: Boolean = false, + channelChatEnabled: Boolean = false, + maxChannelsPerServer: Int = 10, + maxMembersPerChannel: Int = 50, + maxMembershipPerPlayer: Int = 5, + debug: Boolean = false, + checkForUpdates: Boolean = false, + language: Language = Language.EN, + ): LunaticChatConfiguration = + LunaticChatConfiguration( + features = + FeaturesConfig( + quickReplies = QuickRepliesFeatureConfig(enabled = quickRepliesEnabled), + japaneseConversion = + JapaneseConversionFeatureConfig( + enabled = japaneseConversionEnabled, + cacheMaxEntries = 500, + cacheSaveIntervalSeconds = 300, + cacheFilePath = "test-conversion-cache.json", + apiTimeout = 3000, + apiRetryAttempts = 2, + ), + channelChat = + ChannelChatFeatureConfig( + enabled = channelChatEnabled, + maxChannelsPerServer = maxChannelsPerServer, + maxMembersPerChannel = maxMembersPerChannel, + maxMembershipPerPlayer = maxMembershipPerPlayer, + ), + ), + messageFormat = + MessageFormatConfig( + directMessageFormat = "§7[§e{sender} §7>> §e{recipient}§7] §f{message}", + channelMessageFormat = "§7[§b#{channel}§7] §e{sender}: §f{message}", + ), + debug = debug, + checkForUpdates = checkForUpdates, + userSettingsFilePath = "test-player-settings.yaml", + language = language, + ) + + /** + * Creates a default player settings for testing. + */ + fun createTestPlayerSettings( + uuid: UUID = UUID.randomUUID(), + japaneseConversionEnabled: Boolean = true, + directMessageNotificationEnabled: Boolean = true, + channelMessageNotificationEnabled: Boolean = true, + ): PlayerChatSettings = + PlayerChatSettings( + uuid = uuid, + japaneseConversionEnabled = japaneseConversionEnabled, + directMessageNotificationEnabled = directMessageNotificationEnabled, + channelMessageNotificationEnabled = channelMessageNotificationEnabled, + ) + + /** + * Creates a test channel with default values. + */ + fun createTestChannel( + id: String = "test-channel-1", + name: String = "Test Channel", + description: String? = null, + ownerId: UUID = UUID.randomUUID(), + isPrivate: Boolean = false, + createdAt: Long = System.currentTimeMillis(), + bannedPlayers: Set<UUID> = emptySet(), + ): Channel = + Channel( + id = id, + name = name, + description = description, + ownerId = ownerId, + createdAt = createdAt, + isPrivate = isPrivate, + bannedPlayers = bannedPlayers, + ) + + /** + * Creates a test channel member. + */ + fun createTestChannelMember( + channelId: String = "test-channel-1", + playerId: UUID = UUID.randomUUID(), + role: ChannelRole = ChannelRole.MEMBER, + joinedAt: Long = System.currentTimeMillis(), + ): ChannelMember = + ChannelMember( + channelId = channelId, + playerId = playerId, + role = role, + joinedAt = joinedAt, + ) + + /** + * Creates a mock Player with the given UUID and name. + */ + fun createMockPlayer( + uuid: UUID = UUID.randomUUID(), + name: String = "TestPlayer", + isOnline: Boolean = true, + ): Player { + val player = mockk<Player>(relaxed = true) + io.mockk.every { player.uniqueId } returns uuid + io.mockk.every { player.name } returns name + io.mockk.every { player.isOnline } returns isOnline + return player + } + + /** + * Creates a mock JavaPlugin for testing. + */ + fun createMockPlugin(): JavaPlugin = mockk<JavaPlugin>(relaxed = true) + + /** + * Creates a test UUID from an integer for deterministic testing. + */ + fun createTestUUID(value: Int): UUID = + UUID.fromString( + String.format( + "%08x-0000-0000-0000-000000000000", + value, + ), + ) + + /** + * Assertion helper to check if a string contains all given substrings. + */ + fun assertContainsAll( + actual: String, + vararg expected: String, + ) { + expected.forEach { substring -> + if (!actual.contains(substring)) { + throw AssertionError("Expected '$actual' to contain '$substring'") + } + } + } + + /** + * Assertion helper to check if a list contains items matching a predicate. + */ + fun <T> assertAny( + list: List<T>, + predicate: (T) -> Boolean, + ) { + if (!list.any(predicate)) { + throw AssertionError("Expected list to contain at least one matching item") + } + } +} diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/chat/ChatModeManagerTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/chat/ChatModeManagerTest.kt new file mode 100644 index 0000000..d548f60 --- /dev/null +++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/chat/ChatModeManagerTest.kt @@ -0,0 +1,223 @@ +package dev.m1sk9.lunaticChat.paper.chat + +import dev.m1sk9.lunaticChat.engine.chat.ChatMode +import dev.m1sk9.lunaticChat.engine.chat.ChatModeData +import dev.m1sk9.lunaticChat.paper.TestUtils +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import java.util.UUID +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Tests for ChatModeManager. + * Verifies chat mode management, toggling, and persistence. + */ +class ChatModeManagerTest { + private fun createChatModeManager( + initialData: ChatModeData = ChatModeData(), + ): Triple<ChatModeManager, ChatModeStorage, TestUtils.TestLogger> { + val logger = TestUtils.TestLogger() + val storage = mockk<ChatModeStorage>(relaxed = true) + + every { storage.loadFromDisk() } returns initialData + + val manager = ChatModeManager(storage, logger) + return Triple(manager, storage, logger) + } + + @Test + fun `initialize should load data from storage`() { + val playerId = UUID.randomUUID() + val initialData = ChatModeData(modes = mapOf(playerId to ChatMode.CHANNEL)) + val (manager, _, logger) = createChatModeManager(initialData) + + manager.initialize() + + assertEquals(ChatMode.CHANNEL, manager.getChatMode(playerId)) + assert(logger.infoMessages.any { it.contains("ChatModeManager initialized with 1 saved modes") }) + } + + @Test + fun `getChatMode should return DEFAULT for unknown player`() { + val (manager, _, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + val mode = manager.getChatMode(playerId) + + assertEquals(ChatMode.DEFAULT, mode) + assertEquals(ChatMode.GLOBAL, mode) // DEFAULT is GLOBAL + } + + @Test + fun `getChatMode should return set mode`() { + val (manager, _, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.setChatMode(playerId, ChatMode.CHANNEL) + + assertEquals(ChatMode.CHANNEL, manager.getChatMode(playerId)) + } + + @Test + fun `setChatMode should save to storage`() { + val (manager, storage, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.setChatMode(playerId, ChatMode.CHANNEL) + + verify(exactly = 1) { storage.queueAsyncSave(any()) } + } + + @Test + fun `toggleChatMode should switch from GLOBAL to CHANNEL`() { + val (manager, _, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + // Initial mode is GLOBAL (default) + assertEquals(ChatMode.GLOBAL, manager.getChatMode(playerId)) + + val newMode = manager.toggleChatMode(playerId) + + assertEquals(ChatMode.CHANNEL, newMode) + assertEquals(ChatMode.CHANNEL, manager.getChatMode(playerId)) + } + + @Test + fun `toggleChatMode should switch from CHANNEL to GLOBAL`() { + val (manager, _, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.setChatMode(playerId, ChatMode.CHANNEL) + + val newMode = manager.toggleChatMode(playerId) + + assertEquals(ChatMode.GLOBAL, newMode) + assertEquals(ChatMode.GLOBAL, manager.getChatMode(playerId)) + } + + @Test + fun `toggleChatMode should persist changes`() { + val (manager, storage, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.toggleChatMode(playerId) + + verify(atLeast = 1) { storage.queueAsyncSave(any()) } + } + + @Test + fun `removeChatMode should revert to default`() { + val (manager, _, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.setChatMode(playerId, ChatMode.CHANNEL) + assertEquals(ChatMode.CHANNEL, manager.getChatMode(playerId)) + + manager.removeChatMode(playerId) + + assertEquals(ChatMode.GLOBAL, manager.getChatMode(playerId)) + } + + @Test + fun `removeChatMode should save to storage`() { + val (manager, storage, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.setChatMode(playerId, ChatMode.CHANNEL) + manager.removeChatMode(playerId) + + verify(atLeast = 2) { storage.queueAsyncSave(any()) } // Once for set, once for remove + } + + @Test + fun `saveToDisk should synchronously save`() { + val (manager, storage, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + manager.setChatMode(playerId, ChatMode.CHANNEL) + manager.saveToDisk() + + verify { storage.saveToDisk(any()) } + } + + @Test + fun `shutdown should save and shutdown storage`() { + val (manager, storage, _) = createChatModeManager() + manager.initialize() + + manager.shutdown() + + verify { storage.saveToDisk(any()) } + verify { storage.shutdown() } + } + + @Test + fun `manager should handle multiple players independently`() { + val (manager, _, _) = createChatModeManager() + manager.initialize() + + val player1 = UUID.randomUUID() + val player2 = UUID.randomUUID() + + manager.setChatMode(player1, ChatMode.CHANNEL) + manager.setChatMode(player2, ChatMode.GLOBAL) + + assertEquals(ChatMode.CHANNEL, manager.getChatMode(player1)) + assertEquals(ChatMode.GLOBAL, manager.getChatMode(player2)) + } + + @Test + fun `manager should support rapid mode changes`() { + val (manager, storage, _) = createChatModeManager() + manager.initialize() + + val playerId = UUID.randomUUID() + + // Toggle multiple times + manager.toggleChatMode(playerId) // GLOBAL -> CHANNEL + manager.toggleChatMode(playerId) // CHANNEL -> GLOBAL + manager.toggleChatMode(playerId) // GLOBAL -> CHANNEL + + assertEquals(ChatMode.CHANNEL, manager.getChatMode(playerId)) + verify(atLeast = 3) { storage.queueAsyncSave(any()) } + } + + @Test + fun `manager should handle empty initial data`() { + val (manager, _, logger) = createChatModeManager(ChatModeData()) + manager.initialize() + + assert(logger.infoMessages.any { it.contains("ChatModeManager initialized with 0 saved modes") }) + } + + @Test + fun `manager should restore modes after initialization`() { + val player1 = TestUtils.createTestUUID(1) + val player2 = TestUtils.createTestUUID(2) + val initialData = + ChatModeData( + modes = + mapOf( + player1 to ChatMode.CHANNEL, + player2 to ChatMode.GLOBAL, + ), + ) + + val (manager, _, _) = createChatModeManager(initialData) + manager.initialize() + + assertEquals(ChatMode.CHANNEL, manager.getChatMode(player1)) + assertEquals(ChatMode.GLOBAL, manager.getChatMode(player2)) + } +} diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/DirectMessageHandlerTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/DirectMessageHandlerTest.kt new file mode 100644 index 0000000..b07636b --- /dev/null +++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/chat/handler/DirectMessageHandlerTest.kt @@ -0,0 +1,134 @@ +package dev.m1sk9.lunaticChat.paper.chat.handler + +import dev.m1sk9.lunaticChat.paper.TestUtils +import dev.m1sk9.lunaticChat.paper.converter.RomanjiConverter +import dev.m1sk9.lunaticChat.paper.settings.PlayerSettingsManager +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import kotlin.test.Test +import kotlin.test.assertTrue + +/** + * Tests for DirectMessageHandler. + * Validates message handling with dependency injection and conversion features. + */ +class DirectMessageHandlerTest { + private fun createHandler( + configuration: dev.m1sk9.lunaticChat.paper.config.LunaticChatConfiguration? = null, + settingsManager: PlayerSettingsManager? = null, + romanjiConverter: RomanjiConverter? = null, + ): DirectMessageHandler { + val config = configuration ?: TestUtils.createTestConfiguration() + return DirectMessageHandler(config, settingsManager, romanjiConverter) + } + + @Test + fun `sendDirectMessage should return true on success`() { + val handler = createHandler() + val sender = TestUtils.createMockPlayer() + val recipient = TestUtils.createMockPlayer() + + val result = handler.sendDirectMessage(sender, recipient, "Test message") + + assertTrue(result) + } + + @Test + fun `sendDirectMessage should use custom message format from configuration`() { + val customConfig = + TestUtils.createTestConfiguration().copy( + messageFormat = + TestUtils + .createTestConfiguration() + .messageFormat + .copy( + directMessageFormat = "[DM] {sender} -> {recipient}: {message}", + ), + ) + + val handler = createHandler(configuration = customConfig) + val sender = TestUtils.createMockPlayer(name = "Alice") + val recipient = TestUtils.createMockPlayer(name = "Bob") + + val result = handler.sendDirectMessage(sender, recipient, "Test") + + assertTrue(result) + } + + @Test + fun `sendDirectMessage without conversion should send original message`() { + val settingsManager = mockk<PlayerSettingsManager>() + val senderSettings = + TestUtils.createTestPlayerSettings( + japaneseConversionEnabled = false, + ) + + every { settingsManager.getSettings(any()) } returns senderSettings + + val handler = createHandler(settingsManager = settingsManager) + val sender = TestUtils.createMockPlayer() + val recipient = TestUtils.createMockPlayer() + + val result = handler.sendDirectMessage(sender, recipient, "konnichiwa") + + assertTrue(result) + } + + @Test + fun `sendDirectMessage with conversion should handle conversion timeout gracefully`() { + val settingsManager = mockk<PlayerSettingsManager>() + val senderSettings = + TestUtils.createTestPlayerSettings( + japaneseConversionEnabled = true, + ) + val romanjiConverter = mockk<RomanjiConverter>() + + every { settingsManager.getSettings(any()) } returns senderSettings + // Simulate a slow conversion that would timeout + coEvery { romanjiConverter.convert(any()) } coAnswers { + kotlinx.coroutines.delay(2000) // Exceeds 1s timeout + "こんにちは" + } + + val handler = createHandler(settingsManager = settingsManager, romanjiConverter = romanjiConverter) + val sender = TestUtils.createMockPlayer() + val recipient = TestUtils.createMockPlayer() + + // Should not throw exception and should complete quickly (within timeout) + val result = handler.sendDirectMessage(sender, recipient, "konnichiwa") + + assertTrue(result) + } + + @Test + fun `handler can be created with injected configuration`() { + val config = TestUtils.createTestConfiguration(debug = true) + val handler = DirectMessageHandler(config, null, null) + + // Handler should accept configuration via constructor (DI pattern) + // This validates Issue #1 refactoring - ConfigManager DI + val sender = TestUtils.createMockPlayer() + val recipient = TestUtils.createMockPlayer() + val result = handler.sendDirectMessage(sender, recipient, "Test") + + assertTrue(result) + } + + @Test + fun `handler can be created with all dependencies`() { + val config = TestUtils.createTestConfiguration() + val settingsManager = mockk<PlayerSettingsManager>(relaxed = true) + val romanjiConverter = mockk<RomanjiConverter>(relaxed = true) + + every { settingsManager.getSettings(any()) } returns TestUtils.createTestPlayerSettings() + + val handler = DirectMessageHandler(config, settingsManager, romanjiConverter) + + val sender = TestUtils.createMockPlayer() + val recipient = TestUtils.createMockPlayer() + val result = handler.sendDirectMessage(sender, recipient, "Test") + + assertTrue(result) + } +} diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManagerTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManagerTest.kt new file mode 100644 index 0000000..3f936f6 --- /dev/null +++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/config/ConfigManagerTest.kt @@ -0,0 +1,288 @@ +package dev.m1sk9.lunaticChat.paper.config + +import dev.m1sk9.lunaticChat.paper.i18n.Language +import io.mockk.every +import io.mockk.mockk +import org.bukkit.configuration.file.FileConfiguration +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +/** + * Tests for ConfigManager to ensure proper configuration loading and parsing. + */ +class ConfigManagerTest { + private fun createMockConfig(values: Map<String, Any> = emptyMap()): FileConfiguration { + val config = mockk<FileConfiguration>(relaxed = true) + + // Set up default values + every { config.getBoolean(any(), any()) } answers { + val key = firstArg<String>() + val default = secondArg<Boolean>() + (values[key] as? Boolean) ?: default + } + + every { config.getInt(any(), any()) } answers { + val key = firstArg<String>() + val default = secondArg<Int>() + (values[key] as? Int) ?: default + } + + every { config.getLong(any(), any()) } answers { + val key = firstArg<String>() + val default = secondArg<Long>() + (values[key] as? Long) ?: default + } + + every { config.getString(any(), any()) } answers { + val key = firstArg<String>() + val default = secondArg<String>() + (values[key] as? String) ?: default + } + + every { config.getString(any()) } answers { + val key = firstArg<String>() + values[key] as? String + } + + return config + } + + @Test + fun `loadConfiguration should load all default values`() { + val configManager = ConfigManager() + val mockConfig = createMockConfig() + + val configuration = configManager.loadConfiguration(mockConfig) + + assertNotNull(configuration) + assertTrue(configuration.features.quickReplies.enabled) + assertFalse(configuration.features.japaneseConversion.enabled) + assertFalse(configuration.features.channelChat.enabled) + assertFalse(configuration.debug) + assertFalse(configuration.checkForUpdates) + assertEquals(Language.EN, configuration.language) + } + + @Test + fun `loadConfiguration should load quick replies configuration`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "features.quickReplies.enabled" to false, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertFalse(configuration.features.quickReplies.enabled) + } + + @Test + fun `loadConfiguration should load Japanese conversion configuration`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "features.japaneseConversion.enabled" to true, + "features.japaneseConversion.cache.maxEntries" to 1000, + "features.japaneseConversion.cache.saveIntervalSeconds" to 600, + "features.japaneseConversion.cache.filePath" to "custom_cache.json", + "features.japaneseConversion.api.timeout" to 5000L, + "features.japaneseConversion.api.retryAttempts" to 3, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertTrue(configuration.features.japaneseConversion.enabled) + assertEquals(1000, configuration.features.japaneseConversion.cacheMaxEntries) + assertEquals(600, configuration.features.japaneseConversion.cacheSaveIntervalSeconds) + assertEquals("custom_cache.json", configuration.features.japaneseConversion.cacheFilePath) + assertEquals(5000L, configuration.features.japaneseConversion.apiTimeout) + assertEquals(3, configuration.features.japaneseConversion.apiRetryAttempts) + } + + @Test + fun `loadConfiguration should load channel chat configuration`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "features.channelChat.enabled" to true, + "features.channelChat.maxChannelsPerServer" to 20, + "features.channelChat.maxMembersPerChannel" to 100, + "features.channelChat.maxMembershipPerPlayer" to 10, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertTrue(configuration.features.channelChat.enabled) + assertEquals(20, configuration.features.channelChat.maxChannelsPerServer) + assertEquals(100, configuration.features.channelChat.maxMembersPerChannel) + assertEquals(10, configuration.features.channelChat.maxMembershipPerPlayer) + } + + @Test + fun `loadConfiguration should load message format configuration`() { + val configManager = ConfigManager() + val customDMFormat = "DM: {sender} -> {recipient}: {message}" + val customChannelFormat = "[{channel}] {sender}: {message}" + val mockConfig = + createMockConfig( + mapOf( + "messageFormat.directMessageFormat" to customDMFormat, + "messageFormat.channelMessageFormat" to customChannelFormat, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertEquals(customDMFormat, configuration.messageFormat.directMessageFormat) + assertEquals(customChannelFormat, configuration.messageFormat.channelMessageFormat) + } + + @Test + fun `loadConfiguration should load debug and update check settings`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "debug" to true, + "checkForUpdates" to true, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertTrue(configuration.debug) + assertTrue(configuration.checkForUpdates) + } + + @Test + fun `loadConfiguration should load custom settings file path`() { + val configManager = ConfigManager() + val customPath = "custom-player-settings.yaml" + val mockConfig = + createMockConfig( + mapOf( + "userSettingsFilePath" to customPath, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertEquals(customPath, configuration.userSettingsFilePath) + } + + @Test + fun `loadConfiguration should load Japanese language`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "language" to "ja", + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertEquals(Language.JA, configuration.language) + } + + @Test + fun `loadConfiguration should handle unknown language code`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "language" to "fr", // French not supported + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + // Should fall back to English + assertEquals(Language.EN, configuration.language) + } + + @Test + fun `loadConfiguration should use default values when keys are missing`() { + val configManager = ConfigManager() + val mockConfig = createMockConfig(emptyMap()) + + val configuration = configManager.loadConfiguration(mockConfig) + + // All features should have their defaults + assertTrue(configuration.features.quickReplies.enabled) + assertEquals(500, configuration.features.japaneseConversion.cacheMaxEntries) + assertEquals(300, configuration.features.japaneseConversion.cacheSaveIntervalSeconds) + assertEquals("conversion_cache.json", configuration.features.japaneseConversion.cacheFilePath) + assertEquals(3000L, configuration.features.japaneseConversion.apiTimeout) + assertEquals(2, configuration.features.japaneseConversion.apiRetryAttempts) + assertEquals(0, configuration.features.channelChat.maxChannelsPerServer) + assertEquals(0, configuration.features.channelChat.maxMembersPerChannel) + assertEquals(0, configuration.features.channelChat.maxMembershipPerPlayer) + } + + @Test + fun `loadConfiguration can be called multiple times`() { + val configManager = ConfigManager() + val mockConfig1 = + createMockConfig( + mapOf("debug" to true), + ) + val mockConfig2 = + createMockConfig( + mapOf("debug" to false), + ) + + val config1 = configManager.loadConfiguration(mockConfig1) + val config2 = configManager.loadConfiguration(mockConfig2) + + assertTrue(config1.debug) + assertFalse(config2.debug) + } + + @Test + fun `loadConfiguration should handle all features enabled`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "features.quickReplies.enabled" to true, + "features.japaneseConversion.enabled" to true, + "features.channelChat.enabled" to true, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertTrue(configuration.features.quickReplies.enabled) + assertTrue(configuration.features.japaneseConversion.enabled) + assertTrue(configuration.features.channelChat.enabled) + } + + @Test + fun `loadConfiguration should handle all features disabled`() { + val configManager = ConfigManager() + val mockConfig = + createMockConfig( + mapOf( + "features.quickReplies.enabled" to false, + "features.japaneseConversion.enabled" to false, + "features.channelChat.enabled" to false, + ), + ) + + val configuration = configManager.loadConfiguration(mockConfig) + + assertFalse(configuration.features.quickReplies.enabled) + assertFalse(configuration.features.japaneseConversion.enabled) + assertFalse(configuration.features.channelChat.enabled) + } +} |
