Event ingestion now uses the source syslog PRI and an explicit-offset timezone timeline instead of assuming host-local time. CPU PPIN is exported as the source-backed CPU serial, and an active NVMe fault SEL event promotes the matching drive's storage status. See ADL-056, ADL-057. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
105 lines
3.9 KiB
Go
105 lines
3.9 KiB
Go
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)
|
|
}
|
|
}
|