Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

streaks_test.gno

3.75 Kb · 148 lines
  1package streaks
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/avl/v0"
  8	"gno.land/p/nt/testutils/v0"
  9)
 10
 11func resetState() {
 12	streaks = avl.Tree{}
 13}
 14
 15func TestCheckinFirstTimeStartsStreak(t *testing.T) {
 16	resetState()
 17	alice := testutils.TestAddress("alice")
 18
 19	checkin(alice, 100)
 20
 21	r, ok := get(alice)
 22	if !ok {
 23		t.Fatal("expected a record after first check-in")
 24	}
 25	if r.current != 1 || r.longest != 1 || r.total != 1 {
 26		t.Fatalf("expected current=1 longest=1 total=1, got current=%d longest=%d total=%d", r.current, r.longest, r.total)
 27	}
 28}
 29
 30func TestCheckinWithinGraceExtendsStreak(t *testing.T) {
 31	resetState()
 32	alice := testutils.TestAddress("alice")
 33
 34	checkin(alice, 100)
 35	checkin(alice, 150) // gap of 50 <= graceBlocks(100)
 36	checkin(alice, 200) // gap of 50 again
 37
 38	r, _ := get(alice)
 39	if r.current != 3 {
 40		t.Fatalf("expected streak of 3, got %d", r.current)
 41	}
 42	if r.longest != 3 {
 43		t.Fatalf("expected longest of 3, got %d", r.longest)
 44	}
 45}
 46
 47func TestCheckinAfterGraceResetsStreak(t *testing.T) {
 48	resetState()
 49	alice := testutils.TestAddress("alice")
 50
 51	checkin(alice, 100)
 52	checkin(alice, 150)
 53	checkin(alice, 400) // gap of 250 > graceBlocks(100): lapsed
 54
 55	r, _ := get(alice)
 56	if r.current != 1 {
 57		t.Fatalf("expected streak reset to 1, got %d", r.current)
 58	}
 59	if r.longest != 2 {
 60		t.Fatalf("expected longest to stay at 2, got %d", r.longest)
 61	}
 62	if r.total != 3 {
 63		t.Fatalf("expected total check-ins of 3, got %d", r.total)
 64	}
 65}
 66
 67func TestCheckinSameOrEarlierHeightPanics(t *testing.T) {
 68	resetState()
 69	alice := testutils.TestAddress("alice")
 70	checkin(alice, 100)
 71
 72	defer func() {
 73		if r := recover(); r == nil {
 74			t.Fatal("expected panic checking in at a non-later height")
 75		}
 76	}()
 77	checkin(alice, 100)
 78}
 79
 80func TestProjectReportsLapsedWithoutMutating(t *testing.T) {
 81	resetState()
 82	alice := testutils.TestAddress("alice")
 83	checkin(alice, 100)
 84
 85	r, _ := get(alice)
 86	current, alive := project(r, 100+graceBlocks+1)
 87	if alive || current != 0 {
 88		t.Fatalf("expected lapsed projection (0, false), got (%d, %v)", current, alive)
 89	}
 90	// Stored state must be untouched by a read-only projection.
 91	if r.current != 1 {
 92		t.Fatalf("project must not mutate stored state, got current=%d", r.current)
 93	}
 94}
 95
 96func TestCurrentStreakAndLongestStreakQueries(t *testing.T) {
 97	resetState()
 98	alice := testutils.TestAddress("alice")
 99	checkin(alice, 100)
100	checkin(alice, 150)
101
102	if got := LongestStreak(alice.String()); got != 2 {
103		t.Fatalf("LongestStreak() = %d, want 2", got)
104	}
105	if got := LongestStreak(testutils.TestAddress("bob").String()); got != 0 {
106		t.Fatalf("LongestStreak() for unknown address = %d, want 0", got)
107	}
108}
109
110func TestRenderShowsEmptyThenLeaderboardThenDetail(t *testing.T) {
111	resetState()
112	if !strings.Contains(Render(""), "Nobody has checked in yet") {
113		t.Fatal("expected empty-state placeholder")
114	}
115
116	alice := testutils.TestAddress("alice")
117	checkin(alice, 100)
118
119	out := Render("")
120	if !strings.Contains(out, "Rank") {
121		t.Fatal("expected leaderboard table in root render")
122	}
123
124	detail := Render(alice.String())
125	if !strings.Contains(detail, "Current streak") {
126		t.Fatal("expected detail card to show current streak")
127	}
128
129	if !strings.Contains(Render(testutils.TestAddress("bob").String()), "No check-ins yet") {
130		t.Fatal("expected placeholder for an address with no check-ins")
131	}
132}
133
134// TestCheckInViaCrossingCall smoke-tests the exported, realm-crossing entry
135// point end to end (as an on-chain caller would invoke it).
136func TestCheckInViaCrossingCall(cur realm, t *testing.T) {
137	resetState()
138	alice := testutils.TestAddress("alice")
139	testing.SetRealm(testing.NewUserRealm(alice))
140
141	msg := CheckIn(cross(cur))
142	if !strings.Contains(msg, "checked in") {
143		t.Fatalf("expected check-in confirmation, got %q", msg)
144	}
145	if _, ok := get(alice); !ok {
146		t.Fatal("expected a record to be persisted for caller")
147	}
148}