controller v0.269.0: whole restore from the second drive; crash loops stopped; exact image digests; steps judged by their own .felhom.yml (decisions 26-28, R-661 R-666 R-667 R-668 R-664 R-665 R-662, 09 6.4 part 6)
gates / gates (push) Successful in 27s

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
2026-09-24 12:18:39 +02:00
parent 7c3b3a9694
commit 3c6b49b31c
141 changed files with 3401 additions and 237 deletions
@@ -0,0 +1,35 @@
package web
import (
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/stacks"
)
// Decision 28 on the page: an unhealthy stop offers START (its way back) and no Mentések; the badge
// does not say "restore needed". Render test per branch.
func TestD28_UnhealthyStopPageOffersStart(t *testing.T) {
st := ubStack(nil, map[string]string{"web": "nginx:1.27"}, "2026-07-18")
st.Name = "bookstack"
st.State = stacks.StateStopped
st.HoldReason = "STOPPED BY THE BOX"
st.HoldKind = "unhealthy_stop"
h := renderBackupPage(t, "stacks", ubStacksData(st))
if !strings.Contains(h, `data-unhealthy-stop="true"`) || !strings.Contains(h, `stackAction(event, 'bookstack', 'start')`) {
t.Fatal("the unhealthy stop must show its sentence and a Start button")
}
if strings.Contains(h, `data-held="true"`) || strings.Contains(h, "visszaállítás szükséges") {
t.Fatal("an unhealthy stop is not a restore case")
}
a := renderBackupPage(t, "app_info", appInfoData(st))
if !strings.Contains(a, `data-unhealthy-stop="true"`) || strings.Contains(a, `data-held="true"`) {
t.Fatal("app page: wrong branch for an unhealthy stop")
}
// positive control: an UPDATE hold keeps the restore branch
st.HoldKind = "update_failed"
h2 := renderBackupPage(t, "stacks", ubStacksData(st))
if !strings.Contains(h2, `data-held="true"`) || strings.Contains(h2, `stackAction(event, 'bookstack', 'start')`) {
t.Fatal("an update hold must keep its restore branch and offer no Start")
}
}