package models import ( "testing" "time" ) // A car collected new sits at 0 km; its first service is still due a full // interval later, and the reminder must show the distance left to run. func TestZeroKmStillYieldsNextService(t *testing.T) { car := &Car{ServiceIntervalDays: 365, ServiceIntervalKm: 15000, CurrentKm: 0} pickup := time.Date(2026, 8, 10, 0, 0, 0, 0, time.UTC) rec := ServiceRecord{Date: pickup, Km: 0} rec.ComputeDerived(car) if rec.NextServiceDate == nil || !rec.NextServiceDate.Equal(pickup.AddDate(0, 0, 365)) { t.Fatalf("next service date = %v", rec.NextServiceDate) } if rec.NextServiceKm == nil { t.Fatal("next service km is nil for a 0 km car") } if *rec.NextServiceKm != 15000 { t.Fatalf("next service km = %d, want 15000", *rec.NextServiceKm) } rem := Reminder{DueKm: *rec.NextServiceKm} rem.ComputeReminderDerived(time.Now(), car.CurrentKm) if rem.KmLeft == nil || *rem.KmLeft != 15000 { t.Fatalf("km left = %v, want 15000", rem.KmLeft) } if rem.Status != "upcoming" { t.Fatalf("status = %q, want upcoming", rem.Status) } } // A repeating km reminder completed on a car that reads 0 rolls from 0, not // from the old target — the odometer is where the car is, not a missing value. func TestZeroKmReminderStatus(t *testing.T) { for _, tc := range []struct { name string currentKm int dueKm int wantLeft int wantState string }{ {"collected new", 0, 15000, 15000, "upcoming"}, {"nearly due", 14500, 15000, 500, "due_soon"}, {"overdue", 15500, 15000, -500, "overdue"}, } { t.Run(tc.name, func(t *testing.T) { rem := Reminder{DueKm: tc.dueKm} rem.ComputeReminderDerived(time.Now(), tc.currentKm) if rem.KmLeft == nil || *rem.KmLeft != tc.wantLeft { t.Fatalf("km left = %v, want %d", rem.KmLeft, tc.wantLeft) } if rem.Status != tc.wantState { t.Fatalf("status = %q, want %q", rem.Status, tc.wantState) } }) } }