item_test.gno
12.85 Kb · 341 lines
1package item
2
3import (
4 "chain"
5 "testing"
6
7 "gno.land/p/nt/testutils/v0"
8 "gno.land/p/nt/uassert/v0"
9 "gno.land/r/akkadia/beta/admin"
10)
11
12var itemTest = struct {
13 Admin, Operator, Owner, Other address
14 SetupDone bool
15}{
16 Admin: testutils.TestAddress("itemAdmin"),
17 Operator: testutils.TestAddress("itemOperator"),
18 Owner: testutils.TestAddress("itemOwner"),
19 Other: testutils.TestAddress("itemOther"),
20}
21
22func setupItemTest(cur realm, t *testing.T) {
23 t.Helper()
24 resetItemState()
25 testing.SetOriginSend(chain.Coins{})
26 if itemTest.SetupDone {
27 return
28 }
29 originalAdmin := admin.GetAdmin()
30 testing.SetOriginCaller(originalAdmin)
31 testing.SetRealm(testing.NewUserRealm(originalAdmin))
32 admin.TransferAdmin(cross(cur), itemTest.Admin)
33 testing.SetOriginCaller(itemTest.Admin)
34 testing.SetRealm(testing.NewUserRealm(itemTest.Admin))
35 admin.AcceptAdmin(cross(cur))
36 admin.SetOperator(cross(cur), itemTest.Operator)
37 itemTest.SetupDone = true
38}
39
40func TestSafeMintTracksSupply(cur realm, t *testing.T) {
41 setupItemTest(cross(cur), t)
42 testing.SetOriginCaller(itemTest.Operator)
43 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
44 SafeMint(cross(cur), itemTest.Owner, "42", 5)
45 uassert.Equal(t, int64(5), BalanceOf(itemTest.Owner, "42"))
46 uassert.Equal(t, int64(5), TotalSupply("42"))
47 SafeMint(cross(cur), itemTest.Owner, "42", 1)
48 SafeMint(cross(cur), itemTest.Other, "42", 2)
49 uassert.Equal(t, int64(6), BalanceOf(itemTest.Owner, "42"))
50 uassert.Equal(t, int64(2), BalanceOf(itemTest.Other, "42"))
51 uassert.Equal(t, int64(8), TotalSupply("42"))
52}
53
54func TestItemTokenIDAcceptsOpaqueStrings(cur realm, t *testing.T) {
55 setupItemTest(cross(cur), t)
56 testing.SetOriginCaller(itemTest.Operator)
57 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
58 SafeMint(cross(cur), itemTest.Owner, "0", 1)
59 SafeMint(cross(cur), itemTest.Owner, "4294967295", 2)
60 SafeMint(cross(cur), itemTest.Owner, "sword", 3)
61 uassert.Equal(t, int64(1), BalanceOf(itemTest.Owner, "0"))
62 uassert.Equal(t, int64(2), TotalSupply("4294967295"))
63 uassert.Equal(t, int64(3), TotalSupply("sword"))
64 uassert.AbortsWithMessage(t, cur, "token id is required", func() {
65 SafeMint(cross(cur), itemTest.Owner, "", 1)
66 })
67}
68
69func TestItemTransferAndBurnUpdateSupply(cur realm, t *testing.T) {
70 setupItemTest(cross(cur), t)
71 testing.SetOriginCaller(itemTest.Operator)
72 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
73 SafeMint(cross(cur), itemTest.Owner, "42", 5)
74 testing.SetOriginCaller(itemTest.Owner)
75 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
76 SafeTransferFrom(cross(cur), itemTest.Owner, itemTest.Other, "42", 2)
77 Burn(cross(cur), itemTest.Owner, "42", 1)
78 uassert.Equal(t, int64(2), BalanceOf(itemTest.Owner, "42"))
79 uassert.Equal(t, int64(2), BalanceOf(itemTest.Other, "42"))
80 uassert.Equal(t, int64(4), TotalSupply("42"))
81}
82
83func TestItemApprovalAllowsTransferAndBurn(cur realm, t *testing.T) {
84 setupItemTest(cross(cur), t)
85 testing.SetOriginCaller(itemTest.Operator)
86 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
87 SafeMint(cross(cur), itemTest.Owner, "42", 5)
88 testing.SetOriginCaller(itemTest.Owner)
89 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
90 SetApprovalForAll(cross(cur), itemTest.Other, true)
91 testing.SetOriginCaller(itemTest.Other)
92 testing.SetRealm(testing.NewUserRealm(itemTest.Other))
93 SafeTransferFrom(cross(cur), itemTest.Owner, itemTest.Admin, "42", 2)
94 Burn(cross(cur), itemTest.Owner, "42", 1)
95 uassert.Equal(t, int64(2), BalanceOf(itemTest.Owner, "42"))
96 uassert.Equal(t, int64(2), BalanceOf(itemTest.Admin, "42"))
97 uassert.Equal(t, int64(4), TotalSupply("42"))
98}
99
100func TestSafeMintSecurity(cur realm, t *testing.T) {
101 setupItemTest(cross(cur), t)
102 testing.SetOriginCaller(itemTest.Owner)
103 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
104 uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() {
105 SafeMint(cross(cur), itemTest.Owner, "1", 1)
106 })
107 testing.SetOriginCaller(itemTest.Operator)
108 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/item_relay"))
109 uassert.AbortsWithMessage(t, cur, "admin or operator access required", func() {
110 SafeMint(cross(cur), itemTest.Owner, "1", 1)
111 })
112 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
113 testing.SetOriginSend(chain.Coins{{"ugnot", 1}})
114 uassert.AbortsWithMessage(t, cur, "attached coins not allowed", func() {
115 SafeMint(cross(cur), itemTest.Owner, "1", 1)
116 })
117}
118
119func TestItemOwnedList(cur realm, t *testing.T) {
120 setupItemTest(cross(cur), t)
121 testing.SetOriginCaller(itemTest.Operator)
122 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
123 uassert.Equal(t, 0, GetOwnedCount(itemTest.Owner))
124 uassert.Equal(t, 0, len(ListByOwner(itemTest.Owner, 1, 10)))
125 uassert.PanicsWithMessage(t, cur, "invalid item owner", func() {
126 GetOwnedCount(address(""))
127 })
128 uassert.PanicsWithMessage(t, cur, "page must be at least 1", func() {
129 ListByOwner(itemTest.Owner, 0, 1)
130 })
131 uassert.PanicsWithMessage(t, cur, "count must be at least 1", func() {
132 ListByOwner(itemTest.Owner, 1, 0)
133 })
134 uassert.PanicsWithMessage(t, cur, "count exceeds listLimit (max: 100)", func() {
135 ListByOwner(itemTest.Owner, 1, 101)
136 })
137 uassert.Equal(t, 100, GetListLimit())
138
139 SafeMint(cross(cur), itemTest.Owner, "10", 5)
140 SafeMint(cross(cur), itemTest.Owner, "20", 3)
141 SafeMint(cross(cur), itemTest.Owner, "30", 1)
142 uassert.Equal(t, 3, GetOwnedCount(itemTest.Owner))
143 page1 := ListByOwner(itemTest.Owner, 1, 2)
144 uassert.Equal(t, 2, len(page1))
145 uassert.Equal(t, "10", page1[0]["tokenId"])
146 uassert.Equal(t, "5", page1[0]["amount"])
147 uassert.Equal(t, "20", page1[1]["tokenId"])
148 page2 := ListByOwner(itemTest.Owner, 2, 2)
149 uassert.Equal(t, 1, len(page2))
150 uassert.Equal(t, "30", page2[0]["tokenId"])
151 uassert.Equal(t, 0, len(ListByOwner(itemTest.Owner, 3, 2)))
152 page1[0]["amount"] = "hacked"
153 uassert.Equal(t, "5", ListByOwner(itemTest.Owner, 1, 1)[0]["amount"])
154
155 testing.SetOriginCaller(itemTest.Owner)
156 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
157 SafeTransferFrom(cross(cur), itemTest.Owner, itemTest.Other, "10", 2)
158 uassert.Equal(t, 3, GetOwnedCount(itemTest.Owner))
159 uassert.Equal(t, 1, GetOwnedCount(itemTest.Other))
160 uassert.Equal(t, "3", ListByOwner(itemTest.Owner, 1, 10)[0]["amount"])
161 uassert.Equal(t, "2", ListByOwner(itemTest.Other, 1, 10)[0]["amount"])
162 SafeTransferFrom(cross(cur), itemTest.Owner, itemTest.Other, "10", 3)
163 uassert.Equal(t, 2, GetOwnedCount(itemTest.Owner))
164 uassert.Equal(t, 1, GetOwnedCount(itemTest.Other))
165 Burn(cross(cur), itemTest.Owner, "20", 3)
166 uassert.Equal(t, 1, GetOwnedCount(itemTest.Owner))
167 uassert.Equal(t, "30", ListByOwner(itemTest.Owner, 1, 10)[0]["tokenId"])
168
169 testing.SetOriginCaller(itemTest.Operator)
170 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
171 SafeMint(cross(cur), itemTest.Owner, "30", 2)
172 uassert.Equal(t, 1, GetOwnedCount(itemTest.Owner))
173 uassert.Equal(t, "3", ListByOwner(itemTest.Owner, 1, 10)[0]["amount"])
174}
175
176func TestSetListLimit(cur realm, t *testing.T) {
177 setupItemTest(cross(cur), t)
178
179 testing.SetOriginCaller(itemTest.Admin)
180 testing.SetRealm(testing.NewUserRealm(itemTest.Admin))
181 SetListLimit(cross(cur), 50)
182 uassert.Equal(t, 50, GetListLimit())
183
184 SetListLimit(cross(cur), 1)
185 uassert.PanicsWithMessage(t, cur, "count exceeds listLimit (max: 1)", func() {
186 ListByOwner(itemTest.Owner, 1, 2)
187 })
188
189 uassert.AbortsWithMessage(t, cur, "limit must be at least 1", func() {
190 SetListLimit(cross(cur), 0)
191 })
192 uassert.AbortsWithMessage(t, cur, "limit must be at least 1", func() {
193 SetListLimit(cross(cur), -1)
194 })
195
196 testing.SetOriginCaller(itemTest.Operator)
197 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
198 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
199 SetListLimit(cross(cur), 50)
200 })
201
202 testing.SetOriginCaller(itemTest.Owner)
203 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
204 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
205 SetListLimit(cross(cur), 50)
206 })
207
208 testing.SetOriginCaller(itemTest.Admin)
209 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/item_admin_relay"))
210 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
211 SetListLimit(cross(cur), 50)
212 })
213}
214
215func TestSetMinterRealm(cur realm, t *testing.T) {
216 setupItemTest(cross(cur), t)
217 minterPath := "gno.land/r/example/item_minter"
218 minter := testing.NewCodeRealm(minterPath)
219 minterAddr := minter.Address()
220
221 uassert.Equal(t, minterAddr.String(), chain.PackageAddress(minterPath).String())
222 uassert.Equal(t, false, IsMinterRealm(minterAddr))
223
224 testing.SetOriginCaller(itemTest.Operator)
225 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
226 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
227 SetMinterRealm(cross(cur), minterPath)
228 })
229
230 testing.SetOriginCaller(itemTest.Admin)
231 testing.SetRealm(testing.NewUserRealm(itemTest.Admin))
232 uassert.AbortsWithMessage(t, cur, "package path is required", func() {
233 SetMinterRealm(cross(cur), "")
234 })
235 uassert.AbortsWithMessage(t, cur, "invalid package path", func() {
236 SetMinterRealm(cross(cur), "gno.land/p/akkadia/beta/item")
237 })
238 SetMinterRealm(cross(cur), minterPath)
239 uassert.Equal(t, true, IsMinterRealm(minterAddr))
240 SetMinterRealm(cross(cur), minterPath)
241 uassert.Equal(t, true, IsMinterRealm(minterAddr))
242
243 UnsetMinterRealm(cross(cur), minterPath)
244 uassert.Equal(t, false, IsMinterRealm(minterAddr))
245 uassert.AbortsWithMessage(t, cur, "package path is not a minter realm", func() {
246 UnsetMinterRealm(cross(cur), minterPath)
247 })
248 uassert.AbortsWithMessage(t, cur, "package path is required", func() {
249 UnsetMinterRealm(cross(cur), "")
250 })
251
252 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/item_admin_relay"))
253 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
254 SetMinterRealm(cross(cur), minterPath)
255 })
256}
257
258func TestSafeMintFromRealm(cur realm, t *testing.T) {
259 setupItemTest(cross(cur), t)
260 minter := testing.NewCodeRealm("gno.land/r/example/item_minter")
261 unregistered := testing.NewCodeRealm("gno.land/r/example/item_minter_other")
262
263 testing.SetOriginCaller(itemTest.Owner)
264 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
265 uassert.AbortsWithMessage(t, cur, "user calls not allowed: function must be called by a realm", func() {
266 SafeMintFromRealm(cross(cur), itemTest.Owner, "1", 1)
267 })
268
269 testing.SetRealm(unregistered)
270 uassert.AbortsWithMessage(t, cur, "minter realm required", func() {
271 SafeMintFromRealm(cross(cur), itemTest.Owner, "1", 1)
272 })
273
274 testing.SetOriginCaller(itemTest.Admin)
275 testing.SetRealm(testing.NewUserRealm(itemTest.Admin))
276 SetMinterRealm(cross(cur), "gno.land/r/example/item_minter")
277
278 testing.SetOriginCaller(itemTest.Owner)
279 testing.SetRealm(minter)
280 SafeMintFromRealm(cross(cur), itemTest.Owner, "7", 3)
281 uassert.Equal(t, int64(3), BalanceOf(itemTest.Owner, "7"))
282 uassert.Equal(t, int64(3), TotalSupply("7"))
283 uassert.Equal(t, 1, GetOwnedCount(itemTest.Owner))
284
285 uassert.AbortsWithMessage(t, cur, "item recipient must be origin caller", func() {
286 SafeMintFromRealm(cross(cur), itemTest.Other, "8", 1)
287 })
288 uassert.Equal(t, int64(0), TotalSupply("8"))
289
290 testing.SetOriginSend(chain.Coins{{"ugnot", 1}})
291 SafeMintFromRealm(cross(cur), itemTest.Owner, "9", 1)
292 testing.SetOriginSend(chain.Coins{})
293 uassert.Equal(t, int64(1), BalanceOf(itemTest.Owner, "9"))
294}
295
296func TestBurnFromRealm(cur realm, t *testing.T) {
297 setupItemTest(cross(cur), t)
298 minter := testing.NewCodeRealm("gno.land/r/example/item_minter")
299
300 testing.SetOriginCaller(itemTest.Operator)
301 testing.SetRealm(testing.NewUserRealm(itemTest.Operator))
302 SafeMint(cross(cur), itemTest.Owner, "42", 5)
303
304 testing.SetOriginCaller(itemTest.Admin)
305 testing.SetRealm(testing.NewUserRealm(itemTest.Admin))
306 SetMinterRealm(cross(cur), "gno.land/r/example/item_minter")
307
308 testing.SetOriginCaller(itemTest.Owner)
309 testing.SetRealm(testing.NewUserRealm(itemTest.Owner))
310 uassert.AbortsWithMessage(t, cur, "user calls not allowed: function must be called by a realm", func() {
311 BurnFromRealm(cross(cur), itemTest.Owner, "42", 1)
312 })
313
314 testing.SetRealm(minter)
315 uassert.AbortsWithMessage(t, cur, "item owner must be origin caller", func() {
316 BurnFromRealm(cross(cur), itemTest.Other, "42", 1)
317 })
318 uassert.Equal(t, int64(5), BalanceOf(itemTest.Owner, "42"))
319
320 BurnFromRealm(cross(cur), itemTest.Owner, "42", 2)
321 uassert.Equal(t, int64(3), BalanceOf(itemTest.Owner, "42"))
322 uassert.Equal(t, int64(3), TotalSupply("42"))
323
324 uassert.AbortsWithMessage(t, cur, "item amount must be positive", func() {
325 SafeMintFromRealm(cross(cur), itemTest.Owner, "99", 0)
326 })
327 uassert.Equal(t, int64(3), BalanceOf(itemTest.Owner, "42"))
328 uassert.Equal(t, int64(3), TotalSupply("42"))
329 uassert.Equal(t, int64(0), TotalSupply("99"))
330 uassert.Equal(t, 1, GetOwnedCount(itemTest.Owner))
331
332 testing.SetOriginCaller(itemTest.Admin)
333 testing.SetRealm(testing.NewUserRealm(itemTest.Admin))
334 UnsetMinterRealm(cross(cur), "gno.land/r/example/item_minter")
335 testing.SetOriginCaller(itemTest.Owner)
336 testing.SetRealm(minter)
337 uassert.AbortsWithMessage(t, cur, "minter realm required", func() {
338 BurnFromRealm(cross(cur), itemTest.Owner, "42", 1)
339 })
340 uassert.Equal(t, int64(3), BalanceOf(itemTest.Owner, "42"))
341}