package storage import ( "context" "errors" "io" "strings" "testing" ) // scriptedRunner returns canned stdout/stderr/err per command name (last arg = device). type scriptedRunner struct { calls [][]string outputs map[string][]byte // keyed by binary basename errs map[string]error } func (r *scriptedRunner) RunStdin(ctx context.Context, _ io.Reader, name string, args ...string) ([]byte, []byte, error) { return r.Run(ctx, name, args...) } func (r *scriptedRunner) Run(_ context.Context, name string, args ...string) ([]byte, []byte, error) { r.calls = append(r.calls, append([]string{name}, args...)) base := name if i := strings.LastIndexByte(name, '/'); i >= 0 { base = name[i+1:] } return r.outputs[base], nil, r.errs[base] } func (r *scriptedRunner) ran(substr string) bool { for _, c := range r.calls { if strings.Contains(strings.Join(c, " "), substr) { return true } } return false } func newSudo(r *scriptedRunner) *SudoHostOps { return NewSudoHostOps(SudoHostOpsConfig{Runner: r}) } // ---- validators ------------------------------------------------------------------------- func TestValidateBlockDevice(t *testing.T) { ok := []string{"/dev/sdb", "/dev/sdb1", "/dev/nvme0n1", "/dev/nvme0n1p2", "/dev/vdb3"} for _, d := range ok { if err := ValidateBlockDevice(d); err != nil { t.Errorf("expected %q valid: %v", d, err) } } bad := []string{"/dev/disk/by-uuid/x", "/dev/../etc/passwd", "/dev/sdb; rm -rf /", "/etc/shadow", "/dev/mapper/x", "sdb", ""} for _, d := range bad { if err := ValidateBlockDevice(d); err == nil { t.Errorf("expected %q rejected", d) } } } func TestValidateFSType(t *testing.T) { for _, f := range []string{"ext4", "xfs"} { if err := ValidateFSType(f); err != nil { t.Errorf("expected %q valid", f) } } for _, f := range []string{"ntfs", "vfat", "ext4 ", "", "ext4;ls"} { if err := ValidateFSType(f); err == nil { t.Errorf("expected %q rejected", f) } } } // ---- InspectDevice (data-bearing detection) --------------------------------------------- func TestInspect_Blank(t *testing.T) { // blkid finds nothing (empty output); lsblk reads cleanly and shows a bare disk. r := &scriptedRunner{ outputs: map[string][]byte{ "blkid": nil, "lsblk": []byte(`{"blockdevices":[{"name":"sdb","fstype":null,"pttype":null,"mountpoint":null}]}`), }, errs: map[string]error{"blkid": errors.New("exit status 2")}, // blkid exits non-zero on blank } p, err := newSudo(r).InspectDevice(context.Background(), "/dev/sdb") if err != nil { t.Fatal(err) } if !p.Probed { t.Fatal("expected a clean probe (lsblk read cleanly)") } if p.DataBearing() { t.Fatalf("blank device classified data-bearing: %+v", p) } } func TestInspect_HasFilesystem(t *testing.T) { r := &scriptedRunner{ outputs: map[string][]byte{ "blkid": []byte("DEVNAME=/dev/sdb\nTYPE=ext4\nUSAGE=filesystem\n"), "lsblk": []byte(`{"blockdevices":[{"name":"sdb","fstype":"ext4","pttype":null,"mountpoint":null}]}`), }, } p, _ := newSudo(r).InspectDevice(context.Background(), "/dev/sdb") if !p.DataBearing() || !p.HasFilesystem || p.FSType != "ext4" { t.Fatalf("filesystem not detected: %+v", p) } } func TestInspect_HasPartitionTable(t *testing.T) { r := &scriptedRunner{ outputs: map[string][]byte{ "blkid": []byte("DEVNAME=/dev/sdb\nPTTYPE=gpt\n"), "lsblk": []byte(`{"blockdevices":[{"name":"sdb","pttype":"gpt","children":[{"name":"sdb1","fstype":"ext4"}]}]}`), }, } p, _ := newSudo(r).InspectDevice(context.Background(), "/dev/sdb") if !p.DataBearing() || !p.HasPartitionTable || !p.HasPartitions { t.Fatalf("partition table/children not detected: %+v", p) } } func TestInspect_Mounted(t *testing.T) { r := &scriptedRunner{ outputs: map[string][]byte{ "blkid": []byte("TYPE=xfs\n"), "lsblk": []byte(`{"blockdevices":[{"name":"sdb","fstype":"xfs","mountpoint":"/mnt/data"}]}`), }, } p, _ := newSudo(r).InspectDevice(context.Background(), "/dev/sdb") if !p.Mounted || !p.DataBearing() { t.Fatalf("mounted not detected: %+v", p) } } // A probe that fails to read cleanly must be conservative (data-bearing). func TestInspect_FailedProbe_FailSafe(t *testing.T) { r := &scriptedRunner{ outputs: map[string][]byte{"blkid": nil, "lsblk": nil}, errs: map[string]error{"blkid": errors.New("blkid broke"), "lsblk": errors.New("lsblk broke")}, } p, _ := newSudo(r).InspectDevice(context.Background(), "/dev/sdb") if p.Probed { t.Fatal("a broken probe must not be 'Probed'") } if !p.DataBearing() { t.Fatal("a broken probe must be treated as data-bearing (fail-safe)") } } func TestInspect_RejectsBadDevice(t *testing.T) { if _, err := newSudo(&scriptedRunner{}).InspectDevice(context.Background(), "/dev/../etc"); err == nil { t.Fatal("expected a bad device to be rejected before any exec") } } // ---- Format (mkfs) ---------------------------------------------------------------------- // Format now (Impl-1) runs the unclaimed guard then execs the guarded wrapper ( ), // not raw mkfs. These use the stubbed guard (unclaimed /dev/sdb; system disk = /dev/sda). func TestFormat_Ext4(t *testing.T) { defer stubRO(false)() sr := &scriptRunner{out: map[string][]byte{ "/usr/bin/lsblk": []byte(`{"blockdevices":[{"name":"sdb"}]}`), }} ops := newGuardOps(sr, []Mount{{Device: "/dev/sda2", MountPoint: "/"}}) if err := ops.Format(context.Background(), "/dev/sdb", "ext4"); err != nil { t.Fatal(err) } if !srRan(sr, "/usr/local/sbin/felhom-mkfs-guarded /dev/sdb ext4") { t.Fatalf("guarded wrapper not invoked correctly: %v", sr.calls) } } func TestFormat_Xfs(t *testing.T) { defer stubRO(false)() sr := &scriptRunner{out: map[string][]byte{ "/usr/bin/lsblk": []byte(`{"blockdevices":[{"name":"nvme0n1","children":[{"name":"nvme0n1p1"}]}]}`), }} ops := newGuardOps(sr, []Mount{{Device: "/dev/sda2", MountPoint: "/"}}) if err := ops.Format(context.Background(), "/dev/nvme0n1p1", "xfs"); err != nil { t.Fatal(err) } if !srRan(sr, "/usr/local/sbin/felhom-mkfs-guarded /dev/nvme0n1p1 xfs") { t.Fatalf("guarded wrapper not invoked correctly: %v", sr.calls) } } // srRan reports whether the scriptRunner recorded a call whose joined form contains substr. func srRan(sr *scriptRunner, substr string) bool { for _, c := range sr.calls { if strings.Contains(strings.Join(c, " "), substr) { return true } } return false } func TestFormat_RejectsBadArgs(t *testing.T) { r := &scriptedRunner{} if err := newSudo(r).Format(context.Background(), "/dev/disk/by-uuid/x", "ext4"); err == nil { t.Fatal("expected bad device rejected") } if err := newSudo(r).Format(context.Background(), "/dev/sdb", "ntfs"); err == nil { t.Fatal("expected bad fstype rejected") } if len(r.calls) != 0 { t.Fatalf("mkfs ran despite invalid input: %v", r.calls) } }