chore: upgrade ktlint to 1.8.0 and format codebase (#1024)

Co-authored-by: M57 <hy4ri@users.noreply.github.com>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-08-31 12:27:40 +03:00
1 parent 3f3b853758
commit cfe3e736fd
66 files changed
+1287 -541

No files matched your search

@@ -20,7 +20,13 @@ class KanbanEventsClientTest {
assertEquals("t-1", event.taskId)
assertEquals("r-9", event.runId)
assertEquals("status", event.kind)
assertEquals("done", event.payload?.get("status")?.jsonPrimitive?.content)
assertEquals(
"done",
event.payload
?.get("status")
?.jsonPrimitive
?.content,
)
assertEquals(1710000000L, event.createdAt)
}
@@ -143,12 +143,16 @@ class BotsViewModelTest {
// Toggle show hidden (sorted: default (active), scout (worker active now), reviewer (canonical last_active))
viewModel.toggleShowHidden()
val displayedWithHidden = viewModel.uiState.value.displayProfiles.map { it.name }
val displayedWithHidden =
viewModel.uiState.value.displayProfiles
.map { it.name }
assertEquals(listOf("default", "reviewer", "scout"), displayedWithHidden)
// Search filter
viewModel.setSearchQuery("arxiv")
val searchResults = viewModel.uiState.value.displayProfiles.map { it.name }
val searchResults =
viewModel.uiState.value.displayProfiles
.map { it.name }
assertEquals(listOf("scout"), searchResults)
}
@@ -230,8 +234,16 @@ class BotsViewModelTest {
advanceUntilIdle()
assertEquals(2, viewModel.uiState.value.allGroups.size)
assertEquals("Dream Team", viewModel.uiState.value.allGroups[0].name)
assertEquals(2, viewModel.uiState.value.allGroups[0].members.size)
assertEquals(
"Dream Team",
viewModel.uiState.value.allGroups[0]
.name,
)
assertEquals(
2,
viewModel.uiState.value.allGroups[0]
.members.size,
)
var success = false
viewModel.disbandGroupChat("Dream Team") { success = true }
@@ -205,7 +205,9 @@ class GroupChatViewModelTest {
testScheduler.runCurrent()
// State should contain user message
val userMsg = viewModel.uiState.value.messages.first()
val userMsg =
viewModel.uiState.value.messages
.first()
assertTrue(userMsg.isUser)
assertEquals("@scout what's the status?", userMsg.text)
@@ -213,7 +215,9 @@ class GroupChatViewModelTest {
eventsFlow.emit(WsEvent.MessageToken(token = "Building ", sessionId = "session-scout-1"))
testScheduler.runCurrent()
val streamingMsg = viewModel.uiState.value.messages.last()
val streamingMsg =
viewModel.uiState.value.messages
.last()
assertFalse(streamingMsg.isUser)
assertEquals("scout", streamingMsg.senderName)
assertEquals("Building ", streamingMsg.text)
@@ -222,7 +226,9 @@ class GroupChatViewModelTest {
eventsFlow.emit(WsEvent.MessageToken(token = "the APK...", sessionId = "session-scout-1"))
testScheduler.runCurrent()
val updatedStreaming = viewModel.uiState.value.messages.last()
val updatedStreaming =
viewModel.uiState.value.messages
.last()
assertEquals("Building the APK...", updatedStreaming.text)
// Complete turn
@@ -285,7 +291,11 @@ class GroupChatViewModelTest {
testScheduler.runCurrent()
assertEquals(2, viewModel.uiState.value.messages.size)
assertEquals("scout", viewModel.uiState.value.messages[1].senderName)
assertEquals(
"scout",
viewModel.uiState.value.messages[1]
.senderName,
)
// Continuation pass triggers coder
eventsFlow.emit(
@@ -345,7 +355,11 @@ class GroupChatViewModelTest {
// Because lease is held by desktop_client_99, mobile only posts the user message and yields
assertEquals(1, viewModel.uiState.value.messages.size)
assertTrue(viewModel.uiState.value.messages.first().isUser)
assertTrue(
viewModel.uiState.value.messages
.first()
.isUser,
)
}
@Test
@@ -452,7 +466,9 @@ class GroupChatViewModelTest {
)
testScheduler.runCurrent()
val msgWithTool = viewModel.uiState.value.messages.last()
val msgWithTool =
viewModel.uiState.value.messages
.last()
assertEquals(1, msgWithTool.toolCalls.size)
val toolCall = msgWithTool.toolCalls.first()
assertEquals("terminal", toolCall.name)
@@ -469,7 +485,9 @@ class GroupChatViewModelTest {
)
testScheduler.runCurrent()
val msgAfterToolDone = viewModel.uiState.value.messages.last()
val msgAfterToolDone =
viewModel.uiState.value.messages
.last()
assertFalse(msgAfterToolDone.toolCalls.first().isRunning)
// Complete message
@@ -481,7 +499,9 @@ class GroupChatViewModelTest {
)
testScheduler.runCurrent()
val finalMsg = viewModel.uiState.value.messages.last()
val finalMsg =
viewModel.uiState.value.messages
.last()
assertEquals("Current time is Sat Aug 29", finalMsg.text)
assertEquals(1, finalMsg.toolCalls.size)
assertFalse(finalMsg.toolCalls.first().isRunning)
@@ -158,7 +158,13 @@ class ChatInputPolicyTest {
@Test
fun applyMention_insertsHandleAndAdvancesCursor() {
val initial = TextFieldValue("ask @sc", selection = androidx.compose.ui.text.TextRange(7))
val initial =
TextFieldValue(
"ask @sc",
selection =
androidx.compose.ui.text
.TextRange(7),
)
val updated = ChatInputPolicy.applyMention(initial, "scout")
assertEquals("ask @scout ", updated.text)
assertEquals(11, updated.selection.end)
@@ -369,12 +369,13 @@ class ChatToolDedupeTest {
var stream = StreamingState()
stream = ChatWsEventReducer.reduce(s, stream, WsEvent.MessageStart("session-1"), "session-1").streamingState
stream =
ChatWsEventReducer.reduce(
s,
stream,
WsEvent.ReasoningDelta("thinking about meow", "session-1"),
"session-1",
).streamingState
ChatWsEventReducer
.reduce(
s,
stream,
WsEvent.ReasoningDelta("thinking about meow", "session-1"),
"session-1",
).streamingState
var r =
ChatWsEventReducer.reduce(
s,
@@ -159,7 +159,12 @@ class ChatUpdateCommandTest {
advanceUntilIdle()
assertTrue("confirm dialog should open", vm.uiState.value.updateConfirmOpen)
assertEquals("/update", vm.uiState.value.messages.last().content)
assertEquals(
"/update",
vm.uiState.value.messages
.last()
.content,
)
// The whole point of the fix: NOTHING goes over the wire.
verify(exactly = 0) {
@@ -513,7 +513,10 @@ class ChatViewModelTest {
// A blocklisted command can never dispatch — it must not climb
// the autocomplete ranking either.
assertTrue(viewModel.uiState.value.slashUsageCounts.isEmpty())
assertTrue(
viewModel.uiState.value.slashUsageCounts
.isEmpty(),
)
verify(exactly = 0) {
HermesWsClient.request(WsMethods.COMMAND_DISPATCH, any(), any())
}
@@ -1033,7 +1036,10 @@ class ChatViewModelTest {
viewModel.uiState.value.messages[1]
.role,
)
assertFalse(viewModel.uiState.value.messages[1].isStreaming)
assertFalse(
viewModel.uiState.value.messages[1]
.isStreaming,
)
assertEquals(
MessageRole.TOOL,
viewModel.uiState.value.messages[2]
@@ -1041,7 +1047,10 @@ class ChatViewModelTest {
)
assertNull(viewModel.streamingState.value.streamingMessage)
assertEquals(
listOf(viewModel.uiState.value.messages[1].id),
listOf(
viewModel.uiState.value.messages[1]
.id,
),
viewModel.streamingState.value.sealedOrphanIds,
)
@@ -1050,7 +1059,8 @@ class ChatViewModelTest {
mockEventsFlow.emit(WsEvent.MessageComplete("Calculating sum = 4", sessionId))
advanceUntilIdle()
val assistant =
viewModel.uiState.value.messages.filter { it.role == MessageRole.ASSISTANT }
viewModel.uiState.value.messages
.filter { it.role == MessageRole.ASSISTANT }
assertEquals(2, assistant.size)
assertEquals("Calculating sum", assistant[0].content)
assertEquals(" = 4", assistant[1].content)
@@ -1573,7 +1583,8 @@ class ChatViewModelTest {
every { AuthManager.getBaseUrl() } returns "http://test.local/"
coEvery { mockApi.getSessions(any(), any(), any()) } returns
retrofit2.Response.success(
com.m57.hermescontrol.data.model.SessionListResponse(sessions = emptyList(), total = 0),
com.m57.hermescontrol.data.model
.SessionListResponse(sessions = emptyList(), total = 0),
)
coEvery {
mockApi.getSessionMessages(
@@ -1623,7 +1634,8 @@ class ChatViewModelTest {
val empty =
retrofit2.Response.success(
com.m57.hermescontrol.data.model.SessionMessagesResponse(messages = emptyList()),
com.m57.hermescontrol.data.model
.SessionMessagesResponse(messages = emptyList()),
)
messagesA.complete(empty)
messagesB.complete(empty)
@@ -1641,7 +1653,8 @@ class ChatViewModelTest {
every { AuthManager.getBaseUrl() } returns "http://test.local/"
coEvery { mockApi.getSessions(any(), any(), any()) } returns
retrofit2.Response.success(
com.m57.hermescontrol.data.model.SessionListResponse(sessions = emptyList(), total = 0),
com.m57.hermescontrol.data.model
.SessionListResponse(sessions = emptyList(), total = 0),
)
coEvery {
mockApi.getSessionMessages(
@@ -1698,7 +1711,11 @@ class ChatViewModelTest {
advanceUntilIdle()
assertEquals("session-b", viewModel.uiState.value.currentSessionId)
assertEquals(listOf("message-b"), viewModel.uiState.value.messages.map { it.content })
assertEquals(
listOf("message-b"),
viewModel.uiState.value.messages
.map { it.content },
)
}
@Test
@@ -1927,7 +1944,8 @@ class ChatViewModelTest {
sessionIds.forEach { sessionId ->
coEvery { mockApi.getSessionMessages(sessionId, any(), any(), any(), any()) } returns
retrofit2.Response.success(
com.m57.hermescontrol.data.model.SessionMessagesResponse(messages = emptyList()),
com.m57.hermescontrol.data.model
.SessionMessagesResponse(messages = emptyList()),
)
}
}
@@ -1956,7 +1974,11 @@ class ChatViewModelTest {
// Warm cache painted and survived the exhausted retry cycle — the
// screen never went blank, and the user gets history + an error.
assertEquals(1, viewModel.uiState.value.messages.size)
assertEquals("Cached hello", viewModel.uiState.value.messages[0].content)
assertEquals(
"Cached hello",
viewModel.uiState.value.messages[0]
.content,
)
assertFalse(viewModel.uiState.value.isLoading)
assertNotNull("resumeError must be set after retries exhaust", viewModel.uiState.value.resumeError)
assertFalse(viewModel.uiState.value.isResumeRetrying)
@@ -2305,7 +2327,8 @@ class ChatViewModelTest {
assertEquals("session-new", viewModel.uiState.value.currentSessionId)
assertTrue(
"recovery notice must be visible",
viewModel.uiState.value.messages.any { it.content.contains("no longer available") },
viewModel.uiState.value.messages
.any { it.content.contains("no longer available") },
)
}
@@ -2328,7 +2351,8 @@ class ChatViewModelTest {
ApiClient.hermesApi.getSessionMessages(capture(fetchedSessions), any(), any(), any(), any())
} returns
retrofit2.Response.success(
com.m57.hermescontrol.data.model.SessionMessagesResponse(messages = emptyList()),
com.m57.hermescontrol.data.model
.SessionMessagesResponse(messages = emptyList()),
)
// /fork sends session.branch keyed on the runtime id.
@@ -2379,7 +2403,8 @@ class ChatViewModelTest {
)
} else {
retrofit2.Response.success(
com.m57.hermescontrol.data.model.SessionMessagesResponse(messages = emptyList()),
com.m57.hermescontrol.data.model
.SessionMessagesResponse(messages = emptyList()),
)
}
}
@@ -3301,7 +3326,12 @@ class ChatViewModelTest {
assertTrue(viewModel.uiState.value.hasOlderMessages)
assertEquals(150, viewModel.uiState.value.messages.size)
// Stable keys come from the server row id, not the from-end position.
assertEquals("rest-session-456-150", viewModel.uiState.value.messages.last().id)
assertEquals(
"rest-session-456-150",
viewModel.uiState.value.messages
.last()
.id,
)
}
@Test
@@ -3444,7 +3474,10 @@ class ChatViewModelTest {
// 160 rows: the 10 new ones appended, the existing 150 kept —
// no duplicates, no dropped newest copy (stable row-id keys).
assertEquals(160, viewModel.uiState.value.messages.size)
assertTrue(viewModel.uiState.value.messages.any { it.id == "rest-session-456-160" })
assertTrue(
viewModel.uiState.value.messages
.any { it.id == "rest-session-456-160" },
)
}
// ── Attachment open (issue #724) ─────────────────────────────────────
@@ -3506,15 +3539,17 @@ class ChatViewModelTest {
runTest {
mockkObject(GatewayFileClient)
val cacheDir =
java.io.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
java.io
.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
every { app.cacheDir } returns cacheDir
every { app.applicationContext } returns app
every { app.getApplicationContext() } returns app
val file =
java.io.File(System.getProperty("java.io.tmpdir"), "note_${System.nanoTime()}.txt")
java.io
.File(System.getProperty("java.io.tmpdir"), "note_${System.nanoTime()}.txt")
.apply { writeText("downloaded") }
coEvery { GatewayFileClient.fetch("/tmp/note.txt", any()) } returns
GatewayFileResult.Success(GatewayFile("note.txt", "text/plain", file))
@@ -3552,10 +3587,11 @@ class ChatViewModelTest {
runTest {
mockkObject(GatewayFileClient)
val cacheDir =
java.io.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
java.io
.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
every { app.cacheDir } returns cacheDir
every { app.applicationContext } returns app
every { app.getApplicationContext() } returns app
@@ -3590,10 +3626,11 @@ class ChatViewModelTest {
runTest {
mockkObject(GatewayFileClient)
val cacheDir =
java.io.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
java.io
.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
every { app.cacheDir } returns cacheDir
every { app.applicationContext } returns app
every { app.getApplicationContext() } returns app
@@ -3606,7 +3643,8 @@ class ChatViewModelTest {
GatewayFile(
"report.pdf",
"application/pdf",
java.io.File(System.getProperty("java.io.tmpdir"), "report_${System.nanoTime()}.pdf")
java.io
.File(System.getProperty("java.io.tmpdir"), "report_${System.nanoTime()}.pdf")
.apply { writeBytes(byteArrayOf(1)) },
),
)
@@ -3625,7 +3663,11 @@ class ChatViewModelTest {
verify(exactly = 0) { resolver.delete(any(), any(), any()) }
assertNull(viewModel.uiState.value.savingAttachmentPath)
assertTrue(viewModel.uiState.value.openError.orEmpty().startsWith("Could not save"))
assertTrue(
viewModel.uiState.value.openError
.orEmpty()
.startsWith("Could not save"),
)
}
@Test
@@ -3633,10 +3675,11 @@ class ChatViewModelTest {
runTest {
mockkObject(GatewayFileClient)
val cacheDir =
java.io.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
java.io
.File(
System.getProperty("java.io.tmpdir"),
"gw_save_${System.nanoTime()}",
).apply { mkdirs() }
every { app.cacheDir } returns cacheDir
every { app.applicationContext } returns app
every { app.getApplicationContext() } returns app
@@ -3675,10 +3718,11 @@ class ChatViewModelTest {
runTest {
mockkObject(GatewayFileClient)
val cacheDir =
java.io.File(
System.getProperty("java.io.tmpdir"),
"gw_open_${System.nanoTime()}",
).apply { mkdirs() }
java.io
.File(
System.getProperty("java.io.tmpdir"),
"gw_open_${System.nanoTime()}",
).apply { mkdirs() }
every { app.cacheDir } returns cacheDir
every { app.applicationContext } returns app
every { app.getApplicationContext() } returns app
@@ -3711,10 +3755,11 @@ class ChatViewModelTest {
runTest {
mockkObject(GatewayFileClient)
val cacheDir =
java.io.File(
System.getProperty("java.io.tmpdir"),
"gw_open_${System.nanoTime()}",
).apply { mkdirs() }
java.io
.File(
System.getProperty("java.io.tmpdir"),
"gw_open_${System.nanoTime()}",
).apply { mkdirs() }
every { app.cacheDir } returns cacheDir
every { app.applicationContext } returns app
every { app.getApplicationContext() } returns app
@@ -3743,7 +3788,11 @@ class ChatViewModelTest {
// …and cleared in all outcomes.
assertNull(vm.uiState.value.openingAttachmentPath)
assertTrue(vm.uiState.value.openError.orEmpty().contains("big.pdf"))
assertTrue(
vm.uiState.value.openError
.orEmpty()
.contains("big.pdf"),
)
}
@Test
@@ -3763,7 +3812,10 @@ class ChatViewModelTest {
advanceUntilIdle()
// Optimistic UI state has the user message immediately
assertTrue(vm.uiState.value.messages.any { it.content == "My super important long prompt" })
assertTrue(
vm.uiState.value.messages
.any { it.content == "My super important long prompt" },
)
assertTrue(vm.uiState.value.isAgentTyping)
// But wsClient.sendMessage not dispatched yet because session_id was null
assertTrue(sentPrompts.isEmpty())
@@ -3781,7 +3833,10 @@ class ChatViewModelTest {
// Prompt should be dispatched automatically!
assertEquals(listOf("My super important long prompt"), sentPrompts)
assertEquals("session-storage-969", vm.uiState.value.currentSessionId)
assertTrue(vm.uiState.value.messages.any { it.content == "My super important long prompt" })
assertTrue(
vm.uiState.value.messages
.any { it.content == "My super important long prompt" },
)
}
@Test
@@ -3806,7 +3861,10 @@ class ChatViewModelTest {
advanceUntilIdle()
// Optimistic message in UI
assertTrue(vm.uiState.value.messages.any { it.content == "Prompt typed during reconnect" })
assertTrue(
vm.uiState.value.messages
.any { it.content == "Prompt typed during reconnect" },
)
// Complete the new SESSION_CREATE RPC
mockEventsFlow.emit(
@@ -3842,6 +3900,9 @@ class ChatViewModelTest {
assertFalse(vm.uiState.value.isAgentTyping)
assertNotNull(vm.uiState.value.errorMessage)
assertTrue(vm.uiState.value.errorMessage!!.contains("Backend exploded"))
assertTrue(
vm.uiState.value.errorMessage!!
.contains("Backend exploded"),
)
}
}
@@ -421,7 +421,12 @@ class ChatWsEventReducerTest {
)
assertEquals(1, result.state.messages.size)
assertEquals("complete", result.state.messages.single().content)
assertEquals(
"complete",
result.state.messages
.single()
.content,
)
}
@Test
@@ -556,12 +561,13 @@ class ChatWsEventReducerTest {
var reasoningState = start.streamingState
for (token in reasoningTokens) {
reasoningState =
ChatWsEventReducer.reduce(
state,
reasoningState,
WsEvent.ReasoningDelta(token, "session-1"),
"session-1",
).streamingState
ChatWsEventReducer
.reduce(
state,
reasoningState,
WsEvent.ReasoningDelta(token, "session-1"),
"session-1",
).streamingState
}
assertEquals(true, reasoningState.isReasoning)
assertEquals("The user just said \"run echo hi\". Let me run it.", reasoningState.reasoningText)
@@ -578,8 +584,18 @@ class ChatWsEventReducerTest {
"session-1",
)
assertEquals(1, result.state.messages.size)
assertEquals(MessageRole.TOOL, result.state.messages.first().role)
assertEquals("terminal", result.state.messages.first().toolName)
assertEquals(
MessageRole.TOOL,
result.state.messages
.first()
.role,
)
assertEquals(
"terminal",
result.state.messages
.first()
.toolName,
)
// Issue #771: streaming message + reasoning survive the tool call
assertEquals(true, result.streamingState.streamingMessage?.isStreaming)
assertEquals("The user just said \"run echo hi\". Let me run it.", result.streamingState.reasoningText)
@@ -592,7 +608,12 @@ class ChatWsEventReducerTest {
WsEvent.ToolComplete("terminal", mapOf("output" to "hi"), "session-1"),
"session-1",
)
assertEquals(ToolStatus.COMPLETED, result.state.messages.first().toolStatus)
assertEquals(
ToolStatus.COMPLETED,
result.state.messages
.first()
.toolStatus,
)
// 5. reasoning.available — authoritative full-trace fill
val fullReasoning = "The user just said \"run echo hi\". That's a simple terminal command. Let me just run it."
@@ -683,7 +704,12 @@ class ChatWsEventReducerTest {
"session-1",
)
assertEquals("payload reasoning", result.state.messages.last().reasoningText)
assertEquals(
"payload reasoning",
result.state.messages
.last()
.reasoningText,
)
}
@Test
@@ -223,7 +223,8 @@ class SlashCommandDispatchRpcTest {
// The optimistic bubble shows the queued TEXT (prefix stripped) so
// the transcript sync dedupes it against the server echo.
val userBubble =
vm.uiState.value.messages.lastOrNull { it.role == MessageRole.USER }
vm.uiState.value.messages
.lastOrNull { it.role == MessageRole.USER }
assertEquals("do the thing", userBubble?.content)
}
@@ -247,7 +248,9 @@ class SlashCommandDispatchRpcTest {
vm.sendMessage("/queue")
advanceUntilIdle()
val last = vm.uiState.value.messages.lastOrNull()
val last =
vm.uiState.value.messages
.lastOrNull()
assertEquals("usage: /queue <prompt>", last?.content)
assertEquals(0, sendCalls)
assertTrue(
@@ -628,7 +631,9 @@ class SlashCommandDispatchRpcTest {
vm.sendMessage("/focus on")
advanceUntilIdle()
val last = vm.uiState.value.messages.lastOrNull()
val last =
vm.uiState.value.messages
.lastOrNull()
assertEquals("Focus view: ON (tool progress pinned off)", last?.content)
}
}
@@ -202,7 +202,10 @@ class ActionProgressControllerTest {
tick()
assertEquals(ActionProgressPhase.SUCCEEDED, controller.state.value.phase)
assertTrue(controller.state.value.trailingLines.isEmpty())
assertTrue(
controller.state.value.trailingLines
.isEmpty(),
)
controller.pushTrailingLines(listOf("Outcome: success", "Version: 0.20.4 → 0.20.5"))
@@ -216,7 +219,10 @@ class ActionProgressControllerTest {
val controller = controller()
controller.open()
controller.pushTrailingLines(emptyList())
assertTrue(controller.state.value.trailingLines.isEmpty())
assertTrue(
controller.state.value.trailingLines
.isEmpty(),
)
}
@Test
@@ -204,7 +204,9 @@ class CronJobsViewModelTest {
vm.saveEditor()
settle()
val toast = vm.uiState.value.editorState.toastMessage.orEmpty()
val toast =
vm.uiState.value.editorState.toastMessage
.orEmpty()
assertTrue(toast, toast.contains("invalid time '25:00'"))
assertFalse(vm.uiState.value.editorState.isSaving)
}
@@ -394,7 +396,11 @@ class CronJobsViewModelTest {
coVerify { mockApi.deleteCronJob("j5") }
assertTrue(vm.uiState.value.editorState.isOpen)
assertTrue(vm.uiState.value.editorState.toastMessage.orEmpty().contains("Failed to save"))
assertTrue(
vm.uiState.value.editorState.toastMessage
.orEmpty()
.contains("Failed to save"),
)
}
@Test
@@ -549,7 +555,9 @@ class CronJobsViewModelTest {
vm.loadCronJobs()
settle()
val job = vm.uiState.value.jobs.first()
val job =
vm.uiState.value.jobs
.first()
assertNotNull(job.last_fire_error)
assertEquals("gateway unreachable", job.last_fire_error?.detail)
assertEquals("2026-08-18T09:00:00", job.last_fire_error?.at)
@@ -222,7 +222,12 @@ class KanbanViewModelTest {
coEvery { mockApi.updateKanbanTask(any(), any()) } returns Response.success(Unit)
vm.moveTask(KanbanTask(id = "t1", title = "Task 1", status = "todo"), KanbanTaskAction.READY)
settle()
assertEquals("ready", vm.uiState.value.tasks.first { it.id == "t1" }.status)
assertEquals(
"ready",
vm.uiState.value.tasks
.first { it.id == "t1" }
.status,
)
coVerify { mockApi.updateKanbanTask("t1", mapOf("status" to "ready")) }
}
@@ -234,8 +239,16 @@ class KanbanViewModelTest {
coEvery { mockApi.updateKanbanTask(any(), any()) } returns Response.error(409, "".toResponseBody())
vm.moveTask(KanbanTask(id = "t1", title = "Task 1", status = "todo"), KanbanTaskAction.READY)
settle()
assertEquals("todo", vm.uiState.value.tasks.first { it.id == "t1" }.status)
assertTrue(vm.uiState.value.toastMessage?.contains("Move failed") == true)
assertEquals(
"todo",
vm.uiState.value.tasks
.first { it.id == "t1" }
.status,
)
assertTrue(
vm.uiState.value.toastMessage
?.contains("Move failed") == true,
)
}
@Test
@@ -81,7 +81,9 @@ class MemoryViewModelTest {
@Test
fun `load also fetches learning graph for self-improvement section`() {
val graph = com.m57.hermescontrol.data.model.LearningGraphResponse(nodes = emptyList())
val graph =
com.m57.hermescontrol.data.model
.LearningGraphResponse(nodes = emptyList())
coEvery { mockApi.getLearningGraph() } returns Response.success(graph)
val vm = MemoryViewModel()
vm.load()
@@ -100,7 +102,11 @@ class MemoryViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
assertNull(vm.uiState.value.memory)
assertTrue(vm.uiState.value.errorMessage.orEmpty().isNotBlank())
assertTrue(
vm.uiState.value.errorMessage
.orEmpty()
.isNotBlank(),
)
}
@Test
@@ -127,7 +133,11 @@ class MemoryViewModelTest {
vm.resetMemory("memory")
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage.orEmpty().contains("Failed to reset memory"))
assertTrue(
vm.uiState.value.toastMessage
.orEmpty()
.contains("Failed to reset memory"),
)
assertNull(vm.uiState.value.resetting)
}
}
@@ -97,9 +97,21 @@ class PairingViewModelTest {
assertFalse(state.isLoading)
assertNull(state.errorMessage)
assertEquals(1, state.pairing?.pending?.size)
assertEquals("bob", state.pairing?.pending?.first()?.userName)
assertEquals(
"bob",
state.pairing
?.pending
?.first()
?.userName,
)
assertEquals(1, state.pairing?.approved?.size)
assertEquals("alice", state.pairing?.approved?.first()?.userName)
assertEquals(
"alice",
state.pairing
?.approved
?.first()
?.userName,
)
}
@Test
@@ -148,7 +160,10 @@ class PairingViewModelTest {
assertEquals("discord", requestSlot.captured.platform)
assertEquals("u2", requestSlot.captured.userId)
coVerify(exactly = 1) { mockApi.getPairing() }
assertTrue(vm.uiState.value.toastMessage?.contains("u2") == true)
assertTrue(
vm.uiState.value.toastMessage
?.contains("u2") == true,
)
}
@Test
@@ -229,7 +229,11 @@ class MemoryProviderDetailViewModelTest {
vm.saveConfig()
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage.orEmpty().contains("Failed to save"))
assertTrue(
vm.uiState.value.toastMessage
.orEmpty()
.contains("Failed to save"),
)
}
@Test
@@ -242,7 +246,13 @@ class MemoryProviderDetailViewModelTest {
val state = vm.uiState.value
assertEquals("Setup completed", state.toastMessage)
assertTrue(state.setupResult?.ok == true)
assertEquals("installed", state.setupResult?.results?.first()?.status)
assertEquals(
"installed",
state.setupResult
?.results
?.first()
?.status,
)
}
@Test
@@ -253,7 +263,11 @@ class MemoryProviderDetailViewModelTest {
vm.runSetup()
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage.orEmpty().contains("Setup failed"))
assertTrue(
vm.uiState.value.toastMessage
.orEmpty()
.contains("Setup failed"),
)
}
@Test
@@ -136,7 +136,10 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
coVerify { mockApi.renameProfile("work", RenameProfileRequest("play")) }
assertTrue(vm.uiState.value.toastMessage!!.contains("renamed"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("renamed"),
)
// reload happened
coVerify { mockApi.getProfiles() }
}
@@ -162,7 +165,10 @@ class ProfilesViewModelTest {
vm.renameProfile("work", "play")
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage!!.contains("Failed to rename"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Failed to rename"),
)
assertFalse(vm.uiState.value.isLoading)
}
@@ -175,7 +181,10 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
coVerify { mockApi.deleteProfile("work") }
assertTrue(vm.uiState.value.toastMessage!!.contains("deleted"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("deleted"),
)
coVerify { mockApi.getProfiles() }
}
@@ -187,7 +196,10 @@ class ProfilesViewModelTest {
vm.deleteProfile("work")
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage!!.contains("Failed to delete"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Failed to delete"),
)
}
@Test
@@ -200,7 +212,10 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
coVerify { mockApi.describeProfileAuto("work", ProfileDescribeAutoRequest(overwrite = true)) }
assertTrue(vm.uiState.value.toastMessage!!.contains("Auto-described"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Auto-described"),
)
assertFalse(vm.uiState.value.isAutoDescribing)
coVerify { mockApi.getProfiles() }
}
@@ -214,7 +229,10 @@ class ProfilesViewModelTest {
vm.autoDescribeProfile("work")
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage!!.contains("no aux client configured"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("no aux client configured"),
)
assertFalse(vm.uiState.value.isAutoDescribing)
}
@@ -241,7 +259,10 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
assertNull(vm.uiState.value.setupCommand)
assertTrue(vm.uiState.value.toastMessage!!.contains("Failed to fetch setup command"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Failed to fetch setup command"),
)
}
@Test
@@ -265,7 +286,11 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
assertEquals(1, vm.uiState.value.modelProviders.size)
assertEquals("gpt-4o", vm.uiState.value.modelProviders[0].models!![0])
assertEquals(
"gpt-4o",
vm.uiState.value.modelProviders[0]
.models!![0],
)
assertEquals(listOf(PinnedModel("openai", "gpt-4")), vm.uiState.value.modelPickerPinned)
assertFalse(vm.uiState.value.isLoadingBuilderData)
}
@@ -278,7 +303,10 @@ class ProfilesViewModelTest {
vm.loadModelOptions()
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage!!.contains("Failed to load models"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Failed to load models"),
)
assertNull(vm.uiState.value.errorMessage)
assertFalse(vm.uiState.value.isLoadingBuilderData)
}
@@ -292,7 +320,10 @@ class ProfilesViewModelTest {
assertEquals(listOf(PinnedModel("openai", "gpt-4")), storedPinnedModels)
vm.togglePinModel("openai", "gpt-4")
assertTrue(vm.uiState.value.modelPickerPinned.isEmpty())
assertTrue(
vm.uiState.value.modelPickerPinned
.isEmpty(),
)
assertTrue(storedPinnedModels.isEmpty())
}
@@ -305,7 +336,10 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
coVerify { ProfileSwitchCoordinator.switchProfile("work") }
assertTrue(vm.uiState.value.toastMessage!!.contains("Switched to profile work"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Switched to profile work"),
)
coVerify { mockApi.getProfiles() }
}
@@ -320,7 +354,10 @@ class ProfilesViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
assertNull(vm.uiState.value.activeProfileName)
assertTrue(vm.uiState.value.toastMessage!!.contains("Failed to switch profile"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Failed to switch profile"),
)
}
@Test
@@ -339,7 +376,10 @@ class ProfilesViewModelTest {
),
)
}
assertTrue(vm.uiState.value.toastMessage!!.contains("cloned successfully"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("cloned successfully"),
)
assertFalse(vm.uiState.value.isLoading)
coVerify { mockApi.getProfiles() }
}
@@ -352,7 +392,10 @@ class ProfilesViewModelTest {
vm.cloneProfile("default", "dev-copy")
testDispatcher.scheduler.advanceUntilIdle()
assertTrue(vm.uiState.value.toastMessage!!.contains("Failed to clone profile"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Failed to clone profile"),
)
assertFalse(vm.uiState.value.isLoading)
}
@@ -378,12 +421,20 @@ class ProfilesViewModelTest {
vm.hideProfile("secret-bot")
assertEquals(listOf("secret-bot"), storedHiddenProfiles)
assertTrue(vm.uiState.value.hasHiddenProfiles)
assertEquals(listOf("default"), vm.uiState.value.displayProfiles.map { it.name })
assertEquals(
listOf("default"),
vm.uiState.value.displayProfiles
.map { it.name },
)
// Toggle show hidden
vm.toggleShowHidden()
assertTrue(vm.uiState.value.showHidden)
assertEquals(listOf("default", "secret-bot"), vm.uiState.value.displayProfiles.map { it.name })
assertEquals(
listOf("default", "secret-bot"),
vm.uiState.value.displayProfiles
.map { it.name },
)
// Unhide
vm.unhideProfile("secret-bot")
@@ -116,7 +116,11 @@ class SessionsViewModelTest {
vm.loadSessions()
testDispatcher.scheduler.advanceUntilIdle()
assertEquals(listOf("s-1", "s-2"), vm.uiState.value.sessions.map { it.id })
assertEquals(
listOf("s-1", "s-2"),
vm.uiState.value.sessions
.map { it.id },
)
assertEquals(2, vm.uiState.value.total)
assertFalse(vm.uiState.value.hasMore)
assertFalse(vm.uiState.value.isLoadingMore)
@@ -206,7 +210,11 @@ class SessionsViewModelTest {
vm.loadSessions()
testDispatcher.scheduler.advanceUntilIdle()
assertEquals(listOf("old-pinned", "recent"), vm.uiState.value.sessions.map { it.id })
assertEquals(
listOf("old-pinned", "recent"),
vm.uiState.value.sessions
.map { it.id },
)
}
@Test
@@ -226,8 +234,17 @@ class SessionsViewModelTest {
vm.togglePin("s-2")
testDispatcher.scheduler.advanceUntilIdle()
assertEquals(listOf("s-2", "s-1"), vm.uiState.value.sessions.map { it.id })
assertEquals(true, vm.uiState.value.sessions.first().pinned)
assertEquals(
listOf("s-2", "s-1"),
vm.uiState.value.sessions
.map { it.id },
)
assertEquals(
true,
vm.uiState.value.sessions
.first()
.pinned,
)
assertEquals("Session pinned", vm.uiState.value.toastMessage)
}
@@ -249,9 +266,19 @@ class SessionsViewModelTest {
vm.togglePin("s-2")
testDispatcher.scheduler.advanceUntilIdle()
assertEquals(listOf("s-1", "s-2"), vm.uiState.value.sessions.map { it.id })
assertFalse(vm.uiState.value.sessions[1].pinned ?: false)
assertEquals(
listOf("s-1", "s-2"),
vm.uiState.value.sessions
.map { it.id },
)
assertFalse(
vm.uiState.value.sessions[1]
.pinned ?: false,
)
assertNotNull(vm.uiState.value.toastMessage)
assertTrue(vm.uiState.value.toastMessage!!.contains("Pin failed"))
assertTrue(
vm.uiState.value.toastMessage!!
.contains("Pin failed"),
)
}
}
@@ -126,7 +126,11 @@ class SkillsViewModelTest {
vm.loadHubSources()
settle()
assertEquals(true, vm.uiState.value.hubSourcesError?.contains("Failed to load hub sources"))
assertEquals(
true,
vm.uiState.value.hubSourcesError
?.contains("Failed to load hub sources"),
)
assertEquals(false, vm.uiState.value.isHubSourcesLoading)
}
@@ -165,7 +165,15 @@ class ToolsetDetailViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
val state = vm.uiState.value
assertEquals(true, state.config?.providers?.get(1)?.envVars?.first { it.key == "KEY_B" }?.isSet)
assertEquals(
true,
state.config
?.providers
?.get(1)
?.envVars
?.first { it.key == "KEY_B" }
?.isSet,
)
assertEquals("Saved KEY_B", state.toastMessage)
assertNull(state.savingEnvKey)
}
@@ -196,7 +204,15 @@ class ToolsetDetailViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
val state = vm.uiState.value
assertEquals(false, state.config?.providers?.get(1)?.envVars?.first { it.key == "KEY_B" }?.isSet)
assertEquals(
false,
state.config
?.providers
?.get(1)
?.envVars
?.first { it.key == "KEY_B" }
?.isSet,
)
assertFalse(state.revealedValues.containsKey("KEY_B"))
assertEquals("Cleared KEY_B", state.toastMessage)
assertNull(state.deletingEnvKey)
@@ -213,7 +229,10 @@ class ToolsetDetailViewModelTest {
assertEquals("sekret", vm.uiState.value.revealedValues["KEY_B"])
vm.hideEnvVar("KEY_B")
assertFalse(vm.uiState.value.revealedValues.containsKey("KEY_B"))
assertFalse(
vm.uiState.value.revealedValues
.containsKey("KEY_B"),
)
}
@Test
@@ -316,7 +335,10 @@ class ToolsetDetailViewModelTest {
testDispatcher.scheduler.advanceUntilIdle()
assertNull(vm.uiState.value.postSetup)
assertTrue(vm.uiState.value.toastMessage?.contains("spawn rejected") == true)
assertTrue(
vm.uiState.value.toastMessage
?.contains("spawn rejected") == true,
)
coVerify(exactly = 0) { mockApi.getActionStatus(any()) }
}
}