package classes import ( "testing" "time" ) // TestAssignSlots pins stable numbering without a database (D-30, RESEARCH // Pitfall 5): appends fill the last file of their group, a left-out row // keeps its number as a hole, only a renumber packs the listed rows again, // and groups are numbered apart. func TestAssignSlots(t *testing.T) { t0 := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC) rows := []*Slot{} add := func(id uint64, grp string, at time.Duration) *Slot { s := &Slot{ID: id, Grp: grp, FirstSeen: t0.Add(at), Listed: true} rows = append(rows, s) return s } files := func(grp string) []int { var out []int for _, r := range rows { if r.Grp == grp && r.Listed && r.FileNo != nil { out = append(out, *r.FileNo) } } return out } eq := func(a []int, b ...int) bool { if len(a) != len(b) { return false } for i := range a { if a[i] != b[i] { return false } } return true } for i := uint64(1); i <= 5; i++ { add(i, "pages", 0) } add(9, "posts", 0) if !AssignSlots(rows, 2, true) || !eq(files("pages"), 1, 1, 2, 2, 3) || !eq(files("posts"), 1) { t.Fatalf("first numbering: pages %v posts %v", files("pages"), files("posts")) } if AssignSlots(rows, 2, false) { t.Fatal("a second pass changed numbers") } rows[1].Listed = false if AssignSlots(rows, 2, false) || !eq(files("pages"), 1, 2, 2, 3) || *rows[1].FileNo != 1 { t.Fatalf("after a delete: %v, hole %v", files("pages"), rows[1].FileNo) } add(6, "pages", time.Hour) add(7, "pages", 2*time.Hour) AssignSlots(rows, 2, false) if *rows[len(rows)-2].FileNo != 3 || *rows[len(rows)-1].FileNo != 4 { t.Fatalf("appends: %v %v", *rows[len(rows)-2].FileNo, *rows[len(rows)-1].FileNo) } AssignSlots(rows, 3, true) if !eq(files("pages"), 1, 1, 1, 2, 2, 2) || rows[1].FileNo != nil { t.Fatalf("renumber: %v, left-out row %v", files("pages"), rows[1].FileNo) } // A returning row fills its own hole when it still has one. rows[1].Listed = true n := 1 rows[1].FileNo = &n AssignSlots(rows, 3, false) if *rows[1].FileNo != 1 { t.Fatalf("returning row moved to %d", *rows[1].FileNo) } }