package dyzurbot import ( "testing" "time" ) // rows mirrors the real "Dyżury" sheet: cols A–C are the duty schedule, // cols E–G are the person roster (name, Telegram handle, duty count), with // UNFORMATTED_VALUE rendering the week date and duty count as float64 and // trailing empty cells dropped. func sampleRows() [][]interface{} { return [][]interface{}{ // row 0: header — must be skipped {"Tydzień zaczynający się od", "Osoba 1", "Osoba 2", "", "Lista osób", "Telegram", "Liczba dyżurów"}, // full week + roster entry (handle stored with leading @) {float64(46027), "Jan Kowalski", "Anna Nowak", "", "Piotr Wiśniewski", "@piotr", float64(1)}, // empty week (no one assigned) + roster entry (handle without @) {float64(46034), "", "", "", "Maria Wójcik", "maria", float64(0)}, // one-person week, ragged row (no roster columns at all) {float64(46041), "Tomasz Zieliński"}, // week present, blank roster slot with a count -> person skipped {float64(46048), "", "", "", "", "", float64(0)}, // roster-only row (week column empty) -> person added, no handle {"", "", "", "", "Katarzyna Lewandowska", "", float64(3)}, } } func TestParseRotaSkipsHeaderAndCountsWeeks(t *testing.T) { rota := ParseRota(sampleRows()) // rows 1..4 carry a week serial; row 5's week column is empty if got := len(rota.Weeks); got != 4 { t.Fatalf("weeks: got %d, want 4", got) } } func TestParseRotaDecodesWeekStartToMonday(t *testing.T) { rota := ParseRota(sampleRows()) want := time.Date(2026, 1, 5, 0, 0, 0, 0, time.UTC) // serial 46027 = Mon 2026-01-05 if got := rota.Weeks[0].WeekStart; !got.Equal(want) { t.Errorf("week0 start: got %v, want %v", got, want) } } func TestParseRotaWeekPersons(t *testing.T) { rota := ParseRota(sampleRows()) if w := rota.Weeks[0]; w.Person1 != "Jan Kowalski" || w.Person2 != "Anna Nowak" { t.Errorf("full week persons: got %q/%q", w.Person1, w.Person2) } if w := rota.Weeks[1]; w.Person1 != "" || w.Person2 != "" { t.Errorf("empty week should keep blank persons: got %q/%q", w.Person1, w.Person2) } if w := rota.Weeks[2]; w.Person1 != "Tomasz Zieliński" || w.Person2 != "" { t.Errorf("one-person ragged week: got %q/%q", w.Person1, w.Person2) } } func TestParseRotaRosterSkipsBlankSlots(t *testing.T) { rota := ParseRota(sampleRows()) // Piotr Wiśniewski, Maria Wójcik, Katarzyna Lewandowska — the blank-name slot is skipped if got := len(rota.People); got != 3 { t.Fatalf("people: got %d, want 3", got) } if p := rota.People[0]; p.Name != "Piotr Wiśniewski" || p.DutyCount != 1 { t.Errorf("person0: got %+v, want {Piotr Wiśniewski 1}", p) } if p := rota.People[2]; p.Name != "Katarzyna Lewandowska" || p.DutyCount != 3 { t.Errorf("person2: got %+v, want {Katarzyna Lewandowska 3}", p) } } func TestParseRotaReadsHandles(t *testing.T) { rota := ParseRota(sampleRows()) // Handles are stored verbatim from column F; normalization happens at // render time, not at parse time. if p := rota.People[0]; p.Handle != "@piotr" { t.Errorf("person0 handle: got %q, want %q", p.Handle, "@piotr") } if p := rota.People[1]; p.Handle != "maria" { t.Errorf("person1 handle: got %q, want %q", p.Handle, "maria") } if p := rota.People[2]; p.Handle != "" { t.Errorf("person2 handle: got %q, want empty", p.Handle) } }