controller v0.50.0: slice 10 P4 — dual-role drives + backup-aware wipe warning

4A: user-data drives are backup-target-eligible (not role-locked) — surfaced in
the drive purpose note. 4B: handleStorageImpact returns backup_copies (apps whose
cross-drive backups live on the drive, via backupCopiesOnPath); the wipe/eject
modal warns they'd be destroyed (stays customer-confirmable — copies redundant).
Cross-drive backup engine remains out of scope. Test: TestBackupCopiesOnPath.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-12 18:00:27 +02:00
parent 2a353572f7
commit 4913130514
4 changed files with 92 additions and 4 deletions
@@ -4,7 +4,9 @@ import (
"context"
"io"
"log"
"os"
"path/filepath"
"sort"
"testing"
"text/template"
@@ -274,6 +276,30 @@ func TestSortDisksForView(t *testing.T) {
}
}
// P4 (4B): a drive's cross-drive backup copies (felhom-data/backups/secondary/<app>) are listed so the
// wipe confirmation can warn they'd be destroyed. Shared repo / infra dirs and files are skipped.
func TestBackupCopiesOnPath(t *testing.T) {
root := t.TempDir()
sec := filepath.Join(root, "felhom-data", "backups", "secondary")
for _, d := range []string{"immich", "nextcloud", "restic", "_infra"} {
if err := os.MkdirAll(filepath.Join(sec, d), 0o755); err != nil {
t.Fatal(err)
}
}
if err := os.WriteFile(filepath.Join(sec, "stray-file"), []byte("x"), 0o644); err != nil {
t.Fatal(err)
}
got := backupCopiesOnPath(root)
sort.Strings(got)
if len(got) != 2 || got[0] != "immich" || got[1] != "nextcloud" {
t.Fatalf("backup copies: got %v, want [immich nextcloud] (restic/_infra/files skipped)", got)
}
// A drive with no secondary backups → nil (no warning).
if c := backupCopiesOnPath(t.TempDir()); c != nil {
t.Fatalf("a drive with no cross-drive backups should report none, got %v", c)
}
}
func TestMountWhere(t *testing.T) {
if w, err := mountWhere("hdd_1"); err != nil || w != "/mnt/hdd_1" {
t.Errorf("mountWhere(hdd_1) = %q, %v", w, err)