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

moultest_test.gno

7.81 Kb · 207 lines
  1package moultest
  2
  3import (
  4	"chain"
  5	"strings"
  6	"testing"
  7
  8	"gno.land/p/nt/testutils/v0"
  9	"gno.land/p/nt/uassert/v0"
 10)
 11
 12// testing.SetRealm governs only the crossing calls made from the frame that
 13// called it, so every account switch below is written inline in the test body
 14// rather than in a helper.
 15
 16func TestDenom(t *testing.T) {
 17	// The denom is what a wallet, an explorer and a -send flag all type, and it
 18	// is immutable once the first coin exists. Pin the literal: deriving it from
 19	// Path would restate the code rather than check it.
 20	uassert.Equal(t, "/gno.land/r/moul/x/moultest/v0:moultest", Denom)
 21}
 22
 23func TestClaimIssuesRealCoins(cur realm, t *testing.T) {
 24	alice := testutils.TestAddress("mt-alice")
 25	testing.SetRealm(testing.NewUserRealm(alice))
 26
 27	_, ever := NextClaim(alice)
 28	uassert.False(t, ever, "no claim recorded before the first one")
 29	uassert.Equal(t, int64(0), BalanceOf(alice))
 30
 31	before := Circulating()
 32	got := Claim(cross(cur))
 33
 34	uassert.Equal(t, ClaimAmount, got)
 35	// The balance comes from the bank, not from realm storage: nothing in this
 36	// realm records who holds what.
 37	uassert.Equal(t, ClaimAmount, BalanceOf(alice))
 38	uassert.Equal(t, before+ClaimAmount, Circulating())
 39	uassert.Equal(t, ClaimAmount, ClaimedBy(alice))
 40
 41	next, ever := NextClaim(alice)
 42	uassert.True(t, ever, "a cooldown is recorded")
 43	uassert.Equal(t, ClaimEvery, next-123, "first test block is 123, so the cooldown lands there")
 44}
 45
 46func TestClaimCooldown(cur realm, t *testing.T) {
 47	bob := testutils.TestAddress("mt-bob")
 48	testing.SetRealm(testing.NewUserRealm(bob))
 49
 50	Claim(cross(cur))
 51	uassert.AbortsContains(t, cur, "too soon", func() { Claim(cross(cur)) })
 52
 53	testing.SkipHeights(ClaimEvery - 1)
 54	uassert.AbortsContains(t, cur, "too soon", func() { Claim(cross(cur)) })
 55
 56	testing.SkipHeights(1)
 57	Claim(cross(cur))
 58	uassert.Equal(t, 2*ClaimAmount, BalanceOf(bob))
 59}
 60
 61func TestAccountCap(cur realm, t *testing.T) {
 62	carol := testutils.TestAddress("mt-carol")
 63	testing.SetRealm(testing.NewUserRealm(carol))
 64
 65	for ClaimedBy(carol)+ClaimAmount <= MaxPerAccount {
 66		Claim(cross(cur))
 67		testing.SkipHeights(ClaimEvery)
 68	}
 69	uassert.Equal(t, MaxPerAccount, ClaimedBy(carol))
 70	uassert.AbortsContains(t, cur, "account cap reached", func() { Claim(cross(cur)) })
 71
 72	// Waiting does not help: the cooldown is not what stopped it.
 73	testing.SkipHeights(ClaimEvery * 10)
 74	uassert.AbortsContains(t, cur, "account cap reached", func() { Claim(cross(cur)) })
 75}
 76
 77func TestBurnIsScopedToTheCaller(cur realm, t *testing.T) {
 78	dave := testutils.TestAddress("mt-dave")
 79	erin := testutils.TestAddress("mt-erin")
 80
 81	testing.SetRealm(testing.NewUserRealm(erin))
 82	Claim(cross(cur))
 83	erinBefore := BalanceOf(erin)
 84
 85	testing.SetRealm(testing.NewUserRealm(dave))
 86	Claim(cross(cur))
 87
 88	issuedBefore, burnedBefore, circBefore := Issued(), Burned(), Circulating()
 89	claimedBefore := ClaimedBy(dave)
 90
 91	Burn(cross(cur), 400)
 92
 93	uassert.Equal(t, ClaimAmount-400, BalanceOf(dave))
 94	uassert.Equal(t, circBefore-400, Circulating(), "burning destroys the coin, it is not moved")
 95	uassert.Equal(t, burnedBefore+400, Burned())
 96	// Issuance is monotonic on purpose: if burning gave cap headroom back,
 97	// claim-burn-claim would be an unlimited faucet.
 98	uassert.Equal(t, issuedBefore, Issued())
 99	uassert.Equal(t, claimedBefore, ClaimedBy(dave))
100	// Burn takes no address, so erin cannot be reached from dave's call.
101	uassert.Equal(t, erinBefore, BalanceOf(erin))
102
103	uassert.AbortsContains(t, cur, "cannot burn", func() { Burn(cross(cur), 1_000_000) })
104	uassert.AbortsContains(t, cur, "positive amount", func() { Burn(cross(cur), 0) })
105	uassert.AbortsContains(t, cur, "positive amount", func() { Burn(cross(cur), -5) })
106}
107
108func TestTipRidesTheEnvelope(cur realm, t *testing.T) {
109	frank := testutils.TestAddress("mt-frank")
110	grace := testutils.TestAddress("mt-grace")
111
112	// On chain the -send envelope is credited to this realm's address before
113	// the function runs. The test harness has no bank-send primitive, so the
114	// credit is issued directly here; SetOriginSend is what the banker checks
115	// against, and it is the half that bounds what Tip may spend.
116	realmAddr := chain.PackageAddress(Path)
117	envelope := chain.NewCoins(chain.NewCoin(Denom, 250))
118	testing.IssueCoins(realmAddr, envelope)
119	// Unrelated coins already sitting at the realm's address. A RealmSend
120	// banker could spend these; the OriginSend one may not, and that is the
121	// property being pinned.
122	testing.IssueCoins(realmAddr, chain.NewCoins(chain.NewCoin(Denom, 5_000)))
123	realmBefore := BalanceOf(realmAddr)
124
125	testing.SetRealm(testing.NewUserRealm(frank))
126	testing.SetOriginSend(envelope)
127	sent := Tip(cross(cur), grace, "nice work")
128
129	uassert.Equal(t, int64(250), sent)
130	uassert.Equal(t, int64(250), BalanceOf(grace), "the tip landed on the recipient")
131	uassert.Equal(t, realmBefore-250, BalanceOf(realmAddr),
132		"exactly the envelope left the realm, and nothing else it was holding")
133	uassert.Equal(t, 1, len(tips))
134	uassert.Equal(t, "nice work", tips[0].note)
135
136	testing.SetOriginSend(nil)
137}
138
139func TestTipRefusals(cur realm, t *testing.T) {
140	heidi := testutils.TestAddress("mt-heidi")
141	ivan := testutils.TestAddress("mt-ivan")
142
143	testing.SetRealm(testing.NewUserRealm(heidi))
144	testing.SetOriginSend(nil)
145	uassert.AbortsContains(t, cur, "attach moultest", func() { Tip(cross(cur), ivan, "hi") })
146
147	envelope := chain.NewCoins(chain.NewCoin(Denom, 10))
148	testing.IssueCoins(chain.PackageAddress(Path), envelope)
149	testing.SetOriginSend(envelope)
150	uassert.AbortsContains(t, cur, "tip somebody else", func() { Tip(cross(cur), heidi, "me") })
151	uassert.AbortsContains(t, cur, "valid address", func() { Tip(cross(cur), "nope", "hi") })
152	uassert.AbortsContains(t, cur, "note is", func() {
153		Tip(cross(cur), ivan, strings.Repeat("x", MaxNote+1))
154	})
155
156	testing.SetOriginSend(nil)
157}
158
159func TestSupplyCap(cur realm, t *testing.T) {
160	// Fill the remaining supply with fresh accounts, each capped at
161	// MaxPerAccount, then check that the next one is refused. Issuance carries
162	// across tests, so the loop starts from whatever is left rather than from
163	// MaxSupply.
164	names := []string{"cap-a", "cap-b", "cap-c", "cap-d", "cap-e", "cap-f",
165		"cap-g", "cap-h", "cap-i", "cap-j", "cap-k", "cap-l"}
166	for _, name := range names {
167		if Issued()+ClaimAmount > MaxSupply {
168			break
169		}
170		who := testutils.TestAddress(name)
171		testing.SetRealm(testing.NewUserRealm(who))
172		for ClaimedBy(who)+ClaimAmount <= MaxPerAccount && Issued()+ClaimAmount <= MaxSupply {
173			Claim(cross(cur))
174			testing.SkipHeights(ClaimEvery)
175		}
176	}
177	uassert.Equal(t, MaxSupply, Issued(), "the accounts above exhaust the supply exactly")
178
179	late := testutils.TestAddress("cap-late")
180	testing.SetRealm(testing.NewUserRealm(late))
181	uassert.AbortsContains(t, cur, "supply cap reached", func() { Claim(cross(cur)) })
182
183	// Burning does not reopen the faucet either: circulating drops, issued does
184	// not, and the cap is on issued.
185	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("cap-a")))
186	Burn(cross(cur), 500)
187	uassert.Equal(t, MaxSupply, Issued())
188	testing.SetRealm(testing.NewUserRealm(late))
189	uassert.AbortsContains(t, cur, "supply cap reached", func() { Claim(cross(cur)) })
190}
191
192func TestRender(t *testing.T) {
193	index := Render("")
194	uassert.True(t, strings.Contains(index, "# moultest"), "heading:\n"+index)
195	uassert.True(t, strings.Contains(index, Denom), "the denom is on the page")
196	uassert.True(t, strings.Contains(index, "| issued, ever |"), "supply table:\n"+index)
197	uassert.True(t, strings.Contains(index, "nice work"), "the tip board is shown:\n"+index)
198
199	frank := testutils.TestAddress("mt-frank")
200	acct := Render(frank.String())
201	uassert.True(t, strings.Contains(acct, frank.String()), "the account section names it:\n"+acct)
202	uassert.True(t, strings.Contains(acct, "claimed for life"), acct)
203
204	// A path that is not an address renders the index without an account block.
205	uassert.False(t, strings.Contains(Render("not-an-address"), "claimed for life"),
206		"a junk path adds no account section")
207}