Both surfaces now show the same buckets with the same labels and the same cutoffs: Überfällig (conditional, alarming) · Heute · Diese Woche · Nächste Woche · Erledigt. Single-source bucket math via computeDeadlineBucketBounds — Heute = today, Diese Woche = tomorrow through the upcoming Sunday inclusive, Nächste Woche = next Monday through next Sunday inclusive, all disjoint. Items past next Sunday are visible only via "All open"/"Upcoming" filters; the Überfällig card stays hidden when count == 0 and switches to a saturated red pulse + bold white text when count > 0. Filter dropdown on /deadlines gains today / next_week entries; old "upcoming" filter still works as a back-compat alias for everything pending past this Sunday so legacy bookmarks don't 4xx. Tests: 8 deterministic table cases for the bucket pivots (every weekday + a 21-day disjointness walk).
89 lines
3.4 KiB
Go
89 lines
3.4 KiB
Go
package services
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestComputeDeadlineBucketBounds checks the 5-bucket pivots for every
|
|
// weekday — Heute / Diese Woche / Nächste Woche must be disjoint and
|
|
// every day from today through next Sunday must land in exactly one
|
|
// bucket (overdue stays before today; everything past next Sunday is
|
|
// "later" and not visible in the four pending cards).
|
|
func TestComputeDeadlineBucketBounds(t *testing.T) {
|
|
type expectation struct {
|
|
name string
|
|
now time.Time
|
|
wantSunday string // upcoming Sunday including today, YYYY-MM-DD
|
|
wantNextMon string // Monday of next week
|
|
}
|
|
cases := []expectation{
|
|
{"Mon 2026-05-04", time.Date(2026, 5, 4, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
{"Tue 2026-05-05", time.Date(2026, 5, 5, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
{"Wed 2026-05-06", time.Date(2026, 5, 6, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
{"Thu 2026-05-07", time.Date(2026, 5, 7, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
{"Fri 2026-05-08", time.Date(2026, 5, 8, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
{"Sat 2026-05-09", time.Date(2026, 5, 9, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
// Sunday: spec says "Diese Woche" inclusive of today's Sunday
|
|
// itself ⇒ Sunday-of-this-week == today, so Diese Woche has
|
|
// no future-eligible entries beyond Heute.
|
|
{"Sun 2026-05-10", time.Date(2026, 5, 10, 12, 0, 0, 0, time.UTC), "2026-05-10", "2026-05-11"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
b := computeDeadlineBucketBounds(tc.now)
|
|
if got := b.today.Format("2006-01-02"); got != tc.now.UTC().Format("2006-01-02") {
|
|
t.Errorf("today = %s, want %s", got, tc.now.Format("2006-01-02"))
|
|
}
|
|
if got := b.tomorrow.Format("2006-01-02"); got != tc.now.AddDate(0, 0, 1).UTC().Format("2006-01-02") {
|
|
t.Errorf("tomorrow = %s, want %s", got, tc.now.AddDate(0, 0, 1).Format("2006-01-02"))
|
|
}
|
|
sunday := b.nextMonday.AddDate(0, 0, -1).Format("2006-01-02")
|
|
if sunday != tc.wantSunday {
|
|
t.Errorf("Sunday-of-this-week = %s, want %s", sunday, tc.wantSunday)
|
|
}
|
|
if got := b.nextMonday.Format("2006-01-02"); got != tc.wantNextMon {
|
|
t.Errorf("nextMonday = %s, want %s", got, tc.wantNextMon)
|
|
}
|
|
if got := b.weekAfter.Sub(b.nextMonday); got != 7*24*time.Hour {
|
|
t.Errorf("weekAfter - nextMonday = %v, want 7d", got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestBucketsAreDisjoint walks 21 consecutive days from a known Monday
|
|
// and asserts that every day falls into at most one of the three
|
|
// pending-future buckets (today / this_week / next_week). Days past
|
|
// next Sunday must fall into none of them.
|
|
func TestBucketsAreDisjoint(t *testing.T) {
|
|
now := time.Date(2026, 5, 4, 12, 0, 0, 0, time.UTC) // Monday
|
|
b := computeDeadlineBucketBounds(now)
|
|
for i := range 21 {
|
|
day := b.today.AddDate(0, 0, i)
|
|
isToday := day.Equal(b.today)
|
|
isThisWeek := day.After(b.today) && day.Before(b.nextMonday)
|
|
isNextWeek := !day.Before(b.nextMonday) && day.Before(b.weekAfter)
|
|
hits := 0
|
|
if isToday {
|
|
hits++
|
|
}
|
|
if isThisWeek {
|
|
hits++
|
|
}
|
|
if isNextWeek {
|
|
hits++
|
|
}
|
|
// First 14 days from "today" must each hit exactly one bucket.
|
|
// Days 14..20 (the third week and beyond) must hit none.
|
|
want := 1
|
|
if i >= 14 {
|
|
want = 0
|
|
}
|
|
if hits != want {
|
|
t.Errorf("offset +%dd (%s): %d bucket hits, want %d (today=%v thisWeek=%v nextWeek=%v)",
|
|
i, day.Format("2006-01-02"), hits, want, isToday, isThisWeek, isNextWeek)
|
|
}
|
|
}
|
|
}
|