package exporter import ( "encoding/json" "strings" "testing" "time" "git.mchus.pro/mchus/logpile/internal/models" ) // TestFullReanimatorExport tests complete export with realistic data func TestFullReanimatorExport(t *testing.T) { // Create a realistic AnalysisResult similar to import-example-full.json result := &models.AnalysisResult{ Filename: "redfish://10.10.10.103", SourceType: "api", Protocol: "redfish", TargetHost: "10.10.10.103", CollectedAt: time.Date(2026, 2, 10, 15, 30, 0, 0, time.UTC), Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{ Manufacturer: "Supermicro", ProductName: "X12DPG-QT6", SerialNumber: "21D634101", PartNumber: "X12DPG-QT6-REV1.01", UUID: "d7ef2fe5-2fd0-11f0-910a-346f11040868", }, Firmware: []models.FirmwareInfo{ {DeviceName: "BIOS", Version: "06.08.05"}, {DeviceName: "BMC", Version: "5.17.00"}, {DeviceName: "CPLD", Version: "01.02.03"}, }, CPUs: []models.CPU{ { Socket: 0, Model: "INTEL(R) XEON(R) GOLD 6530", Cores: 32, Threads: 64, FrequencyMHz: 2100, MaxFreqMHz: 4000, }, { Socket: 1, Model: "INTEL(R) XEON(R) GOLD 6530", Cores: 32, Threads: 64, FrequencyMHz: 2100, MaxFreqMHz: 4000, }, }, Memory: []models.MemoryDIMM{ { Slot: "CPU0_C0D0", Location: "CPU0_C0D0", Present: true, SizeMB: 32768, Type: "DDR5", MaxSpeedMHz: 4800, CurrentSpeedMHz: 4800, Manufacturer: "Hynix", SerialNumber: "80AD032419E17CEEC1", PartNumber: "HMCG88AGBRA191N", Status: "OK", }, { Slot: "CPU0_C1D0", Location: "CPU0_C1D0", Present: false, SizeMB: 0, Type: "", MaxSpeedMHz: 0, CurrentSpeedMHz: 0, Status: "Empty", }, }, Storage: []models.Storage{ { Slot: "OB01", Type: "NVMe", Model: "INTEL SSDPF2KX076T1", SizeGB: 7680, SerialNumber: "BTAX41900GF87P6DGN", Manufacturer: "Intel", Firmware: "9CV10510", Interface: "NVMe", Present: true, }, { Slot: "FP00HDD00", Type: "HDD", Model: "ST12000NM0008", SizeGB: 12000, SerialNumber: "ZJV01234ABC", Manufacturer: "Seagate", Firmware: "SN03", Interface: "SATA", Present: true, }, }, PCIeDevices: []models.PCIeDevice{ { Slot: "PCIeCard1", VendorID: 32902, DeviceID: 2912, BDF: "0000:18:00.0", DeviceClass: "MassStorageController", Manufacturer: "Intel", PartNumber: "RAID Controller RSP3DD080F", LinkWidth: 8, LinkSpeed: "Gen3", MaxLinkWidth: 8, MaxLinkSpeed: "Gen3", SerialNumber: "RAID-001-12345", }, { Slot: "PCIeCard2", VendorID: 5555, DeviceID: 4401, BDF: "0000:3b:00.0", DeviceClass: "NetworkController", Manufacturer: "Mellanox", PartNumber: "ConnectX-5", LinkWidth: 16, LinkSpeed: "Gen3", MaxLinkWidth: 16, MaxLinkSpeed: "Gen3", SerialNumber: "MT2892012345", }, }, PowerSupply: []models.PSU{ { Slot: "0", Present: true, Model: "GW-CRPS3000LW", Vendor: "Great Wall", WattageW: 3000, SerialNumber: "2P06C102610", PartNumber: "V0310C9000000000", Firmware: "00.03.05", Status: "OK", InputType: "ACWideRange", InputPowerW: 137, OutputPowerW: 104, InputVoltage: 215.25, }, }, }, } // Convert to Reanimator format reanimator, err := ConvertToReanimator(result) if err != nil { t.Fatalf("ConvertToReanimator failed: %v", err) } // Verify top-level fields if reanimator.Filename != "redfish://10.10.10.103" { t.Errorf("Filename mismatch: got %q", reanimator.Filename) } if reanimator.SourceType != "api" { t.Errorf("SourceType mismatch: got %q", reanimator.SourceType) } if reanimator.Protocol != "redfish" { t.Errorf("Protocol mismatch: got %q", reanimator.Protocol) } if reanimator.TargetHost != "10.10.10.103" { t.Errorf("TargetHost mismatch: got %q", reanimator.TargetHost) } if reanimator.CollectedAt != "2026-02-10T15:30:00Z" { t.Errorf("CollectedAt mismatch: got %q", reanimator.CollectedAt) } // Verify hardware sections hw := reanimator.Hardware // Board if hw.Board.SerialNumber != "21D634101" { t.Errorf("Board serial mismatch: got %q", hw.Board.SerialNumber) } // Firmware if len(hw.Firmware) != 3 { t.Errorf("Expected 3 firmware entries, got %d", len(hw.Firmware)) } // CPUs if len(hw.CPUs) != 2 { t.Fatalf("Expected 2 CPUs, got %d", len(hw.CPUs)) } if hw.CPUs[0].Manufacturer != "Intel" { t.Errorf("CPU manufacturer not inferred: got %q", hw.CPUs[0].Manufacturer) } if hw.CPUs[0].Status != "Unknown" { t.Errorf("CPU status mismatch: got %q", hw.CPUs[0].Status) } // Memory (empty slots are excluded) if len(hw.Memory) != 1 { t.Errorf("Expected 1 memory entry (installed only), got %d", len(hw.Memory)) } // Storage if len(hw.Storage) != 2 { t.Errorf("Expected 2 storage devices, got %d", len(hw.Storage)) } if hw.Storage[0].Status != "Unknown" { t.Errorf("Storage status mismatch: got %q", hw.Storage[0].Status) } // PCIe devices if len(hw.PCIeDevices) != 2 { t.Errorf("Expected 2 PCIe devices, got %d", len(hw.PCIeDevices)) } if hw.PCIeDevices[0].Model == "" { t.Error("PCIe model should be populated from PartNumber") } // Power supplies if len(hw.PowerSupplies) != 1 { t.Errorf("Expected 1 PSU, got %d", len(hw.PowerSupplies)) } // Verify JSON marshaling works jsonData, err := json.MarshalIndent(reanimator, "", " ") if err != nil { t.Fatalf("Failed to marshal to JSON: %v", err) } // Check that JSON contains expected fields jsonStr := string(jsonData) expectedFields := []string{ `"filename"`, `"source_type"`, `"protocol"`, `"target_host"`, `"collected_at"`, `"hardware"`, `"board"`, `"cpus"`, `"memory"`, `"storage"`, `"pcie_devices"`, `"power_supplies"`, `"firmware"`, } for _, field := range expectedFields { if !strings.Contains(jsonStr, field) { t.Errorf("JSON missing expected field: %s", field) } } // Optional: print JSON for manual inspection (commented out for normal test runs) // t.Logf("Generated Reanimator JSON:\n%s", string(jsonData)) } // TestReanimatorExportWithoutTargetHost tests that target_host is inferred from filename func TestReanimatorExportWithoutTargetHost(t *testing.T) { result := &models.AnalysisResult{ Filename: "redfish://192.168.1.100", SourceType: "api", Protocol: "redfish", TargetHost: "", // Empty - should be inferred CollectedAt: time.Now(), Hardware: &models.HardwareConfig{ BoardInfo: models.BoardInfo{ SerialNumber: "TEST123", }, }, } reanimator, err := ConvertToReanimator(result) if err != nil { t.Fatalf("ConvertToReanimator failed: %v", err) } if reanimator.TargetHost != "192.168.1.100" { t.Errorf("Expected target_host to be inferred from filename, got %q", reanimator.TargetHost) } }