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:
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()
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.m57.hermescontrol.ui.system
|
||||
|
||||
import com.m57.hermescontrol.data.model.UpdateReceipt
|
||||
import com.m57.hermescontrol.data.model.UpdateReceiptFleetEntry
|
||||
import com.m57.hermescontrol.data.model.UpdateReceiptResponse
|
||||
import com.m57.hermescontrol.data.model.UpdateReceiptSummary
|
||||
import com.m57.hermescontrol.data.model.UpdateReceiptVersion
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
|
||||
/**
|
||||
* Pure-logic tests for [formatUpdateReceiptLines] (issue #958). Mockk cannot
|
||||
* self-attach its agent in this sandbox, so these avoid the API layer entirely
|
||||
* and exercise the formatting directly.
|
||||
*/
|
||||
class UpdateReceiptFormattingTest {
|
||||
@Test
|
||||
fun `null receipt yields empty list`() {
|
||||
assertTrue(formatUpdateReceiptLines(null).isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `empty receipt (no summary, no record) yields empty list`() {
|
||||
assertTrue(formatUpdateReceiptLines(UpdateReceiptResponse()).isEmpty())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `full receipt renders outcome version and fleet`() {
|
||||
val receipt =
|
||||
UpdateReceiptResponse(
|
||||
receipt =
|
||||
UpdateReceipt(
|
||||
outcome = "success",
|
||||
preUpdate = UpdateReceiptVersion(version = "0.20.4", sha = "a".repeat(40)),
|
||||
postUpdate = UpdateReceiptVersion(version = "0.20.5", sha = "b".repeat(40)),
|
||||
fleet =
|
||||
listOf(
|
||||
UpdateReceiptFleetEntry(profile = "default", state = "current"),
|
||||
UpdateReceiptFleetEntry(profile = "work", state = "stale"),
|
||||
),
|
||||
),
|
||||
summary =
|
||||
UpdateReceiptSummary(
|
||||
outcome = "success",
|
||||
postVersion = "0.20.5",
|
||||
fleetStates = listOf("current", "stale"),
|
||||
),
|
||||
)
|
||||
|
||||
val lines = formatUpdateReceiptLines(receipt)
|
||||
assertEquals(
|
||||
listOf(
|
||||
"Update receipt",
|
||||
"Outcome: success",
|
||||
"Version: 0.20.4 → 0.20.5",
|
||||
"Fleet: default: current, work: stale",
|
||||
),
|
||||
lines,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `summary-only receipt falls back to postVersion and fleetStates`() {
|
||||
val receipt =
|
||||
UpdateReceiptResponse(
|
||||
summary =
|
||||
UpdateReceiptSummary(
|
||||
outcome = "partial",
|
||||
postVersion = "0.21.0",
|
||||
fleetStates = listOf("current"),
|
||||
),
|
||||
)
|
||||
|
||||
val lines = formatUpdateReceiptLines(receipt)
|
||||
assertEquals(
|
||||
listOf(
|
||||
"Update receipt",
|
||||
"Outcome: partial",
|
||||
"Version: → 0.21.0",
|
||||
"Fleet: current",
|
||||
),
|
||||
lines,
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `version falls back to short sha when version is missing`() {
|
||||
val receipt =
|
||||
UpdateReceiptResponse(
|
||||
receipt =
|
||||
UpdateReceipt(
|
||||
outcome = "success",
|
||||
preUpdate = UpdateReceiptVersion(sha = "abcdef1234567890deadbeef"),
|
||||
postUpdate = UpdateReceiptVersion(sha = "11112222"),
|
||||
),
|
||||
)
|
||||
|
||||
val lines = formatUpdateReceiptLines(receipt)
|
||||
assertEquals(
|
||||
listOf(
|
||||
"Update receipt",
|
||||
"Outcome: success",
|
||||
"Version: abcdef12 → 11112222",
|
||||
),
|
||||
lines,
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user