feat(update): surface backend update receipt in action popup (#958) (#964)

Adopt GET /api/hermes/update/receipt and append its summary to the
end of the hermes-update action progress popup after it finishes.
Additive only: existing checkHermesUpdate + connection-retry polling
is untouched. 404 (old backend) is handled gracefully.

- add UpdateReceiptResponse data model matching backend schema
- add getUpdateReceipt() to HermesApiService
- ActionProgressController gains trailingLines + pushTrailingLines
- ActionProgressDialog renders trailingLines after the log
- SystemViewModel fetches receipt on action finish and formats it
- extract pure formatUpdateReceiptLines for mockk-free testing
This commit is contained in:
M57 authored and GitHub committed 2026-08-24 01:35:59 +03:00
1 parent 5fd732be9b
commit ece0288ca8
8 files changed
+342 -1

No files matched your search

@@ -189,6 +189,36 @@ class ActionProgressControllerTest {
assertEquals(callsAfterFirstPoll, calls)
}
@Test
fun `pushTrailingLines appends after the log without touching the phase`() {
val controller = controller()
coEvery { mockApi.getActionStatus("hermes-update") } returns
Response.success(
ActionStatusResponse(name = "hermes-update", running = false, exit_code = 0),
)
controller.open()
controller.markStarted("hermes-update")
tick()
assertEquals(ActionProgressPhase.SUCCEEDED, controller.state.value.phase)
assertTrue(controller.state.value.trailingLines.isEmpty())
controller.pushTrailingLines(listOf("Outcome: success", "Version: 0.20.4 → 0.20.5"))
val state = controller.state.value
assertEquals(ActionProgressPhase.SUCCEEDED, state.phase)
assertEquals(listOf("Outcome: success", "Version: 0.20.4 → 0.20.5"), state.trailingLines)
}
@Test
fun `pushTrailingLines ignores empty input`() {
val controller = controller()
controller.open()
controller.pushTrailingLines(emptyList())
assertTrue(controller.state.value.trailingLines.isEmpty())
}
@Test
fun `onFinished is not called when dismissed mid-run`() {
val controller = controller()