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) } if result[0].SerialNumber != "" { t.Errorf("expected empty CPU serial when source serial is absent, got %q", result[0].SerialNumber) } } 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) != 1 { t.Fatalf("expected 1 populated memory module, got %d", len(result)) } if result[0].Status != "OK" { t.Errorf("expected OK status for first module, got %q", result[0].Status) } } func TestConvertMemory_KeepsInstalledDIMMWithUnknownSize(t *testing.T) { memory := []models.MemoryDIMM{ { Slot: "PROC 1 DIMM 3", Present: true, SizeMB: 0, Manufacturer: "Hynix", PartNumber: "HMCG88AEBRA115N", SerialNumber: "2B5F92C6", Status: "OK", }, } result := convertMemory(memory, "2026-03-30T10:00:00Z") if len(result) != 1 { t.Fatalf("expected 1 inventory-only DIMM, got %d", len(result)) } if result[0].PartNumber != "HMCG88AEBRA115N" || result[0].SerialNumber != "2B5F92C6" || result[0].SizeMB != 0 { t.Fatalf("unexpected converted memory: %+v", result[0]) } } func TestConvertToReanimator_CPUSerialIsNotSynthesizedAndSocketIsDeduped(t *testing.T) { input := &models.AnalysisResult{ Filename: "cpu-dedupe.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Devices: []models.HardwareDevice{ { Kind: models.DeviceKindCPU, Slot: "CPU1", Model: "Xeon Platinum", Cores: 56, Status: "OK", Details: map[string]any{ "socket": 1, }, }, }, CPUs: []models.CPU{ {Socket: 1, Model: "Xeon Platinum", Cores: 56, Status: "OK"}, {Socket: 2, Model: "Xeon Platinum", Cores: 56, Status: "OK"}, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.CPUs) != 2 { t.Fatalf("expected exactly two CPUs after socket dedupe, got %d", len(out.Hardware.CPUs)) } for _, cpu := range out.Hardware.CPUs { if cpu.SerialNumber != "" { t.Fatalf("expected CPU serial to stay empty when source serial is absent, got %q", cpu.SerialNumber) } } } func TestConvertToReanimator_ExportsEventLogsAndOmitsPCIeBDFJSON(t *testing.T) { input := &models.AnalysisResult{ Filename: "events.json", CollectedAt: time.Date(2026, 3, 15, 12, 0, 0, 0, time.UTC), Events: []models.Event{ { ID: "0x0042", Timestamp: time.Date(2026, 3, 15, 11, 59, 0, 0, time.UTC), Source: "SEL", SensorName: "CPU0_C0D0", Severity: models.SeverityWarning, Description: "Correctable ECC error threshold exceeded", RawData: "sel_record_id=42", }, { Source: "LOGPile", Severity: models.SeverityWarning, Description: "internal warning should not leak to event_logs", }, }, Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Devices: []models.HardwareDevice{ { Kind: models.DeviceKindPCIe, Slot: "", BDF: "0000:18:00.0", DeviceClass: "NetworkController", Manufacturer: "Mellanox", Model: "ConnectX-6", Status: "OK", Details: map[string]any{ "manufactured_year_week": "2024-W07", }, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.EventLogs) != 1 { t.Fatalf("expected 1 exported event log, got %d", len(out.Hardware.EventLogs)) } log := out.Hardware.EventLogs[0] if log.Source != "bmc" { t.Fatalf("expected SEL source to map to bmc, got %#v", log) } if log.ComponentRef != "CPU0_C0D0" { t.Fatalf("expected sensor name to map to component_ref, got %#v", log) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected 1 pcie device, got %d", len(out.Hardware.PCIeDevices)) } if out.Hardware.PCIeDevices[0].Slot != "0000:18:00.0" { t.Fatalf("expected slot to fall back to BDF, got %#v", out.Hardware.PCIeDevices[0]) } if out.Hardware.PCIeDevices[0].ManufacturedYearWeek != "2024-W07" { t.Fatalf("expected manufactured_year_week to be exported, got %#v", out.Hardware.PCIeDevices[0]) } payload, err := json.Marshal(out) if err != nil { t.Fatalf("json.Marshal() failed: %v", err) } if strings.Contains(string(payload), `"bdf"`) { t.Fatalf("expected pcie bdf field to stay out of JSON payload: %s", payload) } } func TestConvertToReanimator_EventLogSourceMappingSupportsDellAndHostSyslog(t *testing.T) { input := &models.AnalysisResult{ Filename: "event-source-map.json", CollectedAt: time.Date(2026, 3, 15, 12, 0, 0, 0, time.UTC), Events: []models.Event{ { ID: "SYS1001", Timestamp: time.Date(2026, 3, 15, 11, 58, 0, 0, time.UTC), Source: "iDRAC", SensorName: "NIC.Slot.1-1-1", Severity: models.SeverityWarning, Description: "Link is down", }, { ID: "syslog_1", Timestamp: time.Date(2026, 3, 15, 11, 59, 0, 0, time.UTC), Source: "syslog", SensorName: "systemd[1]", Severity: models.SeverityInfo, Description: "Started Example Service", }, }, Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.EventLogs) != 2 { t.Fatalf("expected 2 event logs, got %d", len(out.Hardware.EventLogs)) } if out.Hardware.EventLogs[0].Source != "bmc" { t.Fatalf("expected iDRAC event to map to bmc, got %#v", out.Hardware.EventLogs[0]) } if out.Hardware.EventLogs[1].Source != "host" { t.Fatalf("expected syslog event to map to host, got %#v", out.Hardware.EventLogs[1]) } } 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: "", Present: true, }, } result := convertStorage(storage, "2026-02-10T15:30:00Z") if len(result) != 2 { t.Fatalf("expected both inventory slots to be exported, got %d", len(result)) } if result[0].Status != "Unknown" { t.Errorf("expected Unknown status, got %q", result[0].Status) } if result[1].SerialNumber != "" { t.Errorf("expected empty serial for second storage slot, got %q", result[1].SerialNumber) } if result[1].Present == nil || !*result[1].Present { t.Fatalf("expected present=true to be preserved for populated slot without serial") } } func TestConvertToReanimator_SkipsAMIVirtualStorageDevices(t *testing.T) { input := &models.AnalysisResult{ Filename: "virtual-media.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Storage: []models.Storage{ { Slot: "USB_Device1_Port4", Type: "HDD", Model: "Virtual Cdrom Device", SerialNumber: "AAAABBBBCCCC1", Manufacturer: "American Megatrends Inc.", Interface: "USB", Present: true, }, { Slot: "OB01", Type: "NVMe", Model: "Memblaze PBlaze7", SerialNumber: "REAL-NVME-001", Manufacturer: "Memblaze", Interface: "NVMe", Present: true, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.Storage) != 1 { t.Fatalf("expected only one real storage device to remain, got %d", len(out.Hardware.Storage)) } if out.Hardware.Storage[0].SerialNumber != "REAL-NVME-001" { t.Fatalf("expected virtual AMI storage to be skipped, got %#v", out.Hardware.Storage) } } func TestConvertStorage_RemainingEndurance(t *testing.T) { pct100 := 100 pct3 := 3 storage := []models.Storage{ { Slot: "0", Model: "HFS480G3H2X069N", SerialNumber: "ESEAN5254I030B26B", Present: true, RemainingEndurancePct: &pct100, }, { Slot: "1", Model: "HFS480G3H2X069N", SerialNumber: "ESEAN5254I030B26C", Present: true, // no endurance data }, { Slot: "2", Model: "HFS480G3H2X069N", SerialNumber: "ESEAN5254I030B26D", Present: true, RemainingEndurancePct: &pct3, }, } result := convertStorage(storage, "2026-03-15T00:00:00Z") if len(result) != 3 { t.Fatalf("expected 3 results, got %d", len(result)) } if result[0].RemainingEndurancePct == nil || *result[0].RemainingEndurancePct != 100 { t.Errorf("slot 0: expected remaining_endurance_pct=100, got %v", result[0].RemainingEndurancePct) } if result[1].RemainingEndurancePct != nil { t.Errorf("slot 1: expected remaining_endurance_pct absent, got %v", *result[1].RemainingEndurancePct) } if result[2].RemainingEndurancePct == nil || *result[2].RemainingEndurancePct != 3 { t.Errorf("slot 2: expected remaining_endurance_pct=3, got %v", result[2].RemainingEndurancePct) } } 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)) } // Missing serials must remain absent. 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 != "VideoController" { t.Errorf("expected GPU device_class VideoController, 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 TestConvertToReanimator_MapsHGXNVSwitchFirmwareToPCIeDevice(t *testing.T) { input := &models.AnalysisResult{ Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Firmware: []models.FirmwareInfo{ {DeviceName: "HGX_FW_ERoT_NVSwitch_0", Version: "00.02.0192.0000_n00"}, {DeviceName: "HGX_FW_NVSwitch_0", Version: "96.10.73.00.01"}, }, PCIeDevices: []models.PCIeDevice{ { Slot: "NVSwitch_0", DeviceClass: "NVSwitch", Manufacturer: "NVIDIA", Details: map[string]any{ "temperature_c": 31.59375, }, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected one NVSwitch PCIe device, got %d", len(out.Hardware.PCIeDevices)) } got := out.Hardware.PCIeDevices[0] if got.Firmware != "96.10.73.00.01" { t.Fatalf("expected HGX NVSwitch firmware to map to device, got %#v", got) } if got.TemperatureC != 31.59375 { t.Fatalf("expected NVSwitch temperature to be exported, got %#v", got) } } func TestBuildNVSwitchFirmwareBySlot_SkipsERoTFirmware(t *testing.T) { got := buildNVSwitchFirmwareBySlot([]models.FirmwareInfo{ {DeviceName: "HGX_FW_ERoT_NVSwitch_0", Version: "00.02.0192.0000_n00"}, {DeviceName: "HGX_FW_NVSwitch_0", Version: "96.10.73.00.01"}, }) if len(got) != 1 { t.Fatalf("expected only main NVSwitch firmware to remain, got %#v", got) } if got["NVSWITCH_0"] != "96.10.73.00.01" { t.Fatalf("expected main NVSwitch firmware, got %#v", got) } } 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 != "VideoController" { t.Fatalf("expected GPU record with VideoController 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) } } 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":"logfile"`) { t.Fatalf("expected archive source_type to map to logfile, 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) != 1 { t.Fatalf("expected cpus len=1 after socket dedupe, got %d", len(out.Hardware.CPUs)) } if len(out.Hardware.Memory) != 1 { t.Fatalf("expected memory len=1 after slot dedupe, 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) != 2 { t.Fatalf("expected pcie len=2 after final pcie-class dedupe, got %d", len(out.Hardware.PCIeDevices)) } gpuCount := 0 for _, dev := range out.Hardware.PCIeDevices { if dev.Slot == "#GPU0" { gpuCount++ } } if gpuCount != 1 { t.Fatalf("expected one merged #GPU0 record, 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) } } func TestConvertToReanimator_ExportsStorageInventoryWithoutSerial(t *testing.T) { collectedAt := time.Date(2026, 4, 1, 9, 0, 0, 0, time.UTC) input := &models.AnalysisResult{ Filename: "nvme-inventory.json", CollectedAt: collectedAt, Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Storage: []models.Storage{ { Slot: "OB01", Type: "NVMe", Model: "PM9A3", SerialNumber: "SSD-001", Present: true, }, { Slot: "OB02", Type: "NVMe", Model: "PM9A3", Present: true, }, { Slot: "OB03", Type: "NVMe", Model: "PM9A3", Present: false, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.Storage) != 3 { t.Fatalf("expected 3 storage entries including inventory slots without serial, got %d", len(out.Hardware.Storage)) } if out.Hardware.Storage[1].Slot != "OB02" || out.Hardware.Storage[1].SerialNumber != "" { t.Fatalf("expected OB02 storage slot without serial to survive export, got %#v", out.Hardware.Storage[1]) } if out.Hardware.Storage[2].Present == nil || *out.Hardware.Storage[2].Present { t.Fatalf("expected OB03 to preserve present=false, got %#v", out.Hardware.Storage[2]) } } func TestConvertToReanimator_FirmwareExcludesDeviceBoundEntries(t *testing.T) { input := &models.AnalysisResult{ Filename: "fw-filter-test.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, CPUs: []models.CPU{ {Socket: 0, Model: "Intel Xeon Gold"}, }, 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") } if len(out.Hardware.CPUs) != 1 { t.Fatalf("expected 1 CPU entry, got %d", len(out.Hardware.CPUs)) } if out.Hardware.CPUs[0].Firmware != "0x2b000643" { t.Fatalf("expected CPU firmware field to carry microcode, got %q", out.Hardware.CPUs[0].Firmware) } } // TestConvertToReanimator_FirmwareExcludesDellFQDDEntries verifies that Dell TSR // SoftwareIdentity firmware entries whose Description contains a device-bound FQDD // (InfiniBand.Slot.*, RAID.SL.*, etc.) are filtered from hardware.firmware. // // Regression guard: PowerEdge R6625 (8VS2LG4) — "Mellanox Network Adapter" (FQDD // InfiniBand.Slot.1-1) and "PERC H755 Front" (FQDD RAID.SL.3-1) leaked into // hardware.firmware. (2026-03-15) func TestConvertToReanimator_FirmwareExcludesDellFQDDEntries(t *testing.T) { input := &models.AnalysisResult{ Filename: "dell-fw-filter-test.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "8VS2LG4"}, Firmware: []models.FirmwareInfo{ // system-level — must be kept {DeviceName: "BIOS", Version: "1.15.3", Description: "system bios"}, {DeviceName: "iDRAC", Version: "7.20.80.50", Description: "idrac card"}, {DeviceName: "Lifecycle Controller", Version: "7.20.80.50", Description: "idrac lifecycle"}, // device-bound via FQDD — must be filtered {DeviceName: "Mellanox Network Adapter", Version: "20.39.35.60", Description: "InfiniBand.Slot.1-1"}, {DeviceName: "PERC H755 Front", Version: "52.30.0-6115", Description: "RAID.SL.3-1"}, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } got := make(map[string]string, len(out.Hardware.Firmware)) for _, fw := range out.Hardware.Firmware { got[fw.DeviceName] = fw.Version } for _, keep := range []string{"BIOS", "iDRAC", "Lifecycle Controller"} { if _, ok := got[keep]; !ok { t.Errorf("expected %q in hardware.firmware, but it was missing", keep) } } for _, drop := range []string{"Mellanox Network Adapter", "PERC H755 Front"} { if _, ok := got[drop]; ok { t.Errorf("%q must not appear in hardware.firmware (device-bound FQDD)", drop) } } } 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_MergesCanonicalAndLegacyDevices(t *testing.T) { input := &models.AnalysisResult{ Filename: "merged.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Devices: []models.HardwareDevice{ { Kind: models.DeviceKindPCIe, Slot: "PCIe 3", Model: "RAID Controller", DeviceClass: "raid_controller", Status: "ok", }, }, CPUs: []models.CPU{ {Socket: 0, Model: "Xeon Platinum", SerialNumber: "CPU-001"}, }, Memory: []models.MemoryDIMM{ {Slot: "DIMM0", Location: "DIMM0", Present: true, SizeMB: 32768, Type: "DDR5", SerialNumber: "MEM-001"}, }, Storage: []models.Storage{ {Slot: "U.2-1", Type: "NVMe", Model: "Drive1", SerialNumber: "SSD-001", Present: true}, }, PowerSupply: []models.PSU{ {Slot: "PSU0", Model: "PSU", SerialNumber: "PSU-001", Present: 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 legacy inventory to survive canonical merge, got %d", len(out.Hardware.CPUs)) } if len(out.Hardware.Memory) != 1 { t.Fatalf("expected memory from legacy inventory to survive canonical merge, got %d", len(out.Hardware.Memory)) } if len(out.Hardware.Storage) != 1 { t.Fatalf("expected storage from legacy inventory to survive canonical merge, got %d", len(out.Hardware.Storage)) } if len(out.Hardware.PowerSupplies) != 1 { t.Fatalf("expected psu from legacy inventory to survive canonical merge, got %d", len(out.Hardware.PowerSupplies)) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected supplemental canonical pcie device to remain present, got %d", len(out.Hardware.PCIeDevices)) } } func TestConvertToReanimator_DoesNotMergeStorageIntoPCIeBySharedSerial(t *testing.T) { input := &models.AnalysisResult{ Filename: "nvme-redfish.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Storage: []models.Storage{ { Slot: "Disk.Bay.0", Type: "NVMe", Model: "MZQL21T9HCJR-00A07", SerialNumber: "S64GNNFX612200", Manufacturer: "Samsung", Firmware: "GDC5A02Q", Present: true, }, }, PCIeDevices: []models.PCIeDevice{ { Slot: "NVMeSSD1", BDF: "0000:81:00.0", DeviceClass: "MassStorageController", Description: "MZQL21T9HCJR-00A07", SerialNumber: "S64GNNFX612200", Manufacturer: "Samsung", }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.Storage) != 1 { t.Fatalf("expected storage record to survive shared-serial canonical merge, got %d", len(out.Hardware.Storage)) } if out.Hardware.Storage[0].Slot != "Disk.Bay.0" { t.Fatalf("expected storage slot Disk.Bay.0, got %q", out.Hardware.Storage[0].Slot) } if len(out.Hardware.PCIeDevices) != 0 { t.Fatalf("expected NVMe storage endpoint to be excluded from pcie export, got %d records", len(out.Hardware.PCIeDevices)) } } func TestConvertToReanimator_LeavesStorageControllersInPCIe(t *testing.T) { input := &models.AnalysisResult{ Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-123"}, PCIeDevices: []models.PCIeDevice{ { Slot: "PCIe Slot 3", BDF: "0000:5e:00.0", DeviceClass: "MassStorageController", Description: "MegaRAID Controller", PartNumber: "PERC H755", SerialNumber: "RAID-001", Manufacturer: "Dell", }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected RAID controller to remain in pcie export, got %d", len(out.Hardware.PCIeDevices)) } } func TestConvertToReanimator_PCIePlaceholderModelFallsBackToPCIIDs(t *testing.T) { input := &models.AnalysisResult{ Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-123"}, Devices: []models.HardwareDevice{ { Kind: models.DeviceKindNetwork, Slot: "NIC1", Model: "Network Device View", VendorID: 0x15b3, DeviceID: 0x101d, Manufacturer: "Mellanox", Present: boolPtr(true), }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected one pcie export, got %d", len(out.Hardware.PCIeDevices)) } if strings.EqualFold(out.Hardware.PCIeDevices[0].Model, "Network Device View") { t.Fatalf("expected placeholder model to be replaced, got %q", out.Hardware.PCIeDevices[0].Model) } if out.Hardware.PCIeDevices[0].SerialNumber != "" { t.Fatalf("expected missing pcie serial to stay empty, got %q", out.Hardware.PCIeDevices[0].SerialNumber) } } func TestConvertToReanimator_SkipsPlaceholderNetworkPCIeRecords(t *testing.T) { input := &models.AnalysisResult{ Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-123"}, Devices: []models.HardwareDevice{ { Kind: models.DeviceKindNetwork, Slot: "1", Status: "Unknown", }, { Kind: models.DeviceKindNetwork, Slot: "NIC2", Model: "ConnectX-7", Manufacturer: "NVIDIA", Present: boolPtr(true), }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected only one meaningful pcie-class device, got %d", len(out.Hardware.PCIeDevices)) } if out.Hardware.PCIeDevices[0].Slot != "NIC2" { t.Fatalf("expected placeholder numeric-slot NIC to be skipped, got %+v", out.Hardware.PCIeDevices) } } func TestConvertToReanimator_ExportsSensorsAndPSUTelemetry(t *testing.T) { input := &models.AnalysisResult{ Filename: "vitals.json", Sensors: []models.SensorReading{ {Name: "FAN1", Type: "fan", Value: 4200, Unit: "RPM", Status: "OK"}, {Name: "12V Rail", Type: "voltage", Value: 12.1, Unit: "V", Status: "OK"}, {Name: "CPU0 Temp", Type: "temperature", Value: 71, Unit: "C", Status: "Warning"}, {Name: "Humidity", Type: "humidity", Value: 38.5, Unit: "%", Status: "OK"}, }, 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", }, { 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 != 0 { t.Fatalf("expected canonical GPU telemetry to stay off the component unless sourced from details/gpu path, got %.2f", pcie.TemperatureC) } if len(out.Hardware.PowerSupplies) != 1 { t.Fatalf("expected one PSU, got %d", len(out.Hardware.PowerSupplies)) } psu := out.Hardware.PowerSupplies[0] if psu.InputPowerW != 1400 { t.Fatalf("expected PSU input power 1400W, got %.2f", psu.InputPowerW) } if psu.TemperatureC != 44 { t.Fatalf("expected PSU temperature 44C, got %.2f", psu.TemperatureC) } if out.Hardware.Sensors == nil { t.Fatalf("expected sensors section") } if len(out.Hardware.Sensors.Fans) != 1 || out.Hardware.Sensors.Fans[0].RPM != 4200 { t.Fatalf("expected fan sensor export, got %#v", out.Hardware.Sensors.Fans) } if len(out.Hardware.Sensors.Power) != 1 || out.Hardware.Sensors.Power[0].VoltageV != 12.1 { t.Fatalf("expected power sensor export, got %#v", out.Hardware.Sensors.Power) } if len(out.Hardware.Sensors.Temperatures) != 1 || out.Hardware.Sensors.Temperatures[0].Celsius != 71 { t.Fatalf("expected temperature sensor export, got %#v", out.Hardware.Sensors.Temperatures) } if len(out.Hardware.Sensors.Other) != 1 || out.Hardware.Sensors.Other[0].Unit != "%" { t.Fatalf("expected other sensor export, got %#v", out.Hardware.Sensors.Other) } } func TestConvertToReanimator_SkipsSensorsWithoutNumericReadings(t *testing.T) { input := &models.AnalysisResult{ Filename: "sensor-gaps.json", Sensors: []models.SensorReading{ {Name: "CPU0 Temp", Type: "temperature", Status: "OK", RawValue: "N/A"}, {Name: "PSU1 Power", Type: "power", Status: "OK", RawValue: ""}, {Name: "Fan1", Type: "fan", Status: "OK", RawValue: "not present"}, {Name: "Humidity", Type: "humidity", Status: "OK", RawValue: "unknown"}, }, Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if out.Hardware.Sensors != nil { t.Fatalf("expected sensors to be omitted when all readings are non-numeric, got %+v", out.Hardware.Sensors) } } func TestConvertToReanimator_MergesSiblingPowerSensors(t *testing.T) { input := &models.AnalysisResult{ Filename: "power-sensors.json", Sensors: []models.SensorReading{ {Name: "Power Supply Bay 8_InputPower", Type: "power", Value: 231, Unit: "W", Status: "OK"}, {Name: "Power Supply Bay 8_InputVoltage", Type: "voltage", Value: 228, Unit: "V", Status: "OK"}, }, Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if out.Hardware.Sensors == nil || len(out.Hardware.Sensors.Power) != 1 { t.Fatalf("expected one merged power sensor, got %#v", out.Hardware.Sensors) } got := out.Hardware.Sensors.Power[0] if got.Name != "Power Supply Bay 8" { t.Fatalf("expected merged sensor name, got %q", got.Name) } if got.PowerW != 231 || got.VoltageV != 228 { t.Fatalf("expected merged power/voltage readings, got %#v", got) } if got.Location != "" { t.Fatalf("expected sensor location to be omitted, got %#v", got) } } func TestConvertToReanimator_MergesInspurPSUInputAndOutputSensors(t *testing.T) { input := &models.AnalysisResult{ Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-123"}, }, Sensors: []models.SensorReading{ {Name: "PSU0_VIN", Type: "voltage", Value: 224, Unit: "V", Status: "OK"}, {Name: "PSU0_PIN", Type: "power", Value: 120, Unit: "W", Status: "OK"}, {Name: "PSU0_VOUT", Type: "voltage", Value: 12, Unit: "V", Status: "OK"}, {Name: "PSU0_POUT", Type: "power", Value: 88, Unit: "W", Status: "OK"}, {Name: "PSU0_OutputPower", Type: "power", Value: 95, Unit: "W", Status: "OK"}, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if out.Hardware.Sensors == nil || len(out.Hardware.Sensors.Power) != 2 { t.Fatalf("expected 2 grouped PSU power sensors, got %#v", out.Hardware.Sensors) } byName := map[string]ReanimatorPowerSensor{} for _, item := range out.Hardware.Sensors.Power { byName[item.Name] = item } if got, ok := byName["PSU0"]; !ok || got.VoltageV != 224 || got.PowerW != 120 { t.Fatalf("expected PSU0 input group with VIN/PIN merged, got %#v", byName) } if got, ok := byName["PSU0_Output"]; !ok || got.VoltageV != 12 || got.PowerW != 95 { t.Fatalf("expected PSU0 output group with VOUT/POUT merged, got %#v", byName) } } func TestConvertToReanimator_PreservesCanonicalDedupWithoutDeviceVitals(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.DeviceClass != "VideoController" { t.Fatalf("expected GPU to export as VideoController, got %q", got.DeviceClass) } if got.TemperatureC != 67 { t.Fatalf("expected deduped GPU temperature 67C, got %.2f", got.TemperatureC) } if got.PowerW != 330 { t.Fatalf("expected deduped GPU power 330W, got %.2f", got.PowerW) } } func TestConvertToReanimator_DedupesLooseCanonicalNICAndPCIeEntries(t *testing.T) { input := &models.AnalysisResult{ Filename: "loose-dedup.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, PCIeDevices: []models.PCIeDevice{ { Slot: "Slot 4", DeviceClass: "NetworkController", VendorID: 0x15b3, DeviceID: 0x1021, Manufacturer: "Mellanox", PartNumber: "MCX623106AC-CDAT", }, }, NetworkAdapters: []models.NetworkAdapter{ { Slot: "Slot 4", Model: "ConnectX-6", VendorID: 0x15b3, DeviceID: 0x1021, Vendor: "Mellanox", MACAddresses: []string{"00:11:22:33:44:55"}, PortCount: 2, Present: true, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.PCIeDevices) != 1 { t.Fatalf("expected one merged loose-key pcie entry, got %d", len(out.Hardware.PCIeDevices)) } got := out.Hardware.PCIeDevices[0] if got.Model == "" { t.Fatalf("expected merged pcie entry to retain a model, got empty") } if len(got.MACAddresses) != 1 || got.MACAddresses[0] != "00:11:22:33:44:55" { t.Fatalf("expected MACs from NIC side after loose merge, got %#v", got.MACAddresses) } } func TestConvertToReanimator_ExportsContractV24Telemetry(t *testing.T) { input := &models.AnalysisResult{ Filename: "contract-v24.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Devices: []models.HardwareDevice{ { Kind: models.DeviceKindCPU, Slot: "CPU0", Model: "INTEL(R) XEON(R) GOLD 6530", Details: map[string]any{ "socket": 0, "temperature_c": 61.5, "power_w": 182.0, "throttled": false, "correctable_error_count": int64(4), "uncorrectable_error_count": int64(1), "life_remaining_pct": 98.5, "life_used_pct": 1.5, }, }, { Kind: models.DeviceKindMemory, Slot: "DIMM_A1", SerialNumber: "MEM-001", Present: boolPtr(true), SizeMB: 32768, Type: "DDR5", Details: map[string]any{ "temperature_c": 43.0, "correctable_ecc_error_count": int64(2), "uncorrectable_ecc_error_count": int64(0), "life_remaining_pct": 99.0, "spare_blocks_remaining_pct": 97.0, "performance_degraded": false, }, }, { Kind: models.DeviceKindStorage, Slot: "U.2-1", SerialNumber: "SSD-001", Model: "PM9A3", Present: boolPtr(true), Details: map[string]any{ "temperature_c": 38.5, "power_on_hours": int64(12450), "unsafe_shutdowns": int64(3), "written_bytes": int64(9876543210), "life_remaining_pct": 91.0, "available_spare_pct": 88.0, "offline_uncorrectable": int64(0), }, }, { Kind: models.DeviceKindPCIe, Slot: "PCIeCard2", SerialNumber: "NIC-001", DeviceClass: "EthernetController", NUMANode: 1, Details: map[string]any{ "temperature_c": 48.5, "power_w": 18.2, "life_remaining_pct": 95.0, "ecc_corrected_total": int64(12), "battery_health_pct": 87.0, "sfp_temperature_c": 36.2, "sfp_tx_power_dbm": -1.8, "sfp_rx_power_dbm": -2.1, "sfp_bias_ma": 5.5, }, }, { Kind: models.DeviceKindPSU, Slot: "PSU0", SerialNumber: "PSU-001", Present: boolPtr(true), Details: map[string]any{ "life_remaining_pct": 97.0, "life_used_pct": 3.0, }, TemperatureC: 39, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if got := out.Hardware.CPUs[0]; got.TemperatureC != 61.5 || got.PowerW != 182.0 || got.Throttled == nil || *got.Throttled { t.Fatalf("unexpected CPU telemetry: %#v", got) } if got := out.Hardware.Memory[0]; got.TemperatureC != 43.0 || got.CorrectableECCErrorCount != 2 || got.PerformanceDegraded == nil || *got.PerformanceDegraded { t.Fatalf("unexpected memory telemetry: %#v", got) } if got := out.Hardware.Storage[0]; got.TemperatureC != 38.5 || got.PowerOnHours != 12450 || got.LifeRemainingPct != 91.0 { t.Fatalf("unexpected storage telemetry: %#v", got) } if got := out.Hardware.PCIeDevices[0]; got.NUMANode != 1 || got.TemperatureC != 48.5 || got.PowerW != 18.2 || got.SFPTemperatureC != 36.2 { t.Fatalf("unexpected PCIe telemetry: %#v", got) } if got := out.Hardware.PowerSupplies[0]; got.TemperatureC != 39 || got.LifeRemainingPct != 97.0 || got.LifeUsedPct != 3.0 { t.Fatalf("unexpected PSU telemetry: %#v", got) } } func TestConvertToReanimator_PreservesLegacyStorageAndPSUDetails(t *testing.T) { input := &models.AnalysisResult{ Filename: "legacy-details.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, Storage: []models.Storage{ { Slot: "Drive0", Type: "NVMe", Model: "NVMe SSD", SerialNumber: "SSD-001", Present: true, Details: map[string]any{ "temperature_c": 38.5, "power_on_hours": int64(12450), "life_remaining_pct": 91.0, }, }, }, PowerSupply: []models.PSU{ { Slot: "PSU0", Model: "PSU", SerialNumber: "PSU-001", Present: true, Details: map[string]any{ "temperature_c": 41.0, "life_remaining_pct": 96.0, }, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if got := out.Hardware.Storage[0]; got.TemperatureC != 38.5 || got.PowerOnHours != 12450 || got.LifeRemainingPct != 91.0 { t.Fatalf("expected storage details from legacy model to survive canonical conversion, got %+v", got) } if got := out.Hardware.PowerSupplies[0]; got.TemperatureC != 41.0 || got.LifeRemainingPct != 96.0 { t.Fatalf("expected psu details from legacy model to survive canonical conversion, got %+v", got) } } func TestConvertToReanimator_PreservesLegacyPCIeAndNICDetails(t *testing.T) { input := &models.AnalysisResult{ Filename: "legacy-pcie-details.json", Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{SerialNumber: "BOARD-001"}, PCIeDevices: []models.PCIeDevice{ { Slot: "PCIe 1", BDF: "0000:17:00.0", VendorID: 0x10de, DeviceID: 0x2331, PartNumber: "H100", Manufacturer: "NVIDIA", SerialNumber: "GPU-001", Details: map[string]any{ "temperature_c": 48.5, "power_w": 315.0, "ecc_corrected_total": int64(12), "battery_health_pct": 87.0, }, }, }, NetworkAdapters: []models.NetworkAdapter{ { Slot: "Slot 4", BDF: "0000:18:00.0", VendorID: 0x15b3, DeviceID: 0x1021, Model: "ConnectX-6", SerialNumber: "NIC-001", Present: true, Details: map[string]any{ "sfp_temperature_c": 34.0, "sfp_tx_power_dbm": -1.8, "sfp_rx_power_dbm": -2.1, }, }, }, }, } out, err := ConvertToReanimator(input) if err != nil { t.Fatalf("ConvertToReanimator() failed: %v", err) } if len(out.Hardware.PCIeDevices) != 2 { t.Fatalf("expected two pcie-class devices, got %d", len(out.Hardware.PCIeDevices)) } foundGPU := false foundNIC := false for _, dev := range out.Hardware.PCIeDevices { switch dev.SerialNumber { case "GPU-001": foundGPU = true if dev.TemperatureC != 48.5 || dev.PowerW != 315.0 || dev.ECCCorrectedTotal != 12 || dev.BatteryHealthPct != 87.0 { t.Fatalf("expected GPU telemetry preserved, got %+v", dev) } case "NIC-001": foundNIC = true if dev.SFPTemperatureC != 34.0 || dev.SFPTXPowerDBm != -1.8 || dev.SFPRXPowerDBm != -2.1 { t.Fatalf("expected NIC sfp telemetry preserved, got %+v", dev) } } } if !foundGPU || !foundNIC { t.Fatalf("expected both gpu and nic pcie-class exports, got %+v", out.Hardware.PCIeDevices) } } 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_FW_ERoT_NVSwitch_0", 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) } } } // TestIsDeviceBoundFirmwareFQDD verifies that Dell TSR SoftwareIdentity FQDD strings // (stored in FirmwareInfo.Description) correctly identify device-bound entries. // // Regression guard: "InfiniBand.Slot.1-1" (Mellanox ConnectX-6) and "RAID.SL.3-1" // (PERC H755 Front) were not filtered because only "raid.backplane." was listed and // "infiniband." was absent. Both firmware entries leaked into hardware.firmware on // PowerEdge R6625 (8VS2LG4). (2026-03-15) func TestIsDeviceBoundFirmwareFQDD(t *testing.T) { cases := []struct { desc string want bool }{ // Dell TSR SoftwareIdentity FQDDs — device-bound, must be excluded {"InfiniBand.Slot.1-1", true}, // Mellanox ConnectX-6 {"InfiniBand.Slot.2-1", true}, // any InfiniBand slot {"RAID.SL.3-1", true}, // PERC H755 Front {"RAID.Integrated.1-1", true}, // embedded RAID controller {"RAID.Backplane.Firmware.0", true}, // backplane (previously covered) {"NIC.Integrated.1-1-1", true}, // embedded NIC {"NIC.Slot.1-1-1", true}, // slotted NIC {"PSU.Slot.1", true}, // PSU {"Disk.Bay.0:Enclosure.Internal.0-1:RAID.SL.3-1", true}, {"GPU.Slot.1-1", true}, {"FC.Slot.1-1", true}, // Fibre Channel HBA // System-level descriptions — must NOT be excluded {"system bios", false}, {"idrac lifecycle", false}, {"idrac card", false}, {"storage controller", false}, // legacy description before fqdd fix {"", false}, } for _, tc := range cases { got := isDeviceBoundFirmwareFQDD(tc.desc) if got != tc.want { t.Errorf("isDeviceBoundFirmwareFQDD(%q) = %v, want %v", tc.desc, got, tc.want) } } }