numguess_test.gno
2.87 Kb · 115 lines
1package numguess
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/nt/avl/v0"
8)
9
10// resetGame re-initializes package globals so each test starts clean; init()
11// runs only once for the whole test binary, and t.Run subtests don't reset it.
12func resetGame(height int64) {
13 current = newRound(1, height)
14 attempts = avl.NewTree()
15 leaderboard = nil
16}
17
18func TestTargetFromHeight(t *testing.T) {
19 tests := []struct {
20 name string
21 height int64
22 }{
23 {"zero", 0},
24 {"one", 1},
25 {"large", 123456},
26 {"negative", -42},
27 }
28 for _, tt := range tests {
29 got := targetFromHeight(tt.height)
30 if got < 1 || got > 100 {
31 t.Errorf("%s: targetFromHeight(%d)=%d, want in 1..100", tt.name, tt.height, got)
32 }
33 // determinism: same input -> same output
34 if again := targetFromHeight(tt.height); again != got {
35 t.Errorf("%s: non-deterministic: %d != %d", tt.name, again, got)
36 }
37 }
38}
39
40// TestGuessHigherLowerCorrect exercises the crossing Guess function, so it
41// takes cur realm and calls through cross(cur).
42func TestGuessHigherLowerCorrect(cur realm, t *testing.T) {
43 resetGame(50)
44 target := current.target
45
46 low := target - 1
47 if low < 1 {
48 low = 1
49 }
50 high := target + 1
51 if high > 100 {
52 high = 100
53 }
54
55 if low != target {
56 if got := Guess(cross(cur), low); got != "higher" {
57 t.Errorf("Guess(%d) with target %d = %q, want higher", low, target, got)
58 }
59 }
60 if high != target {
61 if got := Guess(cross(cur), high); got != "lower" {
62 t.Errorf("Guess(%d) with target %d = %q, want lower", high, target, got)
63 }
64 }
65
66 got := Guess(cross(cur), target)
67 if got != "correct" {
68 t.Fatalf("Guess(%d) = %q, want correct", target, got)
69 }
70 if !current.solved {
71 t.Error("round should be solved after correct guess")
72 }
73 if len(leaderboard) != 1 {
74 t.Errorf("leaderboard len = %d, want 1", len(leaderboard))
75 }
76}
77
78// TestNewRoundResets solves a round then verifies NewRound advances the round
79// number and clears attempts.
80func TestNewRoundResets(cur realm, t *testing.T) {
81 resetGame(7)
82 target := current.target
83
84 Guess(cross(cur), target) // solve it
85 if !current.solved {
86 t.Fatal("expected solved round")
87 }
88 prevRound := current.number
89
90 testing.SkipHeights(11) // change height so the next target can differ
91 msg := NewRound(cross(cur))
92 if current.number != prevRound+1 {
93 t.Errorf("round number = %d, want %d", current.number, prevRound+1)
94 }
95 if current.solved {
96 t.Error("fresh round should not be solved")
97 }
98 if totalAttempts() != 0 {
99 t.Errorf("attempts should reset, got %d", totalAttempts())
100 }
101 if !strings.Contains(msg, "started") {
102 t.Errorf("NewRound message = %q, want it to mention 'started'", msg)
103 }
104}
105
106// TestRenderContainsState is a plain read — no cur.
107func TestRenderContainsState(t *testing.T) {
108 resetGame(3)
109 out := Render("")
110 for _, want := range []string{"Number Guessing Game", "Current round", "Leaderboard"} {
111 if !strings.Contains(out, want) {
112 t.Errorf("Render() missing %q", want)
113 }
114 }
115}