Files
felhom-controller/controller/internal/backup/r1348_putback_mail_test.go
T

76 lines
2.8 KiB
Go

package backup
import (
"errors"
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
)
// R-1348: the restore_mixed hold written at START-UP after an interrupted put-back fails must reach the operator as the
// R-1340 mail. Recover runs long before the notifier exists (main.go), so the guard keeps the name and hands it over when
// SetHeldNotify is wired; a failure after that (a drive that returned at run time) goes out at once.
//
// RED-PROOF (2026-11-02): seen failing before the seam existed (build: SetHeldNotify); with the call dropped from
// holdPutBackFailed these fail on „no mail for a held app".
func TestR1348_AFailedPutBackAtStartupMailsOnceTheNotifierIsWired(t *testing.T) {
r := newR1251Rig(t)
r.putErr = errors.New("compose down: timeout")
g := r.guard()
if res := g.Recover(); res == nil || len(res.Held) != 1 {
t.Fatalf("recovery must hold the app: %+v", res)
}
var mailed []string
g.SetHeldNotify(func(stack string) { mailed = append(mailed, stack) })
if len(mailed) != 1 || mailed[0] != "immich" {
t.Fatalf("no mail for a held app: the hold from Recover must be handed over when the notifier is wired, got %v", mailed)
}
h, ok := r.sett.GetRestoreHold("immich")
if !ok || h.Reason != settings.HoldReasonRestoreMixed || h.MixedCause != settings.MixedCauseInterruptedPutBack ||
!strings.Contains(h.FailedStep, "compose down: timeout") {
t.Fatalf("the mail reads this record, and it is wrong: %+v", h)
}
g.SetHeldNotify(func(stack string) { mailed = append(mailed, "again:"+stack) })
if len(mailed) != 1 {
t.Fatalf("a held app is mailed once, not at every wiring: %v", mailed)
}
}
// A clean put-back, or one still waiting for its drive, is no failure: no mail.
func TestR1348_NoMailWithoutAFailedPutBack(t *testing.T) {
r := newR1251Rig(t)
g := r.guard()
g.Recover()
var mailed []string
g.SetHeldNotify(func(stack string) { mailed = append(mailed, stack) })
if len(mailed) != 0 {
t.Fatalf("a put-back that worked mailed: %v", mailed)
}
w := newR1251Rig(t)
w.unbind(t)
gw := w.guard()
gw.Recover()
gw.SetHeldNotify(func(stack string) { mailed = append(mailed, stack) })
if len(mailed) != 0 {
t.Fatalf("a put-back that waits for its drive has its own hold and reminder, not this mail: %v", mailed)
}
}
// After the wiring a failed put-back (the drive returned at run time: ResumePutBack) mails at once.
func TestR1348_AFailedResumeMailsAtOnce(t *testing.T) {
r := newR1251Rig(t)
r.unbind(t)
g := r.guard()
g.Recover() // waiting for its drive
var mailed []string
g.SetHeldNotify(func(stack string) { mailed = append(mailed, stack) })
r.bind(t)
r.putErr = errors.New("tar xf failed")
g.ResumePutBack("immich")
if len(mailed) != 1 || mailed[0] != "immich" {
t.Fatalf("a put-back that failed on resume must mail: %v", mailed)
}
}