179 lines
5.3 KiB
Go
179 lines
5.3 KiB
Go
package collector
|
|
|
|
import (
|
|
"bee/audit/internal/schema"
|
|
"testing"
|
|
)
|
|
|
|
func TestParsePSUSDR(t *testing.T) {
|
|
raw := `
|
|
PS1 Input Power | 215 Watts | ok
|
|
PS1 Output Power | 198 Watts | ok
|
|
PS1 Input Voltage | 229 Volts | ok
|
|
PS1 Temp | 39 C | ok
|
|
PS1 Health | 97 % | ok
|
|
PS2 Input Power | 0 Watts | cr
|
|
`
|
|
|
|
got := parsePSUSDR(raw)
|
|
if len(got) != 2 {
|
|
t.Fatalf("len(got)=%d want 2", len(got))
|
|
}
|
|
if got[1].status != statusOK {
|
|
t.Fatalf("ps1 status=%q", got[1].status)
|
|
}
|
|
if got[1].inputPowerW == nil || *got[1].inputPowerW != 215 {
|
|
t.Fatalf("ps1 input power=%v", got[1].inputPowerW)
|
|
}
|
|
if got[1].outputPowerW == nil || *got[1].outputPowerW != 198 {
|
|
t.Fatalf("ps1 output power=%v", got[1].outputPowerW)
|
|
}
|
|
if got[1].inputVoltage == nil || *got[1].inputVoltage != 229 {
|
|
t.Fatalf("ps1 input voltage=%v", got[1].inputVoltage)
|
|
}
|
|
if got[1].temperatureC == nil || *got[1].temperatureC != 39 {
|
|
t.Fatalf("ps1 temperature=%v", got[1].temperatureC)
|
|
}
|
|
if got[1].healthPct == nil || *got[1].healthPct != 97 {
|
|
t.Fatalf("ps1 health=%v", got[1].healthPct)
|
|
}
|
|
if got[2].status != statusCritical {
|
|
t.Fatalf("ps2 status=%q", got[2].status)
|
|
}
|
|
}
|
|
|
|
func TestParsePSUSlotVendorVariants(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
want int
|
|
}{
|
|
{name: "PWS1 Status", want: 1},
|
|
{name: "Power Supply Bay 8", want: 8},
|
|
{name: "PS 6 Input Power", want: 6},
|
|
// MSI underscore format — \b does not fire between digit and '_'
|
|
{name: "PSU1_POWER_IN", want: 1},
|
|
{name: "PSU2_POWER_OUT", want: 2},
|
|
{name: "PSU4_STATUS", want: 4},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
got, ok := parsePSUSlot(tt.name)
|
|
if !ok || got != tt.want {
|
|
t.Fatalf("parsePSUSlot(%q)=(%d,%v) want (%d,true)", tt.name, got, ok, tt.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestParsePSUSDRMSIFormat(t *testing.T) {
|
|
t.Parallel()
|
|
raw := `
|
|
PSU1_STATUS | F1h | ok
|
|
PSU1_POWER_OUT | 928 Watts | ok
|
|
PSU1_POWER_IN | 976 Watts | ok
|
|
PSU2_STATUS | F2h | ok
|
|
PSU2_POWER_OUT | 944 Watts | ok
|
|
PSU2_POWER_IN | 992 Watts | ok
|
|
`
|
|
got := parsePSUSDR(raw)
|
|
if len(got) != 2 {
|
|
t.Fatalf("len(got)=%d want 2", len(got))
|
|
}
|
|
if got[1].inputPowerW == nil || *got[1].inputPowerW != 976 {
|
|
t.Fatalf("psu1 input power=%v want 976", got[1].inputPowerW)
|
|
}
|
|
if got[1].outputPowerW == nil || *got[1].outputPowerW != 928 {
|
|
t.Fatalf("psu1 output power=%v want 928", got[1].outputPowerW)
|
|
}
|
|
if got[2].inputPowerW == nil || *got[2].inputPowerW != 992 {
|
|
t.Fatalf("psu2 input power=%v want 992", got[2].inputPowerW)
|
|
}
|
|
}
|
|
|
|
func TestSynthesizePSUsFromSDR(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
health := 97.0
|
|
outputPower := 915.0
|
|
got := synthesizePSUsFromSDR(map[int]psuSDR{
|
|
1: {
|
|
slot: 1,
|
|
status: statusOK,
|
|
outputPowerW: &outputPower,
|
|
healthPct: &health,
|
|
},
|
|
})
|
|
|
|
if len(got) != 1 {
|
|
t.Fatalf("len(got)=%d want 1", len(got))
|
|
}
|
|
if got[0].Slot == nil || *got[0].Slot != "0" {
|
|
t.Fatalf("slot=%v want 0", got[0].Slot)
|
|
}
|
|
if got[0].OutputPowerW == nil || *got[0].OutputPowerW != 915 {
|
|
t.Fatalf("output power=%v", got[0].OutputPowerW)
|
|
}
|
|
if got[0].LifeRemainingPct == nil || *got[0].LifeRemainingPct != 97 {
|
|
t.Fatalf("life remaining=%v", got[0].LifeRemainingPct)
|
|
}
|
|
if got[0].LifeUsedPct == nil || *got[0].LifeUsedPct != 3 {
|
|
t.Fatalf("life used=%v", got[0].LifeUsedPct)
|
|
}
|
|
}
|
|
|
|
func TestMergePSUSDRAppendsPSUMissingFromFRU(t *testing.T) {
|
|
model := "PSU0"
|
|
slot := "0"
|
|
got := mergePSUSDR([]schema.HardwarePowerSupply{{Slot: &slot, Model: &model}}, map[int]psuSDR{
|
|
1: {slot: 1, status: statusOK},
|
|
2: {slot: 2, status: statusOK},
|
|
})
|
|
if len(got) != 2 || got[1].Slot == nil || *got[1].Slot != "1" {
|
|
t.Fatalf("PSUs=%#v, want FRU PSU0 plus synthesized PSU1", got)
|
|
}
|
|
}
|
|
|
|
func TestParseFRUFiltersNonPSUAndNormalizesSlots(t *testing.T) {
|
|
psus := parseFRU(`
|
|
FRU Device Description : Builtin FRU Device (ID 0)
|
|
Board Mfg : Example Systems
|
|
FRU Device Description : PSU 2 (ID 12)
|
|
Board Product : Server PSU 1600W
|
|
Board Mfg : Acme Power
|
|
Board Serial : PS2-SERIAL
|
|
Board Part Number : ACME-1600
|
|
Board Extra : 1.4
|
|
FRU Device Description : Power Supply Bay 3 (ID 13)
|
|
Product Name : Redundant 800W
|
|
Product Manufacturer : Acme Power
|
|
Product Serial : PS3-SERIAL
|
|
`)
|
|
if len(psus) != 2 {
|
|
t.Fatalf("PSUs = %#v, want two PSU FRUs", psus)
|
|
}
|
|
if psus[0].Slot == nil || *psus[0].Slot != "1" || psus[0].Model == nil || *psus[0].Model != "Server PSU 1600W" ||
|
|
psus[0].WattageW == nil || *psus[0].WattageW != 1600 || psus[0].Status == nil || *psus[0].Status != statusOK {
|
|
t.Fatalf("first PSU = %#v", psus[0])
|
|
}
|
|
if psus[1].Slot == nil || *psus[1].Slot != "2" || psus[1].SerialNumber == nil || *psus[1].SerialNumber != "PS3-SERIAL" {
|
|
t.Fatalf("second PSU = %#v", psus[1])
|
|
}
|
|
}
|
|
|
|
func TestPSUNumericParsersRejectInvalidReadings(t *testing.T) {
|
|
for _, raw := range []string{"", "na", "0", "-1", "6001", "not-a-number"} {
|
|
if got := parseBoundedFloat(raw, 6000); got != nil {
|
|
t.Errorf("parseBoundedFloat(%q) = %v, want nil", raw, got)
|
|
}
|
|
}
|
|
if got := parseBoundedFloat("1200 Watts", 6000); got == nil || *got != 1200 {
|
|
t.Fatalf("parseBoundedFloat valid value = %v", got)
|
|
}
|
|
for raw, want := range map[string]int{"PSU 800W": 800, "1200W PLATINUM": 1200, "no wattage": 0, "6000W": 0} {
|
|
if got := parseWattage(raw); got != want {
|
|
t.Errorf("parseWattage(%q) = %d, want %d", raw, got, want)
|
|
}
|
|
}
|
|
}
|