package stacks import ( "os" "path/filepath" "testing" ) // T1: the deploy belt SKIPS ensure when an EXTERNAL drive root is not a live mountpoint (drive absent). // Creating ${USERDATA_PATH}/... then would land app data on the rootfs, shadowed when the drive returns. // Companion red-proof: removing the gate in ensureUserdataMounts makes the dir get created → this fails. func TestEnsureUserdataMounts_SkipsAbsentExternalDrive(t *testing.T) { m := newMigManager(t, "") // sysDataPath = /mnt/sys_drive m.isMountPoint = func(string) bool { return false } // external drive is NOT mounted stackDir := t.TempDir() if err := os.WriteFile(filepath.Join(stackDir, "docker-compose.yml"), []byte(beltCompose), 0o644); err != nil { t.Fatal(err) } ud := filepath.Join(t.TempDir(), "userdata") env := []string{"HDD_PATH=/mnt/felhom-drives/flash", "USERDATA_PATH=" + ud} // external, != sysDataPath m.ensureUserdataMounts(stackDir, env) if _, err := os.Stat(filepath.Join(ud, "media", "movies")); err == nil { t.Fatal("belt MUST NOT create userdata dirs when the external drive is absent (rootfs-shadow hazard)") } if _, err := os.Stat(filepath.Join(ud, "downloads")); err == nil { t.Fatal("belt MUST NOT create userdata dirs when the external drive is absent") } } // T2: the belt ENSURES the bind-source dirs when the external drive IS a live mountpoint. func TestEnsureUserdataMounts_EnsuresWhenMounted(t *testing.T) { m := newMigManager(t, "") m.isMountPoint = func(string) bool { return true } // drive mounted stackDir := t.TempDir() if err := os.WriteFile(filepath.Join(stackDir, "docker-compose.yml"), []byte(beltCompose), 0o644); err != nil { t.Fatal(err) } ud := filepath.Join(t.TempDir(), "userdata") env := []string{"HDD_PATH=/mnt/felhom-drives/flash", "USERDATA_PATH=" + ud} m.ensureUserdataMounts(stackDir, env) for _, p := range []string{filepath.Join(ud, "media", "movies"), filepath.Join(ud, "downloads")} { if fi, err := os.Stat(p); err != nil || !fi.IsDir() { t.Errorf("belt should create %s when the drive is mounted (%v)", p, err) } } } // T3: the SYSTEM/local path (HDD_PATH == sysDataPath) is NEVER gated — the SSD path is legitimately not // a mountpoint, so ensure must always run there (must-not-over-gate). func TestEnsureUserdataMounts_SystemPathNeverSkipped(t *testing.T) { m := newMigManager(t, "") // sysDataPath = /mnt/sys_drive m.isMountPoint = func(string) bool { return false } // SSD path is not a mountpoint — but must not gate stackDir := t.TempDir() if err := os.WriteFile(filepath.Join(stackDir, "docker-compose.yml"), []byte(beltCompose), 0o644); err != nil { t.Fatal(err) } ud := filepath.Join(t.TempDir(), "userdata") env := []string{"HDD_PATH=" + m.sysDataPath, "USERDATA_PATH=" + ud} // system path m.ensureUserdataMounts(stackDir, env) if fi, err := os.Stat(filepath.Join(ud, "media", "movies")); err != nil || !fi.IsDir() { t.Errorf("belt must ALWAYS ensure on the system/local path (not a mountpoint, but never gated): %v", err) } }