package inspur import ( "testing" "time" "git.mchus.pro/mchus/logpile/internal/models" baseparser "git.mchus.pro/mchus/logpile/internal/parser" ) func TestParseIDLLog_UsesBMCSourceForEventLogs(t *testing.T) { content := []byte(`|2025-12-02T17:54:27+08:00|MEMORY|Assert|Warning|0C180401|CPU1_C4D0 Memory Device Disabled - Assert|`) events := ParseIDLLog(content) if len(events) != 1 { t.Fatalf("expected 1 event, got %d", len(events)) } if events[0].Source != "BMC" { t.Fatalf("expected IDL events to use BMC source, got %#v", events[0]) } if events[0].SensorName != "CPU1_C4D0" { t.Fatalf("expected extracted DIMM component ref, got %#v", events[0]) } } func TestParseIDLLog_DeassertedFaultIsInfo(t *testing.T) { content := []byte(`|2026-05-07T23:57:08+08:00|BMC|Deassert|Critical|1800B002|Sys_Health Transition to Critical from less severe - Deassert|`) events := ParseIDLLog(content) if len(events) != 1 || events[0].Severity != models.SeverityInfo { t.Fatalf("expected deasserted IDL fault to be informational, got %+v", events) } } func TestParseSyslog_UsesHostSourceAndProcessAsSensorName(t *testing.T) { content := []byte(`<13>2026-03-15T14:03:11+00:00 host123 systemd[1]: Started Example Service`) events := ParseSyslog(content, "syslog/info") if len(events) != 1 { t.Fatalf("expected 1 event, got %d", len(events)) } if events[0].Source != "syslog" { t.Fatalf("expected syslog source, got %#v", events[0]) } if events[0].SensorName != "systemd[1]" { t.Fatalf("expected process name in sensor/component slot, got %#v", events[0]) } } func TestParseSyslog_UsesPriorityAndDowngradesKnownBenignMessages(t *testing.T) { content := []byte("<1> 1970-01-01T08:01:38.795020+08:00 host kernel: [ 17.763621] Helper Module Driver Version 1.2\n" + "<1> 2026-01-29T16:24:12.888636+08:00 host kernel: [ 16.191801] Helper Module Driver Version 1.2\n" + "<132> 2026-07-25T08:42:47.001654+03:00 host kernel: [100009.801150] Color Depth is 15 bpp or higher\n" + "<130> 2026-07-25T08:43:48.001964+03:00 host MCTPMain: MCTP_ERROR: No Response for Application. Timing-out...TimeoutMsec:4000\n") events := ParseSyslog(content, "onekeylog/log/syslog/alert.log") if len(events) != 3 { t.Fatalf("expected 3 events, got %d", len(events)) } if events[0].Severity != models.SeverityInfo { t.Fatalf("expected benign driver alert to be info, got %#v", events[0]) } if events[1].Severity != models.SeverityInfo { t.Fatalf("expected KVM status line to be info, got %#v", events[1]) } if events[2].Severity != models.SeverityCritical { t.Fatalf("expected PRI=2 MCTP fault to be critical, got %#v", events[2]) } } func TestParserIncludesCriticalSyslogMCTPAndMESelfTest(t *testing.T) { files := []baseparser.ExtractedFile{{ Path: "onekeylog/log/syslog/crit.log", Content: []byte("<130> 2026-07-25T08:42:47.001654+03:00 host MCTPMain: MCTP_ERROR: No Response for Application. Timing-out...TimeoutMsec:4000\n" + "<130> 2026-07-25T08:43:28.072198+03:00 host IPMIMain: Me self-tests is not normal, return code is 195!\n"), }} result, err := (&Parser{}).Parse(files) if err != nil { t.Fatalf("parse: %v", err) } if len(result.Events) != 2 { t.Fatalf("expected both critical syslog events, got %d: %+v", len(result.Events), result.Events) } for _, event := range result.Events { if event.Severity != models.SeverityCritical { t.Fatalf("expected critical event, got %#v", event) } } if result.Events[0].Timestamp.After(result.Events[1].Timestamp) { t.Fatalf("events are not sorted: %+v", result.Events) } } func TestSortInspurEventsPlacesUnknownTimestampsLast(t *testing.T) { events := []models.Event{ {ID: "late", Timestamp: time.Date(2026, 7, 25, 8, 0, 0, 0, time.UTC)}, {ID: "unknown"}, {ID: "early", Timestamp: time.Date(2026, 7, 24, 8, 0, 0, 0, time.UTC)}, } sortInspurEvents(events) if events[0].ID != "early" || events[1].ID != "late" || events[2].ID != "unknown" { t.Fatalf("unexpected event order: %+v", events) } }