Initial Alma assignments reporter

This commit is contained in:
Joe Julian
2026-03-28 11:06:40 -07:00
commit 5d1be582b5
8 changed files with 1035 additions and 0 deletions
+102
View File
@@ -0,0 +1,102 @@
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(`
<table class="pure-table pure-table-horizontal missing-assignments">
<tbody>
<tr>
<td>Mar<br>25</td>
<td>
<small><a href="/class/1">US History</a></small>
<a data-alma-modal="StudentAssignmentDetailsModal" href="/assignment/1">HW: Timeline Events</a>
</td>
<td><i title="Missing"></i></td>
</tr>
</tbody>
</table>`)
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
}