Files
felhom-controller/controller/internal/appexport/estimate_volsize_test.go
T
admin c6d8bc82a2 C6B-F1 cause 1 + §8: additive .fab export (HDD binds AND named volumes)
executeExport no longer either/or gates user data on needs_hdd — a needs_hdd
app bundles BOTH its HDD mounts and its named volumes (sonarr_config = the
whole app DB was silently dropped pre-fix). exportHDDData returns error and
fails LOUDLY on a basename collision between mounts (the manifest keys tars by
basename; the old code silently overwrote the first tar). EstimateExport made
additive to match, so the fits-on-dest gate counts both. Round-trip placement
test proves a userdata tar restores to <HDD_PATH>/userdata through the
untouched import mapping. Red-proofs recorded: either/or revert fails
scenario A; collision-check removal fails the collision test.
2026-07-14 15:18:21 +02:00

101 lines
3.4 KiB
Go

package appexport
import (
"errors"
"io"
"log"
"strings"
"testing"
)
// hddProvider is an rtProvider that reports an HDD-backed stack (estimate scenario H + the
// v0.130.0 additive-export tests in export_additive_test.go).
type hddProvider struct {
*rtProvider
mounts []string
hddPath string
}
func (p *hddProvider) GetStackNeedsHDD(string) bool { return true }
func (p *hddProvider) GetStackHDDMounts(string) []string { return p.mounts }
func (p *hddProvider) GetStackHDDPath(string) string { return p.hddPath }
func newEstimator(t *testing.T, provider ExportStackProvider) *Exporter {
t.Helper()
return NewExporter(provider, log.New(io.Discard, "", 0), "test")
}
// Scenario F (the F-A fix): a volume-only app with a >1 GiB volume reports the REAL size via the
// container-view sizer — not 0/"3.6 KB". This is the F-A red-proof anchor (revert EstimateExport to
// dockerVolumeSize → reads 0).
func TestEstimate_VolumeSize_RealNotZero(t *testing.T) {
const twoGiB = int64(2) << 30
orig := volumeSizer
volumeSizer = func(vol string) (int64, error) { return twoGiB, nil }
defer func() { volumeSizer = orig }()
e := newEstimator(t, &rtProvider{stackDir: t.TempDir(), volumes: []string{"app_data"}})
est, err := e.EstimateExport("app", t.TempDir())
if err != nil {
t.Fatal(err)
}
if est.SizeUnknown {
t.Fatalf("size must be known when the sizer succeeds")
}
if est.DataSizeBytes != twoGiB {
t.Fatalf("DataSizeBytes = %d, want %d (WRONG would be 0 — the F-A bug)", est.DataSizeBytes, twoGiB)
}
if !strings.Contains(est.DataSizeHuman, "GB") {
t.Fatalf("DataSizeHuman = %q, want GB-scale (WRONG would be \"3.6 KB\")", est.DataSizeHuman)
}
}
// Scenario G: a failed volume read must never render as "fits". Size is marked unknown, the human
// string says so, and FitsOnDest is forced false.
func TestEstimate_VolumeSize_FailureNeverFits(t *testing.T) {
orig := volumeSizer
volumeSizer = func(vol string) (int64, error) { return 0, errors.New("docker: no such image") }
defer func() { volumeSizer = orig }()
e := newEstimator(t, &rtProvider{stackDir: t.TempDir(), volumes: []string{"app_data"}})
est, err := e.EstimateExport("app", t.TempDir())
if err != nil {
t.Fatal(err)
}
if !est.SizeUnknown {
t.Fatalf("a failed volume read must set SizeUnknown")
}
if est.FitsOnDest {
t.Fatalf("an unknown size must NEVER render as fits_on_dest:true")
}
if est.DataSizeHuman != "ismeretlen méret" {
t.Fatalf("DataSizeHuman = %q, want \"ismeretlen méret\"", est.DataSizeHuman)
}
if est.DataSizeBytes != 0 {
t.Fatalf("no successful read → DataSizeBytes should be 0, got %d", est.DataSizeBytes)
}
}
// Scenario H (regression): an HDD-backed stack must NOT touch the new volume sizer — the HDD branch
// (duBytes on the mounted /mnt path) is unchanged. Platform-independent: assert the seam is not
// invoked and SizeUnknown stays false.
func TestEstimate_HDDPath_DoesNotUseVolumeSizer(t *testing.T) {
called := false
orig := volumeSizer
volumeSizer = func(vol string) (int64, error) { called = true; return 0, nil }
defer func() { volumeSizer = orig }()
p := &hddProvider{rtProvider: &rtProvider{stackDir: t.TempDir()}, mounts: []string{t.TempDir()}}
e := newEstimator(t, p)
est, err := e.EstimateExport("app", t.TempDir())
if err != nil {
t.Fatal(err)
}
if called {
t.Fatalf("HDD-backed stack must not call the docker volume sizer")
}
if est.SizeUnknown {
t.Fatalf("HDD branch must not set SizeUnknown")
}
}