package disk import ( "testing" "time" ) func TestSMARTCriticalAttributesOverrideGenericPassed(t *testing.T) { now := time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) raw := RawSnapshot{ObservedAt: now, ReceivedAt: now, Disks: []RawDisk{{ID: "disk-smart", Name: "SMART", SizeBytes: 100, SMART: &RawSMART{Available: true, Overall: SMARTPassed, ObservedAt: now, Attributes: []RawSMARTAttribute{{ID: "5", Name: "Reallocated Sector Count", RawValue: 1}, {ID: "197", Name: "Current Pending Sector", RawValue: 2}, {ID: "198", Name: "Offline Uncorrectable", RawValue: 1}, {ID: "199", Name: "UDMA CRC Error Count", RawValue: 3}, {ID: "177", Name: "Wear Leveling Count", RawValue: 95}}}}}} got, err := Normalize(raw, now, Limits{}, Policy{}) if err != nil { t.Fatal(err) } smart := got.Disks[0].SMART if smart == nil || smart.State != SMARTAvailable || smart.Overall != SMARTAttention { t.Fatalf("smart=%+v", smart) } if len(smart.Reasons) != 5 { t.Fatalf("reasons=%v", smart.Reasons) } for _, attribute := range smart.Attributes { if !attribute.Critical || attribute.Status != SMARTAttention { t.Fatalf("attribute=%+v", attribute) } } } func TestSMARTUnavailableAndStaleAreUnknown(t *testing.T) { now := time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) unavailable := RawSnapshot{ObservedAt: now, ReceivedAt: now, Disks: []RawDisk{{ID: "disk", Name: "Disk", SizeBytes: 1, SMART: &RawSMART{Available: false, Overall: SMARTPassed}}}} got, err := Normalize(unavailable, now, Limits{}, Policy{}) if err != nil || got.Disks[0].SMART.State != SMARTUnknown || got.Disks[0].SMART.Overall != SMARTUnknown { t.Fatalf("unavailable=%+v err=%v", got, err) } stale := unavailable stale.Disks[0].SMART = &RawSMART{Available: true, Overall: SMARTPassed, ObservedAt: now.Add(-2 * time.Hour)} got, err = Normalize(stale, now, Limits{}, Policy{SMARTFreshnessMaxAge: time.Hour}) if err != nil || got.Disks[0].SMART.State != SMARTUnknown || got.Disks[0].SMART.Reasons[0] != "smart_stale" { t.Fatalf("stale=%+v err=%v", got, err) } } func TestSMARTSelfTestAgeAndNoFutureTimestamp(t *testing.T) { now := time.Date(2026, 8, 1, 12, 0, 0, 0, time.UTC) completed := now.Add(-2 * time.Hour) raw := RawSnapshot{ObservedAt: now, ReceivedAt: now, Disks: []RawDisk{{ID: "disk", Name: "Disk", SizeBytes: 1, SMART: &RawSMART{Available: true, Overall: SMARTPassed, ObservedAt: now, SelfTest: &RawSelfTest{Supported: true, Result: SMARTPassed, CompletedAt: &completed}}}}} got, err := Normalize(raw, now, Limits{}, Policy{}) if err != nil || got.Disks[0].SMART.SelfTest == nil || *got.Disks[0].SMART.SelfTest.AgeSeconds != 7200 { t.Fatalf("selftest=%+v err=%v", got.Disks[0].SMART.SelfTest, err) } future := now.Add(2 * time.Hour) raw.Disks[0].SMART.SelfTest.CompletedAt = &future if _, err = Normalize(raw, now, Limits{}, Policy{}); err == nil { t.Fatal("expected future self-test error") } }