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

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

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}