package collector import ( "bee/audit/internal/schema" "testing" ) func TestParseNVIDIASMIQuery(t *testing.T) { raw := "0, 00000000:65:00.0, NVIDIA H100 80GB HBM3, GPU-SERIAL-1, 96.00.1F.00.02, 54, 210.33, 0, 5, Not Active, 4, 4, 16, 16\n" byBDF, err := parseNVIDIASMIQuery(raw) if err != nil { t.Fatalf("parse failed: %v", err) } gpu, ok := byBDF["0000:65:00.0"] if !ok { t.Fatalf("gpu by normalized bdf not found") } if gpu.Name != "NVIDIA H100 80GB HBM3" { t.Fatalf("name: got %q", gpu.Name) } if gpu.Serial != "GPU-SERIAL-1" { t.Fatalf("serial: got %q", gpu.Serial) } if gpu.VBIOS != "96.00.1F.00.02" { t.Fatalf("vbios: got %q", gpu.VBIOS) } if gpu.ECCUncorrected == nil || *gpu.ECCUncorrected != 0 { t.Fatalf("ecc uncorrected: got %v", gpu.ECCUncorrected) } if gpu.HWSlowdown == nil || *gpu.HWSlowdown { t.Fatalf("hw slowdown: got %v, want false", gpu.HWSlowdown) } if gpu.PCIeLinkGenCurrent == nil || *gpu.PCIeLinkGenCurrent != 4 { t.Fatalf("pcie link gen current: got %v, want 4", gpu.PCIeLinkGenCurrent) } if gpu.PCIeLinkGenMax == nil || *gpu.PCIeLinkGenMax != 4 { t.Fatalf("pcie link gen max: got %v, want 4", gpu.PCIeLinkGenMax) } } func TestNormalizePCIeBDF(t *testing.T) { tests := []struct { in string want string }{ {"00000000:17:00.0", "0000:17:00.0"}, {"0000:17:00.0", "0000:17:00.0"}, {"17:00.0", "0000:17:00.0"}, } for _, tt := range tests { got := normalizePCIeBDF(tt.in) if got != tt.want { t.Fatalf("normalizePCIeBDF(%q)=%q want %q", tt.in, got, tt.want) } } } func TestEnrichPCIeWithNVIDIAData_driverLoaded(t *testing.T) { vendorID := NvidiaVendorID bdf := "0000:65:00.0" manufacturer := "NVIDIA Corporation" status := "OK" devices := []schema.HardwarePCIeDevice{ { HardwareComponentStatus: schema.HardwareComponentStatus{Status: &status}, VendorID: &vendorID, BDF: &bdf, Manufacturer: &manufacturer, }, } byBDF := map[string]nvidiaGPUInfo{ "0000:65:00.0": { BDF: "0000:65:00.0", Serial: "GPU-ABC", VBIOS: "96.00.1F.00.02", ECCUncorrected: ptrInt64(2), ECCCorrected: ptrInt64(10), TemperatureC: ptrFloat(55.5), PowerW: ptrFloat(230.2), }, } out := enrichPCIeWithNVIDIAData(devices, byBDF, true) if out[0].SerialNumber == nil || *out[0].SerialNumber != "GPU-ABC" { t.Fatalf("serial: got %v", out[0].SerialNumber) } if out[0].Firmware == nil || *out[0].Firmware != "96.00.1F.00.02" { t.Fatalf("firmware: got %v", out[0].Firmware) } if out[0].Telemetry == nil || out[0].Telemetry["nvidia_gpu_index"] != 0 { t.Fatalf("telemetry nvidia_gpu_index: got %#v", out[0].Telemetry) } if out[0].Status == nil || *out[0].Status != statusWarning { t.Fatalf("status: got %v", out[0].Status) } if out[0].ECCUncorrectedTotal == nil || *out[0].ECCUncorrectedTotal != 2 { t.Fatalf("ecc_uncorrected_total: got %#v", out[0].ECCUncorrectedTotal) } if out[0].TemperatureC == nil || *out[0].TemperatureC != 55.5 { t.Fatalf("temperature_c: got %#v", out[0].TemperatureC) } } func TestEnrichPCIeWithNVIDIAData_driverMissingFallback(t *testing.T) { vendorID := NvidiaVendorID bdf := "0000:17:00.0" manufacturer := "NVIDIA Corporation" devices := []schema.HardwarePCIeDevice{ { VendorID: &vendorID, BDF: &bdf, Manufacturer: &manufacturer, }, } out := enrichPCIeWithNVIDIAData(devices, nil, false) if out[0].SerialNumber != nil { t.Fatalf("serial should stay nil without source data, got %v", out[0].SerialNumber) } if out[0].Status == nil || *out[0].Status != statusUnknown { t.Fatalf("fallback status: got %v", out[0].Status) } } func ptrInt64(v int64) *int64 { return &v } func ptrFloat(v float64) *float64 { return &v } func TestParseNVIDIANVLinkStatusByGPU(t *testing.T) { // Real-world 2-GPU direct-bridge H100 SXM output: link 15 inactive on both GPUs. input := `GPU 0: NVIDIA H100 80GB HBM3 (UUID: GPU-a59f6931-c099-8fba-a0b3-08469d86f140) Link 0: 26.562 GB/s Link 15: Link 17: 26.562 GB/s GPU 1: NVIDIA H100 80GB HBM3 (UUID: GPU-603fe750-0516-9db5-86ec-ea61af3fce35) Link 0: 26.562 GB/s Link 15: ` got := parseNVIDIANVLinkStatusByGPU(input) if len(got[0]) != 3 { t.Fatalf("gpu0 ports=%d want 3 (%#v)", len(got[0]), got[0]) } if got[0][1].Index != 15 || got[0][1].Active { t.Fatalf("gpu0 link15=%#v want inactive", got[0][1]) } if got[0][0].SpeedGBs == nil || *got[0][0].SpeedGBs != 26.562 { t.Fatalf("gpu0 link0 speed=%#v want 26.562", got[0][0].SpeedGBs) } if len(got[1]) != 2 { t.Fatalf("gpu1 ports=%d want 2 (%#v)", len(got[1]), got[1]) } if got[1][1].Active { t.Fatalf("gpu1 link15 should be inactive: %#v", got[1][1]) } } func TestParseNVIDIANVLinkErrorsByGPU(t *testing.T) { input := `GPU 0: NVIDIA H100 80GB HBM3 (UUID: GPU-a59f6931-c099-8fba-a0b3-08469d86f140) Link 0: Replay Errors: 0 Link 0: Recovery Errors: 0 Link 0: CRC Errors: 0 Link 1: Replay Errors: 3 Link 1: Recovery Errors: 1 Link 1: CRC Errors: 2 GPU 1: NVIDIA H100 80GB HBM3 (UUID: GPU-603fe750-0516-9db5-86ec-ea61af3fce35) Link 0: Replay Errors: 0 Link 0: Recovery Errors: 0 Link 0: CRC Errors: 0 ` got := parseNVIDIANVLinkErrorsByGPU(input) c := got[0][1] if c.Replay != 3 || c.Recovery != 1 || c.CRC != 2 { t.Fatalf("gpu0 link1 counters=%#v want {3,1,2}", c) } zero := got[0][0] if zero.Replay != 0 || zero.Recovery != 0 || zero.CRC != 0 { t.Fatalf("gpu0 link0 counters=%#v want all zero", zero) } if _, ok := got[1][0]; !ok { t.Fatalf("expected gpu1 link0 entry present") } } func TestEnrichPCIeWithNVIDIANVLinksAttachesPortsByIndex(t *testing.T) { oldStatus, oldErrors := nvlinkStatusFn, nvlinkErrorsFn t.Cleanup(func() { nvlinkStatusFn, nvlinkErrorsFn = oldStatus, oldErrors }) nvlinkStatusFn = func() (map[int][]schema.HardwareNVLinkPort, error) { return map[int][]schema.HardwareNVLinkPort{ 0: {{Index: 0, Active: true, SpeedGBs: ptrFloat(26.562)}, {Index: 15, Active: false}}, }, nil } nvlinkErrorsFn = func() (map[int]map[int]nvlinkErrorCounters, error) { return map[int]map[int]nvlinkErrorCounters{ 0: {0: {Replay: 1}}, }, nil } devices := []schema.HardwarePCIeDevice{ {Telemetry: map[string]any{"nvidia_gpu_index": 0}}, } out := enrichPCIeWithNVIDIANVLinks(devices) if len(out[0].NVLinks) != 2 { t.Fatalf("nvlinks=%d want 2", len(out[0].NVLinks)) } if out[0].NVLinks[0].ReplayErrors == nil || *out[0].NVLinks[0].ReplayErrors != 1 { t.Fatalf("link0 replay errors=%#v want 1", out[0].NVLinks[0].ReplayErrors) } if out[0].NVLinks[1].Active { t.Fatalf("link15 should stay inactive") } }