craft_test.gno
21.10 Kb · 578 lines
1package craft
2
3import (
4 "chain"
5 "testing"
6
7 "gno.land/p/nt/uassert/v0"
8 "gno.land/r/akkadia/beta/equipment"
9 "gno.land/r/akkadia/beta/item"
10)
11
12func TestSetRecipeCreatesAndUpdatesRecipe(cur realm, t *testing.T) {
13 setupCraftTest(cross(cur), t)
14 testing.SetOriginCaller(craftTest.Admin)
15 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
16
17 SetRecipe(cross(cur), "0001|minecraft:iron_ingot:01", 100)
18 uassert.True(t, HasRecipe("0001|minecraft:iron_ingot:1"))
19 uassert.True(t, HasRecipe("0001|minecraft:iron_ingot:01"))
20 uassert.True(t, HasRecipe("0001|0:minecraft:iron_ingot:1"))
21 uassert.Equal(t, uint32(100), GetRecipe("0001|minecraft:iron_ingot:01"))
22 uassert.Equal(t, 1, GetRecipeCount())
23
24 SetRecipe(cross(cur), "0001|0:minecraft:iron_ingot:1", 101)
25 uassert.Equal(t, uint32(101), GetRecipe("0001|minecraft:iron_ingot:1"))
26 uassert.Equal(t, 1, GetRecipeCount())
27
28 SetRecipe(cross(cur), "000A|craft-hex-a:1|craft-hex-b:2", 102)
29 uassert.True(t, HasRecipe("000a|craft-hex-a:1|craft-hex-b:2"))
30 uassert.Equal(t, uint32(102), GetRecipe("000A|craft-hex-a:1|craft-hex-b:2"))
31 uassert.Equal(t, 2, GetRecipeCount())
32}
33
34func TestSetRecipeRequiresDirectAdminWithoutCoins(cur realm, t *testing.T) {
35 setupCraftTest(cross(cur), t)
36 recipe := "0001|craft-security-item:1"
37
38 testing.SetOriginCaller(craftTest.Owner)
39 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
40 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
41 SetRecipe(cross(cur), recipe, 200)
42 })
43 uassert.False(t, HasRecipe(recipe))
44
45 testing.SetOriginCaller(craftTest.Admin)
46 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/craft_admin_relay"))
47 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
48 SetRecipe(cross(cur), recipe, 200)
49 })
50 uassert.False(t, HasRecipe(recipe))
51
52 testing.SetOriginCaller(craftTest.Admin)
53 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
54 testing.SetOriginSend(chain.Coins{{"ugnot", 1}})
55 uassert.AbortsWithMessage(t, cur, "attached coins not allowed", func() {
56 SetRecipe(cross(cur), recipe, 200)
57 })
58 testing.SetOriginSend(chain.Coins{})
59 uassert.False(t, HasRecipe(recipe))
60}
61
62func TestSetRecipesStoresMultipleRecipes(cur realm, t *testing.T) {
63 setupCraftTest(cross(cur), t)
64 testing.SetOriginCaller(craftTest.Admin)
65 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
66
67 SetRecipes(
68 cross(cur),
69 "0001|craft-batch-iron:1,0002|craft-batch-gold:2",
70 "300,301",
71 )
72
73 uassert.Equal(t, uint32(300), GetRecipe("0001|craft-batch-iron:1"))
74 uassert.Equal(t, uint32(301), GetRecipe("0002|craft-batch-gold:2"))
75}
76
77func TestSetRecipesStoresOccupancyBatch(cur realm, t *testing.T) {
78 setupCraftTest(cross(cur), t)
79 testing.SetOriginCaller(craftTest.Admin)
80 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
81
82 SetRecipes(
83 cross(cur),
84 "0013|test-iron:1|test-iron:2|test-stick:2,"+
85 "0003|test-wood:2|test-stone:1,"+
86 "0001|test-herb:3,"+
87 "0007|test-copper:1|test-leather:2|test-cloth:1",
88 "1101000,1101001,1101002,1101003",
89 )
90
91 uassert.Equal(t, 4, GetRecipeCount())
92 uassert.Equal(t, uint32(1101000), GetRecipe("0013|test-iron:1|test-iron:2|test-stick:2"))
93 uassert.Equal(t, uint32(1101001), GetRecipe("0003|test-wood:2|test-stone:1"))
94 uassert.Equal(t, uint32(1101002), GetRecipe("0001|test-herb:3"))
95 uassert.Equal(t, uint32(1101003), GetRecipe("0007|test-copper:1|test-leather:2|test-cloth:1"))
96 uassert.True(t, HasRecipe("0013|0:test-iron:1|1:test-iron:2|4:test-stick:2"))
97 uassert.Equal(t, 4, len(ListRecipes(1, 20)))
98}
99
100func TestSetRecipesAllowsLastDuplicateToWin(cur realm, t *testing.T) {
101 setupCraftTest(cross(cur), t)
102 testing.SetOriginCaller(craftTest.Admin)
103 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
104 recipe := "0001|craft-batch-duplicate:1"
105
106 SetRecipes(cross(cur), recipe+","+recipe, "310,311")
107
108 uassert.Equal(t, uint32(311), GetRecipe(recipe))
109}
110
111func TestSetRecipesRequiresDirectAdmin(cur realm, t *testing.T) {
112 setupCraftTest(cross(cur), t)
113 recipe := "0001|craft-batch-security:1"
114
115 testing.SetOriginCaller(craftTest.Owner)
116 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
117 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
118 SetRecipes(cross(cur), recipe, "320")
119 })
120 uassert.False(t, HasRecipe(recipe))
121
122 testing.SetOriginCaller(craftTest.Admin)
123 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/craft_batch_relay"))
124 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
125 SetRecipes(cross(cur), recipe, "320")
126 })
127 uassert.False(t, HasRecipe(recipe))
128}
129
130func TestSetRecipesRejectsCountMismatchAndRollsBack(cur realm, t *testing.T) {
131 setupCraftTest(cross(cur), t)
132 testing.SetOriginCaller(craftTest.Admin)
133 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
134 firstRecipe := "0001|craft-mismatch-a:1"
135 secondRecipe := "0001|craft-mismatch-b:1"
136
137 uassert.AbortsWithMessage(t, cur, "recipes and item ids count mismatch", func() {
138 SetRecipes(cross(cur), firstRecipe+","+secondRecipe, "400")
139 })
140
141 uassert.False(t, HasRecipe(firstRecipe))
142 uassert.False(t, HasRecipe(secondRecipe))
143}
144
145func TestSetRecipesRejectsInvalidItemID(cur realm, t *testing.T) {
146 setupCraftTest(cross(cur), t)
147 testing.SetOriginCaller(craftTest.Admin)
148 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
149 recipe := "0001|craft-invalid-id:1"
150
151 uassert.AbortsWithMessage(t, cur, "invalid item id: invalid", func() {
152 SetRecipes(cross(cur), recipe, "invalid")
153 })
154 uassert.False(t, HasRecipe(recipe))
155}
156
157func TestSetRecipesRejectsEmptyTokensAndTrailingComma(cur realm, t *testing.T) {
158 setupCraftTest(cross(cur), t)
159 testing.SetOriginCaller(craftTest.Admin)
160 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
161 recipe := "0001|craft-empty-token:1"
162
163 uassert.AbortsWithMessage(t, cur, "empty recipe not allowed", func() {
164 SetRecipes(cross(cur), recipe+",", "410,411")
165 })
166 uassert.False(t, HasRecipe(recipe))
167
168 uassert.AbortsWithMessage(t, cur, "empty item id not allowed", func() {
169 SetRecipes(cross(cur), recipe+",0001|craft-empty-id:1", "412,")
170 })
171 uassert.False(t, HasRecipe(recipe))
172}
173
174func TestSetRecipesRejectsBatchAboveLimit(cur realm, t *testing.T) {
175 setupCraftTest(cross(cur), t)
176 testing.SetOriginCaller(craftTest.Admin)
177 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
178 recipe := "0001|craft-batch-limit:1"
179 recipesCSV := recipe
180 itemIDsCSV := "500"
181 for i := 1; i < 101; i++ {
182 recipesCSV += "," + recipe
183 itemIDsCSV += ",500"
184 }
185
186 uassert.AbortsWithMessage(t, cur, "recipes exceeds 100", func() {
187 SetRecipes(cross(cur), recipesCSV, itemIDsCSV)
188 })
189 uassert.False(t, HasRecipe(recipe))
190}
191
192func TestDeleteRecipeRemovesRecipe(cur realm, t *testing.T) {
193 setupCraftTest(cross(cur), t)
194 testing.SetOriginCaller(craftTest.Admin)
195 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
196 recipe := "0001|craft-delete-item:1"
197 SetRecipe(cross(cur), recipe, 600)
198
199 DeleteRecipe(cross(cur), "0001|craft-delete-item:01")
200 uassert.False(t, HasRecipe(recipe))
201 uassert.Equal(t, 0, GetRecipeCount())
202}
203
204func TestDeleteRecipesRemovesBatchAndRequiresAdmin(cur realm, t *testing.T) {
205 setupCraftTest(cross(cur), t)
206 testing.SetOriginCaller(craftTest.Admin)
207 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
208 firstRecipe := "0001|craft-delete-a:1"
209 secondRecipe := "0002|craft-delete-b:1"
210 SetRecipe(cross(cur), firstRecipe, 601)
211 SetRecipe(cross(cur), secondRecipe, 602)
212
213 testing.SetOriginCaller(craftTest.Owner)
214 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
215 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
216 DeleteRecipes(cross(cur), firstRecipe)
217 })
218 uassert.True(t, HasRecipe(firstRecipe))
219
220 testing.SetOriginCaller(craftTest.Admin)
221 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
222 DeleteRecipes(cross(cur), firstRecipe+","+secondRecipe)
223 uassert.False(t, HasRecipe(firstRecipe))
224 uassert.False(t, HasRecipe(secondRecipe))
225}
226
227func TestDeleteRecipesRejectsMissingAndRollsBack(cur realm, t *testing.T) {
228 setupCraftTest(cross(cur), t)
229 testing.SetOriginCaller(craftTest.Admin)
230 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
231 firstRecipe := "0001|craft-delete-missing-a:1"
232 secondRecipe := "0002|craft-delete-missing-b:1"
233 SetRecipe(cross(cur), firstRecipe, 603)
234 SetRecipe(cross(cur), secondRecipe, 604)
235
236 uassert.AbortsWithMessage(t, cur, "recipe not found: 0001|0:craft-delete-missing-c:1", func() {
237 DeleteRecipes(cross(cur), firstRecipe+",0001|craft-delete-missing-c:1")
238 })
239 uassert.True(t, HasRecipe(firstRecipe))
240 uassert.True(t, HasRecipe(secondRecipe))
241}
242
243func TestDeleteRecipeRejectsMissingRecipe(cur realm, t *testing.T) {
244 setupCraftTest(cross(cur), t)
245 testing.SetOriginCaller(craftTest.Admin)
246 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
247 uassert.AbortsWithMessage(t, cur, "recipe not found: 0001|0:craft-missing-delete:1", func() {
248 DeleteRecipe(cross(cur), "0001|craft-missing-delete:1")
249 })
250}
251
252func TestListRecipesPagesAndCopiesRows(cur realm, t *testing.T) {
253 setupCraftTest(cross(cur), t)
254 testing.SetOriginCaller(craftTest.Admin)
255 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
256 SetRecipe(cross(cur), "0001|craft-list-a:1", 701)
257 SetRecipe(cross(cur), "0002|craft-list-b:1", 702)
258 SetRecipe(cross(cur), "0004|craft-list-c:1", 703)
259
260 uassert.Equal(t, 3, GetRecipeCount())
261 page1 := ListRecipes(1, 2)
262 uassert.Equal(t, 2, len(page1))
263 page1[0]["itemId"] = "hacked"
264 uassert.Equal(t, "701", ListRecipes(1, 2)[0]["itemId"])
265 page2 := ListRecipes(2, 2)
266 uassert.Equal(t, 1, len(page2))
267 uassert.Equal(t, 0, len(ListRecipes(3, 2)))
268
269 uassert.PanicsWithMessage(t, cur, "page must be at least 1", func() {
270 ListRecipes(0, 1)
271 })
272 uassert.PanicsWithMessage(t, cur, "count must be at least 1", func() {
273 ListRecipes(1, 0)
274 })
275 uassert.PanicsWithMessage(t, cur, "count exceeds listLimit (max: 100)", func() {
276 ListRecipes(1, 101)
277 })
278}
279
280func TestSetListLimit(cur realm, t *testing.T) {
281 setupCraftTest(cross(cur), t)
282 testing.SetOriginCaller(craftTest.Admin)
283 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
284 SetListLimit(cross(cur), 1)
285 uassert.Equal(t, 1, GetListLimit())
286 uassert.PanicsWithMessage(t, cur, "count exceeds listLimit (max: 1)", func() {
287 ListRecipes(1, 2)
288 })
289
290 uassert.AbortsWithMessage(t, cur, "limit must be at least 1", func() {
291 SetListLimit(cross(cur), 0)
292 })
293
294 testing.SetOriginCaller(craftTest.Owner)
295 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
296 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
297 SetListLimit(cross(cur), 50)
298 })
299}
300
301func TestMustParseRecipeCanonicalizesSlotsAndCountsIngredients(cur realm, t *testing.T) {
302 canonical, ingredients := mustParseRecipe("0013|minecraft:iron_ingot:01|minecraft:iron_ingot:2|minecraft:stick:3")
303
304 uassert.Equal(t, "0013|0:minecraft:iron_ingot:1|1:minecraft:iron_ingot:2|4:minecraft:stick:3", canonical)
305 uassert.Equal(t, 3, len(ingredients))
306 uassert.Equal(t, uint(0), ingredients[0].Slot)
307 uassert.Equal(t, "minecraft:iron_ingot", ingredients[0].TokenID)
308 uassert.Equal(t, int64(1), ingredients[0].Amount)
309 uassert.Equal(t, uint(4), ingredients[2].Slot)
310 uassert.Equal(t, "minecraft:stick", ingredients[2].TokenID)
311 uassert.Equal(t, int64(3), ingredients[2].Amount)
312
313 roundTrip, _ := mustParseRecipe(canonical)
314 uassert.Equal(t, canonical, roundTrip)
315
316 hexCanonical, hexIngredients := mustParseRecipe("000A|craft-hex-a:1|craft-hex-b:2")
317 uassert.Equal(t, "000a|1:craft-hex-a:1|3:craft-hex-b:2", hexCanonical)
318 uassert.Equal(t, uint(1), hexIngredients[0].Slot)
319 uassert.Equal(t, uint(3), hexIngredients[1].Slot)
320}
321
322func TestMustParseRecipeRejectsInvalidRecipes(cur realm, t *testing.T) {
323 tests := []struct {
324 recipe, panicMsg string
325 }{
326 {"", "invalid recipe"},
327 {"0000|item:1", "invalid recipe occupancy"},
328 {"0001|item", "invalid recipe ingredient"},
329 {"0001|item:0", "ingredient amount must be positive"},
330 {"0003|item:1", "occupied slots and ingredients count mismatch"},
331 {"0001|1:item:1", "ingredient slot mismatch"},
332 {"0013|4:stick:1|0:iron:1|1:iron:1", "ingredient slot mismatch"},
333 {"0001|%zz:1", "invalid item token id"},
334 }
335
336 for _, tc := range tests {
337 recipe, panicMsg := tc.recipe, tc.panicMsg
338 uassert.PanicsWithMessage(t, cur, panicMsg, func() {
339 mustParseRecipe(recipe)
340 })
341 }
342}
343
344func TestHasRecipeReturnsFalseForInvalidAndMissingRecipes(cur realm, t *testing.T) {
345 setupCraftTest(cross(cur), t)
346 uassert.False(t, HasRecipe(""))
347 uassert.False(t, HasRecipe("not-a-recipe"))
348 uassert.False(t, HasRecipe("0001|craft-missing-query:1"))
349 uassert.PanicsWithMessage(t, cur, "recipe not found: 0001|0:craft-missing-query:1", func() {
350 GetRecipe("0001|craft-missing-query:1")
351 })
352}
353
354func TestCraftBurnsItemsAndMintsEquipment(cur realm, t *testing.T) {
355 setupCraftTest(cross(cur), t)
356 recipe := "0013|craft-success-iron:1|craft-success-iron:2|craft-success-stick:2"
357
358 testing.SetOriginCaller(craftTest.Admin)
359 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
360 SetRecipe(cross(cur), recipe, 1101000)
361 mintCraftItem(cross(cur), "craft-success-iron", 3)
362 mintCraftItem(cross(cur), "craft-success-stick", 2)
363
364 testing.SetOriginCaller(craftTest.Owner)
365 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
366 before := equipment.GetLatestTokenID()
367 collectorBefore := ugnotBalanceForTest(craftTest.FeeCollector)
368 realmBefore := ugnotBalanceForTest(craftTest.PkgAddr)
369 payCraftFee(defaultCraftFeeCoins())
370 tokenID := Craft(cross(cur), "0013|craft-success-iron:01|craft-success-iron:2|craft-success-stick:2")
371 uassert.Equal(t, before+1, tokenID)
372 uassert.Equal(t, int64(0), item.BalanceOf(craftTest.Owner, "craft-success-iron"))
373 uassert.Equal(t, int64(0), item.BalanceOf(craftTest.Owner, "craft-success-stick"))
374 uassert.Equal(t, craftTest.Owner, equipment.OwnerOf(tokenID))
375 uassert.Equal(t, "1101000", equipment.GetMetadata(tokenID)["definitionId"])
376 uassert.Equal(t, defaultCraftFee, ugnotBalanceForTest(craftTest.FeeCollector)-collectorBefore)
377 uassert.Equal(t, realmBefore, ugnotBalanceForTest(craftTest.PkgAddr))
378}
379
380func TestCraftAllocatesSequentialEquipmentTokenIDs(cur realm, t *testing.T) {
381 setupCraftTest(cross(cur), t)
382 recipe := "0001|craft-skip-id:1"
383
384 testing.SetOriginCaller(craftTest.Admin)
385 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
386 SetRecipe(cross(cur), recipe, 1101003)
387 mintCraftItem(cross(cur), "craft-skip-id", 2)
388 before := equipment.GetLatestTokenID()
389 equipment.Mint(cross(cur), craftTest.Other, 1101998, "", "")
390
391 testing.SetOriginCaller(craftTest.Owner)
392 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
393 payCraftFee(defaultCraftFeeCoins())
394 tokenID := Craft(cross(cur), recipe)
395 uassert.Equal(t, before+2, tokenID)
396 uassert.Equal(t, craftTest.Other, equipment.OwnerOf(before+1))
397 uassert.Equal(t, craftTest.Owner, equipment.OwnerOf(tokenID))
398}
399
400func TestCraftRequiresEquipmentMinterRealm(cur realm, t *testing.T) {
401 setupCraftTest(cross(cur), t)
402 recipe := "0001|craft-mint-rollback:2"
403
404 testing.SetOriginCaller(craftTest.Admin)
405 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
406 SetRecipe(cross(cur), recipe, 1101001)
407 mintCraftItem(cross(cur), "craft-mint-rollback", 2)
408 equipment.UnsetMinterRealm(cross(cur), "gno.land/r/akkadia/beta/craft")
409
410 testing.SetOriginCaller(craftTest.Owner)
411 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
412 uassert.AbortsWithMessage(t, cur, "minter realm required", func() {
413 payCraftFee(defaultCraftFeeCoins())
414 Craft(cross(cur), recipe)
415 })
416}
417
418func TestCraftRequiresItemMinterRealm(cur realm, t *testing.T) {
419 setupCraftTest(cross(cur), t)
420 recipe := "0001|craft-item-minter:1"
421
422 testing.SetOriginCaller(craftTest.Admin)
423 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
424 SetRecipe(cross(cur), recipe, 1101005)
425 mintCraftItem(cross(cur), "craft-item-minter", 1)
426 item.UnsetMinterRealm(cross(cur), "gno.land/r/akkadia/beta/craft")
427
428 testing.SetOriginCaller(craftTest.Owner)
429 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
430 uassert.AbortsWithMessage(t, cur, "minter realm required", func() {
431 payCraftFee(defaultCraftFeeCoins())
432 Craft(cross(cur), recipe)
433 })
434
435 uassert.Equal(t, int64(1), item.BalanceOf(craftTest.Owner, "craft-item-minter"))
436}
437
438func TestCraftRejectsUnknownRecipeAndInsufficientItems(cur realm, t *testing.T) {
439 setupCraftTest(cross(cur), t)
440 recipe := "0001|craft-fail-item:2"
441
442 testing.SetOriginCaller(craftTest.Owner)
443 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
444 uassert.AbortsWithMessage(t, cur, "recipe not found: 0001|0:craft-fail-item:2", func() {
445 Craft(cross(cur), recipe)
446 })
447 uassert.AbortsWithMessage(t, cur, "invalid recipe", func() {
448 Craft(cross(cur), "bad")
449 })
450
451 testing.SetOriginCaller(craftTest.Admin)
452 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
453 SetRecipe(cross(cur), recipe, 1101004)
454 mintCraftItem(cross(cur), "craft-fail-item", 1)
455
456 testing.SetOriginCaller(craftTest.Owner)
457 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
458 uassert.AbortsWithMessage(t, cur, "item burn amount exceeds supply", func() {
459 payCraftFee(defaultCraftFeeCoins())
460 Craft(cross(cur), recipe)
461 })
462 uassert.Equal(t, int64(1), item.BalanceOf(craftTest.Owner, "craft-fail-item"))
463}
464
465func TestCraftRequiresDirectUserAndExactFee(cur realm, t *testing.T) {
466 setupCraftTest(cross(cur), t)
467 recipe := "0001|craft-craft-security:1"
468 testing.SetOriginCaller(craftTest.Admin)
469 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
470 SetRecipe(cross(cur), recipe, 1101002)
471 before := equipment.GetLatestTokenID()
472
473 testing.SetOriginCaller(craftTest.Owner)
474 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/craft_craft_relay"))
475 uassert.AbortsWithMessage(t, cur, "contract calls not allowed: function must be called directly by user", func() {
476 Craft(cross(cur), recipe)
477 })
478
479 testing.SetOriginCaller(craftTest.Owner)
480 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
481 uassert.AbortsWithMessage(t, cur, "exact payment required: expected 1000000ugnot, got ", func() {
482 Craft(cross(cur), recipe)
483 })
484 uassert.AbortsWithMessage(t, cur, "exact payment required: expected 1000000ugnot, got 1ugnot", func() {
485 payCraftFee(chain.Coins{{"ugnot", 1}})
486 Craft(cross(cur), recipe)
487 })
488 testing.SetOriginSend(chain.Coins{})
489 uassert.Equal(t, before, equipment.GetLatestTokenID())
490}
491
492func TestCraftRejectsInexactPayment(cur realm, t *testing.T) {
493 setupCraftTest(cross(cur), t)
494 recipe := "0001|craft-inexact-fee:1"
495 testing.SetOriginCaller(craftTest.Admin)
496 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
497 SetRecipe(cross(cur), recipe, 1101006)
498 mintCraftItem(cross(cur), "craft-inexact-fee", 3)
499 before := equipment.GetLatestTokenID()
500
501 testing.SetOriginCaller(craftTest.Owner)
502 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
503 uassert.AbortsWithMessage(t, cur, "exact payment required: expected 1000000ugnot, got 999999ugnot", func() {
504 payCraftFee(chain.Coins{{"ugnot", 999_999}})
505 Craft(cross(cur), recipe)
506 })
507 uassert.AbortsWithMessage(t, cur, "exact payment required: expected 1000000ugnot, got 1000001ugnot", func() {
508 payCraftFee(chain.Coins{{"ugnot", 1_000_001}})
509 Craft(cross(cur), recipe)
510 })
511 uassert.AbortsWithMessage(t, cur, "exact payment required: expected 1000000ugnot, got 1000000ugnot,1foo", func() {
512 payCraftFee(chain.Coins{{"ugnot", defaultCraftFee}, {"foo", 1}})
513 Craft(cross(cur), recipe)
514 })
515 uassert.Equal(t, before, equipment.GetLatestTokenID())
516 uassert.Equal(t, int64(3), item.BalanceOf(craftTest.Owner, "craft-inexact-fee"))
517}
518
519func TestSetCraftFeeUpdatesFeeAndAllowsFreeCraft(cur realm, t *testing.T) {
520 setupCraftTest(cross(cur), t)
521 uassert.Equal(t, defaultCraftFee, GetCraftFee())
522
523 testing.SetOriginCaller(craftTest.Admin)
524 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
525 SetCraftFee(cross(cur), 2_000_000)
526 uassert.Equal(t, int64(2_000_000), GetCraftFee())
527 SetCraftFee(cross(cur), 0)
528 uassert.Equal(t, int64(0), GetCraftFee())
529
530 recipe := "0001|craft-free-fee:1"
531 SetRecipe(cross(cur), recipe, 1101007)
532 mintCraftItem(cross(cur), "craft-free-fee", 1)
533
534 testing.SetOriginCaller(craftTest.Owner)
535 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
536 uassert.AbortsWithMessage(t, cur, "exact payment required: expected no coins, got 1ugnot", func() {
537 payCraftFee(chain.Coins{{"ugnot", 1}})
538 Craft(cross(cur), recipe)
539 })
540 collectorBefore := ugnotBalanceForTest(craftTest.FeeCollector)
541 payCraftFee(chain.Coins{})
542 tokenID := Craft(cross(cur), recipe)
543 uassert.Equal(t, craftTest.Owner, equipment.OwnerOf(tokenID))
544 uassert.Equal(t, collectorBefore, ugnotBalanceForTest(craftTest.FeeCollector))
545}
546
547func TestSetCraftFeeRequiresDirectAdminWithoutCoins(cur realm, t *testing.T) {
548 setupCraftTest(cross(cur), t)
549 uassert.Equal(t, defaultCraftFee, GetCraftFee())
550
551 testing.SetOriginCaller(craftTest.Owner)
552 testing.SetRealm(testing.NewUserRealm(craftTest.Owner))
553 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
554 SetCraftFee(cross(cur), 50)
555 })
556 uassert.Equal(t, defaultCraftFee, GetCraftFee())
557
558 testing.SetOriginCaller(craftTest.Admin)
559 testing.SetRealm(testing.NewCodeRealm("gno.land/r/example/craft_fee_relay"))
560 uassert.AbortsWithMessage(t, cur, "admin access required", func() {
561 SetCraftFee(cross(cur), 50)
562 })
563 uassert.Equal(t, defaultCraftFee, GetCraftFee())
564
565 testing.SetOriginCaller(craftTest.Admin)
566 testing.SetRealm(testing.NewUserRealm(craftTest.Admin))
567 testing.SetOriginSend(chain.Coins{{"ugnot", 1}})
568 uassert.AbortsWithMessage(t, cur, "attached coins not allowed", func() {
569 SetCraftFee(cross(cur), 50)
570 })
571 testing.SetOriginSend(chain.Coins{})
572 uassert.Equal(t, defaultCraftFee, GetCraftFee())
573
574 uassert.AbortsWithMessage(t, cur, "fee must be non-negative", func() {
575 SetCraftFee(cross(cur), -1)
576 })
577 uassert.Equal(t, defaultCraftFee, GetCraftFee())
578}