package storage import ( "context" "reflect" "testing" "gitea.dooplex.hu/admin/felhom-agent/internal/proxmox" ) // The report rows carry the `16` §3 identity per storage type: local + lvmthin → the system disk; a dir // on its own partition → that disk, kind "partition"; nfs/pbs → network; an UNMOUNTED dir → not computed // (never the root disk — the same rule as backing_device). func TestObserve_FillsPhysicalDiskIdentity(t *testing.T) { f := newFx(t) oneDiskLVM(f) f.node("sdb", "", "ID_BUS=ata", "ID_MODEL=TOSHIBA_MQ04ABF100", "ID_SERIAL_SHORT=65NOP3HDT") f.node("sdb1", "sdb") f.mount("/dev/sdb1", "/mnt/usb-backup", "ext4") res := f.resolver() api := &fakeStorageAPI{ node: "demo", cluster: []proxmox.Storage{ {Storage: "local", Type: "dir", Path: "/var/lib/vz"}, {Storage: "local-lvm", Type: "lvmthin", VGName: "pve", ThinPool: "data"}, {Storage: "usb-backup", Type: "dir", Path: "/mnt/usb-backup"}, {Storage: "gone", Type: "dir", Path: "/mnt/gone"}, {Storage: "nfs-arch", Type: "nfs", Server: "10.0.0.5", Export: "/x"}, }, nodeSt: []proxmox.Storage{ {Storage: "local", Type: "dir", Path: "/var/lib/vz", Active: 1}, {Storage: "local-lvm", Type: "lvmthin", Active: 1}, {Storage: "usb-backup", Type: "dir", Path: "/mnt/usb-backup", Active: 1}, {Storage: "gone", Type: "dir", Path: "/mnt/gone", Active: 1}, {Storage: "nfs-arch", Type: "nfs", Active: 1}, }, } host := &fakeHostReader{ mounts: []Mount{ {Device: "/dev/mapper/pve-root", MountPoint: "/", FSType: "ext4"}, {Device: "/dev/sdb1", MountPoint: "/mnt/usb-backup", FSType: "ext4"}, }, exists: map[string]bool{"/dev/sdb1": true, "/dev/mapper/pve-root": true}, } ob := NewObserver(api, host, nil, quietLogger()) ob.SetIdentity(res) got, err := ob.Observe(context.Background()) if err != nil { t.Fatal(err) } m := byName(got) ssd := []string{"serial:ata:SanDisk_X600:182195804614"} for _, n := range []string{"local", "local-lvm"} { if tg := m[n]; tg.Kind != "system" || tg.Identity != "proven" || !reflect.DeepEqual(tg.PhysicalDisks, ssd) { t.Errorf("%s: want system/proven on the SSD, got kind=%q id=%q disks=%v", n, tg.Kind, tg.Identity, tg.PhysicalDisks) } } if tg := m["usb-backup"]; tg.Kind != "partition" || tg.Identity != "proven" || tg.PhysicalDisks[0] != "serial:ata:TOSHIBA_MQ04ABF100:65NOP3HDT" { t.Errorf("usb-backup: got %+v", tg) } if tg := m["nfs-arch"]; tg.Kind != "network" || tg.IdentityReason != "network" { t.Errorf("nfs: got kind=%q reason=%q", tg.Kind, tg.IdentityReason) } if tg := m["gone"]; tg.Identity != "" || len(tg.PhysicalDisks) != 0 { t.Errorf("an unmounted dir must not be computed (never root's disk), got %+v", tg) } // Without a resolver the fields stay empty (the pre-0.157 shape). plain, _ := NewObserver(api, host, nil, quietLogger()).Observe(context.Background()) if tg := byName(plain)["local"]; tg.Identity != "" || tg.Kind != "" { t.Errorf("no resolver: want empty identity, got %+v", tg) } }