package main import ( "strings" "testing" "time" "golang.org/x/net/html" ) func TestInferAssignmentDate(t *testing.T) { now := time.Date(2026, 3, 28, 10, 0, 0, 0, time.UTC) got := inferAssignmentDate("Jan 20", now) want := time.Date(2026, 1, 20, 0, 0, 0, 0, time.UTC) if !got.Equal(want) { t.Fatalf("got %s want %s", got, want) } } func TestInferAssignmentDateRollsBackYear(t *testing.T) { now := time.Date(2026, 3, 28, 10, 0, 0, 0, time.UTC) got := inferAssignmentDate("Dec 15", now) want := time.Date(2025, 12, 15, 0, 0, 0, 0, time.UTC) if !got.Equal(want) { t.Fatalf("got %s want %s", got, want) } } func TestInferUpcomingDateRollsForwardYear(t *testing.T) { now := time.Date(2026, 12, 28, 10, 0, 0, 0, time.UTC) got := inferUpcomingDate("Jan 03", now) want := time.Date(2027, 1, 3, 0, 0, 0, 0, time.UTC) if !got.Equal(want) { t.Fatalf("got %s want %s", got, want) } } func TestFilterUpcomingAssignments(t *testing.T) { now := time.Date(2026, 3, 28, 10, 0, 0, 0, time.UTC) assignments := []Assignment{ {DueDate: time.Date(2026, 3, 29, 0, 0, 0, 0, time.UTC), Title: "Soon"}, {DueDate: time.Date(2026, 4, 20, 0, 0, 0, 0, time.UTC), Title: "Later"}, } got := filterUpcomingAssignments(assignments, now, 14) if len(got) != 1 || got[0].Title != "Soon" { t.Fatalf("unexpected filtered assignments: %#v", got) } } func TestDedupeAssignments(t *testing.T) { assignment := Assignment{ DueDate: time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC), Title: "Preparedness", Link: "https://example.invalid/a", } got := dedupeAssignments([]Assignment{assignment, assignment}) if len(got) != 1 { t.Fatalf("got %d items", len(got)) } } func TestExtractLikeLiveMissingTable(t *testing.T) { doc := mustParseHTML(`
Mar
25
US History HW: Timeline Events
`) table := findNode(doc, func(n *html.Node) bool { return n.Type == html.ElementNode && n.Data == "table" && hasClass(n, "missing-assignments") }) if table == nil { t.Fatal("missing table not found") } row := findNode(table, func(n *html.Node) bool { return n.Type == html.ElementNode && n.Data == "tr" }) cells := directChildrenByTag(row, "td") if got := nodeText(cells[0]); got != "Mar 25" { t.Fatalf("got %q", got) } link := findNode(cells[1], func(n *html.Node) bool { return n.Type == html.ElementNode && n.Data == "a" && attr(n, "data-alma-modal") != "" }) if got := nodeText(link); got != "HW: Timeline Events" { t.Fatalf("got %q", got) } } func mustParseHTML(text string) *html.Node { doc, err := html.Parse(strings.NewReader(text)) if err != nil { panic(err) } return doc }