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.

streak_test.gno

4.90 Kb · 179 lines
  1package streak
  2
  3import (
  4	"strconv"
  5	"strings"
  6	"testing"
  7
  8	"chain/runtime"
  9
 10	"gno.land/p/nt/avl/v0"
 11	"gno.land/p/nt/testutils/v0"
 12	"gno.land/p/nt/uassert/v0"
 13)
 14
 15// reset clears realm state between tests.
 16func reset() { users = avl.NewTree() }
 17
 18// alignDay moves to the start of the next check-in window, so a test begins on
 19// a clean day boundary. testing only exposes a RELATIVE SkipHeights (there is
 20// no absolute height setter, and no testing.Height), so days are driven
 21// forward from wherever the previous test left the chain — never assume an
 22// absolute day number, ask Day().
 23func alignDay() {
 24	if rem := runtime.ChainHeight() % BlocksPerDay; rem != 0 {
 25		testing.SkipHeights(BlocksPerDay - rem)
 26	}
 27}
 28
 29// advanceDays moves the chain forward by n whole windows.
 30func advanceDays(n int64) { testing.SkipHeights(n * BlocksPerDay) }
 31
 32// checkIn runs a check-in as addr.
 33func checkIn(cur realm, addr address) int {
 34	testing.SetRealm(testing.NewUserRealm(addr))
 35	return CheckIn(cross(cur))
 36}
 37
 38func TestFirstCheckInStartsAtOne(cur realm, t *testing.T) {
 39	reset()
 40	alignDay()
 41	alice := testutils.TestAddress("alice")
 42
 43	uassert.Equal(t, 1, checkIn(cur, alice))
 44	uassert.Equal(t, 1, Current(alice))
 45	uassert.Equal(t, 1, Best(alice))
 46}
 47
 48func TestConsecutiveDaysExtendStreak(cur realm, t *testing.T) {
 49	reset()
 50	alignDay()
 51	alice := testutils.TestAddress("alice")
 52
 53	uassert.Equal(t, 1, checkIn(cur, alice))
 54	advanceDays(1)
 55	uassert.Equal(t, 2, checkIn(cur, alice))
 56	advanceDays(1)
 57	uassert.Equal(t, 3, checkIn(cur, alice))
 58	uassert.Equal(t, 3, Best(alice))
 59}
 60
 61// The whole point of the realm: a missed window restarts the run at 1, but the
 62// record survives.
 63func TestMissedDayResetsCurrentButKeepsBest(cur realm, t *testing.T) {
 64	reset()
 65	alignDay()
 66	alice := testutils.TestAddress("alice")
 67
 68	checkIn(cur, alice)
 69	advanceDays(1)
 70	checkIn(cur, alice)
 71	uassert.Equal(t, 2, Best(alice))
 72
 73	advanceDays(7) // a long gap
 74	uassert.Equal(t, 1, checkIn(cur, alice))
 75	uassert.Equal(t, 1, Current(alice))
 76	uassert.Equal(t, 2, Best(alice))
 77}
 78
 79func TestTwiceInSameDayPanics(cur realm, t *testing.T) {
 80	reset()
 81	alignDay()
 82	alice := testutils.TestAddress("alice")
 83	checkIn(cur, alice)
 84
 85	testing.SetRealm(testing.NewUserRealm(alice))
 86	rec := revive(func() { CheckIn(cross(cur)) })
 87	uassert.True(t, rec != nil)
 88}
 89
 90// Current() reports the live truth: once the window lapses the streak is 0,
 91// even though the stored record still holds the old value until the next
 92// check-in rewrites it.
 93func TestCurrentGoesStaleWithoutACheckIn(cur realm, t *testing.T) {
 94	reset()
 95	alignDay()
 96	alice := testutils.TestAddress("alice")
 97	checkIn(cur, alice)
 98
 99	advanceDays(1) // yesterday — still within reach
100	uassert.Equal(t, 1, Current(alice))
101	advanceDays(1) // gap: the run is over
102	uassert.Equal(t, 0, Current(alice))
103	uassert.Equal(t, 1, Best(alice)) // record untouched
104}
105
106func TestStreaksAreIndependentPerAddress(cur realm, t *testing.T) {
107	reset()
108	alignDay()
109	alice := testutils.TestAddress("alice")
110	bob := testutils.TestAddress("bob")
111
112	checkIn(cur, alice)
113	advanceDays(1)
114	checkIn(cur, alice)
115	checkIn(cur, bob)
116
117	uassert.Equal(t, 2, Current(alice))
118	uassert.Equal(t, 1, Current(bob))
119}
120
121func TestUnknownAddressIsZero(t *testing.T) {
122	reset()
123	carol := testutils.TestAddress("carol")
124	uassert.Equal(t, 0, Current(carol))
125	uassert.Equal(t, 0, Best(carol))
126}
127
128func TestRenderEmptyBoard(t *testing.T) {
129	reset()
130	out := Render("")
131	uassert.True(t, strings.Contains(out, "Nobody has checked in yet"))
132	uassert.True(t, strings.Contains(out, "Current day: **"+strconv.FormatInt(Day(), 10)+"**"))
133}
134
135func TestRenderRanksByBest(cur realm, t *testing.T) {
136	reset()
137	alignDay()
138	alice := testutils.TestAddress("alice")
139	bob := testutils.TestAddress("bob")
140	// alice reaches 2, bob only 1
141	checkIn(cur, alice)
142	advanceDays(1)
143	checkIn(cur, alice)
144	checkIn(cur, bob)
145
146	out := Render("")
147	ai := strings.Index(out, alice.String())
148	bi := strings.Index(out, bob.String())
149	uassert.True(t, ai >= 0)
150	uassert.True(t, bi >= 0)
151	uassert.True(t, ai < bi) // alice ranks first
152}
153
154func TestRenderSingleAddress(cur realm, t *testing.T) {
155	reset()
156	alignDay()
157	alice := testutils.TestAddress("alice")
158	checkIn(cur, alice)
159
160	out := Render("/" + alice.String())
161	uassert.True(t, strings.Contains(out, "current streak: **1**"))
162	uassert.True(t, strings.Contains(out, "total check-ins: **1**"))
163}
164
165func TestRenderRejectsBadAddress(t *testing.T) {
166	reset()
167	out := Render("/not-an-address")
168	uassert.True(t, strings.Contains(out, "Not a valid address"))
169}
170
171// TestRenderOneValidatesBeforeEcho pins the order in renderOne: a path that is
172// not an address is refused without being written back. v0 wrote it into a
173// heading between two backticks first, then checked it.
174func TestRenderOneValidatesBeforeEcho(t *testing.T) {
175	const payload = "x` ![p](https:e.io) `y"
176	got := Render("/" + payload)
177	uassert.True(t, strings.HasSuffix(got, "**\n\n_Not a valid address._\n"), "no refusal")
178	uassert.False(t, strings.Contains(got, "![p]"), "the path was echoed")
179}