coin_test.gno
3.75 Kb · 129 lines
1package coin
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/nt/seqid/v0"
8 "gno.land/p/nt/testutils/v0"
9 "gno.land/p/nt/uassert/v0"
10 "gno.land/p/nt/urequire/v0"
11)
12
13var fullPolicy = Policy{
14 Mint: "1 per distinct replier",
15 Sink: "burned to pin a thread",
16 Buyer: "anyone who wants placement",
17}
18
19// newTestCoin builds a Coin from a test. The same-realm cross promotes the
20// test's EOA-origin cur into a code realm frame, which is what grc20.NewToken
21// requires: it is the shape grc20's own tests use (vendor/.../token_test.gno).
22func newTestCoin(t *testing.T, policy Policy, id seqid.ID, rlm realm) (c *Coin) {
23 t.Helper()
24 func(cur realm) {
25 c = New("Test Points", "TEST", 0, id, policy, 0, cur)
26 }(cross(rlm))
27 return
28}
29
30func TestPolicyValid(t *testing.T) {
31 tests := []struct {
32 name string
33 p Policy
34 want bool
35 }{
36 {"complete", fullPolicy, true},
37 {"no mint rule", Policy{Sink: "s", Buyer: "b"}, false},
38 {"no sink", Policy{Mint: "m", Buyer: "b"}, false},
39 {"no buyer", Policy{Mint: "m", Sink: "s"}, false},
40 {"empty", Policy{}, false},
41 }
42 for _, tt := range tests {
43 uassert.Equal(t, tt.want, tt.p.Valid(), tt.name)
44 }
45}
46
47// New's refusal is asserted with AbortsContains and not PanicsContains: the
48// constructor runs behind a same-realm cross (the frame grc20.NewToken
49// requires), and a panic raised across a crossing call is an abort that no
50// recover() in the caller can catch.
51func TestNewRefusesAnIncompletePolicy(cur realm, t *testing.T) {
52 uassert.AbortsContains(t, cur, "mint rule, a sink and a buyer", func() {
53 newTestCoin(t, Policy{Mint: "m", Sink: "s"}, 0, cur)
54 })
55}
56
57func TestEarnAndSpendMoveSupplyInOppositeDirections(cur realm, t *testing.T) {
58 c := newTestCoin(t, fullPolicy, 1, cur)
59 alice := testutils.TestAddress("alice")
60
61 c.Earn(alice, 10)
62 uassert.Equal(t, int64(10), c.BalanceOf(alice))
63 uassert.Equal(t, int64(10), c.Supply())
64
65 c.Spend(alice, 4)
66 uassert.Equal(t, int64(6), c.BalanceOf(alice))
67 uassert.Equal(t, int64(6), c.Supply())
68
69 // The flows stay visible after the fact, which the supply alone cannot
70 // show: 6 in supply could be 6 earned and nothing burned.
71 uassert.Equal(t, int64(10), c.Earned())
72 uassert.Equal(t, int64(4), c.Spent())
73}
74
75func TestEarnOfNothingIsANoOp(cur realm, t *testing.T) {
76 c := newTestCoin(t, fullPolicy, 2, cur)
77 alice := testutils.TestAddress("alice")
78
79 c.Earn(alice, 0)
80 c.Earn(alice, -5)
81 uassert.Equal(t, int64(0), c.Supply())
82 uassert.Equal(t, int64(0), c.Earned())
83 uassert.Equal(t, 0, c.Holders())
84}
85
86func TestSpendNamesBothNumbersWhenShort(cur realm, t *testing.T) {
87 c := newTestCoin(t, fullPolicy, 3, cur)
88 alice := testutils.TestAddress("alice")
89 c.Earn(alice, 2)
90
91 uassert.PanicsContains(t, cur, "balance 2 is short of 5", func() {
92 c.Spend(alice, 5)
93 })
94 uassert.PanicsContains(t, cur, "must be positive", func() {
95 c.Spend(alice, 0)
96 })
97 uassert.Equal(t, int64(2), c.BalanceOf(alice))
98}
99
100func TestRenderCarriesTheThreeDeclarations(cur realm, t *testing.T) {
101 c := newTestCoin(t, fullPolicy, 4, cur)
102 c.Earn(testutils.TestAddress("alice"), 7)
103 c.Spend(testutils.TestAddress("alice"), 3)
104
105 out := c.Render()
106 for _, want := range []string{
107 "Test Points",
108 "TEST",
109 fullPolicy.Mint,
110 fullPolicy.Sink,
111 fullPolicy.Buyer,
112 } {
113 uassert.True(t, strings.Contains(out, want), "render is missing "+want)
114 }
115 // 4 in supply, 7 earned, 3 burned, 1 holder.
116 uassert.True(t, strings.Contains(out, "| 4 | 7 | 3 | 1 |"), "render is missing the flow row: "+out)
117}
118
119func TestTokenIsAnOrdinaryGRC20(cur realm, t *testing.T) {
120 c := newTestCoin(t, fullPolicy, 5, cur)
121 alice := testutils.TestAddress("alice")
122 c.Earn(alice, 3)
123
124 tok := c.Token()
125 urequire.False(t, tok == nil, "token should not be nil")
126 uassert.Equal(t, "TEST", tok.GetSymbol())
127 uassert.Equal(t, int64(3), tok.TotalSupply())
128 uassert.Equal(t, int64(3), tok.BalanceOf(alice))
129}