70 lines
3.2 KiB
Go
70 lines
3.2 KiB
Go
package backup
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// W25 (rehearsal 2026-10-20): a failed off-site run showed the household „Utolsó hiba: offbox backup immich: exit
|
|
// status 1". The household now reads which app failed (its own name), that the other apps are fine when they are,
|
|
// and that the box tries again the next night; the raw text stays in the log and reaches the operator's event.
|
|
func TestW25_FailedOffsiteRunSpeaksToTheHousehold(t *testing.T) {
|
|
m, sett := newOffboxManager(t)
|
|
usb := t.TempDir()
|
|
addSchedulablePath(t, sett, usb)
|
|
ns := m.namespaceRoot(usb)
|
|
writeUnit(t, ns, "immich", "2026-10-20T00:00:00Z")
|
|
writeUnit(t, ns, "mealie", "2026-10-20T00:00:00Z")
|
|
b, _ := json.Marshal(RecoveryManifest{AppName: "immich", DisplayName: "Immich Fotók", CreatedAt: "2026-10-20T00:00:00Z"})
|
|
if err := os.WriteFile(RecoveryUnitManifestPath(ns, "immich"), b, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_ = sett.SetAppOffbox("immich", true)
|
|
_ = sett.SetAppOffbox("mealie", true)
|
|
rr := &recordingOffboxRunner{backupErr: map[string]error{"immich": errors.New("exit status 1")}}
|
|
m.SetOffboxRunner(rr.run)
|
|
var operatorErr error
|
|
m.SetOffboxNotify(func(_ time.Duration, _ int, err error) { operatorErr = err })
|
|
if err := m.RunOffboxBackup(context.Background()); err == nil {
|
|
t.Fatal("a run with a failed app must report an error")
|
|
}
|
|
got := sett.GetOffboxTarget().LastError
|
|
for _, want := range []string{"Nem sikerült a távoli mentés: Immich Fotók.", "A többi alkalmazás távoli mentése rendben van.",
|
|
"A doboz a következő éjjel újra próbálkozik."} {
|
|
if !strings.Contains(got, want) {
|
|
t.Errorf("the household's note lacks %q: %q", want, got)
|
|
}
|
|
}
|
|
for _, raw := range []string{"exit status", "offbox backup", "immich:"} {
|
|
if strings.Contains(got, raw) {
|
|
t.Errorf("raw technical text %q reached the household's note: %q", raw, got)
|
|
}
|
|
}
|
|
// the operator's event keeps the technical detail, sanitised
|
|
if op := m.OffsiteFailureMessage(operatorErr, time.Second); !strings.Contains(op, "offbox backup immich: exit status 1") {
|
|
t.Errorf("the operator's event lost the technical detail: %q", op)
|
|
}
|
|
|
|
// English, and the cases where the other apps are NOT fine or no night comes
|
|
res := offboxRunResult{backedUp: 1, failedApps: []string{"Immich", offsiteSharesName}}
|
|
runErr := errors.New("offbox backup immich: exit status 1")
|
|
en := offsiteHouseholdFailure("en", runErr, res, true)
|
|
if en != "The remote backup did not work for: Immich, Shared folders. The remote backup of the other apps is fine. The box tries again the next night." {
|
|
t.Errorf("en: %q", en)
|
|
}
|
|
res.missingUnprotected = []string{"paperless"}
|
|
if hu := offsiteHouseholdFailure("hu", runErr, res, false); strings.Contains(hu, "többi") || strings.Contains(hu, "éjjel") {
|
|
t.Errorf("with an app left out the others are not all fine, and with no nightly run there is no retry: %q", hu)
|
|
}
|
|
// a whole-run failure: its class sentence, never the raw text
|
|
tr := offsiteHouseholdFailure("hu", errors.New("ssh: connect to host x port 23: connection refused"), offboxRunResult{}, true)
|
|
if tr != "A távoli tárhely nem érhető el (hálózat vagy bejelentkezés). A doboz a következő éjjel újra próbálkozik." {
|
|
t.Errorf("transport: %q", tr)
|
|
}
|
|
}
|