package handlers import ( "testing" "time" "task-dashboard/internal/models" ) func multiDayEvent(startISO, endISO string) models.TimelineItem { start, _ := time.Parse(time.RFC3339, startISO) end, _ := time.Parse(time.RFC3339, endISO) return models.TimelineItem{ ID: "evt-1", Title: "Offsite", Type: models.TimelineItemTypeEvent, Time: start, EndTime: &end, } } func TestIsMultiDayEvent_SameDayTimedEvent_False(t *testing.T) { item := multiDayEvent("2026-07-13T10:00:00Z", "2026-07-13T11:00:00Z") if isMultiDayEvent(item) { t.Error("expected same-day timed event to not be multi-day") } } func TestIsMultiDayEvent_SpansMultipleDays_True(t *testing.T) { item := multiDayEvent("2026-07-13T17:35:00Z", "2026-07-30T22:30:00Z") if !isMultiDayEvent(item) { t.Error("expected event spanning multiple days to be multi-day") } } func TestIsMultiDayEvent_NonEventType_AlwaysFalse(t *testing.T) { start, _ := time.Parse(time.RFC3339, "2026-07-13T10:00:00Z") end, _ := time.Parse(time.RFC3339, "2026-07-30T22:30:00Z") item := models.TimelineItem{ID: "t1", Type: models.TimelineItemTypeTask, Time: start, EndTime: &end} if isMultiDayEvent(item) { t.Error("expected non-event type to never be considered multi-day, regardless of span") } } func TestIsMultiDayEvent_NoEndTime_False(t *testing.T) { start, _ := time.Parse(time.RFC3339, "2026-07-13T10:00:00Z") item := models.TimelineItem{ID: "e1", Type: models.TimelineItemTypeEvent, Time: start} if isMultiDayEvent(item) { t.Error("expected event with nil EndTime to not be multi-day") } } func TestMultiDayVariant_StartDay_ReturnsStarts(t *testing.T) { item := multiDayEvent("2026-07-13T17:35:00Z", "2026-07-30T22:30:00Z") renderDay, _ := time.Parse(time.RFC3339, "2026-07-13T00:00:00Z") if got := multiDayVariant(item, renderDay); got != "starts" { t.Errorf("variant = %q, want %q", got, "starts") } } func TestMultiDayVariant_EndDay_ReturnsEnds(t *testing.T) { item := multiDayEvent("2026-07-13T17:35:00Z", "2026-07-30T22:30:00Z") renderDay, _ := time.Parse(time.RFC3339, "2026-07-30T00:00:00Z") if got := multiDayVariant(item, renderDay); got != "ends" { t.Errorf("variant = %q, want %q", got, "ends") } } func TestMultiDayVariant_MiddleDay_ReturnsSpans(t *testing.T) { item := multiDayEvent("2026-07-13T17:35:00Z", "2026-07-30T22:30:00Z") renderDay, _ := time.Parse(time.RFC3339, "2026-07-20T00:00:00Z") if got := multiDayVariant(item, renderDay); got != "spans" { t.Errorf("variant = %q, want %q", got, "spans") } } func TestMultiDayVariant_DayOutsideSpan_ReturnsEmpty(t *testing.T) { item := multiDayEvent("2026-07-13T17:35:00Z", "2026-07-30T22:30:00Z") renderDay, _ := time.Parse(time.RFC3339, "2026-08-01T00:00:00Z") if got := multiDayVariant(item, renderDay); got != "" { t.Errorf("variant = %q, want empty", got) } } func TestIsMultiDayEvent_SingleDayAllDayEvent_False(t *testing.T) { // Exclusive end-date convention: a single-day all-day event on July 13 // has Start=Jul13 00:00, End=Jul14 00:00 (one day past its own day). start, _ := time.Parse(time.RFC3339, "2026-07-13T00:00:00Z") end, _ := time.Parse(time.RFC3339, "2026-07-14T00:00:00Z") item := models.TimelineItem{ ID: "allday-1", Type: models.TimelineItemTypeEvent, Time: start, EndTime: &end, IsAllDay: true, } if isMultiDayEvent(item) { t.Error("expected a single-day all-day event (exclusive end-date) to not be multi-day") } } func TestIsMultiDayEvent_MultiDayAllDayEvent_True(t *testing.T) { // A 3-day all-day event (Jul 13, 14, 15) has End=Jul16 00:00 (exclusive). start, _ := time.Parse(time.RFC3339, "2026-07-13T00:00:00Z") end, _ := time.Parse(time.RFC3339, "2026-07-16T00:00:00Z") item := models.TimelineItem{ ID: "allday-2", Type: models.TimelineItemTypeEvent, Time: start, EndTime: &end, IsAllDay: true, } if !isMultiDayEvent(item) { t.Error("expected a 3-day all-day event to be multi-day") } } func TestMultiDayVariant_MultiDayAllDayEvent_LastRealDayIsEnds(t *testing.T) { // The 3-day all-day event above (Jul 13-15 inclusive, End=Jul16 exclusive) // must report "ends" on Jul 15 (its real last day), not Jul 16. start, _ := time.Parse(time.RFC3339, "2026-07-13T00:00:00Z") end, _ := time.Parse(time.RFC3339, "2026-07-16T00:00:00Z") item := models.TimelineItem{ ID: "allday-2", Type: models.TimelineItemTypeEvent, Time: start, EndTime: &end, IsAllDay: true, } renderDay, _ := time.Parse(time.RFC3339, "2026-07-15T00:00:00Z") if got := multiDayVariant(item, renderDay); got != "ends" { t.Errorf("variant on last real day = %q, want %q", got, "ends") } exclusiveEndDay, _ := time.Parse(time.RFC3339, "2026-07-16T00:00:00Z") if got := multiDayVariant(item, exclusiveEndDay); got != "" { t.Errorf("variant on the exclusive (non-real) end day = %q, want empty", got) } }