package probes import ( "strings" "testing" ) // Golden dmidecode -t bios output (trimmed, representative). A real // host will have more lines; parse must tolerate the unknown fields. const dmidecodeBIOS = `# dmidecode 3.3 Getting SMBIOS data from sysfs. SMBIOS 3.2.0 present. Handle 0x0000, DMI type 0, 26 bytes BIOS Information Vendor: American Megatrends Inc. Version: 3.2 Release Date: 07/15/2021 Address: 0xF0000 Runtime Size: 64 kB ROM Size: 32 MB Characteristics: PCI is supported BIOS is upgradeable Handle 0x0001, DMI type 1, 27 bytes System Information Manufacturer: Supermicro Product Name: X11SSL-F ` func TestParseDmidecodeBIOS(t *testing.T) { snap := parseDmidecodeBIOS(strings.NewReader(dmidecodeBIOS)) if snap == nil { t.Fatal("parseDmidecodeBIOS returned nil") } if snap.Component != "bios" { t.Errorf("component = %q, want bios", snap.Component) } if snap.Version != "3.2" { t.Errorf("version = %q, want 3.2", snap.Version) } if snap.Vendor != "American Megatrends Inc." { t.Errorf("vendor = %q, want American Megatrends Inc.", snap.Vendor) } if snap.Raw["Release Date"] != "07/15/2021" { t.Errorf("release date = %q, want 07/15/2021", snap.Raw["Release Date"]) } } func TestParseDmidecodeBIOSMissingBlock(t *testing.T) { // No BIOS Information block → nil result, not a crash. input := "Handle 0x0001, DMI type 1, 27 bytes\nSystem Information\n\tManufacturer: Acme\n" if snap := parseDmidecodeBIOS(strings.NewReader(input)); snap != nil { t.Fatalf("expected nil when BIOS block absent, got %+v", snap) } } const ipmitoolMCInfo = `Device ID : 32 Device Revision : 1 Firmware Revision : 1.74 IPMI Version : 2.0 Manufacturer ID : 10876 Manufacturer Name : Supermicro Product ID : 2051 (0x0803) Product Name : Unknown (0x803) ` func TestParseIpmitoolMCInfo(t *testing.T) { snap := parseIpmitoolMCInfo(strings.NewReader(ipmitoolMCInfo)) if snap == nil { t.Fatal("parseIpmitoolMCInfo returned nil") } if snap.Component != "bmc" { t.Errorf("component = %q, want bmc", snap.Component) } if snap.Version != "1.74" { t.Errorf("version = %q, want 1.74", snap.Version) } if snap.Vendor != "Supermicro" { t.Errorf("vendor = %q, want Supermicro", snap.Vendor) } } func TestParseIpmitoolMCInfoEmpty(t *testing.T) { if snap := parseIpmitoolMCInfo(strings.NewReader("")); snap != nil { t.Fatalf("expected nil on empty input, got %+v", snap) } } const ethtoolEth0 = `driver: mlx5_core version: 5.15.0 firmware-version: 16.32.1010 (MT_0000000008) expansion-rom-version: bus-info: 0000:5e:00.0 supports-statistics: yes ` func TestParseEthtoolI(t *testing.T) { snap := parseEthtoolI(strings.NewReader(ethtoolEth0), "eth0") if snap == nil { t.Fatal("parseEthtoolI returned nil") } if snap.Component != "nic" || snap.Identifier != "eth0" { t.Errorf("component/id = %q/%q, want nic/eth0", snap.Component, snap.Identifier) } if snap.Version != "16.32.1010 (MT_0000000008)" { t.Errorf("version = %q, want 16.32.1010 (MT_0000000008)", snap.Version) } if snap.Vendor != "mlx5_core" { t.Errorf("vendor = %q, want mlx5_core", snap.Vendor) } } func TestParseEthtoolIEmpty(t *testing.T) { if snap := parseEthtoolI(strings.NewReader("not a valid output"), "eth0"); snap != nil { t.Fatalf("expected nil on garbage input, got %+v", snap) } } const nvmeIDCtrl = `NVME Identify Controller: vid : 0x144d ssvid : 0x144d sn : S5GYNX0R500123X mn : Samsung SSD 980 PRO 1TB fr : 5B2QGXA7 rab : 2 ` func TestParseNVMeIDCtrl(t *testing.T) { if got := parseNVMeIDCtrl(strings.NewReader(nvmeIDCtrl), "fr"); got != "5B2QGXA7" { t.Errorf("fr = %q, want 5B2QGXA7", got) } if got := parseNVMeIDCtrl(strings.NewReader(nvmeIDCtrl), "mn"); got != "Samsung SSD 980 PRO 1TB" { t.Errorf("mn = %q, want Samsung SSD 980 PRO 1TB", got) } if got := parseNVMeIDCtrl(strings.NewReader(nvmeIDCtrl), "missing"); got != "" { t.Errorf("missing key should be empty, got %q", got) } } const lspciHBA = `0000:01:00.0 Ethernet controller [0200]: Intel Corporation I350 [8086:1521] (rev 01) Subsystem: Intel Corporation I350 [8086:0001] Kernel driver in use: igb Kernel modules: igb 0000:03:00.0 Serial Attached SCSI controller [0107]: Broadcom / LSI SAS3008 PCI-Express Fusion-MPT SAS-3 [1000:0097] (rev 02) Subsystem: Broadcom / LSI SAS9300-8i [1000:30e0] Kernel driver in use: mpt3sas Kernel modules: mpt3sas 0000:04:00.0 RAID bus controller [0104]: LSI MegaRAID SAS-3 3108 [1000:005d] (rev 02) Subsystem: LSI MegaRAID SAS 9361-8i [1000:9361] Kernel driver in use: megaraid_sas Kernel modules: megaraid_sas ` func TestParseLspciHBA(t *testing.T) { got := parseLspciHBA(strings.NewReader(lspciHBA)) if len(got) != 2 { t.Fatalf("got %d HBA snapshots, want 2 (SAS + RAID; Ethernet must be skipped)", len(got)) } for _, s := range got { if s.Component != "hba" { t.Errorf("component = %q, want hba", s.Component) } if s.Version != "rev 02" { t.Errorf("version = %q, want 'rev 02'", s.Version) } } if got[0].Identifier != "0000:03:00.0" { t.Errorf("first identifier = %q, want 0000:03:00.0", got[0].Identifier) } if got[1].Identifier != "0000:04:00.0" { t.Errorf("second identifier = %q, want 0000:04:00.0", got[1].Identifier) } } const cpuinfo = `processor : 0 vendor_id : GenuineIntel cpu family : 6 model : 85 model name : Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz stepping : 7 microcode : 0x5003006 cpu MHz : 2100.000 ` func TestParseMicrocode(t *testing.T) { snap := parseMicrocode(strings.NewReader(cpuinfo)) if snap == nil { t.Fatal("parseMicrocode returned nil") } if snap.Version != "0x5003006" { t.Errorf("version = %q, want 0x5003006", snap.Version) } if snap.Vendor != "GenuineIntel" { t.Errorf("vendor = %q, want GenuineIntel", snap.Vendor) } if snap.Identifier != "cpu" { t.Errorf("identifier = %q, want cpu", snap.Identifier) } } func TestParseMicrocodeMissing(t *testing.T) { // A /proc/cpuinfo without a microcode line returns nil. input := "processor\t: 0\nvendor_id\t: GenuineIntel\n" if snap := parseMicrocode(strings.NewReader(input)); snap != nil { t.Fatalf("expected nil when microcode line absent, got %+v", snap) } } func TestIsRealNIC(t *testing.T) { cases := []struct { name string want bool // want=true means a real-looking name (the /sys/class/net//device check is skipped here) }{ {"lo", false}, {"", false}, {"docker0", false}, {"br-abc", false}, {"veth1234", false}, {"virbr0", false}, {"bond0", false}, {"tun0", false}, } for _, tc := range cases { if got := isRealNIC(tc.name); got != tc.want { t.Errorf("isRealNIC(%q) = %v, want %v", tc.name, got, tc.want) } } } // dmidecode -t 39 fixture with two PSU blocks. Verifies that // parseDmidecodeAllSections returns every matching block, not just the // first (which was the old parseDmidecodeSection behavior). const dmidecodePSU = `# dmidecode 3.3 Handle 0x0040, DMI type 39, 22 bytes System Power Supply Location: PSU1 Manufacturer: DELTA Model Part Number: ABC760 Max Power Capacity: 760 W Status: Present, OK Handle 0x0041, DMI type 39, 22 bytes System Power Supply Location: PSU2 Manufacturer: DELTA Model Part Number: ABC760 Max Power Capacity: 760 W Status: Present, Unplugged ` func TestParseDmidecodeAllSections(t *testing.T) { blocks := parseDmidecodeAllSections(strings.NewReader(dmidecodePSU), "System Power Supply") if len(blocks) != 2 { t.Fatalf("expected 2 blocks, got %d", len(blocks)) } if blocks[0]["Location"] != "PSU1" || blocks[1]["Location"] != "PSU2" { t.Fatalf("locations wrong: %+v", blocks) } if blocks[1]["Status"] != "Present, Unplugged" { t.Fatalf("psu2 status: %q", blocks[1]["Status"]) } } func TestParseDmidecodeAllSectionsEmpty(t *testing.T) { blocks := parseDmidecodeAllSections(strings.NewReader(""), "Memory Device") if len(blocks) != 0 { t.Fatalf("expected 0 blocks on empty input, got %d", len(blocks)) } }