diff --git a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt index 5f16ff9c..928c0f84 100644 --- a/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt +++ b/app/src/main/java/com/m57/hermescontrol/ui/chat/ChatViewModel.kt @@ -113,6 +113,11 @@ class ChatViewModel( init { connectWebSocket() + viewModelScope.launch { + wsClient.events.collect { event -> + handleWsEvent(event) + } + } if (startCleanup) { startPendingRequestCleanup() } @@ -128,12 +133,6 @@ class ChatViewModel( viewModelScope.launch(Dispatchers.IO) { wsClient.connect() } - - viewModelScope.launch { - wsClient.events.collect { event -> - handleWsEvent(event) - } - } } // ── WS Event Handling ──────────────────────────────────────────────── diff --git a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt index 4f26d780..14772454 100644 --- a/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt +++ b/app/src/test/java/com/m57/hermescontrol/ui/chat/ChatViewModelTest.kt @@ -657,4 +657,37 @@ class ChatViewModelTest { assertNull(state.streamingMessage) assertNull(state.clarifyRequest) } + + @Test + fun testReconnectDoesNotDuplicateEventCollection() = + runTest { + val viewModel = ChatViewModel(app, startCleanup = false) + advanceUntilIdle() + + // Setup active session + var createReqId = "" + every { HermesWsClient.send(WsMethods.SESSION_CREATE, any(), any()) } answers { + createReqId = "create-req-reconnect-test" + val onSent = arg<((String) -> Unit)?>(2) + onSent?.invoke(createReqId) + createReqId + } + mockEventsFlow.emit(WsEvent.GatewayReady(null)) + advanceUntilIdle() + mockEventsFlow.emit(WsEvent.RpcResult(createReqId, mapOf("session_id" to "session-123"))) + advanceUntilIdle() + + // Trigger reconnect + viewModel.reconnect() + advanceUntilIdle() + + // Emit a message token + mockEventsFlow.emit(WsEvent.MessageStart("session-123")) + mockEventsFlow.emit(WsEvent.MessageToken("Hello", "session-123")) + advanceUntilIdle() + + val state = viewModel.uiState.value + // Content should be "Hello", not "HelloHello" (no duplicate collectors running) + assertEquals("Hello", state.streamingMessage?.content) + } }