package inspur import ( "strings" "testing" "git.mchus.pro/mchus/logpile/internal/models" "git.mchus.pro/mchus/logpile/internal/parser" ) const solSmartdSample = ` [ 17.219818] smartd[3321]: Device: /dev/sda [SAT], Micron_5400_MTFDDAK480TGA, S/N:2310400DC7E3, WWN:5-00a075-1400dc7e3, FW:D4CM003, 480 GB [ 17.553024] smartd[3321]: Device: /dev/sdc [SAT], MTFDDAK3T8TGA-1BC1ZABDA, S/N:25134F172DB3, WWN:5-00a075-14f172db3, FW:D4DK403, 3.84 TB [ 17.553331] smartd[3321]: Device: /dev/sde [SAT], Micron_5400_MTFDDAK480TGA, S/N:2310400DC80F, WWN:5-00a075-1400dc80f, FW:D4CM003, 480 GB [ 17.553709] smartd[3321]: Device: /dev/sdh [SAT], MTFDDAK3T8TGA-1BC1ZABDA, S/N:25134F57DAB8, WWN:5-00a075-14f57dab8, FW:D4DK403, 3.84 TB [ 17.886180] smartd[3321]: Device: /dev/sda [SAT], state written to /var/lib/smartmontools/smartd.Micron-2310400DC7E3.ata.state ` func TestParseSOLSmartdDevices_Dedup(t *testing.T) { devices := parseSOLSmartdDevices([]byte(solSmartdSample)) if len(devices) != 4 { t.Fatalf("expected 4 unique devices, got %d: %v", len(devices), devices) } // order matches first-seen if devices[0].Serial != "2310400DC7E3" { t.Errorf("first device serial: got %q, want 2310400DC7E3", devices[0].Serial) } if devices[0].SizeGB != 480 { t.Errorf("first device size: got %d, want 480", devices[0].SizeGB) } if devices[1].SizeGB != 3840 { t.Errorf("TB device size: got %d, want 3840", devices[1].SizeGB) } if devices[1].Firmware != "D4DK403" { t.Errorf("firmware: got %q, want D4DK403", devices[1].Firmware) } } func TestParseSOLSmartdDevices_SkipsNonInfoLines(t *testing.T) { content := ` [ 17.886177] smartd[3321]: Device: /dev/sda [SAT], state written to /var/lib/smartmontools/smartd.foo.ata.state [ 17.040843] smartd[3321]: Device: /dev/sda [SAT], not found in smartd database 7.3/5319. [ 17.040865] smartd[3321]: Device: /dev/sda [SAT], is SMART capable. Adding to "monitor" list. ` devices := parseSOLSmartdDevices([]byte(content)) if len(devices) != 0 { t.Errorf("expected 0 devices, got %d", len(devices)) } } func TestParseSolSizeGB(t *testing.T) { cases := []struct { value, unit string want int }{ {"480", "GB", 480}, {"1.92", "TB", 1920}, {"3.84", "TB", 3840}, {"1", "TB", 1000}, {"0", "GB", 0}, } for _, c := range cases { got := parseSolSizeGB(c.value, c.unit) if got != c.want { t.Errorf("parseSolSizeGB(%q, %q) = %d, want %d", c.value, c.unit, got, c.want) } } } func TestSolStorageType(t *testing.T) { cases := []struct { model string want string }{ {"MTFDDAK3T8TGA-1BC1ZABDA", "SSD"}, {"Micron_5400_MTFDDAK480TGA", "SSD"}, {"INTEL SSDSC2KB019TZ", "SSD"}, {"SEAGATE ST4000NM0115", "HDD"}, } for _, c := range cases { got := solStorageType(c.model) if got != c.want { t.Errorf("solStorageType(%q) = %q, want %q", c.model, got, c.want) } } } func TestEnrichStorageFromSOLSmartd_ModelMatch(t *testing.T) { files := []parser.ExtractedFile{ { Path: "onekeylog/log/sollog/SOLHostCapture.log", Content: []byte(solSmartdSample), }, } hw := &models.HardwareConfig{ Storage: []models.Storage{ {Slot: "BP0:0", Model: "MTFDDAK3T8TGA-1BC1ZABDA", SizeGB: 3576, Present: true}, {Slot: "BP0:1", Model: "MTFDDAK3T8TGA-1BC1ZABDA", SizeGB: 3576, Present: true}, }, } enrichStorageFromSOLSmartd(files, hw) // The two existing slots must have received serials via model match. for _, s := range hw.Storage[:2] { if s.SerialNumber == "" { t.Errorf("slot %q: expected serial to be assigned via model match", s.Slot) } if s.SizeGB != 3576 { t.Errorf("slot %q: size should be preserved, got %d", s.Slot, s.SizeGB) } } // The two unmatched Micron entries should be added as new storage entries. if len(hw.Storage) != 4 { t.Errorf("expected 4 total storage entries (2 existing + 2 new Micron), got %d", len(hw.Storage)) } } func TestEnrichStorageFromSOLSmartd_PlaceholderSlots(t *testing.T) { files := []parser.ExtractedFile{ { Path: "onekeylog/log/sollog/SOLHostCapture.log", Content: []byte(solSmartdSample), }, } hw := &models.HardwareConfig{ Storage: []models.Storage{ {Slot: "BP0:0", Present: true}, {Slot: "BP0:1", Present: true}, }, } enrichStorageFromSOLSmartd(files, hw) for _, s := range hw.Storage { if s.SerialNumber == "" { t.Errorf("slot %q: expected serial to be assigned", s.Slot) } if s.Model == "" { t.Errorf("slot %q: expected model to be assigned", s.Slot) } } } func TestEnrichStorageFromSOLSmartd_SkipsExistingSerial(t *testing.T) { files := []parser.ExtractedFile{ { Path: "onekeylog/log/sollog/SOLHostCapture.log", Content: []byte(solSmartdSample), }, } hw := &models.HardwareConfig{ Storage: []models.Storage{ {Slot: "BP0:0", SerialNumber: "2310400DC7E3", Present: true}, }, } before := len(hw.Storage) enrichStorageFromSOLSmartd(files, hw) // BP0:0 should still have original serial unchanged if hw.Storage[0].SerialNumber != "2310400DC7E3" { t.Errorf("existing serial was changed: got %q", hw.Storage[0].SerialNumber) } // Remaining 3 devices should be added as new entries if len(hw.Storage) <= before { t.Errorf("expected new entries to be added, got %d (same as before)", len(hw.Storage)) } } func TestEnrichStorageFromSOLSmartd_MergesTwoFiles(t *testing.T) { // Two SOL files with partial overlap; combined unique serials = 3 file1 := `[ 17.0] smartd[1]: Device: /dev/sda [SAT], ModelA, S/N:SN001, WWN:w, FW:fw1, 480 GB` file2 := strings.Join([]string{ `[ 17.0] smartd[2]: Device: /dev/sda [SAT], ModelA, S/N:SN001, WWN:w, FW:fw1, 480 GB`, `[ 17.1] smartd[2]: Device: /dev/sdb [SAT], ModelB, S/N:SN002, WWN:w, FW:fw2, 480 GB`, `[ 17.2] smartd[2]: Device: /dev/sdc [SAT], ModelC, S/N:SN003, WWN:w, FW:fw3, 480 GB`, }, "\n") files := []parser.ExtractedFile{ {Path: "log/sollog/SOLHostCapture.log", Content: []byte(file1)}, {Path: "runningdata/var/sollog/SOLHostCapture.log", Content: []byte(file2)}, } hw := &models.HardwareConfig{} enrichStorageFromSOLSmartd(files, hw) if len(hw.Storage) != 3 { t.Fatalf("expected 3 unique storage entries, got %d", len(hw.Storage)) } }