quotes_test.gno
3.00 Kb · 110 lines
1package quotes
2
3import (
4 "strings"
5 "testing"
6)
7
8// TestFeaturedIndexDeterministic verifies the rotating pick is a pure,
9// deterministic function of (height, count): same inputs -> same index,
10// always within [0, count).
11func TestFeaturedIndexDeterministic(t *testing.T) {
12 tests := []struct {
13 name string
14 height int64
15 count int
16 want int
17 }{
18 {"zero height", 0, 4, 0},
19 {"wraps at count", 4, 4, 0},
20 {"mid range", 5, 4, 1},
21 {"single quote always zero", 12345, 1, 0},
22 {"empty list", 100, 0, -1},
23 {"negative height normalized", -3, 4, 3},
24 {"large height", 1_000_003, 3, 1},
25 }
26 for _, tt := range tests {
27 t.Run(tt.name, func(t *testing.T) {
28 got := featuredIndex(tt.height, tt.count)
29 if got != tt.want {
30 t.Errorf("featuredIndex(%d, %d) = %d, want %d",
31 tt.height, tt.count, got, tt.want)
32 }
33 // For non-empty lists the index must be a valid slot.
34 if tt.count > 0 && (got < 0 || got >= tt.count) {
35 t.Errorf("featuredIndex(%d, %d) = %d, out of range [0,%d)",
36 tt.height, tt.count, got, tt.count)
37 }
38 })
39 }
40
41 // Determinism: repeated calls with identical inputs never diverge.
42 for i := 0; i < 5; i++ {
43 if featuredIndex(7, 4) != featuredIndex(7, 4) {
44 t.Fatal("featuredIndex is not deterministic")
45 }
46 }
47}
48
49// TestValidate covers the content rules enforced before a quote is stored.
50func TestValidate(t *testing.T) {
51 tests := []struct {
52 name string
53 text string
54 wantErr error
55 }{
56 {"ok short", "hello world", nil},
57 {"ok at limit", strings.Repeat("x", maxQuoteLen), nil},
58 {"empty", "", errEmpty},
59 {"too long", strings.Repeat("x", maxQuoteLen+1), errTooLong},
60 }
61 for _, tt := range tests {
62 t.Run(tt.name, func(t *testing.T) {
63 err := validate(tt.text)
64 if err != tt.wantErr {
65 t.Errorf("validate(%q) err = %v, want %v", tt.name, err, tt.wantErr)
66 }
67 })
68 }
69}
70
71// TestRenderSeeded checks that a fresh deploy (seeded board) renders the
72// expected structure: title, a blockquote featured pick, and the full list
73// containing every seeded quote.
74func TestRenderSeeded(t *testing.T) {
75 out := Render("")
76
77 if !strings.Contains(out, "# Quote of the Moment") {
78 t.Error("missing title heading")
79 }
80 if !strings.Contains(out, "\n> ") {
81 t.Error("featured quote is not rendered as a blockquote")
82 }
83 if !strings.Contains(out, "## All quotes (") {
84 t.Error("missing full-list section")
85 }
86
87 // Every seeded quote must appear in the listing.
88 for _, q := range board {
89 if !strings.Contains(out, q.text) {
90 t.Errorf("rendered output missing seeded quote: %q", q.text)
91 }
92 }
93
94 // Seeded quotes have no author and should surface the house label.
95 if !strings.Contains(out, "the board") {
96 t.Error("seeded quotes should be attributed to \"the board\"")
97 }
98}
99
100// TestSeededBoard confirms init populated the board and ids are sequential.
101func TestSeededBoard(t *testing.T) {
102 if len(board) < 3 {
103 t.Fatalf("expected at least 3 seeded quotes, got %d", len(board))
104 }
105 for i, q := range board {
106 if q.id != i {
107 t.Errorf("quote at position %d has id %d, want %d", i, q.id, i)
108 }
109 }
110}