util_test.go
| 1 | package util |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestFormatDate(t *testing.T) { |
| 6 | tests := []struct { |
| 7 | iso, date, dateTime string |
| 8 | }{ |
| 9 | {"2024-03-05T07:08:09.123Z", "2024-03-05", "2024-03-05 07:08:09 UTC"}, |
| 10 | {"2024-12-31T23:59:59Z", "2024-12-31", "2024-12-31 23:59:59 UTC"}, |
| 11 | // A non-UTC offset is converted to UTC, which moves the day. |
| 12 | {"2024-01-01T01:00:00+02:00", "2023-12-31", "2023-12-31 23:00:00 UTC"}, |
| 13 | // git's %ci / %ai format, converted to UTC. |
| 14 | {"2025-01-02 10:00:00 +0000", "2025-01-02", "2025-01-02 10:00:00 UTC"}, |
| 15 | {"2024-05-01 12:00:00 +0200", "2024-05-01", "2024-05-01 10:00:00 UTC"}, |
| 16 | // A stored commit date in RFC 1123 form. |
| 17 | {"Thu, 2 Jan 2025 10:00:00 +0000", "2025-01-02", "2025-01-02 10:00:00 UTC"}, |
| 18 | {"not a date", "not a date", "not a date"}, |
| 19 | {"", "", ""}, |
| 20 | } |
| 21 | for _, tt := range tests { |
| 22 | if got := FormatDate(tt.iso); got != tt.date { |
| 23 | t.Errorf("FormatDate(%q) = %q, want %q", tt.iso, got, tt.date) |
| 24 | } |
| 25 | if got := FormatDateTime(tt.iso); got != tt.dateTime { |
| 26 | t.Errorf("FormatDateTime(%q) = %q, want %q", tt.iso, got, tt.dateTime) |
| 27 | } |
| 28 | } |
| 29 | } |
| 30 | |
| 31 | func TestLabelTextColor(t *testing.T) { |
| 32 | tests := []struct{ hex, want string }{ |
| 33 | {"#ffffff", "#000000"}, |
| 34 | {"#000000", "#ffffff"}, |
| 35 | {"#ffff00", "#000000"}, |
| 36 | {"#0000ff", "#ffffff"}, |
| 37 | {"#808080", "#000000"}, // 128/255 is just above the 0.5 threshold |
| 38 | {"bogus", "#ffffff"}, |
| 39 | } |
| 40 | for _, tt := range tests { |
| 41 | if got := LabelTextColor(tt.hex); got != tt.want { |
| 42 | t.Errorf("LabelTextColor(%q) = %q, want %q", tt.hex, got, tt.want) |
| 43 | } |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | func TestPaginate(t *testing.T) { |
| 48 | tests := []struct { |
| 49 | raw, total, per int |
| 50 | want Page |
| 51 | }{ |
| 52 | {1, 0, 20, Page{1, 1, 0}}, |
| 53 | {999, 45, 20, Page{3, 3, 40}}, |
| 54 | {0, 45, 20, Page{1, 3, 0}}, |
| 55 | {2, 45, 20, Page{2, 3, 20}}, |
| 56 | } |
| 57 | for _, tt := range tests { |
| 58 | if got := Paginate(tt.raw, tt.total, tt.per); got != tt.want { |
| 59 | t.Errorf("Paginate(%d,%d,%d) = %+v, want %+v", tt.raw, tt.total, tt.per, got, tt.want) |
| 60 | } |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | func TestContentDisposition(t *testing.T) { |
| 65 | tests := []struct{ in, want string }{ |
| 66 | {"a.txt", `attachment; filename="a.txt"; filename*=UTF-8''a.txt`}, |
| 67 | {`dir/e"vil.txt`, `attachment; filename="e_vil.txt"; filename*=UTF-8''e%22vil.txt`}, |
| 68 | {"ü.txt", `attachment; filename="ü.txt"; filename*=UTF-8''%C3%BC.txt`}, |
| 69 | {"", `attachment; filename="file"; filename*=UTF-8''`}, |
| 70 | {"it's.txt", `attachment; filename="it's.txt"; filename*=UTF-8''it%27s.txt`}, |
| 71 | } |
| 72 | for _, tt := range tests { |
| 73 | if got := ContentDisposition("attachment", tt.in); got != tt.want { |
| 74 | t.Errorf("ContentDisposition(%q) =\n%q\nwant\n%q", tt.in, got, tt.want) |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | func TestValidNames(t *testing.T) { |
| 80 | for _, s := range []string{"ab", "a-b_1"} { |
| 81 | if !ValidUsername(s) { |
| 82 | t.Errorf("ValidUsername(%q) = false", s) |
| 83 | } |
| 84 | } |
| 85 | for _, s := range []string{"", "a.b", "a b", "a/b"} { |
| 86 | if ValidUsername(s) { |
| 87 | t.Errorf("ValidUsername(%q) = true", s) |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | func TestFormatDateTimeSQLite(t *testing.T) { |
| 93 | if got := FormatDateTime("2026-09-15 12:14:56"); got != "2026-09-15 12:14:56 UTC" { |
| 94 | t.Fatalf("got %q", got) |
| 95 | } |
| 96 | } |
| 97 |