- XML escaping and namespaces, no priority, the image cap and file names - News window and cap, sources and languages, the publication name - IndexNow batches of 10 000, answer outcomes, keys and the host filter - HTML sitemap group order, tree order and paging - default slugs, the effective type setting, lastmod config, slot edges
108 lines
3.3 KiB
Go
108 lines
3.3 KiB
Go
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)
|
|
}
|
|
}
|
|
|
|
// TestAssignSlotsEdges pins the edges: a size below 1 counts as 1, the
|
|
// oldest unnumbered row is appended first (first seen, then id), an empty
|
|
// table changes nothing, and a renumber with nothing listed clears every
|
|
// number.
|
|
func TestAssignSlotsEdges(t *testing.T) {
|
|
if AssignSlots(nil, 10, true) {
|
|
t.Fatal("an empty table changed")
|
|
}
|
|
t0 := time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)
|
|
rows := []*Slot{
|
|
{ID: 3, Grp: "pages", FirstSeen: t0, Listed: true},
|
|
{ID: 1, Grp: "pages", FirstSeen: t0.Add(time.Minute), Listed: true},
|
|
{ID: 2, Grp: "pages", FirstSeen: t0, Listed: true},
|
|
}
|
|
if !AssignSlots(rows, 0, false) {
|
|
t.Fatal("numbering reported no change")
|
|
}
|
|
// Size 0 is 1: one row per file, oldest first (id 2 before id 3 at the
|
|
// same time, then id 1).
|
|
if *rows[2].FileNo != 1 || *rows[0].FileNo != 2 || *rows[1].FileNo != 3 {
|
|
t.Fatalf("files: id3=%d id1=%d id2=%d", *rows[0].FileNo, *rows[1].FileNo, *rows[2].FileNo)
|
|
}
|
|
for _, r := range rows {
|
|
r.Listed = false
|
|
}
|
|
if !AssignSlots(rows, 5, true) {
|
|
t.Fatal("renumber reported no change")
|
|
}
|
|
for _, r := range rows {
|
|
if r.FileNo != nil {
|
|
t.Fatalf("row %d kept file %d after a renumber with nothing listed", r.ID, *r.FileNo)
|
|
}
|
|
}
|
|
}
|