| 1 | // SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | |
| 3 | package repo |
| 4 | |
| 5 | import ( |
| 6 | "net/url" |
| 7 | "testing" |
| 8 | "time" |
| 9 | |
| 10 | "github.com/tenseleyFlow/shithub/internal/repos/git" |
| 11 | "github.com/tenseleyFlow/shithub/internal/repos/identity" |
| 12 | ) |
| 13 | |
| 14 | func TestGroupCommitRowsByDay(t *testing.T) { |
| 15 | rows := []commitRow{ |
| 16 | testCommitRow("a1", "Ada", "ada@example.com", time.Date(2026, 5, 10, 16, 0, 0, 0, time.UTC)), |
| 17 | testCommitRow("b2", "Ada", "ada@example.com", time.Date(2026, 5, 10, 12, 0, 0, 0, time.UTC)), |
| 18 | testCommitRow("c3", "Ben", "ben@example.com", time.Date(2026, 4, 24, 18, 0, 0, 0, time.UTC)), |
| 19 | } |
| 20 | |
| 21 | groups := groupCommitRows(rows) |
| 22 | if len(groups) != 2 { |
| 23 | t.Fatalf("got %d groups, want 2", len(groups)) |
| 24 | } |
| 25 | if groups[0].Title != "May 10, 2026" { |
| 26 | t.Fatalf("first title = %q, want May 10, 2026", groups[0].Title) |
| 27 | } |
| 28 | if len(groups[0].Rows) != 2 { |
| 29 | t.Fatalf("first group rows = %d, want 2", len(groups[0].Rows)) |
| 30 | } |
| 31 | if groups[1].Title != "April 24, 2026" { |
| 32 | t.Fatalf("second title = %q, want April 24, 2026", groups[1].Title) |
| 33 | } |
| 34 | } |
| 35 | |
| 36 | func TestCommitAuthorFiltersDeduplicateAndPreserveQuery(t *testing.T) { |
| 37 | base := url.Values{ |
| 38 | "path": {"cmd/shithubd/main.go"}, |
| 39 | "until": {"2026-05-10"}, |
| 40 | } |
| 41 | rows := []commitRow{ |
| 42 | newCommitRow(git.Commit{ |
| 43 | OID: "a1", |
| 44 | ShortOID: "a1", |
| 45 | AuthorName: "Matthew Forrester Wolffe", |
| 46 | AuthorEmail: "mfwolffe@example.com", |
| 47 | AuthorWhen: time.Date(2026, 5, 10, 16, 0, 0, 0, time.UTC), |
| 48 | Subject: "one", |
| 49 | }, identity.Resolved{User: true, Username: "mfwolffe", AvatarURL: "/avatars/mfwolffe"}), |
| 50 | newCommitRow(git.Commit{ |
| 51 | OID: "b2", |
| 52 | ShortOID: "b2", |
| 53 | AuthorName: "Matthew Forrester Wolffe", |
| 54 | AuthorEmail: "mfwolffe@example.com", |
| 55 | AuthorWhen: time.Date(2026, 5, 10, 15, 0, 0, 0, time.UTC), |
| 56 | Subject: "two", |
| 57 | }, identity.Resolved{User: true, Username: "mfwolffe", AvatarURL: "/avatars/mfwolffe"}), |
| 58 | newCommitRow(git.Commit{ |
| 59 | OID: "c3", |
| 60 | ShortOID: "c3", |
| 61 | AuthorName: "espandonne", |
| 62 | AuthorEmail: "espandonne@example.com", |
| 63 | AuthorWhen: time.Date(2026, 5, 10, 14, 0, 0, 0, time.UTC), |
| 64 | Subject: "three", |
| 65 | }, identity.Resolved{}), |
| 66 | } |
| 67 | |
| 68 | filters := commitAuthorFilters("tenseleyFlow", "shithub", "feature/x", base, rows, "mfwolffe") |
| 69 | if len(filters) != 2 { |
| 70 | t.Fatalf("got %d filters, want 2", len(filters)) |
| 71 | } |
| 72 | if !filters[0].Active || filters[0].Label != "mfwolffe" { |
| 73 | t.Fatalf("first filter = %+v, want active mfwolffe", filters[0]) |
| 74 | } |
| 75 | u, err := url.Parse(filters[0].Href) |
| 76 | if err != nil { |
| 77 | t.Fatal(err) |
| 78 | } |
| 79 | if u.Path != "/tenseleyFlow/shithub/commits/feature/x" { |
| 80 | t.Fatalf("path = %q", u.Path) |
| 81 | } |
| 82 | q := u.Query() |
| 83 | if q.Get("author") != "mfwolffe" || q.Get("path") != "cmd/shithubd/main.go" || q.Get("until") != "2026-05-10" { |
| 84 | t.Fatalf("unexpected query: %s", u.RawQuery) |
| 85 | } |
| 86 | } |
| 87 | |
| 88 | func TestCommitCalendarBuildsMonthGridAndLinks(t *testing.T) { |
| 89 | base := url.Values{"author": {"mfwolffe"}} |
| 90 | selected := time.Date(2026, 5, 10, 12, 0, 0, 0, time.UTC) |
| 91 | now := time.Date(2026, 5, 10, 16, 0, 0, 0, time.UTC) |
| 92 | |
| 93 | cal := commitCalendar("tenseleyFlow", "shithub", "trunk", base, selected, "", nil, now) |
| 94 | if cal.MonthLabel != "May" || cal.YearLabel != "2026" { |
| 95 | t.Fatalf("month = %s %s, want May 2026", cal.MonthLabel, cal.YearLabel) |
| 96 | } |
| 97 | if len(cal.Weeks) != 6 || len(cal.Weeks[0]) != 7 { |
| 98 | t.Fatalf("grid dimensions = %dx%d, want 6x7", len(cal.Weeks), len(cal.Weeks[0])) |
| 99 | } |
| 100 | if cal.Weeks[0][0].Label != "26" || cal.Weeks[0][0].InMonth { |
| 101 | t.Fatalf("first cell = %+v, want muted Apr 26", cal.Weeks[0][0]) |
| 102 | } |
| 103 | if cal.Weeks[0][5].Label != "1" || !cal.Weeks[0][5].InMonth { |
| 104 | t.Fatalf("May 1 cell = %+v", cal.Weeks[0][5]) |
| 105 | } |
| 106 | day := cal.Weeks[2][0] |
| 107 | if day.Label != "10" || !day.IsSelected || !day.IsToday { |
| 108 | t.Fatalf("selected day = %+v, want selected today May 10", day) |
| 109 | } |
| 110 | u, err := url.Parse(day.Href) |
| 111 | if err != nil { |
| 112 | t.Fatal(err) |
| 113 | } |
| 114 | if got := u.Query().Get("until"); got != "2026-05-10" { |
| 115 | t.Fatalf("until = %q, want 2026-05-10", got) |
| 116 | } |
| 117 | if got := u.Query().Get("author"); got != "mfwolffe" { |
| 118 | t.Fatalf("author = %q, want mfwolffe", got) |
| 119 | } |
| 120 | prev, err := url.Parse(cal.PrevMonthHref) |
| 121 | if err != nil { |
| 122 | t.Fatal(err) |
| 123 | } |
| 124 | if got := prev.Query().Get("calendar_month"); got != "2026-04" { |
| 125 | t.Fatalf("prev calendar_month = %q", got) |
| 126 | } |
| 127 | clear, err := url.Parse(cal.ClearHref) |
| 128 | if err != nil { |
| 129 | t.Fatal(err) |
| 130 | } |
| 131 | if clear.Query().Get("until") != "" || clear.Query().Get("since") != "" { |
| 132 | t.Fatalf("clear href kept date filters: %s", clear.RawQuery) |
| 133 | } |
| 134 | if got := clear.Query().Get("author"); got != "mfwolffe" { |
| 135 | t.Fatalf("clear author = %q, want mfwolffe", got) |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | func TestParseUntilDateParamIncludesWholeDay(t *testing.T) { |
| 140 | got := parseUntilDateParam("2026-05-10") |
| 141 | want := time.Date(2026, 5, 10, 23, 59, 59, 0, time.UTC) |
| 142 | if !got.Equal(want) { |
| 143 | t.Fatalf("parseUntilDateParam = %s, want %s", got, want) |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | func testCommitRow(oid, name, email string, when time.Time) commitRow { |
| 148 | return newCommitRow(git.Commit{ |
| 149 | OID: oid, |
| 150 | ShortOID: oid, |
| 151 | AuthorName: name, |
| 152 | AuthorEmail: email, |
| 153 | AuthorWhen: when, |
| 154 | Subject: oid, |
| 155 | }, identity.Resolved{}) |
| 156 | } |
| 157 |