feat(#1216): add cron run history (#1226)

This commit is contained in:
m57 authored and GitHub committed 2026-09-22 14:57:42 +03:00
1 parent a78b20bc99
commit 2e6ae95fe0
8 files changed
+517 -2

No files matched your search

@@ -6,6 +6,8 @@ import com.m57.hermescontrol.data.model.CronBlueprintField
import com.m57.hermescontrol.data.model.CronBlueprintListResponse
import com.m57.hermescontrol.data.model.CronJob
import com.m57.hermescontrol.data.model.CronJobFireError
import com.m57.hermescontrol.data.model.CronRun
import com.m57.hermescontrol.data.model.CronRunHistoryResponse
import com.m57.hermescontrol.data.model.DeliveryTarget
import com.m57.hermescontrol.data.model.DeliveryTargetsResponse
import com.m57.hermescontrol.data.model.InstantiateBlueprintRequest
@@ -19,6 +21,7 @@ import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.slot
import io.mockk.unmockkAll
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
@@ -125,6 +128,77 @@ class CronJobsViewModelTest {
assertEquals(listOf("origin", "local", "telegram"), editor.deliveryOptions)
}
@Test
fun `openRunHistory loads bounded recent runs`() {
coEvery { mockApi.getCronJobRuns("j1", 20) } returns
Response.success(
CronRunHistoryResponse(
runs =
listOf(
CronRun(
id = "cron_j1_1",
started_at = 1_700_000_000.0,
is_active = true,
profile = "default",
),
),
limit = 20,
),
)
val vm = createViewModel()
vm.openRunHistory(CronJob(id = "j1", name = "Morning"))
settle()
val history = vm.uiState.value.runHistoryState
assertTrue(history.isOpen)
assertFalse(history.isLoading)
assertEquals("j1", history.jobId)
assertEquals("Morning", history.jobName)
assertEquals(listOf("cron_j1_1"), history.runs.map { it.id })
coVerify(exactly = 1) { mockApi.getCronJobRuns("j1", 20) }
}
@Test
fun `openRunHistory explains unsupported backend`() {
coEvery { mockApi.getCronJobRuns("j1", 20) } returns
Response.error(404, "{}".toResponseBody())
val vm = createViewModel()
vm.openRunHistory(CronJob(id = "j1", name = "Morning"))
settle()
val history = vm.uiState.value.runHistoryState
assertFalse(history.isLoading)
assertTrue(history.runs.isEmpty())
assertTrue(history.errorMessage.orEmpty().contains("unavailable"))
}
@Test
fun `scope reset cancels in flight run history and clears rows`() {
val response = CompletableDeferred<Response<CronRunHistoryResponse>>()
coEvery { mockApi.getCronJobRuns("j1", 20) } coAnswers { response.await() }
val vm = createViewModel()
vm.openRunHistory(CronJob(id = "j1", name = "Morning"))
testDispatcher.scheduler.runCurrent()
vm.clearScopeOwnedState()
response.complete(
Response.success(
CronRunHistoryResponse(
runs = listOf(CronRun(id = "cron_j1_stale", profile = "old-profile")),
),
),
)
settle()
val history = vm.uiState.value.runHistoryState
assertFalse(history.isOpen)
assertFalse(history.isLoading)
assertTrue(history.runs.isEmpty())
assertNull(history.jobId)
}
@Test
fun `selectBlueprint seeds slot defaults`() {
val vm = createViewModel()