feat(ui): distinguish cancel-from-hold as "Failed (cancelled)"
CI / Lint + build + test (push) Successful in 1m33s
Release / release (push) Successful in 12m41s

Before, a run that failed, held for operator review, and was then
cancelled showed up on the tile and run header as plain "Cancelled"
with an idle-grey mood — indistinguishable from a mid-stage cancel of
a healthy run. That hides the actual failure from the dashboard.

Now: when State=Cancelled with FailedStage still set (the hold-cancel
signature the heartbeat handler already uses to pick reboot vs
cancel_stage), the badge reads "Failed (cancelled)" with a fail-
colored mood. Mid-stage cancels keep reading as plain "Cancelled".
This commit is contained in:
2026-04-20 18:54:04 -04:00
parent 73f727b4c1
commit 599fd156d0
2 changed files with 57 additions and 0 deletions
+42
View File
@@ -131,6 +131,48 @@ func TestHostTile_NoStageStrip(t *testing.T) {
}
}
// TestTileStatusCancelledFromHold: a run that entered FailedHolding and
// was later Cancelled by the operator should read as a failure, not a
// plain cancel. The discriminator is FailedStage being set — mid-stage
// cancels leave it empty.
func TestTileStatusCancelledFromHold(t *testing.T) {
cases := []struct {
name string
run *model.Run
wantStatus string
wantMood string
}{
{
name: "cancelled from hold shows failed",
run: &model.Run{State: model.StateCancelled, FailedStage: "Storage"},
wantStatus: "Failed (cancelled)",
wantMood: "fail",
},
{
name: "mid-stage cancel stays plain cancelled",
run: &model.Run{State: model.StateCancelled},
wantStatus: "Cancelled",
wantMood: "idle",
},
{
name: "failed-holding itself still reads as FailedHolding",
run: &model.Run{State: model.StateFailedHolding, FailedStage: "Storage"},
wantStatus: string(model.StateFailedHolding),
wantMood: "fail",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := tileStatus(tc.run); got != tc.wantStatus {
t.Errorf("tileStatus = %q, want %q", got, tc.wantStatus)
}
if got := tileMood(tc.run); got != tc.wantMood {
t.Errorf("tileMood = %q, want %q", got, tc.wantMood)
}
})
}
}
func TestLastSeenLabelAndClass(t *testing.T) {
if got := lastSeenLabel(nil); got != "never" {
t.Fatalf("label nil = %q, want never", got)