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 TestTimelineFallbackTime(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 := timelineFallbackTime(&actual, collected, ingested); !got.Equal(actual.UTC()) { t.Fatalf("timelineFallbackTime(actual, collected, ingested) = %s, want %s", got.Format(time.RFC3339), actual.UTC().Format(time.RFC3339)) } if got := timelineFallbackTime(nil, collected, ingested); !got.Equal(collected.UTC()) { t.Fatalf("timelineFallbackTime(nil, collected, ingested) = %s, want %s", got.Format(time.RFC3339), collected.UTC().Format(time.RFC3339)) } if got := timelineFallbackTime(nil, time.Time{}, ingested); !got.Equal(ingested.UTC()) { t.Fatalf("timelineFallbackTime(nil, zero, ingested) = %s, want %s", got.Format(time.RFC3339), ingested.UTC().Format(time.RFC3339)) } }