Files
paliad/internal/services/deadline_buckets_test.go
m 37a925d3b2 feat(t-paliad-106): harmonize deadline summary — 5 disjoint buckets across Dashboard + Fristen
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).
2026-05-04 12:03:56 +02:00

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)
}
}
}