github.com/Finschia/finschia-sdk@v0.48.1/x/collection/keeper/supply_test.go (about) 1 package keeper_test 2 3 import ( 4 "fmt" 5 "math/rand" 6 7 sdk "github.com/Finschia/finschia-sdk/types" 8 "github.com/Finschia/finschia-sdk/x/collection" 9 ) 10 11 func (s *KeeperTestSuite) TestCreateContract() { 12 ctx, _ := s.ctx.CacheContext() 13 14 input := collection.Contract{ 15 Name: "tibetian fox", 16 Meta: "Tibetian Fox", 17 Uri: "file:///tibetian_fox.png", 18 } 19 id := s.keeper.CreateContract(ctx, s.vendor, input) 20 s.Require().NotEmpty(id) 21 22 output, err := s.keeper.GetContract(ctx, id) 23 s.Require().NoError(err) 24 s.Require().NotNil(output) 25 26 s.Require().Equal(id, output.Id) 27 s.Require().Equal(input.Name, output.Name) 28 s.Require().Equal(input.Meta, output.Meta) 29 s.Require().Equal(input.Uri, output.Uri) 30 } 31 32 func (s *KeeperTestSuite) TestCreateTokenClass() { 33 testCases := map[string]struct { 34 contractID string 35 class collection.TokenClass 36 err error 37 }{ 38 "valid fungible token class": { 39 contractID: s.contractID, 40 class: &collection.FTClass{}, 41 }, 42 "valid non-fungible token class": { 43 contractID: s.contractID, 44 class: &collection.NFTClass{}, 45 }, 46 } 47 48 for name, tc := range testCases { 49 s.Run(name, func() { 50 ctx, _ := s.ctx.CacheContext() 51 52 id, err := s.keeper.CreateTokenClass(ctx, tc.contractID, tc.class) 53 s.Require().ErrorIs(err, tc.err) 54 if tc.err != nil { 55 s.Require().Nil(id) 56 return 57 } 58 s.Require().NotNil(id) 59 60 class, err := s.keeper.GetTokenClass(ctx, tc.contractID, *id) 61 s.Require().NoError(err) 62 s.Require().NoError(class.ValidateBasic()) 63 }) 64 } 65 } 66 67 func (s *KeeperTestSuite) TestMintFT() { 68 testCases := map[string]struct { 69 contractID string 70 amount collection.Coin 71 err error 72 }{ 73 "valid request": { 74 contractID: s.contractID, 75 amount: collection.NewFTCoin(s.ftClassID, sdk.OneInt()), 76 }, 77 "invalid token id": { 78 contractID: s.contractID, 79 amount: collection.NewNFTCoin(s.ftClassID, 1), 80 err: collection.ErrTokenNotExist, 81 }, 82 "class not found": { 83 contractID: s.contractID, 84 amount: collection.NewFTCoin("00bab10c", sdk.OneInt()), 85 err: collection.ErrTokenNotExist, 86 }, 87 "not a class id of ft": { 88 contractID: s.contractID, 89 amount: collection.NewFTCoin(s.nftClassID, sdk.OneInt()), 90 err: collection.ErrTokenNotMintable, 91 }, 92 } 93 94 for name, tc := range testCases { 95 s.Run(name, func() { 96 ctx, _ := s.ctx.CacheContext() 97 98 // gather state 99 classID := collection.SplitTokenID(tc.amount.TokenId) 100 balanceBefore := s.keeper.GetBalance(ctx, tc.contractID, s.stranger, collection.NewFTID(classID)) 101 supplyBefore := s.keeper.GetSupply(ctx, tc.contractID, classID) 102 mintedBefore := s.keeper.GetMinted(ctx, tc.contractID, classID) 103 burntBefore := s.keeper.GetBurnt(ctx, tc.contractID, classID) 104 105 err := s.keeper.MintFT(ctx, tc.contractID, s.stranger, collection.NewCoins(tc.amount)) 106 s.Require().ErrorIs(err, tc.err) 107 if tc.err != nil { 108 return 109 } 110 111 amount := tc.amount.Amount 112 balanceAfter := s.keeper.GetBalance(ctx, tc.contractID, s.stranger, collection.NewFTID(classID)) 113 s.Require().Equal(balanceBefore.Add(amount), balanceAfter) 114 supplyAfter := s.keeper.GetSupply(ctx, tc.contractID, classID) 115 s.Require().Equal(supplyBefore.Add(amount), supplyAfter) 116 mintedAfter := s.keeper.GetMinted(ctx, tc.contractID, classID) 117 s.Require().Equal(mintedBefore.Add(amount), mintedAfter) 118 burntAfter := s.keeper.GetBurnt(ctx, tc.contractID, classID) 119 s.Require().Equal(burntBefore, burntAfter) 120 }) 121 } 122 123 // accumulation test 124 s.Run("accumulation test", func() { 125 ctx, _ := s.ctx.CacheContext() 126 numMints := int64(100) 127 contractID := s.contractID 128 classID := s.ftClassID 129 for i := int64(1); i <= numMints; i++ { 130 amount := sdk.NewInt(rand.Int63()) 131 132 // gather state 133 balanceBefore := s.keeper.GetBalance(ctx, s.contractID, s.stranger, collection.NewFTID(s.ftClassID)) 134 supplyBefore := s.keeper.GetSupply(ctx, contractID, classID) 135 mintedBefore := s.keeper.GetMinted(ctx, contractID, classID) 136 burntBefore := s.keeper.GetBurnt(ctx, contractID, classID) 137 138 s.keeper.MintFT(ctx, s.contractID, s.stranger, collection.NewCoins(collection.NewFTCoin(s.ftClassID, amount))) 139 140 balanceAfter := s.keeper.GetBalance(ctx, s.contractID, s.stranger, collection.NewFTID(s.ftClassID)) 141 s.Require().Equal(balanceBefore.Add(amount), balanceAfter) 142 supplyAfter := s.keeper.GetSupply(ctx, contractID, classID) 143 s.Require().Equal(supplyBefore.Add(amount), supplyAfter) 144 mintedAfter := s.keeper.GetMinted(ctx, contractID, classID) 145 s.Require().Equal(mintedBefore.Add(amount), mintedAfter) 146 burntAfter := s.keeper.GetBurnt(ctx, contractID, classID) 147 s.Require().Equal(burntBefore, burntAfter) 148 } 149 }) 150 } 151 152 func (s *KeeperTestSuite) TestMintNFT() { 153 testCases := map[string]struct { 154 contractID string 155 param collection.MintNFTParam 156 err error 157 }{ 158 "valid request": { 159 contractID: s.contractID, 160 param: collection.MintNFTParam{TokenType: s.nftClassID}, 161 }, 162 "class not found": { 163 contractID: s.contractID, 164 param: collection.MintNFTParam{TokenType: "deadbeef"}, 165 err: collection.ErrTokenTypeNotExist, 166 }, 167 "not a class id of nft": { 168 contractID: s.contractID, 169 param: collection.MintNFTParam{TokenType: s.ftClassID}, 170 err: collection.ErrTokenTypeNotExist, 171 }, 172 } 173 174 for name, tc := range testCases { 175 s.Run(name, func() { 176 ctx, _ := s.ctx.CacheContext() 177 178 // gather state 179 classID := tc.param.TokenType 180 supplyBefore := s.keeper.GetSupply(ctx, tc.contractID, classID) 181 mintedBefore := s.keeper.GetMinted(ctx, tc.contractID, classID) 182 burntBefore := s.keeper.GetBurnt(ctx, tc.contractID, classID) 183 184 tokens, err := s.keeper.MintNFT(ctx, tc.contractID, s.stranger, []collection.MintNFTParam{tc.param}) 185 s.Require().ErrorIs(err, tc.err) 186 if tc.err != nil { 187 return 188 } 189 190 amount := sdk.OneInt() 191 s.Require().Len(tokens, 1) 192 tokenID := tokens[0].TokenId 193 balanceAfter := s.keeper.GetBalance(ctx, tc.contractID, s.stranger, tokenID) 194 s.Require().Equal(amount, balanceAfter) 195 supplyAfter := s.keeper.GetSupply(ctx, tc.contractID, classID) 196 s.Require().Equal(supplyBefore.Add(amount), supplyAfter) 197 mintedAfter := s.keeper.GetMinted(ctx, tc.contractID, classID) 198 s.Require().Equal(mintedBefore.Add(amount), mintedAfter) 199 burntAfter := s.keeper.GetBurnt(ctx, tc.contractID, classID) 200 s.Require().Equal(burntBefore, burntAfter) 201 }) 202 } 203 } 204 205 func (s *KeeperTestSuite) TestBurnCoins() { 206 testCases := map[string]struct { 207 contractID string 208 amount collection.Coin 209 err error 210 }{ 211 "valid request": { 212 contractID: s.contractID, 213 amount: collection.NewFTCoin(s.ftClassID, sdk.OneInt()), 214 }, 215 "insufficient tokens": { 216 contractID: s.contractID, 217 amount: collection.NewFTCoin("00bab10c", sdk.OneInt()), 218 err: collection.ErrInsufficientToken, 219 }, 220 } 221 222 for name, tc := range testCases { 223 s.Run(name, func() { 224 ctx, _ := s.ctx.CacheContext() 225 226 // gather state 227 classID := collection.SplitTokenID(tc.amount.TokenId) 228 balanceBefore := s.keeper.GetBalance(ctx, tc.contractID, s.vendor, collection.NewFTID(classID)) 229 supplyBefore := s.keeper.GetSupply(ctx, tc.contractID, classID) 230 mintedBefore := s.keeper.GetMinted(ctx, tc.contractID, classID) 231 burntBefore := s.keeper.GetBurnt(ctx, tc.contractID, classID) 232 233 _, err := s.keeper.BurnCoins(ctx, tc.contractID, s.vendor, collection.NewCoins(tc.amount)) 234 s.Require().ErrorIs(err, tc.err) 235 if tc.err != nil { 236 return 237 } 238 239 amount := tc.amount.Amount 240 balanceAfter := s.keeper.GetBalance(ctx, tc.contractID, s.vendor, collection.NewFTID(classID)) 241 s.Require().Equal(balanceBefore.Sub(amount), balanceAfter) 242 supplyAfter := s.keeper.GetSupply(ctx, tc.contractID, classID) 243 s.Require().Equal(supplyBefore.Sub(amount), supplyAfter) 244 mintedAfter := s.keeper.GetMinted(ctx, tc.contractID, classID) 245 s.Require().Equal(mintedBefore, mintedAfter) 246 burntAfter := s.keeper.GetBurnt(ctx, tc.contractID, classID) 247 s.Require().Equal(burntBefore.Add(amount), burntAfter) 248 }) 249 } 250 } 251 252 func (s *KeeperTestSuite) TestModifyContract() { 253 contractDescriptions := map[string]string{ 254 s.contractID: "valid", 255 "deadbeef": "not-exist", 256 } 257 changes := []collection.Attribute{ 258 {Key: collection.AttributeKeyName.String(), Value: "fox"}, 259 {Key: collection.AttributeKeyURI.String(), Value: "file:///fox.png"}, 260 {Key: collection.AttributeKeyMeta.String(), Value: "Fox"}, 261 } 262 263 for contractID, contractDesc := range contractDescriptions { 264 name := fmt.Sprintf("Contract: %s", contractDesc) 265 s.Run(name, func() { 266 ctx, _ := s.ctx.CacheContext() 267 268 call := func() { 269 s.keeper.ModifyContract(ctx, contractID, s.vendor, changes) 270 } 271 272 if contractID != s.contractID { 273 s.Require().Panics(call) 274 return 275 } 276 call() 277 278 contract, err := s.keeper.GetContract(ctx, contractID) 279 s.Require().NoError(err) 280 281 s.Require().Equal(changes[0].Value, contract.Name) 282 s.Require().Equal(changes[1].Value, contract.Uri) 283 s.Require().Equal(changes[2].Value, contract.Meta) 284 }) 285 } 286 } 287 288 func (s *KeeperTestSuite) TestModifyTokenClass() { 289 classDescriptions := map[string]string{ 290 s.nftClassID: "valid", 291 "deadbeef": "not-exist", 292 } 293 changes := []collection.Attribute{ 294 {Key: collection.AttributeKeyName.String(), Value: "arctic fox"}, 295 {Key: collection.AttributeKeyMeta.String(), Value: "Arctic Fox"}, 296 } 297 298 for classID, classDesc := range classDescriptions { 299 name := fmt.Sprintf("Class: %s", classDesc) 300 s.Run(name, func() { 301 ctx, _ := s.ctx.CacheContext() 302 303 err := s.keeper.ModifyTokenClass(ctx, s.contractID, classID, s.vendor, changes) 304 if classID != s.nftClassID { 305 s.Require().ErrorIs(err, collection.ErrTokenTypeNotExist) 306 return 307 } 308 s.Require().NoError(err) 309 310 class, err := s.keeper.GetTokenClass(ctx, s.contractID, classID) 311 s.Require().NoError(err) 312 313 nftClass, ok := class.(*collection.NFTClass) 314 s.Require().True(ok) 315 316 s.Require().Equal(changes[0].Value, nftClass.Name) 317 s.Require().Equal(changes[1].Value, nftClass.Meta) 318 }) 319 } 320 } 321 322 func (s *KeeperTestSuite) TestModifyNFT() { 323 validTokenID := collection.NewNFTID(s.nftClassID, 1) 324 tokenDescriptions := map[string]string{ 325 validTokenID: "valid", 326 collection.NewNFTID("deadbeef", 1): "not-exist", 327 } 328 changes := []collection.Attribute{ 329 {Key: collection.AttributeKeyName.String(), Value: "fennec fox 1"}, 330 {Key: collection.AttributeKeyMeta.String(), Value: "Fennec Fox 1"}, 331 } 332 333 for tokenID, tokenDesc := range tokenDescriptions { 334 name := fmt.Sprintf("Token: %s", tokenDesc) 335 s.Run(name, func() { 336 ctx, _ := s.ctx.CacheContext() 337 338 err := s.keeper.ModifyNFT(ctx, s.contractID, tokenID, s.vendor, changes) 339 if tokenID != validTokenID { 340 s.Require().ErrorIs(err, collection.ErrTokenNotExist) 341 return 342 } 343 s.Require().NoError(err) 344 345 nft, err := s.keeper.GetNFT(ctx, s.contractID, tokenID) 346 s.Require().NoError(err) 347 348 s.Require().Equal(changes[0].Value, nft.Name) 349 s.Require().Equal(changes[1].Value, nft.Meta) 350 }) 351 } 352 }