package localapi import ( "context" "io" "log/slog" "testing" "gitea.dooplex.hu/admin/felhom-agent/internal/hub" "gitea.dooplex.hu/admin/felhom-agent/internal/storage" ) func TestStablePathForRaw_DriveName(t *testing.T) { cases := []struct { where, name, stable string }{ {"/mnt/felhom-usb", "felhom-usb", "/mnt/felhom-drives/felhom-usb"}, {"/mnt/felhom-flash", "felhom-flash", "/mnt/felhom-drives/felhom-flash"}, {"/mnt/felhom-drives/x", "", ""}, // nested (slash in remainder) → not a /mnt/ raw mount {"/var/lib/vz", "", ""}, // not under /mnt {"/mnt/", "", ""}, // empty name } for _, c := range cases { if got := DriveNameFromRaw(c.where); got != c.name { t.Errorf("DriveNameFromRaw(%q) = %q, want %q", c.where, got, c.name) } if got := StablePathForRaw(c.where); got != c.stable { t.Errorf("StablePathForRaw(%q) = %q, want %q", c.where, got, c.stable) } } } // TestDisks_GuestPathAndBoundUnderParent asserts the intermediary reporting the controller relies on: a // user-data drive gets a STABLE guest path (/mnt/felhom-drives/) and BoundUnderParent reflects the // host mount check; a SYSTEM mount gets neither (it never crosses into the guest). // // COMPANION GUARD: a pre-fix impl that left GuestPath empty (no repoint signal) or that reported // BoundUnderParent ignoring the real mount state would fail the assertions below. func TestDisks_GuestPathAndBoundUnderParent(t *testing.T) { d := &fakeDiskOps{probe: storage.DeviceProbe{Probed: true, HasFilesystem: true, FSType: "ext4"}} sv := fakeStorage{targets: []hub.StorageTarget{ {Name: "usb", Type: hub.StorageTypeUSB, BackingDevice: "/dev/sdb1", MountPath: "/mnt/felhom-usb"}, {Name: "flash", Type: hub.StorageTypeUSB, BackingDevice: "/dev/sdc1", MountPath: "/mnt/felhom-flash"}, {Name: "system", Type: "local", MountPath: "/var/lib/vz"}, // system role → no guest path }} fg := &fakeGuestsCfg{} srv, err := NewServer(Options{ ListenAddr: "127.0.0.1:0", Guests: fg, Backups: &fakeBackups{}, Store: &fakeStore{}, Storage: sv, Tokens: staticTokens{"A": 8200}, Disks: d, DiskGate: &fakeGate{}, HostReader: sysOnSDA(), Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), }) if err != nil { t.Fatal(err) } srv.baseCtx = context.Background() // felhom-usb is bound under the parent; felhom-flash is not. srv.boundCheck = func(p string) bool { return p == "/mnt/felhom-drives/felhom-usb" } disks := decodeDisks(t, do(t, srv.Handler(), "GET", "/disks", "A", "").Body.Bytes()) byMount := map[string]DiskInfo{} for _, di := range disks { byMount[di.MountPath] = di } if gp := byMount["/mnt/felhom-usb"].GuestPath; gp != "/mnt/felhom-drives/felhom-usb" { t.Errorf("usb GuestPath = %q, want /mnt/felhom-drives/felhom-usb", gp) } if !byMount["/mnt/felhom-usb"].BoundUnderParent { t.Errorf("usb should report BoundUnderParent=true (bound under the parent)") } if byMount["/mnt/felhom-flash"].BoundUnderParent { t.Errorf("flash should report BoundUnderParent=false (not bound)") } if gp := byMount["/mnt/felhom-flash"].GuestPath; gp != "/mnt/felhom-drives/felhom-flash" { t.Errorf("flash GuestPath = %q, want /mnt/felhom-drives/felhom-flash", gp) } // A SYSTEM mount must never get a guest path (it does not cross into the guest). if gp := byMount["/var/lib/vz"].GuestPath; gp != "" { t.Errorf("system mount GuestPath = %q, want empty (never crosses into the guest)", gp) } }