Files
felhom-controller/controller/internal/backup/offbox_scratch_test.go
T
admin 3603d1fc7f Tier-2 engine rework: class-driven legs, v2 layout, NAS-target exclusion (Task 3b, v0.135.0)
tier2_capture.go: classified apps get TierSecondary per-bind legs (paperless copy shrinks — export
drops); legacy apps keep the byte-identical resolver set. v2 relpath-mirroring layout
(backups/secondary/<stack>/{marker LAST, recovery-unit/, hdd/<rel>/, userdata/<rel>/}); N>1 native
(errTier2MultiDir/tier2AppDataName deleted). Migration=delete-and-rebuild + reconcile; all RemoveAll
via tier2SafeRemove (refuses outside backups/secondary/). SSD=state-only tier. selectTier2Target
never picks network storage (pinned+auto, F-6C-1). Restore reads v2 behind a marker gate.
Part 0: offbox_enlarge_blocked is a persisted one-time Load seed (opt-out sticks), not a getter
append. Part 0.5: offsite restore scratch prefers a local (non-network) path.
Full v2 test suite + all 10 §10 red-proofs verified. Destructive writes bounded to backups/secondary/.
2026-07-15 10:10:20 +02:00

67 lines
2.3 KiB
Go

package backup
import (
"bytes"
"log"
"path/filepath"
"strings"
"testing"
"gitea.dooplex.hu/admin/felhom-controller/internal/settings"
)
// Part 0.5 (F-3afix-1): the offsite restore scratch must prefer a LOCAL path — a network (squashed)
// scratch would feed PlaceOffsiteRestore wrong-owner files. A network app-drive is skipped when a
// local candidate exists; a network-only environment is a last resort WITH a loud WARN.
func TestOffboxScratchDir_PrefersLocal(t *testing.T) {
m, sett := newOffboxManager(t)
prov := &offbox3aProvider{hdd: map[string]string{}, binds: map[string][]ClassifiedBind{}, has: map[string]bool{}}
m.SetStackProvider(prov)
nas := "/mnt/nas"
local := t.TempDir()
if err := sett.AddStoragePath(settings.StoragePath{Path: nas, Label: "nas", Schedulable: true, Kind: settings.StorageKindNetwork, Protocol: "nfs"}); err != nil {
t.Fatal(err)
}
if err := sett.AddStoragePath(settings.StoragePath{Path: local, Label: "local", Schedulable: true}); err != nil {
t.Fatal(err)
}
prov.hdd["immich"] = nas // the app lives ON the NAS
_, nsRoot, err := m.offboxRestoreScratchDir("immich")
if err != nil {
t.Fatalf("scratchDir: %v", err)
}
if strings.Contains(nsRoot, "nas") {
t.Errorf("network app-drive must not be the scratch — got %q, want the local drive", nsRoot)
}
if !strings.HasPrefix(nsRoot, filepath.Clean(local)) {
t.Errorf("scratch nsRoot = %q, want under the local drive %q", nsRoot, local)
}
}
func TestOffboxScratchDir_NetworkOnlyLastResortWarns(t *testing.T) {
var buf bytes.Buffer
m, sett := newOffboxManager(t)
m.logger = log.New(&buf, "", 0)
prov := &offbox3aProvider{hdd: map[string]string{}, binds: map[string][]ClassifiedBind{}, has: map[string]bool{}}
m.SetStackProvider(prov)
nas := "/mnt/nas"
if err := sett.AddStoragePath(settings.StoragePath{Path: nas, Label: "nas", Schedulable: true, Kind: settings.StorageKindNetwork, Protocol: "nfs"}); err != nil {
t.Fatal(err)
}
prov.hdd["immich"] = nas
_, nsRoot, err := m.offboxRestoreScratchDir("immich")
if err != nil {
t.Fatalf("scratchDir: %v", err)
}
if !strings.Contains(nsRoot, "nas") {
t.Errorf("network-only → the NAS is the last resort, got %q", nsRoot)
}
if !strings.Contains(buf.String(), "ownership fidelity not guaranteed") {
t.Errorf("last-resort network scratch must log a loud WARN, got: %s", buf.String())
}
}