Files
logpile/internal/exporter/reanimator_converter_test.go
Mikhail Chusavitin 5815100e2f exporter: filter Supermicro Redfish device-bound firmware from hardware.firmware
isDeviceBoundFirmwareName did not catch Supermicro FirmwareInventory naming
conventions where a digit follows the type prefix directly ("GPU1 System Slot0",
"NIC1 System Slot0 AOM-DP805-IO") instead of a space. Also missing: "Power supply N",
"NVMeController N", and "Software Inventory" (generic label for all HGX per-component
firmware slots — GPU, NVSwitch, PCIeRetimer, ERoT, InfoROM, etc.).

On SYS-A21GE-NBRT (HGX B200) this caused 29 device-bound entries to leak into
hardware.firmware: 8 GPU, 9 NIC, 1 NVMe, 6 PSU, 4 PCIeSwitch, 1 Software Inventory.

Fix: extend isDeviceBoundFirmwareName with patterns for all four new cases.
Add TestIsDeviceBoundFirmwareName covering both excluded and kept entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 13:48:55 +03:00

935 lines
24 KiB
Go

package exporter
import (
"encoding/json"
"strings"
"testing"
"time"
"git.mchus.pro/mchus/logpile/internal/models"
)
func TestConvertToReanimator(t *testing.T) {
tests := []struct {
name string
input *models.AnalysisResult
wantErr bool
errMsg string
}{
{
name: "nil result",
input: nil,
wantErr: true,
errMsg: "no data available",
},
{
name: "no hardware",
input: &models.AnalysisResult{
Filename: "test.json",
},
wantErr: true,
errMsg: "no hardware data available",
},
{
name: "no board serial",
input: &models.AnalysisResult{
Filename: "test.json",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{},
},
},
wantErr: true,
errMsg: "board serial_number is required",
},
{
name: "valid minimal data",
input: &models.AnalysisResult{
Filename: "test.json",
SourceType: "api",
Protocol: "redfish",
TargetHost: "10.10.10.10",
CollectedAt: time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC),
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{
Manufacturer: "Supermicro",
ProductName: "X12DPG-QT6",
SerialNumber: "TEST123",
},
},
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := ConvertToReanimator(tt.input)
if tt.wantErr {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errMsg)
}
return
}
if err != nil {
t.Errorf("unexpected error: %v", err)
return
}
if result == nil {
t.Error("expected non-nil result")
return
}
if result.Hardware.Board.SerialNumber != tt.input.Hardware.BoardInfo.SerialNumber {
t.Errorf("board serial mismatch: got %q, want %q",
result.Hardware.Board.SerialNumber,
tt.input.Hardware.BoardInfo.SerialNumber)
}
})
}
}
func TestInferCPUManufacturer(t *testing.T) {
tests := []struct {
model string
want string
}{
{"INTEL(R) XEON(R) GOLD 6530", "Intel"},
{"Intel Core i9-12900K", "Intel"},
{"AMD EPYC 7763", "AMD"},
{"AMD Ryzen 9 5950X", "AMD"},
{"ARM Cortex-A78", "ARM"},
{"Ampere Altra Max", "Ampere"},
{"Unknown CPU Model", ""},
}
for _, tt := range tests {
t.Run(tt.model, func(t *testing.T) {
got := inferCPUManufacturer(tt.model)
if got != tt.want {
t.Errorf("inferCPUManufacturer(%q) = %q, want %q", tt.model, got, tt.want)
}
})
}
}
func TestNormalizedSerial(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{
name: "empty",
in: "",
want: "",
},
{
name: "n_a",
in: "N/A",
want: "",
},
{
name: "unknown",
in: "unknown",
want: "",
},
{
name: "normal",
in: "SN123",
want: "SN123",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := normalizedSerial(tt.in)
if got != tt.want {
t.Errorf("normalizedSerial() = %q, want %q", got, tt.want)
}
})
}
}
func TestInferStorageStatus(t *testing.T) {
tests := []struct {
name string
stor models.Storage
want string
}{
{
name: "present",
stor: models.Storage{
Present: true,
},
want: "Unknown",
},
{
name: "not present",
stor: models.Storage{
Present: false,
},
want: "Unknown",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := inferStorageStatus(tt.stor)
if got != tt.want {
t.Errorf("inferStorageStatus() = %q, want %q", got, tt.want)
}
})
}
}
func TestNormalizeStatus_PassFail(t *testing.T) {
if got := normalizeStatus("PASS", false); got != "OK" {
t.Fatalf("expected PASS -> OK, got %q", got)
}
if got := normalizeStatus("FAIL", false); got != "Critical" {
t.Fatalf("expected FAIL -> Critical, got %q", got)
}
}
func TestConvertCPUs(t *testing.T) {
cpus := []models.CPU{
{
Socket: 0,
Model: "INTEL(R) XEON(R) GOLD 6530",
Cores: 32,
Threads: 64,
FrequencyMHz: 2100,
MaxFreqMHz: 4000,
},
{
Socket: 1,
Model: "AMD EPYC 7763",
Cores: 64,
Threads: 128,
FrequencyMHz: 2450,
MaxFreqMHz: 3500,
},
}
result := convertCPUs(cpus, "2026-02-10T15:30:00Z")
if len(result) != 2 {
t.Fatalf("expected 2 CPUs, got %d", len(result))
}
if result[0].Manufacturer != "Intel" {
t.Errorf("expected Intel manufacturer for first CPU, got %q", result[0].Manufacturer)
}
if result[1].Manufacturer != "AMD" {
t.Errorf("expected AMD manufacturer for second CPU, got %q", result[1].Manufacturer)
}
if result[0].Status != "Unknown" {
t.Errorf("expected Unknown status, got %q", result[0].Status)
}
}
func TestConvertMemory(t *testing.T) {
memory := []models.MemoryDIMM{
{
Slot: "CPU0_C0D0",
Present: true,
SizeMB: 32768,
Type: "DDR5",
SerialNumber: "TEST-MEM-001",
Status: "OK",
},
{
Slot: "CPU0_C1D0",
Present: false,
},
}
result := convertMemory(memory, "2026-02-10T15:30:00Z")
if len(result) != 2 {
t.Fatalf("expected 2 memory modules, got %d", len(result))
}
if result[0].Status != "OK" {
t.Errorf("expected OK status for first module, got %q", result[0].Status)
}
if result[1].Status != "Empty" {
t.Errorf("expected Empty status for second module, got %q", result[1].Status)
}
}
func TestConvertStorage(t *testing.T) {
storage := []models.Storage{
{
Slot: "OB01",
Type: "NVMe",
Model: "INTEL SSDPF2KX076T1",
SerialNumber: "BTAX41900GF87P6DGN",
Present: true,
},
{
Slot: "OB02",
Type: "NVMe",
Model: "INTEL SSDPF2KX076T1",
SerialNumber: "", // No serial - should be skipped
Present: true,
},
}
result := convertStorage(storage, "2026-02-10T15:30:00Z")
if len(result) != 1 {
t.Fatalf("expected 1 storage device (skipped one without serial), got %d", len(result))
}
if result[0].Status != "Unknown" {
t.Errorf("expected Unknown status, got %q", result[0].Status)
}
}
func TestConvertPCIeDevices(t *testing.T) {
hw := &models.HardwareConfig{
PCIeDevices: []models.PCIeDevice{
{
Slot: "PCIeCard1",
VendorID: 32902,
DeviceID: 2912,
BDF: "0000:18:00.0",
DeviceClass: "MassStorageController",
Manufacturer: "Intel",
PartNumber: "RSP3DD080F",
SerialNumber: "RAID-001",
},
{
Slot: "PCIeCard2",
DeviceClass: "NetworkController",
Manufacturer: "Mellanox",
SerialNumber: "", // Should be generated
},
},
GPUs: []models.GPU{
{
Slot: "GPU1",
Model: "NVIDIA A100",
Manufacturer: "NVIDIA",
SerialNumber: "GPU-001",
Status: "OK",
},
},
NetworkAdapters: []models.NetworkAdapter{
{
Slot: "NIC1",
Model: "ConnectX-6",
Vendor: "Mellanox",
Present: true,
SerialNumber: "NIC-001",
},
},
}
result := convertPCIeDevices(hw, "2026-02-10T15:30:00Z")
// Should have: 2 PCIe devices + 1 GPU + 1 NIC = 4 total
if len(result) != 4 {
t.Fatalf("expected 4 PCIe devices total, got %d", len(result))
}
// Check that serial is empty for second PCIe device (no auto-generation)
if result[1].SerialNumber != "" {
t.Errorf("expected empty serial for missing device serial, got %q", result[1].SerialNumber)
}
// Check GPU was included
foundGPU := false
for _, dev := range result {
if dev.SerialNumber == "GPU-001" {
foundGPU = true
if dev.DeviceClass != "DisplayController" {
t.Errorf("expected GPU device_class DisplayController, got %q", dev.DeviceClass)
}
break
}
}
if !foundGPU {
t.Error("expected GPU to be included in PCIe devices")
}
}
func TestConvertPCIeDevices_NVSwitchWithoutSerialRemainsEmpty(t *testing.T) {
hw := &models.HardwareConfig{
Firmware: []models.FirmwareInfo{
{
DeviceName: "NVSwitch NVSWITCH1 (965-25612-0002-000)",
Version: "96.10.6D.00.01",
},
},
PCIeDevices: []models.PCIeDevice{
{
Slot: "NVSWITCH1",
DeviceClass: "NVSwitch",
BDF: "0000:06:00.0",
// SerialNumber empty on purpose; should remain empty.
},
},
}
result := convertPCIeDevices(hw, "2026-02-10T15:30:00Z")
if len(result) != 1 {
t.Fatalf("expected 1 PCIe device, got %d", len(result))
}
if result[0].SerialNumber != "" {
t.Fatalf("expected empty NVSwitch serial, got %q", result[0].SerialNumber)
}
if result[0].Firmware != "96.10.6D.00.01" {
t.Fatalf("expected NVSwitch firmware 96.10.6D.00.01, got %q", result[0].Firmware)
}
}
func TestConvertPCIeDevices_SkipsDisplayControllerDuplicates(t *testing.T) {
hw := &models.HardwareConfig{
PCIeDevices: []models.PCIeDevice{
{
Slot: "#GPU0",
DeviceClass: "3D Controller",
},
},
GPUs: []models.GPU{
{
Slot: "#GPU0",
Model: "B200 180GB HBM3e",
Manufacturer: "NVIDIA",
SerialNumber: "1655024043371",
Status: "OK",
},
},
}
result := convertPCIeDevices(hw, "2026-02-10T15:30:00Z")
if len(result) != 1 {
t.Fatalf("expected only dedicated GPU record without duplicate display PCIe, got %d", len(result))
}
if result[0].DeviceClass != "DisplayController" {
t.Fatalf("expected GPU record with DisplayController class, got %q", result[0].DeviceClass)
}
if result[0].Status != "OK" {
t.Fatalf("expected GPU status OK, got %q", result[0].Status)
}
}
func TestConvertPCIeDevices_MapsGPUStatusHistory(t *testing.T) {
hw := &models.HardwareConfig{
GPUs: []models.GPU{
{
Slot: "#GPU6",
Model: "B200 180GB HBM3e",
Manufacturer: "NVIDIA",
SerialNumber: "1655024043204",
Status: "Critical",
StatusHistory: []models.StatusHistoryEntry{
{
Status: "Critical",
ChangedAt: time.Date(2026, 1, 12, 15, 5, 18, 0, time.UTC),
Details: "BIOS miss F_GPU6",
},
},
ErrorDescription: "BIOS miss F_GPU6",
},
},
}
result := convertPCIeDevices(hw, "2026-02-10T15:30:00Z")
if len(result) != 1 {
t.Fatalf("expected 1 converted GPU, got %d", len(result))
}
if len(result[0].StatusHistory) != 1 {
t.Fatalf("expected 1 history entry, got %d", len(result[0].StatusHistory))
}
if result[0].StatusHistory[0].ChangedAt != "2026-01-12T15:05:18Z" {
t.Fatalf("unexpected history changed_at: %q", result[0].StatusHistory[0].ChangedAt)
}
if result[0].StatusAtCollect == nil || result[0].StatusAtCollect.At != "2026-02-10T15:30:00Z" {
t.Fatalf("expected status_at_collection to be populated from collected_at")
}
}
func TestConvertPowerSupplies(t *testing.T) {
psus := []models.PSU{
{
Slot: "0",
Present: true,
Model: "GW-CRPS3000LW",
Vendor: "Great Wall",
WattageW: 3000,
SerialNumber: "PSU-001",
Status: "OK",
},
{
Slot: "1",
Present: false,
SerialNumber: "", // Not present, should be skipped
},
}
result := convertPowerSupplies(psus, "2026-02-10T15:30:00Z")
if len(result) != 1 {
t.Fatalf("expected 1 PSU (skipped empty), got %d", len(result))
}
if result[0].Status != "OK" {
t.Errorf("expected OK status, got %q", result[0].Status)
}
}
func TestConvertBoardNormalizesNULL(t *testing.T) {
board := convertBoard(models.BoardInfo{
Manufacturer: " NULL ",
ProductName: "null",
SerialNumber: "TEST123",
})
if board.Manufacturer != "" {
t.Fatalf("expected empty manufacturer, got %q", board.Manufacturer)
}
if board.ProductName != "" {
t.Fatalf("expected empty product_name, got %q", board.ProductName)
}
}
func TestSourceTypeOmittedWhenInvalidOrEmpty(t *testing.T) {
result, err := ConvertToReanimator(&models.AnalysisResult{
Filename: "redfish://10.0.0.1",
SourceType: "archive",
TargetHost: "10.0.0.1",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "TEST123"},
},
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
payload, err := json.Marshal(result)
if err != nil {
t.Fatalf("marshal failed: %v", err)
}
if strings.Contains(string(payload), `"source_type"`) {
t.Fatalf("expected source_type to be omitted for invalid value, got %s", string(payload))
}
}
func TestTargetHostOmittedWhenUnavailable(t *testing.T) {
result, err := ConvertToReanimator(&models.AnalysisResult{
Filename: "test.json",
SourceType: "api",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "TEST123"},
},
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
payload, err := json.Marshal(result)
if err != nil {
t.Fatalf("marshal failed: %v", err)
}
if strings.Contains(string(payload), `"target_host"`) {
t.Fatalf("expected target_host to be omitted when unavailable, got %s", string(payload))
}
}
func TestInferTargetHost(t *testing.T) {
tests := []struct {
name string
targetHost string
filename string
want string
}{
{
name: "explicit target host wins",
targetHost: "10.0.0.10",
filename: "redfish://10.0.0.20",
want: "10.0.0.10",
},
{
name: "hostname from URL",
filename: "redfish://10.10.10.103",
want: "10.10.10.103",
},
{
name: "ip extracted from archive name",
filename: "nvidia_bug_report_192.168.12.34.tar.gz",
want: "192.168.12.34",
},
{
name: "no host available",
filename: "test.json",
want: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := inferTargetHost(tt.targetHost, tt.filename)
if got != tt.want {
t.Fatalf("inferTargetHost() = %q, want %q", got, tt.want)
}
})
}
}
func TestConvertToReanimator_DeduplicatesAllSections(t *testing.T) {
input := &models.AnalysisResult{
Filename: "dup-test.json",
CollectedAt: time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC),
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
Firmware: []models.FirmwareInfo{
{DeviceName: "BMC", Version: "1.0"},
{DeviceName: "BMC", Version: "1.1"},
},
CPUs: []models.CPU{
{Socket: 0, Model: "CPU-A"},
{Socket: 0, Model: "CPU-A-DUP"},
},
Memory: []models.MemoryDIMM{
{Slot: "DIMM_A1", Present: true, SizeMB: 32768, SerialNumber: "MEM-1", Status: "OK"},
{Slot: "DIMM_A1", Present: true, SizeMB: 32768, SerialNumber: "MEM-1-DUP", Status: "OK"},
},
Storage: []models.Storage{
{Slot: "U.2-1", SerialNumber: "SSD-1", Model: "Disk1", Present: true},
{Slot: "U.2-2", SerialNumber: "SSD-1", Model: "Disk1-dup", Present: true},
},
PCIeDevices: []models.PCIeDevice{
{Slot: "#GPU0", DeviceClass: "3D Controller", BDF: "17:00.0"},
{Slot: "SLOT-NIC1", DeviceClass: "NetworkController", BDF: "18:00.0"},
{Slot: "SLOT-NIC1", DeviceClass: "NetworkController", BDF: "18:00.1"},
},
GPUs: []models.GPU{
{Slot: "#GPU0", Model: "B200 180GB HBM3e", SerialNumber: "GPU-1", Status: "OK"},
},
PowerSupply: []models.PSU{
{Slot: "0", Present: true, SerialNumber: "PSU-1", Status: "OK"},
{Slot: "1", Present: true, SerialNumber: "PSU-1", Status: "OK"},
},
},
}
out, err := ConvertToReanimator(input)
if err != nil {
t.Fatalf("ConvertToReanimator() failed: %v", err)
}
if len(out.Hardware.Firmware) != 1 {
t.Fatalf("expected deduped firmware len=1, got %d", len(out.Hardware.Firmware))
}
if len(out.Hardware.CPUs) != 2 {
t.Fatalf("expected cpus len=2 (no serial/bdf dedupe), got %d", len(out.Hardware.CPUs))
}
if len(out.Hardware.Memory) != 2 {
t.Fatalf("expected memory len=2 (different serials), got %d", len(out.Hardware.Memory))
}
if len(out.Hardware.Storage) != 1 {
t.Fatalf("expected deduped storage len=1, got %d", len(out.Hardware.Storage))
}
if len(out.Hardware.PowerSupplies) != 1 {
t.Fatalf("expected deduped psu len=1, got %d", len(out.Hardware.PowerSupplies))
}
if len(out.Hardware.PCIeDevices) != 4 {
t.Fatalf("expected pcie len=4 with serial->bdf dedupe, got %d", len(out.Hardware.PCIeDevices))
}
gpuCount := 0
for _, dev := range out.Hardware.PCIeDevices {
if dev.Slot == "#GPU0" {
gpuCount++
}
}
if gpuCount != 2 {
t.Fatalf("expected two #GPU0 records (pcie+gpu kinds), got %d", gpuCount)
}
}
func TestConvertToReanimator_StatusFallbackUsesCollectedAt(t *testing.T) {
collectedAt := time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC)
input := &models.AnalysisResult{
Filename: "status-fallback.json",
CollectedAt: collectedAt,
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
Storage: []models.Storage{
{
Slot: "U.2-1",
Model: "PM9A3",
SerialNumber: "SSD-001",
Present: true,
Status: "OK",
},
},
},
}
out, err := ConvertToReanimator(input)
if err != nil {
t.Fatalf("ConvertToReanimator() failed: %v", err)
}
if len(out.Hardware.Storage) != 1 {
t.Fatalf("expected 1 storage entry, got %d", len(out.Hardware.Storage))
}
wantTs := collectedAt.UTC().Format(time.RFC3339)
got := out.Hardware.Storage[0]
if got.StatusCheckedAt != wantTs {
t.Fatalf("expected status_checked_at=%q, got %q", wantTs, got.StatusCheckedAt)
}
if got.StatusAtCollect == nil || got.StatusAtCollect.At != wantTs {
t.Fatalf("expected status_at_collection.at=%q, got %#v", wantTs, got.StatusAtCollect)
}
}
func TestConvertToReanimator_FirmwareExcludesDeviceBoundEntries(t *testing.T) {
input := &models.AnalysisResult{
Filename: "fw-filter-test.json",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
Firmware: []models.FirmwareInfo{
{DeviceName: "BIOS", Version: "1.0.0"},
{DeviceName: "BMC", Version: "2.0.0"},
{DeviceName: "GPU GPUSXM1 (692-2G520-0280-501)", Version: "96.00.D0.00.03"},
{DeviceName: "NVSwitch NVSWITCH0 (965-25612-0002-000)", Version: "96.10.6D.00.01"},
{DeviceName: "NIC #CPU1_PCIE9 (MCX512A-ACAT)", Version: "28.38.1900"},
{DeviceName: "CPU0 Microcode", Version: "0x2b000643"},
},
},
}
out, err := ConvertToReanimator(input)
if err != nil {
t.Fatalf("ConvertToReanimator() failed: %v", err)
}
if len(out.Hardware.Firmware) != 2 {
t.Fatalf("expected only machine-level firmware entries, got %d", len(out.Hardware.Firmware))
}
got := map[string]string{}
for _, fw := range out.Hardware.Firmware {
got[fw.DeviceName] = fw.Version
}
if got["BIOS"] != "1.0.0" {
t.Fatalf("expected BIOS firmware to be kept")
}
if got["BMC"] != "2.0.0" {
t.Fatalf("expected BMC firmware to be kept")
}
if _, exists := got["GPU GPUSXM1 (692-2G520-0280-501)"]; exists {
t.Fatalf("expected GPU firmware to be excluded from hardware.firmware")
}
if _, exists := got["NVSwitch NVSWITCH0 (965-25612-0002-000)"]; exists {
t.Fatalf("expected NVSwitch firmware to be excluded from hardware.firmware")
}
}
func TestConvertToReanimator_UsesCanonicalDevices(t *testing.T) {
input := &models.AnalysisResult{
Filename: "canonical.json",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
Devices: []models.HardwareDevice{
{
Kind: models.DeviceKindCPU,
Slot: "CPU0",
Model: "INTEL(R) XEON(R)",
Cores: 32,
Threads: 64,
FrequencyMHz: 2100,
},
{
Kind: models.DeviceKindStorage,
Slot: "U.2-1",
Model: "Disk1",
SerialNumber: "SSD-1",
Present: boolPtr(true),
},
},
},
}
out, err := ConvertToReanimator(input)
if err != nil {
t.Fatalf("ConvertToReanimator() failed: %v", err)
}
if len(out.Hardware.CPUs) != 1 {
t.Fatalf("expected cpu from hardware.devices, got %d", len(out.Hardware.CPUs))
}
if len(out.Hardware.Storage) != 1 {
t.Fatalf("expected storage from hardware.devices, got %d", len(out.Hardware.Storage))
}
}
func TestConvertToReanimator_BindsDeviceVitals(t *testing.T) {
input := &models.AnalysisResult{
Filename: "vitals.json",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
Devices: []models.HardwareDevice{
{
Kind: models.DeviceKindGPU,
Slot: "#GPU0",
Model: "B200 180GB HBM3e",
SerialNumber: "GPU-001",
BDF: "0000:17:00.0",
Details: map[string]any{
"temperature": 71,
"power": 350,
"voltage": 12.2,
},
},
{
Kind: models.DeviceKindPSU,
Slot: "PSU0",
SerialNumber: "PSU-001",
Present: boolPtr(true),
InputPowerW: 1400,
OutputPowerW: 1300,
InputVoltage: 229.5,
TemperatureC: 44,
},
},
},
}
out, err := ConvertToReanimator(input)
if err != nil {
t.Fatalf("ConvertToReanimator() failed: %v", err)
}
if len(out.Hardware.PCIeDevices) != 1 {
t.Fatalf("expected one pcie device, got %d", len(out.Hardware.PCIeDevices))
}
pcie := out.Hardware.PCIeDevices[0]
if pcie.TemperatureC != 71 {
t.Fatalf("expected GPU temperature 71C, got %d", pcie.TemperatureC)
}
if pcie.PowerW != 350 {
t.Fatalf("expected GPU power 350W, got %d", pcie.PowerW)
}
if pcie.VoltageV != 12.2 {
t.Fatalf("expected device voltage 12.2V, got %.2f", pcie.VoltageV)
}
if len(out.Hardware.PowerSupplies) != 1 {
t.Fatalf("expected one PSU, got %d", len(out.Hardware.PowerSupplies))
}
psu := out.Hardware.PowerSupplies[0]
if psu.TemperatureC != 44 {
t.Fatalf("expected PSU temperature 44C, got %d", psu.TemperatureC)
}
}
func TestConvertToReanimator_PreservesVitalsAcrossCanonicalDedup(t *testing.T) {
input := &models.AnalysisResult{
Filename: "dedup-vitals.json",
Hardware: &models.HardwareConfig{
BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"},
PCIeDevices: []models.PCIeDevice{
{
Slot: "#GPU0",
BDF: "0000:17:00.0",
DeviceClass: "3D Controller",
PartNumber: "Generic Display",
Manufacturer: "NVIDIA",
SerialNumber: "GPU-SN-001",
},
},
GPUs: []models.GPU{
{
Slot: "#GPU0",
BDF: "0000:17:00.0",
Model: "B200 180GB HBM3e",
Manufacturer: "NVIDIA",
SerialNumber: "GPU-SN-001",
Temperature: 67,
Power: 330,
Status: "OK",
},
},
},
}
out, err := ConvertToReanimator(input)
if err != nil {
t.Fatalf("ConvertToReanimator() failed: %v", err)
}
if len(out.Hardware.PCIeDevices) != 1 {
t.Fatalf("expected deduped one pcie entry, got %d", len(out.Hardware.PCIeDevices))
}
got := out.Hardware.PCIeDevices[0]
if got.TemperatureC != 67 {
t.Fatalf("expected deduped GPU temperature 67C, got %d", got.TemperatureC)
}
if got.PowerW != 330 {
t.Fatalf("expected deduped GPU power 330W, got %d", got.PowerW)
}
}
func boolPtr(v bool) *bool { return &v }
// TestIsDeviceBoundFirmwareName verifies that device-bound firmware entries from
// Supermicro Redfish FirmwareInventory are correctly identified and excluded from
// hardware.firmware.
//
// Regression guard: names like "GPU1 System Slot0" and "NIC1 System Slot0 ..." were
// not caught because the old check required "gpu " / "nic " (with space), while
// Supermicro places a digit immediately after the type prefix. (2026-03-12)
func TestIsDeviceBoundFirmwareName(t *testing.T) {
cases := []struct {
name string
want bool
}{
// Supermicro Redfish — device-bound, must be excluded
{"GPU1 System Slot0", true},
{"GPU8 System Slot0", true},
{"NIC1 System Slot0 AOM-DP805-IO", true},
{"NIC9 System Slot8 MCX75310AAS-NEAT", true},
{"NVMeController1", true},
{"Power supply 1", true},
{"Power supply 6", true},
{"Software Inventory", true},
{"software inventory", true}, // case-insensitive
// Generic / legacy names already covered before this fix
{"GPU SomeDevice", true},
{"NIC OnboardLAN", true},
{"PSU1", true},
{"NVMe Drive", true},
// HGX FW ID patterns (in case Id is used as name)
{"HGX_FW_GPU_SXM_1", true},
{"HGX_InfoROM_GPU_SXM_2", true},
// System-level firmware — must NOT be excluded
{"BIOS", false},
{"BMC", false},
{"BMC Backup", false},
{"Capsule BIOS", false},
{"Capsule ME", false},
{"CPLD Motherboard Golden", false},
{"CPLD AOMboard", false},
{"FrontFanboard CPLD", false},
{"Motherboard PCIeSwitch 1", false}, // board-integrated, no device record
{"SecureBoot", false},
{"BIOS ME", false},
}
for _, tc := range cases {
got := isDeviceBoundFirmwareName(tc.name)
if got != tc.want {
t.Errorf("isDeviceBoundFirmwareName(%q) = %v, want %v", tc.name, got, tc.want)
}
}
}