refactor(chat): isolate transcript and browser return state (#1320, #1321) (#1357)

This commit is contained in:
m57 authored and GitHub committed 2026-09-28 14:30:05 +03:00
1 parent cc010b451a
commit 76726dd086
12 files changed
+521 -201

No files matched your search

@@ -0,0 +1,62 @@
package com.m57.hermescontrol.ui.chat
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
import org.junit.Test
class BrowserReturnTrackerTest {
@Test
fun `browser return requires a departure and consumes the lease once`() {
val tracker = BrowserReturnTracker()
tracker.start("operation-1")
assertNull(tracker.onResume())
tracker.onPause()
assertEquals("operation-1", tracker.onResume()?.operationId)
assertNull(tracker.onResume())
assertFalse(tracker.abandon())
}
@Test
fun `legacy browser without operation id still reports return`() {
val tracker = BrowserReturnTracker()
tracker.start()
tracker.onPause()
assertTrue(tracker.onResume() != null)
assertNull(tracker.onResume())
}
@Test
fun `failed launch clears tracking and does not release a departed lease`() {
val tracker = BrowserReturnTracker()
tracker.start("operation-1")
tracker.onPause()
tracker.cancel()
assertNull(tracker.onResume())
assertFalse(tracker.abandon())
}
@Test
fun `non configuration disposal releases only a departed lease and clears the id`() {
val tracker = BrowserReturnTracker()
tracker.start("operation-1")
assertFalse(tracker.abandon())
tracker.start("operation-2")
tracker.onPause()
assertTrue(tracker.abandon())
assertFalse(tracker.abandon())
assertNull(tracker.onResume())
}
@Test
fun `new launch resets departed state from the previous launch`() {
val tracker = BrowserReturnTracker()
tracker.start("first")
tracker.onPause()
tracker.start("second")
assertNull(tracker.onResume())
tracker.onPause()
assertEquals("second", tracker.onResume()?.operationId)
}
}
@@ -0,0 +1,88 @@
package com.m57.hermescontrol.ui.chat.fullbleed
import com.m57.hermescontrol.ui.chat.ChatMessage
import com.m57.hermescontrol.ui.chat.ChatTimelineState
import com.m57.hermescontrol.ui.chat.ChatUiState
import com.m57.hermescontrol.ui.chat.ClarifyUi
import com.m57.hermescontrol.ui.chat.MessageRole
import com.m57.hermescontrol.ui.chat.StreamingState
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNull
import org.junit.Assert.assertSame
import org.junit.Assert.assertTrue
import org.junit.Test
class TranscriptUiStateTest {
private val live = ChatMessage(id = "live", role = MessageRole.USER, content = "hello")
private val historical = ChatMessage(id = "historical", role = MessageRole.USER, content = "older")
private val clarify = ClarifyUi(text = "choose")
@Test
fun `live state preserves prompts streaming and paging actions`() {
val stream = StreamingState()
val result =
TranscriptUiState.resolve(
chat =
ChatUiState(
messages = listOf(live),
currentSessionId = "session",
isAgentTyping = true,
isLoading = true,
isLoadingOlder = true,
hasOlderMessages = true,
clarifyRequest = clarify,
isCompressing = true,
savingAttachmentPath = "server-path",
),
timeline = ChatTimelineState(),
streaming = stream,
savingAttachmentPath = "local-path",
speakingMessageId = "live",
)
assertEquals(listOf(live), result.messages)
assertSame(stream, result.streamingState)
assertEquals("session", result.pagingSessionId)
assertTrue(result.isAgentTyping)
assertTrue(result.isLoading)
assertTrue(result.isLoadingOlder)
assertTrue(result.hasOlderMessages)
assertSame(clarify, result.clarifyRequest)
assertTrue(result.isCompressing)
assertEquals("local-path", result.savingAttachmentPath)
assertEquals("live", result.speakingMessageId)
}
@Test
fun `historical state isolates live tail prompts loading and compression`() {
val result =
TranscriptUiState.resolve(
chat =
ChatUiState(
messages = listOf(live),
currentSessionId = "session",
isAgentTyping = true,
isLoading = true,
isLoadingOlder = true,
hasOlderMessages = true,
clarifyRequest = clarify,
isCompressing = true,
compressionStatus = "compressing",
),
timeline = ChatTimelineState(historyMessages = listOf(historical), historyAnchorRowId = 42),
streaming = StreamingState(streamingMessage = live),
savingAttachmentPath = null,
speakingMessageId = null,
)
assertEquals(listOf(historical), result.messages)
assertNull(result.streamingState.streamingMessage)
assertEquals("session:history:42", result.pagingSessionId)
assertFalse(result.isAgentTyping)
assertFalse(result.isLoading)
assertFalse(result.isLoadingOlder)
assertFalse(result.hasOlderMessages)
assertNull(result.clarifyRequest)
assertFalse(result.isCompressing)
assertNull(result.compressionStatus)
}
}