quizstreak_test.gno
3.39 Kb · 130 lines
1package quizstreak
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/nt/avl/v0"
8 "gno.land/p/nt/testutils/v0"
9)
10
11// resetGame re-initializes package globals so each test starts clean.
12func resetGame() {
13 players = avl.NewTree()
14}
15
16func TestStartIndexDeterministicAndInRange(t *testing.T) {
17 n := len(questions)
18 for _, tc := range []struct {
19 addr string
20 height int64
21 }{
22 {"g1aaaa", 0},
23 {"g1bbbb", 42},
24 {"g1cccc", -7},
25 } {
26 idx := startIndex(tc.addr, tc.height)
27 if idx < 0 || idx >= n {
28 t.Errorf("startIndex(%q, %d) = %d, want in 0..%d", tc.addr, tc.height, idx, n-1)
29 }
30 if again := startIndex(tc.addr, tc.height); again != idx {
31 t.Errorf("startIndex(%q, %d) non-deterministic: %d != %d", tc.addr, tc.height, again, idx)
32 }
33 }
34}
35
36func TestNextIndexWrapsAround(t *testing.T) {
37 n := 5
38 if got := nextIndex(4, 0, n); got != 0 {
39 t.Errorf("nextIndex(4, 0, 5) = %d, want 0", got)
40 }
41 if got := nextIndex(2, 3, n); got != 1 {
42 t.Errorf("nextIndex(2, 3, 5) = %d, want 1", got)
43 }
44}
45
46// TestAnswerCorrectAndWrong exercises the crossing Answer function, so it
47// takes cur realm and calls through cross(cur).
48func TestAnswerCorrectAndWrong(cur realm, t *testing.T) {
49 resetGame()
50
51 alice := testutils.TestAddress("alice")
52 testing.SetRealm(testing.NewUserRealm(alice))
53
54 addr := alice.String()
55 p := getPlayer(addr)
56 q := questions[p.qIdx]
57
58 // wrong answer first: pick any index that isn't correct
59 wrong := (q.correctIdx + 1) % len(q.choices)
60 got := Answer(cross(cur), wrong)
61 if !strings.HasPrefix(got, "Wrong") {
62 t.Errorf("Answer(wrong) = %q, want it to start with Wrong", got)
63 }
64 if p.streak != 0 {
65 t.Errorf("streak after wrong answer = %d, want 0", p.streak)
66 }
67 if p.qIdx != questionIndexOf(q) {
68 t.Errorf("question changed after a wrong answer, want it to stay the same")
69 }
70
71 got = Answer(cross(cur), q.correctIdx)
72 if !strings.HasPrefix(got, "Correct!") {
73 t.Errorf("Answer(correct) = %q, want it to start with Correct!", got)
74 }
75 if p.streak != 1 {
76 t.Errorf("streak after correct answer = %d, want 1", p.streak)
77 }
78 if p.best != 1 {
79 t.Errorf("best after first correct answer = %d, want 1", p.best)
80 }
81 if p.correct != 1 || p.wrong != 1 {
82 t.Errorf("correct/wrong = %d/%d, want 1/1", p.correct, p.wrong)
83 }
84 if len(leaderboard()) != 1 {
85 t.Errorf("leaderboard len = %d, want 1", len(leaderboard()))
86 }
87}
88
89// questionIndexOf is a small test helper to find a question's index.
90func questionIndexOf(q question) int {
91 for i, cand := range questions {
92 if cand.text == q.text {
93 return i
94 }
95 }
96 return -1
97}
98
99func TestRenderShowsRulesAndLeaderboard(t *testing.T) {
100 resetGame()
101 out := Render("")
102 for _, want := range []string{"Quiz Streak", "leaderboard", "How to play"} {
103 if !strings.Contains(strings.ToLower(out), strings.ToLower(want)) {
104 t.Errorf("Render(\"\") missing %q", want)
105 }
106 }
107 if !strings.Contains(out, "No one has played yet") {
108 t.Errorf("Render(\"\") should note an empty leaderboard")
109 }
110}
111
112func TestRenderShowsPlayerDashboard(cur realm, t *testing.T) {
113 resetGame()
114
115 bob := testutils.TestAddress("bob")
116 testing.SetRealm(testing.NewUserRealm(bob))
117 addr := bob.String()
118
119 p := getPlayer(addr)
120 q := questions[p.qIdx]
121 Answer(cross(cur), q.correctIdx)
122
123 out := Render(addr)
124 if !strings.Contains(out, "Your question") {
125 t.Errorf("Render(addr) missing player dashboard section")
126 }
127 if !strings.Contains(out, "Current streak: 1") {
128 t.Errorf("Render(addr) missing updated streak, got:\n%s", out)
129 }
130}