Test settings persistence ownership, ordering and failure isolation

This commit is contained in:
evgeny committed 2026-10-29 13:35:19 +01:00
1 parent a95245e272
commit 87166db832
1 file changed
+40
@@ -0,0 +1,40 @@
package com.utun.chat.data
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.Assert.*
import org.junit.Test
class SettingsOperationsTest {
@Test fun acceptedSaveSurvivesUiCancellationAndRestartWaitsForItsCommit() = runTest {
val owner = SettingsOperations(this) { _, _ -> }
val committed = CompletableDeferred<Unit>(); val order = mutableListOf<String>()
val save = owner.submit("name") { committed.await(); order.add("commit") }
val second = owner.submit("runtime") { order.add("apply") }
val ui = launch { save.await() }; ui.cancel()
val restart = async { owner.awaitIdle(); order.add("restart") }
assertTrue(order.isEmpty()); assertFalse(save.isCancelled)
committed.complete(Unit); save.await(); second.await(); restart.await()
assertEquals(listOf("commit", "apply", "restart"), order)
}
@Test fun failedPersistenceCannotRunTheFollowingRuntimeUpdateOrKillTheQueue() = runTest {
val logs = mutableListOf<String>(); val owner = SettingsOperations(this) { _, message -> logs.add(message) }
var runtime = false
val failed = owner.submit("volume") { throw java.io.IOException("disk rejected"); @Suppress("UNREACHABLE_CODE") runtime = true }
assertTrue(runCatching { failed.await() }.exceptionOrNull() is java.io.IOException)
assertFalse(runtime); assertTrue(owner.failed.value); assertTrue(logs.any { "disk rejected" in it })
owner.submit("retry") { runtime = true }.await()
assertTrue(runtime); owner.dismissFailure(); assertFalse(owner.failed.value)
}
@Test fun cancelledBarrierWaiterCannotCancelAnAcceptedChange() = runTest {
val owner = SettingsOperations(this) { _, _ -> }
val gate = CompletableDeferred<Unit>(); var saved = false
val change = owner.submit("stereo") { gate.await(); saved = true }
val waiter = launch { owner.awaitIdle() }; waiter.cancel()
gate.complete(Unit); change.await(); assertTrue(saved); assertFalse(change.isCancelled)
}
}