summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSho Sakuma <me@m1sk9.dev>2026-08-05 04:26:57 +0900
committerSho Sakuma <me@m1sk9.dev>2026-08-05 04:26:57 +0900
commitadbcb0d59dfc96050cac9d578b632b5fab38acbb (patch)
treeb683bdb24d1a2ce8746f4fef7cd6b6b1159438ce
parent2223cf96de63f57cf076b30e8d758c8fc5899d4e (diff)
downloadLunaticChat-adbcb0d59dfc96050cac9d578b632b5fab38acbb.tar.gz
LunaticChat-adbcb0d59dfc96050cac9d578b632b5fab38acbb.tar.bz2
LunaticChat-adbcb0d59dfc96050cac9d578b632b5fab38acbb.zip
test: cover what the player settings store promises
It was the one file store without a test, so the round trip it exists for - and its two fallbacks, an absent file and an unparseable one - were only asserted through PlayerSettingsManager. The write failure matters most: loading a torn file discards every player's settings, so a failed save must leave the previous file untouched. Co-Authored-By: Claude <noreply@anthropic.com>
-rw-r--r--platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/YamlPlayerSettingsStorageTest.kt109
1 files changed, 109 insertions, 0 deletions
diff --git a/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/YamlPlayerSettingsStorageTest.kt b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/YamlPlayerSettingsStorageTest.kt
new file mode 100644
index 0000000..594e475
--- /dev/null
+++ b/platform-paper/src/test/kotlin/dev/m1sk9/lunaticChat/paper/settings/YamlPlayerSettingsStorageTest.kt
@@ -0,0 +1,109 @@
+package dev.m1sk9.lunaticChat.paper.settings
+
+import dev.m1sk9.lunaticChat.engine.settings.PlayerSettingsData
+import dev.m1sk9.lunaticChat.paper.TestUtils
+import dev.m1sk9.lunaticChat.paper.storage.FileStore
+import org.junit.jupiter.api.io.TempDir
+import java.nio.file.Path
+import java.util.UUID
+import kotlin.io.path.listDirectoryEntries
+import kotlin.io.path.readText
+import kotlin.io.path.writeText
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class YamlPlayerSettingsStorageTest {
+ private val alice = UUID.fromString("00000001-0000-0000-0000-000000000000")
+ private val bob = UUID.fromString("00000002-0000-0000-0000-000000000000")
+
+ @TempDir
+ lateinit var directory: Path
+
+ private fun sampleData() =
+ PlayerSettingsData(
+ japaneseConversion = mapOf(alice to true, bob to false),
+ directMessageNotification = mapOf(alice to false),
+ channelMessageNotification = mapOf(bob to true),
+ )
+
+ private fun withStorage(block: (YamlPlayerSettingsStorage, Path, TestUtils.ManualScheduler, TestUtils.TestLogger) -> Unit) {
+ val settingsFile = directory.resolve("player-settings.yaml")
+ val scheduler = TestUtils.ManualScheduler()
+ val logger = TestUtils.TestLogger()
+ val store = FileStore(settingsFile, scheduler, logger)
+ block(YamlPlayerSettingsStorage(store, logger), settingsFile, scheduler, logger)
+ }
+
+ @Test
+ fun `saved settings are loaded back unchanged`() =
+ withStorage { storage, _, _, _ ->
+ val data = sampleData()
+
+ storage.saveToDisk(data)
+
+ assertEquals(data, storage.loadFromDisk())
+ }
+
+ @Test
+ fun `saving leaves no temporary file beside the settings file`() =
+ withStorage { storage, settingsFile, _, _ ->
+ storage.saveToDisk(sampleData())
+
+ assertEquals(listOf(settingsFile), settingsFile.parent.listDirectoryEntries())
+ }
+
+ @Test
+ fun `a missing file loads as empty settings without complaint`() =
+ withStorage { storage, _, _, logger ->
+ assertEquals(PlayerSettingsData(), storage.loadFromDisk())
+ assertTrue(logger.severeMessages.isEmpty())
+ }
+
+ @Test
+ fun `an unparseable file falls back to empty settings rather than failing startup`() =
+ withStorage { storage, settingsFile, _, logger ->
+ settingsFile.writeText("japaneseConversion: [this is not a map")
+
+ assertEquals(PlayerSettingsData(), storage.loadFromDisk())
+ assertTrue(logger.severeMessages.any { it.contains("Failed to load settings file") })
+ }
+
+ @Test
+ fun `a failed write leaves the previous file intact`() =
+ withStorage { storage, settingsFile, _, logger ->
+ storage.saveToDisk(sampleData())
+ val saved = settingsFile.readText()
+
+ // A directory where the temporary file has to be created cannot be written to.
+ settingsFile.parent.toFile().setWritable(false)
+ try {
+ storage.saveToDisk(PlayerSettingsData(japaneseConversion = mapOf(alice to false)))
+ } finally {
+ settingsFile.parent.toFile().setWritable(true)
+ }
+
+ // Loading a torn file discards every player's settings, so a failed write must not
+ // replace what is already there.
+ assertEquals(saved, settingsFile.readText())
+ assertTrue(logger.severeMessages.any { it.contains("Failed to save settings") })
+ }
+
+ @Test
+ fun `a queued save reads the settings when the write runs, not when it is queued`() =
+ withStorage { storage, _, scheduler, _ ->
+ var supplied = false
+
+ storage.queueAsyncSave {
+ supplied = true
+ sampleData()
+ }
+
+ assertTrue(!supplied, "the snapshot must not be taken while queueing")
+
+ scheduler.runPending()
+
+ assertTrue(supplied)
+ assertEquals(sampleData(), storage.loadFromDisk())
+ }
+}