From f46a7c627d1db8c9b2d2ca98a2963a77dd35c9ee Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Fri, 2 Oct 2026 21:22:29 +0200 Subject: [PATCH] test(subgroups): stop a ceremony-queue test throwing into another test's name `./gradlew :composeApp:jvmTest` failed `NavigationRoutingTest > an account already on the device does not stay on the loading screen` with `UncaughtExceptionsBeforeTest: There were uncaught exceptions before the test started`. **"Before the test started" is the tell.** `runTest` reports exceptions that reached the default handler before it began, so the name on the failure is whichever `runTest` ran next -- not the one that threw. `NavigationRoutingTest` passes alone and the subgroups work never touched it. The thrower is `CeremonyQueueRoutingJvmTest`, and the chain is five links with no branch in it: 1. `awaiting()` builds a `ChatMessageListViewModel` on the bare `NO_OP_NOSTR_REPOSITORY` and calls `initiate()`. 2. `initiate()`'s second line is `scheduleSynchronization()`. 3. That launches on `Dispatchers.IO` and asks `MemberProfileSync` who cannot be named. 4. The fixture's `room()` gives every `LocalParticipant` a `profile = null`, so the answer is never empty and the next line always runs. 5. `queueNegentropySynchronizeRequest` is one of the **forty-one** `TODO("Not yet implemented")` bodies on that no-op, out of forty-five members. It throws in a coroutine nobody awaits, four times a run -- once per test in the class. Fixed the way this file already fixes it for `DkgRepository`: an `object : X by NO_OP_X` with the one member overridden. The comment above it carries the chain, because the next person to see `UncaughtExceptionsBeforeTest` will be reading the name of the wrong test. **Verified under the condition that reproduced it.** It is order-dependent, so a filtered run is not evidence -- the culprit passes alone, and the pair passed too. The full suite under `--rerun-tasks` failed reproducibly before and is 982 of 982 after. **Not fixed here: the landmine itself.** Sixty `TODO()` bodies sit across four no-op repositories -- 41 of 45 on `NostrRepository`, 12 of 37 on `ChatRepository`, 6 of 7 on `MarmotRepository`, 1 of 1 on `SearchRepository` -- and seventeen jvmTest files build view models on the Nostr one alone, so this will recur; the reporter had already hit it through `getProfileWithPublicKey`. It is not a sweep, though: "no-op" means something different for each member, the return types vary from `Unit` to `Flow`, and a blanket empty body could make a test pass vacuously where it currently fails honestly. Sixty judgement calls about shared code do not belong in a commit about one flake, so it is flagged for its own change. 982 jvm tests, 0 failed, under `--rerun-tasks`. Co-Authored-By: Claude Opus 5 --- .../view/model/CeremonyQueueRoutingJvmTest.kt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/composeApp/src/jvmTest/kotlin/press/mantra/compose/ui/view/model/CeremonyQueueRoutingJvmTest.kt b/composeApp/src/jvmTest/kotlin/press/mantra/compose/ui/view/model/CeremonyQueueRoutingJvmTest.kt index f6c876cb..faaec773 100644 --- a/composeApp/src/jvmTest/kotlin/press/mantra/compose/ui/view/model/CeremonyQueueRoutingJvmTest.kt +++ b/composeApp/src/jvmTest/kotlin/press/mantra/compose/ui/view/model/CeremonyQueueRoutingJvmTest.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.withTimeout import kotlinx.coroutines.delay import press.mantra.compose.database.model.ChatRoom import press.mantra.compose.database.model.DkgSession +import press.mantra.compose.database.model.NegentropySynchronizeRequest import press.mantra.compose.database.model.Participant import press.mantra.compose.database.model.intermdiate.LocalChatRoom import press.mantra.compose.database.model.intermdiate.LocalParticipant @@ -151,7 +152,23 @@ class CeremonyQueueRoutingJvmTest { val viewModel = ChatMessageListViewModel( initialChatMessageListUIState = ChatMessageListUIState.Loading, localChatRoom = room(), - nostrRepository = NostrRepository.NO_OP_NOSTR_REPOSITORY, + // Not the bare no-op. `initiate` calls `scheduleSynchronization` on + // its second line, which launches on `Dispatchers.IO` and -- because + // `room()` gives every participant a null profile, so there is always + // somebody it cannot name -- reaches + // `queueNegentropySynchronizeRequest`. That member is one of the + // forty-one `TODO("Not yet implemented")` bodies on + // NO_OP_NOSTR_REPOSITORY, so it throws in a coroutine nobody awaits. + // + // Nothing catches it, so it reaches the default handler, and the next + // `runTest` in the same JVM reports it as `UncaughtExceptionsBeforeTest` + // -- against its own name. That is how this test failed + // `NavigationRoutingTest` four times a run while passing itself. + nostrRepository = object : NostrRepository by NostrRepository.NO_OP_NOSTR_REPOSITORY { + override suspend fun queueNegentropySynchronizeRequest( + negentropySynchronizeRequests: List + ) = Unit + }, chatRepository = ChatRepository.NO_OP_CHAT_REPOSITORY, frostSigningRepository = FrostSigningRepository.NO_OP_FROST_SIGNING_REPOSITORY, dkgRepository = object : DkgRepository by DkgRepository.NO_OP_DKG_REPOSITORY {