package facts import ( "testing" "metis/pkg/inventory" ) func TestAggregateGroupsByClass(t *testing.T) { inv := &inventory.Inventory{ Classes: []inventory.NodeClass{{Name: "c1"}, {Name: "c2"}}, Nodes: []inventory.NodeSpec{ {Name: "n1", Class: "c1", USBScratch: &inventory.USBScratchDisk{Mountpoint: "/mnt/scratch", Label: "scratch-1", BindTargets: []string{"/var/lib/rancher"}}}, {Name: "n2", Class: "c2"}, }, } snaps := []Snapshot{ {Hostname: "n1", Kernel: "k1", PackageSample: map[string]string{"containerd": "2.0"}, USBScratch: &USBScratch{Mountpoint: "/mnt/scratch", Label: "scratch-1", MountHealthy: true, LabelHealthy: true, BindHealthy: true, BindTargets: []USBBindTarget{{Path: "/var/lib/rancher", Healthy: true}}}}, {Hostname: "n2", Kernel: "k2", PackageSample: map[string]string{"containerd": "1.7"}}, {Hostname: "n1", Kernel: "k1"}, } sum := Aggregate(inv, snaps) if len(sum) != 2 { t.Fatalf("expected 2 classes, got %d", len(sum)) } c1 := sum["c1"] if c1 == nil || c1.Kernels["k1"] != 2 { t.Fatalf("expected k1 count 2, got %#v", c1) } if c1.PackageStats["containerd"]["2.0"] != 1 { t.Fatalf("package stats not tallied: %#v", c1.PackageStats) } if c1.USBMountHealth["ok"] != 1 || c1.USBLabelHealth["ok"] != 1 || c1.USBBindHealth["ok"] != 1 { t.Fatalf("usb health not tallied: %#v", c1) } } func TestAggregateKeepsUnknownHostnames(t *testing.T) { sum := Aggregate(nil, []Snapshot{{Hostname: "ghost", Kernel: "k"}}) if sum["unknown"].Nodes[0] != "ghost" { t.Fatalf("unexpected unknown aggregate: %#v", sum["unknown"]) } } func TestChooseTargetsHandlesTiesAndEmptyValues(t *testing.T) { sum := &ClassSummary{ Kernels: map[string]int{"k1": 2, "k2": 2}, OSImages: map[string]int{ "img": 1, }, PackageStats: map[string]map[string]int{ "p": {"": 3, "1": 1}, }, } targets := ChooseTargets(sum) if targets.Kernel != "" { t.Fatalf("expected kernel tie to return empty, got %q", targets.Kernel) } if targets.OSImage != "img" { t.Fatalf("expected OS image img, got %q", targets.OSImage) } if _, ok := targets.Packages["p"]; ok { t.Fatalf("expected empty package version to be skipped: %+v", targets.Packages) } } func TestUSBHealthHelpersCoverAllStates(t *testing.T) { addUSBHealth(nil, nil, nil) sum := &ClassSummary{ USBMountHealth: map[string]int{}, USBUUIDHealth: map[string]int{}, USBLabelHealth: map[string]int{}, USBBindHealth: map[string]int{}, } desired := &inventory.USBScratchDisk{ Mountpoint: "/mnt/scratch", UUID: "usb-1", Label: "scratch-a", BindTargets: []string{"/var/lib/rancher"}, } addUSBHealth(sum, desired, nil) if sum.USBMountHealth["missing"] != 1 || sum.USBUUIDHealth["missing"] != 1 || sum.USBLabelHealth["missing"] != 1 || sum.USBBindHealth["missing"] != 1 { t.Fatalf("expected missing usb health entries: %#v", sum) } addUSBHealth(sum, desired, &USBScratch{ MountHealthy: true, UUIDHealthy: true, LabelHealthy: false, BindHealthy: false, }) if sum.USBMountHealth["ok"] != 1 || sum.USBUUIDHealth["ok"] != 1 || sum.USBLabelHealth["bad"] != 1 || sum.USBBindHealth["bad"] != 1 { t.Fatalf("expected healthy/bad usb health entries: %#v", sum) } if got := usbStatus(nil, true); got != "missing" { t.Fatalf("usbStatus missing = %q", got) } if got := usbStatus(&USBScratch{}, true); got != "ok" { t.Fatalf("usbStatus ok = %q", got) } if got := usbStatus(&USBScratch{}, false); got != "bad" { t.Fatalf("usbStatus bad = %q", got) } } func TestAggregateCoversMissingClassAndEmptyFields(t *testing.T) { inv := &inventory.Inventory{ Nodes: []inventory.NodeSpec{ { Name: "ghost", Class: "missing", USBScratch: &inventory.USBScratchDisk{Mountpoint: "/mnt/scratch", Label: "ghost-scratch"}, }, }, } sums := Aggregate(inv, []Snapshot{{Hostname: "ghost"}}) sum := sums["unknown"] if sum == nil { t.Fatal("expected unknown summary") } if len(sum.Nodes) != 1 || sum.Nodes[0] != "ghost" { t.Fatalf("unexpected aggregate nodes: %#v", sum.Nodes) } if sum.USBLabelHealth["missing"] != 1 || sum.USBMountHealth["missing"] != 1 { t.Fatalf("expected missing usb health from class-missing node: %#v", sum) } }