package queue import ( "testing" "time" ) // lokal baut eine lokale Test-Zeit (Stunde/Minute reichen — Ruhefenster // arbeiten auf Minuten-Auflösung). func lokal(hour, minute int) time.Time { return time.Date(2026, 10, 18, hour, minute, 0, 0, time.Local) } // TestParseQuietHours: Format HH:MM-HH:MM (überspannt Mitternacht), // klare Fehler für Müll. func TestParseQuietHours(t *testing.T) { cases := []struct { in string start clockTime end clockTime bad bool }{ {in: "18:00-08:00", start: clockTime{18, 0}, end: clockTime{8, 0}}, {in: "9:30-17:45", start: clockTime{9, 30}, end: clockTime{17, 45}}, {in: "22:00-06:00", start: clockTime{22, 0}, end: clockTime{6, 0}}, {in: "08:00-08:00", start: clockTime{8, 0}, end: clockTime{8, 0}}, // deaktiviert, aber gültig {in: ""}, // Leerstring = deaktiviert, kein Fehler {in: "18:00", bad: true}, {in: "18:00-", bad: true}, {in: "-08:00", bad: true}, {in: "24:00-08:00", bad: true}, {in: "18:60-08:00", bad: true}, {in: "1800-0800", bad: true}, {in: "ab:00-08:00", bad: true}, } for _, tc := range cases { q, err := ParseQuietHours(tc.in) if tc.bad { if err == nil { t.Errorf("ParseQuietHours(%q) = %+v, want Fehler", tc.in, q) } continue } if err != nil { t.Errorf("ParseQuietHours(%q): unerwarteter Fehler %v", tc.in, err) continue } if q.Start != tc.start || q.End != tc.end { t.Errorf("ParseQuietHours(%q) = %v-%v, want %v-%v", tc.in, q.Start, q.End, tc.start, tc.end) } } } // TestQuietHoursActive: Tagfenster, Nachtfenster über Mitternacht und der // deaktivierte Nullwert über alle Grenzen (Start inklusiv, End exklusiv). func TestQuietHoursActive(t *testing.T) { cases := []struct { fenster string hour, minute int want bool }{ // Tagfenster 09:00-17:00 {"09:00-17:00", 8, 59, false}, {"09:00-17:00", 9, 0, true}, // Start inklusiv {"09:00-17:00", 12, 30, true}, {"09:00-17:00", 16, 59, true}, {"09:00-17:00", 17, 0, false}, // End exklusiv {"09:00-17:00", 23, 0, false}, // Nachtfenster 18:00-08:00 (überspannt Mitternacht) {"18:00-08:00", 17, 59, false}, {"18:00-08:00", 18, 0, true}, {"18:00-08:00", 23, 59, true}, {"18:00-08:00", 0, 0, true}, // über Mitternacht {"18:00-08:00", 7, 59, true}, {"18:00-08:00", 8, 0, false}, // End exklusiv {"18:00-08:00", 12, 0, false}, // Zweites Nachtfenster {"22:00-06:00", 3, 0, true}, {"22:00-06:00", 21, 59, false}, // Start == End: deaktiviert {"08:00-08:00", 8, 0, false}, {"08:00-08:00", 20, 0, false}, } for _, tc := range cases { q := mustQuiet(t, tc.fenster) if got := q.Active(lokal(tc.hour, tc.minute)); got != tc.want { t.Errorf("%s um %02d:%02d: Active = %v, want %v", tc.fenster, tc.hour, tc.minute, got, tc.want) } } } // TestQuietHoursUntil: Fenster-Ende (heute bzw. morgen bei Nachtfenstern), // Nullzeit wenn inaktiv. func TestQuietHoursUntil(t *testing.T) { // Tagfenster: Ende heute. q := mustQuiet(t, "09:00-17:00") if want := lokal(17, 0); !q.Until(lokal(12, 0)).Equal(want) { t.Errorf("Until(12:00) = %v, want %v (heute 17:00)", q.Until(lokal(12, 0)), want) } if !q.Until(lokal(3, 0)).IsZero() { t.Error("Until außerhalb des Fensters: want Nullzeit") } // Nachtfenster: ab 18:00 ist das Ende morgen früh. n := mustQuiet(t, "18:00-08:00") if want := lokal(0, 0).Add(32 * time.Hour); !n.Until(lokal(20, 0)).Equal(want) { t.Errorf("Until(20:00) = %v, want %v (morgen 08:00)", n.Until(lokal(20, 0)), want) } // Nach Mitternacht (00:30) ist das Ende heute 08:00. if want := lokal(8, 0); !n.Until(lokal(0, 30)).Equal(want) { t.Errorf("Until(00:30) = %v, want %v (heute 08:00)", n.Until(lokal(0, 30)), want) } } // TestQuietHoursStringRoundtrip: String liefert die konfigurierte Form // zurück und parst identisch; der Nullwert rendert leer. func TestQuietHoursStringRoundtrip(t *testing.T) { for _, s := range []string{"18:00-08:00", "09:00-17:00", "22:00-06:00"} { q := mustQuiet(t, s) if q.String() != s { t.Errorf("String() = %q, want %q", q.String(), s) } q2 := mustQuiet(t, q.String()) if q2 != q { t.Errorf("Roundtrip von %q: %+v ≠ %+v", s, q2, q) } } if (QuietHours{}).String() != "" { t.Error("Nullwert-String nicht leer") } }