166 lines
5.9 KiB
Go
166 lines
5.9 KiB
Go
package ingest
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestObservationDetailsPayloadIncludesStatusCheckedAt(t *testing.T) {
|
|
statusCheckedAt := "2026-02-10T15:28:00Z"
|
|
statusChangedAt := "2026-02-10T15:20:00Z"
|
|
errorDescription := "Error Code on GPU 0 [18:00.0] (S/N 1653925025827) = 020000190097 (unexpected device interrupts)"
|
|
historyDetails := "auto-recovered after reboot"
|
|
component := HardwareComponent{
|
|
ComponentType: "storage",
|
|
Status: "CRITICAL",
|
|
StatusCheckedAt: &statusCheckedAt,
|
|
StatusChangedAt: &statusChangedAt,
|
|
StatusHistory: []HardwareStatusHistoryEntry{
|
|
{Status: "CRITICAL", ChangedAt: "2026-02-10T15:20:00Z"},
|
|
{Status: "OK", ChangedAt: "2026-02-10T15:25:00Z", Details: &historyDetails},
|
|
},
|
|
ErrorDescription: &errorDescription,
|
|
}
|
|
|
|
payload, err := observationDetailsPayload(component)
|
|
if err != nil {
|
|
t.Fatalf("observationDetailsPayload() error = %v", err)
|
|
}
|
|
|
|
var details map[string]any
|
|
if err := json.Unmarshal(payload, &details); err != nil {
|
|
t.Fatalf("unmarshal details: %v", err)
|
|
}
|
|
|
|
got, ok := details["status_checked_at"]
|
|
if !ok {
|
|
t.Fatalf("status_checked_at is missing in details payload")
|
|
}
|
|
if got != statusCheckedAt {
|
|
t.Fatalf("status_checked_at = %v, want %v", got, statusCheckedAt)
|
|
}
|
|
|
|
gotChangedAt, ok := details["status_changed_at"]
|
|
if !ok {
|
|
t.Fatalf("status_changed_at is missing in details payload")
|
|
}
|
|
if gotChangedAt != statusChangedAt {
|
|
t.Fatalf("status_changed_at = %v, want %v", gotChangedAt, statusChangedAt)
|
|
}
|
|
|
|
historyRaw, ok := details["status_history"].([]any)
|
|
if !ok {
|
|
t.Fatalf("status_history is missing in details payload")
|
|
}
|
|
if len(historyRaw) != 2 {
|
|
t.Fatalf("status_history len = %d, want 2", len(historyRaw))
|
|
}
|
|
|
|
gotErr, ok := details["error_description"]
|
|
if !ok {
|
|
t.Fatalf("error_description is missing in details payload")
|
|
}
|
|
if gotErr != errorDescription {
|
|
t.Fatalf("error_description = %v, want %v", gotErr, errorDescription)
|
|
}
|
|
}
|
|
|
|
func TestParseComponentStatusEventTime(t *testing.T) {
|
|
explicit := "2026-02-10T15:22:00Z"
|
|
component := HardwareComponent{
|
|
Status: statusCritical,
|
|
StatusChangedAt: &explicit,
|
|
StatusHistory: []HardwareStatusHistoryEntry{
|
|
{Status: "OK", ChangedAt: "2026-02-10T15:10:00Z"},
|
|
{Status: "CRITICAL", ChangedAt: "2026-02-10T15:18:00Z"},
|
|
},
|
|
}
|
|
|
|
actual := parseComponentStatusEventTime(component)
|
|
if actual == nil {
|
|
t.Fatalf("parseComponentStatusEventTime() = nil, want non-nil")
|
|
}
|
|
want, _ := time.Parse(time.RFC3339, explicit)
|
|
if !actual.Equal(want.UTC()) {
|
|
t.Fatalf("parseComponentStatusEventTime() = %s, want %s", actual.UTC().Format(time.RFC3339), want.UTC().Format(time.RFC3339))
|
|
}
|
|
}
|
|
|
|
func TestParseComponentStatusEventTimeFallsBackToStatusCheckedAt(t *testing.T) {
|
|
checkedAt := "2026-01-22T09:45:36Z"
|
|
component := HardwareComponent{
|
|
Status: statusCritical,
|
|
StatusCheckedAt: &checkedAt,
|
|
}
|
|
|
|
actual := parseComponentStatusEventTime(component)
|
|
if actual == nil {
|
|
t.Fatalf("parseComponentStatusEventTime() = nil, want non-nil")
|
|
}
|
|
want, _ := time.Parse(time.RFC3339, checkedAt)
|
|
if !actual.Equal(want.UTC()) {
|
|
t.Fatalf("parseComponentStatusEventTime() = %s, want %s", actual.UTC().Format(time.RFC3339), want.UTC().Format(time.RFC3339))
|
|
}
|
|
}
|
|
|
|
func TestCollectedFallbackTime(t *testing.T) {
|
|
collected, _ := time.Parse(time.RFC3339, "2026-02-10T15:30:00Z")
|
|
ingested, _ := time.Parse(time.RFC3339, "2026-02-10T15:40:00Z")
|
|
|
|
if got := collectedFallbackTime(collected, ingested); !got.Equal(collected.UTC()) {
|
|
t.Fatalf("collectedFallbackTime(collected, ingested) = %s, want %s", got.Format(time.RFC3339), collected.UTC().Format(time.RFC3339))
|
|
}
|
|
if got := collectedFallbackTime(time.Time{}, ingested); !got.Equal(ingested.UTC()) {
|
|
t.Fatalf("collectedFallbackTime(zero, ingested) = %s, want %s", got.Format(time.RFC3339), ingested.UTC().Format(time.RFC3339))
|
|
}
|
|
}
|
|
|
|
func TestEventFallbackTime(t *testing.T) {
|
|
actual, _ := time.Parse(time.RFC3339, "2026-02-10T15:22:00Z")
|
|
collected, _ := time.Parse(time.RFC3339, "2026-02-10T15:30:00Z")
|
|
ingested, _ := time.Parse(time.RFC3339, "2026-02-10T15:40:00Z")
|
|
|
|
if got := eventFallbackTime(&actual, ingested, collected); !got.Equal(actual.UTC()) {
|
|
t.Fatalf("eventFallbackTime(actual, ingested, collected) = %s, want %s", got.Format(time.RFC3339), actual.UTC().Format(time.RFC3339))
|
|
}
|
|
if got := eventFallbackTime(nil, ingested, collected); !got.Equal(ingested.UTC()) {
|
|
t.Fatalf("eventFallbackTime(nil, ingested, collected) = %s, want %s", got.Format(time.RFC3339), ingested.UTC().Format(time.RFC3339))
|
|
}
|
|
if got := eventFallbackTime(nil, time.Time{}, collected); !got.Equal(collected.UTC()) {
|
|
t.Fatalf("eventFallbackTime(nil, zero, collected) = %s, want %s", got.Format(time.RFC3339), collected.UTC().Format(time.RFC3339))
|
|
}
|
|
}
|
|
|
|
func TestParseComponentFirstSeenTime(t *testing.T) {
|
|
changedAt := "2026-02-10T15:22:00Z"
|
|
checkedAt := "2026-02-10T15:21:00Z"
|
|
component := HardwareComponent{
|
|
StatusChangedAt: &changedAt,
|
|
StatusCheckedAt: &checkedAt,
|
|
StatusHistory: []HardwareStatusHistoryEntry{
|
|
{Status: "OK", ChangedAt: "2026-02-10T15:10:00Z"},
|
|
{Status: "CRITICAL", ChangedAt: "2026-02-10T15:18:00Z"},
|
|
},
|
|
}
|
|
|
|
collected, _ := time.Parse(time.RFC3339, "2026-02-10T15:30:00Z")
|
|
ingested, _ := time.Parse(time.RFC3339, "2026-02-10T15:40:00Z")
|
|
got := parseComponentFirstSeenTime(component, collected, ingested)
|
|
want, _ := time.Parse(time.RFC3339, "2026-02-10T15:10:00Z")
|
|
if !got.Equal(want.UTC()) {
|
|
t.Fatalf("parseComponentFirstSeenTime() = %s, want %s", got.UTC().Format(time.RFC3339), want.UTC().Format(time.RFC3339))
|
|
}
|
|
}
|
|
|
|
func TestParseComponentFirstSeenTimeFallback(t *testing.T) {
|
|
component := HardwareComponent{}
|
|
collected, _ := time.Parse(time.RFC3339, "2026-02-10T15:30:00Z")
|
|
ingested, _ := time.Parse(time.RFC3339, "2026-02-10T15:40:00Z")
|
|
|
|
got := parseComponentFirstSeenTime(component, collected, ingested)
|
|
if !got.Equal(ingested.UTC()) {
|
|
t.Fatalf("parseComponentFirstSeenTime() = %s, want %s", got.UTC().Format(time.RFC3339), ingested.UTC().Format(time.RFC3339))
|
|
}
|
|
}
|