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

vesting_test.gno

9.08 Kb · 253 lines
  1package vesting
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"chain/runtime"
  8
  9	"gno.land/p/nt/uassert/v0"
 10	"gno.land/p/nt/testutils/v0"
 11)
 12
 13const (
 14	mainnetStart int64 = 1789225200
 15	mainnetEnd   int64 = 1852383600
 16	moul               = address("g1manfred47kzduec920z88wfr64ylksmdcedlf5")
 17)
 18
 19// TestSeededSurvivesInit is the property the seed exists for: this realm is
 20// private = true, so a redeploy re-runs init() and wipes everything else. The
 21// one schedule the page is about has to come back without a transaction.
 22func TestSeededSurvivesInit(t *testing.T) {
 23	o, s, e, delayed, ok := ScheduleOf(moul)
 24	uassert.True(t, ok, "moul's schedule is missing after init")
 25	uassert.Equal(t, int64(106560000000), o)
 26	uassert.Equal(t, mainnetStart, s)
 27	uassert.Equal(t, mainnetEnd, e)
 28	uassert.False(t, delayed)
 29}
 30
 31// TestDeclareWritesOnlyYourOwnRow is the entire trust model. A caller cannot
 32// name an address, so it cannot describe one but its own.
 33func TestDeclareWritesOnlyYourOwnRow(cur realm, t *testing.T) {
 34	alice := testutils.TestAddress("alice")
 35	bob := testutils.TestAddress("bob")
 36
 37	testing.SetRealm(testing.NewUserRealm(alice))
 38	Declare(cross(cur), 500, 100, 200, false)
 39
 40	o, s, e, delayed, ok := ScheduleOf(alice)
 41	uassert.True(t, ok)
 42	uassert.Equal(t, int64(500), o)
 43	uassert.Equal(t, int64(100), s)
 44	uassert.Equal(t, int64(200), e)
 45	uassert.False(t, delayed)
 46
 47	// Nothing alice did touched bob.
 48	_, _, _, _, ok = ScheduleOf(bob)
 49	uassert.False(t, ok, "declaring for alice wrote a row for bob")
 50}
 51
 52func TestDeclareReplacesAndForgetRemoves(cur realm, t *testing.T) {
 53	carol := testutils.TestAddress("carol")
 54
 55	testing.SetRealm(testing.NewUserRealm(carol))
 56	Declare(cross(cur), 500, 100, 200, false)
 57	testing.SetRealm(testing.NewUserRealm(carol))
 58	Declare(cross(cur), 900, 10, 20, true)
 59
 60	o, s, e, delayed, ok := ScheduleOf(carol)
 61	uassert.True(t, ok)
 62	uassert.Equal(t, int64(900), o)
 63	uassert.Equal(t, int64(10), s)
 64	uassert.Equal(t, int64(20), e)
 65	uassert.True(t, delayed, "the delayed flag did not survive")
 66
 67	testing.SetRealm(testing.NewUserRealm(carol))
 68	Forget(cross(cur))
 69	_, _, _, _, ok = ScheduleOf(carol)
 70	uassert.False(t, ok, "Forget left the row behind")
 71}
 72
 73func TestForgetWithoutADeclarationAborts(cur realm, t *testing.T) {
 74	dave := testutils.TestAddress("dave")
 75	testing.SetRealm(testing.NewUserRealm(dave))
 76	uassert.AbortsContains(t, cur, "nothing declared", func() { Forget(cross(cur)) })
 77}
 78
 79// TestSeededRowIsNotWritable: the seeds are source, so a signer who happens to
 80// hold a seeded address still cannot rewrite it from a transaction. Otherwise
 81// a redeploy would silently revert whatever they set.
 82func TestSeededRowIsNotWritable(cur realm, t *testing.T) {
 83	testing.SetRealm(testing.NewUserRealm(moul))
 84	uassert.AbortsContains(t, cur, "seeded in the source", func() {
 85		Declare(cross(cur), 1, 2, 3, false)
 86	})
 87	testing.SetRealm(testing.NewUserRealm(moul))
 88	uassert.AbortsContains(t, cur, "seeded in the source", func() { Forget(cross(cur)) })
 89}
 90
 91func TestDeclareRejectsAnImpossibleSchedule(cur realm, t *testing.T) {
 92	erin := testutils.TestAddress("erin")
 93	testing.SetRealm(testing.NewUserRealm(erin))
 94	uassert.AbortsContains(t, cur, "start time must be before end time", func() {
 95		Declare(cross(cur), 100, 200, 100, false)
 96	})
 97	testing.SetRealm(testing.NewUserRealm(erin))
 98	uassert.AbortsContains(t, cur, "cannot be negative", func() {
 99		Declare(cross(cur), -1, 0, 100, false)
100	})
101}
102
103// TestDeclareZeroMeansNoSchedule lets an address say "nothing of mine is
104// locked", which is a real and useful claim.
105func TestDeclareZeroMeansNoSchedule(cur realm, t *testing.T) {
106	frank := testutils.TestAddress("frank")
107	testing.SetRealm(testing.NewUserRealm(frank))
108	Declare(cross(cur), 0, 0, 0, false)
109
110	o, _, _, _, ok := ScheduleOf(frank)
111	uassert.True(t, ok)
112	uassert.Equal(t, int64(0), o)
113
114	spend, known := SpendableOf(frank)
115	uassert.True(t, known)
116	uassert.Equal(t, balanceOf(frank), spend)
117}
118
119// TestUnknownAddressReportsItsBalanceAndSaysSo: the balance is real, the
120// spendable figure is not, and the caller is told which is which.
121func TestUnknownAddressReportsItsBalanceAndSaysSo(t *testing.T) {
122	stranger := testutils.TestAddress("stranger")
123
124	spend, ok := SpendableOf(stranger)
125	uassert.False(t, ok, "an unknown address must not claim a known schedule")
126	uassert.Equal(t, balanceOf(stranger), spend)
127
128	locked, ok := LockedOf(stranger)
129	uassert.False(t, ok)
130	uassert.Equal(t, int64(0), locked)
131}
132
133func TestCountTracksTheRegistry(cur realm, t *testing.T) {
134	before := Count()
135	grace := testutils.TestAddress("grace")
136	testing.SetRealm(testing.NewUserRealm(grace))
137	Declare(cross(cur), 10, 1, 2, false)
138	uassert.Equal(t, before+1, Count())
139	testing.SetRealm(testing.NewUserRealm(grace))
140	Forget(cross(cur))
141	uassert.Equal(t, before, Count())
142}
143
144// TestLockedIsCappedByTheBalance: an account that spent while its coins were
145// free can owe the schedule more than it now holds. The honest answer is that
146// nothing moves, never a negative spendable.
147func TestLockedIsCappedByTheBalance(cur realm, t *testing.T) {
148	poor := testutils.TestAddress("poor")
149	testing.SetRealm(testing.NewUserRealm(poor))
150	Declare(cross(cur), 1000000000000, mainnetStart, mainnetEnd, false)
151
152	locked, ok := LockedOf(poor)
153	uassert.True(t, ok)
154	uassert.Equal(t, balanceOf(poor), locked, "locked must not exceed the balance")
155
156	spend, _ := SpendableOf(poor)
157	uassert.Equal(t, int64(0), spend)
158	uassert.True(t, spend >= 0, "spendable went negative")
159}
160
161func TestIsAddr(t *testing.T) {
162	uassert.True(t, isAddr("g1manfred47kzduec920z88wfr64ylksmdcedlf5"))
163	uassert.False(t, isAddr(""))
164	uassert.False(t, isAddr("g1manfred"))
165	uassert.False(t, isAddr("x1manfred47kzduec920z88wfr64ylksmdcedlf5"))
166	uassert.False(t, isAddr("g1MANFRED47kzduec920z88wfr64ylksmdcedlf5"))
167	uassert.False(t, isAddr("g1manfred47kzduec920z88wfr64ylksmdcedlf5x"))
168}
169
170func TestGnotFormatting(t *testing.T) {
171	uassert.Equal(t, "0.000000 GNOT", gnot(0))
172	uassert.Equal(t, "0.000001 GNOT", gnot(1))
173	uassert.Equal(t, "1.000000 GNOT", gnot(1000000))
174	uassert.Equal(t, "106560.000000 GNOT", gnot(106560000000))
175	uassert.Equal(t, "1.234567 GNOT", gnot(1234567))
176	uassert.Equal(t, "-1.500000 GNOT", gnot(-1500000))
177}
178
179func TestPermilleFormatting(t *testing.T) {
180	uassert.Equal(t, "0.0%", permille(0))
181	uassert.Equal(t, "5.0%", permille(50))
182	uassert.Equal(t, "49.9%", permille(499))
183	uassert.Equal(t, "100.0%", permille(1000))
184}
185
186func TestDurationFormatting(t *testing.T) {
187	uassert.Equal(t, "complete", duration(0))
188	uassert.Equal(t, "complete", duration(-1))
189	uassert.Equal(t, "0h", duration(60))
190	uassert.Equal(t, "1h", duration(3600))
191	uassert.Equal(t, "1d 0h", duration(86400))
192	uassert.Equal(t, "2d 3h", duration(2*86400+3*3600))
193}
194
195// TestCivilDates pins the date arithmetic against dates that are known exactly,
196// including the two ends of the real mainnet term and a leap day.
197func TestCivilDates(t *testing.T) {
198	uassert.Equal(t, "1970-01-01", tstamp(0))
199	uassert.Equal(t, "2000-02-29", tstamp(951782400))
200	uassert.Equal(t, "2026-09-12", tstamp(mainnetStart))
201	uassert.Equal(t, "2028-09-12", tstamp(mainnetEnd))
202}
203
204// TestRenderShapes walks the three routes the page serves and checks each one
205// names its source, which is the promise the whole realm makes.
206func TestRenderShapes(t *testing.T) {
207	index := Render("")
208	uassert.True(t, strings.Contains(index, "# Vesting"), "index has no title")
209	uassert.True(t, strings.Contains(index, "no realm can read one"),
210		"index does not say the schedule is supplied")
211
212	seeded := Render(moul.String())
213	uassert.True(t, strings.Contains(seeded, "seeded in this realm's source"),
214		"a seeded row does not say where it came from")
215	uassert.True(t, strings.Contains(seeded, "spendable now"))
216
217	query := Render(moul.String() + "?o=1000000&s=100&e=200")
218	uassert.True(t, strings.Contains(query, "supplied in the URL by you"),
219		"a query schedule does not say it was supplied")
220	uassert.True(t, strings.Contains(query, "Nothing was stored."))
221
222	unknown := Render(testutils.TestAddress("nobody").String())
223	uassert.True(t, strings.Contains(unknown, "No schedule on file"))
224	uassert.True(t, strings.Contains(unknown, "gnokey query auth/accounts/"),
225		"the unknown page does not say how to find the schedule")
226
227	bad := Render("not-an-address")
228	uassert.True(t, strings.Contains(bad, "is not a gno.land address"))
229}
230
231// TestRenderEscapesWhatTheCallerTyped: the path is attacker-controlled and a
232// live realm can never be patched, so the escaping is checked before deploy.
233func TestRenderEscapesWhatTheCallerTyped(t *testing.T) {
234	out := Render("[x](/r/evil)")
235	uassert.False(t, strings.Contains(out, "[x](/r/evil)"),
236		"a markdown link typed into the path reached the page unescaped")
237}
238
239// TestRenderQueryRejectsGarbage keeps a typo readable instead of rendering a
240// schedule of zeroes as though it were real.
241func TestRenderQueryRejectsGarbage(t *testing.T) {
242	out := Render(moul.String() + "?o=abc")
243	uassert.True(t, strings.Contains(out, "is not a number"))
244
245	out = Render(moul.String() + "?o=100&s=200&e=100")
246	uassert.True(t, strings.Contains(out, "not a valid schedule"))
247}
248
249// TestChainHeightIsReadable guards the one call that would make Declare
250// non-deterministic if it were wrong.
251func TestChainHeightIsReadable(t *testing.T) {
252	uassert.Equal(t, runtime.ChainHeight(), chainHeight())
253}