Kind Realm (renderable)
Name craft
Namespace akkadia / beta
Exported functions 14
Module gno.land/r/akkadia/beta/craft
gno 0.9 craft_test.gno gno
⧉
1 package craft 2 3 import ( 4 "chain" 5 "testing" 6 7 "gno.land/p/nt/uassert/v0" 8 Craft (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipe string) uint64
DeleteRecipe (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipe string)
DeleteRecipes (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipesCSV string)
GetCraftFee () int64
GetListLimit () int
GetRecipe (recipe string) uint32
GetRecipeCount () int
HasRecipe (recipe string) bool
ListRecipes (page int, count int) []map[string]string
Render (iURL string) string
SetCraftFee (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, fee int64)
SetListLimit (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, limit int)
SetRecipe (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipe string, itemID uint32)
SetRecipes (cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, recipesCSV string, itemIDsCSV string)
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.
"gno.land/r/akkadia/beta/equipment"
9 "gno.land/r/akkadia/beta/item"
10 )
11
12 func 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
34 func 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
62 func 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
77 func 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
100 func 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
111 func 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
130 func 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
145 func 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
157 func 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
174 func 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
192 func 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
204 func 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
227 func 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
243 func 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
252 func 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
280 func 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
301 func 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
322 func 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
344 func 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
354 func 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
380 func 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
400 func 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
418 func 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
438 func 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
465 func 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
492 func 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
519 func 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
547 func 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 }
579