Files
felhom-controller/controller/internal/backup/tier2_test.go
T
admin d2071430ea v0.55.0: Phase 3 — auto off-drive Tier 2 (rootfs-headroom guard)
Tier 2 rsync-mirrors each HDD app's recovery unit + appdata to a DIFFERENT physical
disk (the only off-drive protection bind-mounted userdata can get; PBS can't reach it).
Auto-enabled, auto-target: prefer another registered drive (different physical disk via
system.SamePhysicalDevice), else the internal SSD for SMALL units only — with a
size-aware headroom guard that REFUSES rather than fill the ~8G guest rootfs, recording
an honest "needs 2nd HDD" status. Status persisted via the surviving CrossDriveBackup;
"2. mentés" UI card now populated. Daily tier2-backup job + POST /api/backup/tier2.

- backup/tier2.go (engine+selection+headroom), tier2_test.go (headroom arithmetic)
- system.SamePhysicalDevice (linux Stat_t.Dev + stub)
- handlers.go Tier2 UI population + tier2DestLabel; backups.html honest no-target reason
- fixed stale TestBackupCopiesOnPath (old felhom-data layout -> in-guest layout)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 13:24:49 +02:00

31 lines
1.3 KiB
Go

package backup
import "testing"
// TestTier2FitsHeadroom covers the size-aware rootfs-headroom guard that protects the ~8 GB guest
// rootfs from being filled by a Tier 2 SSD copy (reserve = max(2 GB, 20% of total)).
func TestTier2FitsHeadroom(t *testing.T) {
cases := []struct {
name string
availGB, totalGB, unitGB float64
want bool
}{
// 8 GB rootfs, ~2.4 GB free: a tiny unit fits (reserve = 2 GB), a 1 GB unit does NOT.
{"8G rootfs, tiny unit fits", 2.4, 8.0, 0.02, true},
{"8G rootfs, 1G unit refused", 2.4, 8.0, 1.0, false},
{"8G rootfs, 0.3G unit fits", 2.4, 8.0, 0.3, true},
// Reserve is the larger of 2 GB and 20%: on 8 GB, 20% = 1.6 GB < 2 GB, so 2 GB applies.
{"8G rootfs exactly at 2G reserve", 2.0, 8.0, 0.0, true},
{"8G rootfs just under reserve", 2.0, 8.0, 0.01, false},
// Large drive: 20% reserve dominates (204.8 GB on a 1 TB drive).
{"1TB drive, 50G unit fits", 500.0, 1024.0, 50.0, true},
{"1TB drive, 320G unit refused (under 20% reserve)", 500.0, 1024.0, 320.0, false},
}
for _, c := range cases {
if got := tier2FitsHeadroom(c.availGB, c.totalGB, c.unitGB); got != c.want {
t.Errorf("%s: tier2FitsHeadroom(avail=%.2f,total=%.2f,unit=%.2f)=%v want %v",
c.name, c.availGB, c.totalGB, c.unitGB, got, c.want)
}
}
}