github.com/Finschia/finschia-sdk@v0.48.1/types/coin_test.go (about) 1 package types_test 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "github.com/stretchr/testify/suite" 9 10 "github.com/Finschia/finschia-sdk/codec" 11 sdk "github.com/Finschia/finschia-sdk/types" 12 ) 13 14 var ( 15 testDenom1 = "atom" 16 testDenom2 = "muon" 17 ) 18 19 type coinTestSuite struct { 20 suite.Suite 21 ca0, ca1, ca2, cm0, cm1, cm2 sdk.Coin 22 } 23 24 func TestCoinTestSuite(t *testing.T) { 25 suite.Run(t, new(coinTestSuite)) 26 } 27 28 func (s *coinTestSuite) SetupSuite() { 29 s.T().Parallel() 30 zero := sdk.NewInt(0) 31 one := sdk.OneInt() 32 two := sdk.NewInt(2) 33 s.ca0, s.ca1, s.ca2 = sdk.Coin{testDenom1, zero}, sdk.Coin{testDenom1, one}, sdk.Coin{testDenom1, two} 34 s.cm0, s.cm1, s.cm2 = sdk.Coin{testDenom2, zero}, sdk.Coin{testDenom2, one}, sdk.Coin{testDenom2, two} 35 } 36 37 // ---------------------------------------------------------------------------- 38 // Coin tests 39 40 func (s *coinTestSuite) TestCoin() { 41 s.Require().Panics(func() { sdk.NewInt64Coin(testDenom1, -1) }) 42 s.Require().Panics(func() { sdk.NewCoin(testDenom1, sdk.NewInt(-1)) }) 43 s.Require().Equal(sdk.NewInt(10), sdk.NewInt64Coin(strings.ToUpper(testDenom1), 10).Amount) 44 s.Require().Equal(sdk.NewInt(10), sdk.NewCoin(strings.ToUpper(testDenom1), sdk.NewInt(10)).Amount) 45 s.Require().Equal(sdk.NewInt(5), sdk.NewInt64Coin(testDenom1, 5).Amount) 46 s.Require().Equal(sdk.NewInt(5), sdk.NewCoin(testDenom1, sdk.NewInt(5)).Amount) 47 } 48 49 func (s *coinTestSuite) TestCoin_String() { 50 coin := sdk.NewCoin(testDenom1, sdk.NewInt(10)) 51 s.Require().Equal(fmt.Sprintf("10%s", testDenom1), coin.String()) 52 } 53 54 func (s *coinTestSuite) TestIsEqualCoin() { 55 cases := []struct { 56 inputOne sdk.Coin 57 inputTwo sdk.Coin 58 expected bool 59 panics bool 60 }{ 61 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false}, 62 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, 63 {sdk.NewInt64Coin("stake", 1), sdk.NewInt64Coin("stake", 10), false, false}, 64 } 65 66 for tcIndex, tc := range cases { 67 tc := tc 68 if tc.panics { 69 s.Require().Panics(func() { tc.inputOne.IsEqual(tc.inputTwo) }) 70 } else { 71 res := tc.inputOne.IsEqual(tc.inputTwo) 72 s.Require().Equal(tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) 73 } 74 } 75 } 76 77 func (s *coinTestSuite) TestCoinIsValid() { 78 loremIpsum := `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra dui vel nulla aliquet, non dictum elit aliquam. Proin consequat leo in consectetur mattis. Phasellus eget odio luctus, rutrum dolor at, venenatis ante. Praesent metus erat, sodales vitae sagittis eget, commodo non ipsum. Duis eget urna quis erat mattis pulvinar. Vivamus egestas imperdiet sem, porttitor hendrerit lorem pulvinar in. Vivamus laoreet sapien eget libero euismod tristique. Suspendisse tincidunt nulla quis luctus mattis. 79 Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed id turpis at erat placerat fermentum id sed sapien. Fusce mattis enim id nulla viverra, eget placerat eros aliquet. Nunc fringilla urna ac condimentum ultricies. Praesent in eros ac neque fringilla sodales. Donec ut venenatis eros. Quisque iaculis lectus neque, a varius sem ullamcorper nec. Cras tincidunt dignissim libero nec volutpat. Donec molestie enim sed metus venenatis, quis elementum sem varius. Curabitur eu venenatis nulla. 80 Cras sit amet ligula vel turpis placerat sollicitudin. Nunc massa odio, eleifend id lacus nec, ultricies elementum arcu. Donec imperdiet nulla lacus, a venenatis lacus fermentum nec. Proin vestibulum dolor enim, vitae posuere velit aliquet non. Suspendisse pharetra condimentum nunc tincidunt viverra. Etiam posuere, ligula ut maximus congue, mauris orci consectetur velit, vel finibus eros metus non tellus. Nullam et dictum metus. Aliquam maximus fermentum mauris elementum aliquet. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Etiam dapibus lectus sed tellus rutrum tincidunt. Nulla at dolor sem. Ut non dictum arcu, eget congue sem.` 81 82 loremIpsum = strings.ReplaceAll(loremIpsum, " ", "") 83 loremIpsum = strings.ReplaceAll(loremIpsum, ".", "") 84 loremIpsum = strings.ReplaceAll(loremIpsum, ",", "") 85 86 cases := []struct { 87 coin sdk.Coin 88 expectPass bool 89 }{ 90 {sdk.Coin{testDenom1, sdk.NewInt(-1)}, false}, 91 {sdk.Coin{testDenom1, sdk.NewInt(0)}, true}, 92 {sdk.Coin{testDenom1, sdk.OneInt()}, true}, 93 {sdk.Coin{"Atom", sdk.OneInt()}, true}, 94 {sdk.Coin{"ATOM", sdk.OneInt()}, true}, 95 {sdk.Coin{"a", sdk.OneInt()}, false}, 96 {sdk.Coin{loremIpsum, sdk.OneInt()}, false}, 97 {sdk.Coin{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.OneInt()}, true}, 98 {sdk.Coin{"atOm", sdk.OneInt()}, true}, 99 {sdk.Coin{" ", sdk.OneInt()}, false}, 100 } 101 102 for i, tc := range cases { 103 s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) 104 } 105 } 106 107 func (s *coinTestSuite) TestCustomValidation() { 108 newDnmRegex := `[\x{1F600}-\x{1F6FF}]` 109 sdk.SetCoinDenomRegex(func() string { 110 return newDnmRegex 111 }) 112 113 cases := []struct { 114 coin sdk.Coin 115 expectPass bool 116 }{ 117 {sdk.Coin{"🙂", sdk.NewInt(1)}, true}, 118 {sdk.Coin{"🙁", sdk.NewInt(1)}, true}, 119 {sdk.Coin{"🌶", sdk.NewInt(1)}, false}, // outside the unicode range listed above 120 {sdk.Coin{"asdf", sdk.NewInt(1)}, false}, 121 {sdk.Coin{"", sdk.NewInt(1)}, false}, 122 } 123 124 for i, tc := range cases { 125 s.Require().Equal(tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) 126 } 127 sdk.SetCoinDenomRegex(sdk.DefaultCoinDenomRegex) 128 } 129 130 func (s *coinTestSuite) TestAddCoin() { 131 cases := []struct { 132 inputOne sdk.Coin 133 inputTwo sdk.Coin 134 expected sdk.Coin 135 shouldPanic bool 136 }{ 137 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), false}, 138 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 1), false}, 139 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true}, 140 } 141 142 for tcIndex, tc := range cases { 143 tc := tc 144 if tc.shouldPanic { 145 s.Require().Panics(func() { tc.inputOne.Add(tc.inputTwo) }) 146 } else { 147 res := tc.inputOne.Add(tc.inputTwo) 148 s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) 149 } 150 } 151 } 152 153 func (s *coinTestSuite) TestAddCoinAmount() { 154 cases := []struct { 155 coin sdk.Coin 156 amount sdk.Int 157 expected sdk.Coin 158 }{ 159 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt(1), sdk.NewInt64Coin(testDenom1, 2)}, 160 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt(0), sdk.NewInt64Coin(testDenom1, 1)}, 161 } 162 for i, tc := range cases { 163 res := tc.coin.AddAmount(tc.amount) 164 s.Require().Equal(tc.expected, res, "result of addition is incorrect, tc #%d", i) 165 } 166 } 167 168 func (s *coinTestSuite) TestSubCoin() { 169 cases := []struct { 170 inputOne sdk.Coin 171 inputTwo sdk.Coin 172 expected sdk.Coin 173 shouldPanic bool 174 }{ 175 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), sdk.NewInt64Coin(testDenom1, 1), true}, 176 {sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 9), false}, 177 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 3), sdk.NewInt64Coin(testDenom1, 2), false}, 178 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom1, 5), false}, 179 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 5), sdk.Coin{}, true}, 180 } 181 182 for tcIndex, tc := range cases { 183 tc := tc 184 if tc.shouldPanic { 185 s.Require().Panics(func() { tc.inputOne.Sub(tc.inputTwo) }) 186 } else { 187 res := tc.inputOne.Sub(tc.inputTwo) 188 s.Require().Equal(tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) 189 } 190 } 191 192 tc := struct { 193 inputOne sdk.Coin 194 inputTwo sdk.Coin 195 expected int64 196 }{sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), 0} 197 res := tc.inputOne.Sub(tc.inputTwo) 198 s.Require().Equal(tc.expected, res.Amount.Int64()) 199 } 200 201 func (s *coinTestSuite) TestSubCoinAmount() { 202 cases := []struct { 203 coin sdk.Coin 204 amount sdk.Int 205 expected sdk.Coin 206 shouldPanic bool 207 }{ 208 {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt(1), sdk.NewInt64Coin(testDenom1, 1), false}, 209 {sdk.NewInt64Coin(testDenom1, 10), sdk.NewInt(1), sdk.NewInt64Coin(testDenom1, 9), false}, 210 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt(3), sdk.NewInt64Coin(testDenom1, 2), false}, 211 {sdk.NewInt64Coin(testDenom1, 5), sdk.NewInt(0), sdk.NewInt64Coin(testDenom1, 5), false}, 212 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt(5), sdk.Coin{}, true}, 213 } 214 215 for i, tc := range cases { 216 if tc.shouldPanic { 217 s.Require().Panics(func() { tc.coin.SubAmount(tc.amount) }) 218 } else { 219 res := tc.coin.SubAmount(tc.amount) 220 s.Require().Equal(tc.expected, res, "result of subtraction is incorrect, tc #%d", i) 221 } 222 } 223 } 224 225 func (s *coinTestSuite) TestIsGTECoin() { 226 cases := []struct { 227 inputOne sdk.Coin 228 inputTwo sdk.Coin 229 expected bool 230 panics bool 231 }{ 232 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), true, false}, 233 {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), true, false}, 234 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, 235 } 236 237 for tcIndex, tc := range cases { 238 tc := tc 239 if tc.panics { 240 s.Require().Panics(func() { tc.inputOne.IsGTE(tc.inputTwo) }) 241 } else { 242 res := tc.inputOne.IsGTE(tc.inputTwo) 243 s.Require().Equal(tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex) 244 } 245 } 246 } 247 248 func (s *coinTestSuite) TestIsLTCoin() { 249 cases := []struct { 250 inputOne sdk.Coin 251 inputTwo sdk.Coin 252 expected bool 253 panics bool 254 }{ 255 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false}, 256 {sdk.NewInt64Coin(testDenom1, 2), sdk.NewInt64Coin(testDenom1, 1), false, false}, 257 {sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1), false, true}, 258 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom2, 1), false, true}, 259 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 1), false, false}, 260 {sdk.NewInt64Coin(testDenom1, 1), sdk.NewInt64Coin(testDenom1, 2), true, false}, 261 } 262 263 for tcIndex, tc := range cases { 264 tc := tc 265 if tc.panics { 266 s.Require().Panics(func() { tc.inputOne.IsLT(tc.inputTwo) }) 267 } else { 268 res := tc.inputOne.IsLT(tc.inputTwo) 269 s.Require().Equal(tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex) 270 } 271 } 272 } 273 274 func (s *coinTestSuite) TestCoinIsZero() { 275 coin := sdk.NewInt64Coin(testDenom1, 0) 276 res := coin.IsZero() 277 s.Require().True(res) 278 279 coin = sdk.NewInt64Coin(testDenom1, 1) 280 res = coin.IsZero() 281 s.Require().False(res) 282 } 283 284 func (s *coinTestSuite) TestCoinIsNil() { 285 coin := sdk.Coin{} 286 res := coin.IsNil() 287 s.Require().True(res) 288 289 coin = sdk.Coin{Denom: "uatom"} 290 res = coin.IsNil() 291 s.Require().True(res) 292 293 coin = sdk.NewInt64Coin(testDenom1, 1) 294 res = coin.IsNil() 295 s.Require().False(res) 296 } 297 298 func (s *coinTestSuite) TestFilteredZeroCoins() { 299 cases := []struct { 300 name string 301 input sdk.Coins 302 original string 303 expected string 304 }{ 305 { 306 name: "all greater than zero", 307 input: sdk.Coins{ 308 {"testa", sdk.OneInt()}, 309 {"testb", sdk.NewInt(2)}, 310 {"testc", sdk.NewInt(3)}, 311 {"testd", sdk.NewInt(4)}, 312 {"teste", sdk.NewInt(5)}, 313 }, 314 original: "1testa,2testb,3testc,4testd,5teste", 315 expected: "1testa,2testb,3testc,4testd,5teste", 316 }, 317 { 318 name: "zero coin in middle", 319 input: sdk.Coins{ 320 {"testa", sdk.OneInt()}, 321 {"testb", sdk.NewInt(2)}, 322 {"testc", sdk.NewInt(0)}, 323 {"testd", sdk.NewInt(4)}, 324 {"teste", sdk.NewInt(5)}, 325 }, 326 original: "1testa,2testb,0testc,4testd,5teste", 327 expected: "1testa,2testb,4testd,5teste", 328 }, 329 { 330 name: "zero coin end (unordered)", 331 input: sdk.Coins{ 332 {"teste", sdk.NewInt(5)}, 333 {"testc", sdk.NewInt(3)}, 334 {"testa", sdk.OneInt()}, 335 {"testd", sdk.NewInt(4)}, 336 {"testb", sdk.NewInt(0)}, 337 }, 338 original: "5teste,3testc,1testa,4testd,0testb", 339 expected: "1testa,3testc,4testd,5teste", 340 }, 341 } 342 343 for _, tt := range cases { 344 undertest := sdk.NewCoins(tt.input...) 345 s.Require().Equal(tt.expected, undertest.String(), "NewCoins must return expected results") 346 s.Require().Equal(tt.original, tt.input.String(), "input must be unmodified and match original") 347 } 348 } 349 350 // ---------------------------------------------------------------------------- 351 // Coins tests 352 353 func (s *coinTestSuite) TestCoins_String() { 354 cases := []struct { 355 name string 356 input sdk.Coins 357 expected string 358 }{ 359 { 360 "empty coins", 361 sdk.Coins{}, 362 "", 363 }, 364 { 365 "single coin", 366 sdk.Coins{{"tree", sdk.OneInt()}}, 367 "1tree", 368 }, 369 { 370 "multiple coins", 371 sdk.Coins{ 372 {"tree", sdk.OneInt()}, 373 {"gas", sdk.OneInt()}, 374 {"mineral", sdk.OneInt()}, 375 }, 376 "1tree,1gas,1mineral", 377 }, 378 } 379 380 for _, tt := range cases { 381 s.Require().Equal(tt.expected, tt.input.String()) 382 } 383 } 384 385 func (s *coinTestSuite) TestIsZeroCoins() { 386 cases := []struct { 387 inputOne sdk.Coins 388 expected bool 389 }{ 390 {sdk.Coins{}, true}, 391 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true}, 392 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 0)}, true}, 393 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false}, 394 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false}, 395 } 396 397 for _, tc := range cases { 398 res := tc.inputOne.IsZero() 399 s.Require().Equal(tc.expected, res) 400 } 401 } 402 403 func (s *coinTestSuite) TestEqualCoins() { 404 cases := []struct { 405 inputOne sdk.Coins 406 inputTwo sdk.Coins 407 expected bool 408 panics bool 409 }{ 410 {sdk.Coins{}, sdk.Coins{}, true, false}, 411 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, true, false}, 412 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true, false}, 413 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom2, 0)}, false, true}, 414 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 1)}, false, false}, 415 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, false, false}, 416 {sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, sdk.Coins{sdk.NewInt64Coin(testDenom1, 0), sdk.NewInt64Coin(testDenom2, 1)}, true, false}, 417 } 418 419 for tcnum, tc := range cases { 420 tc := tc 421 if tc.panics { 422 s.Require().Panics(func() { tc.inputOne.IsEqual(tc.inputTwo) }) 423 } else { 424 res := tc.inputOne.IsEqual(tc.inputTwo) 425 s.Require().Equal(tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res) 426 } 427 } 428 } 429 430 func (s *coinTestSuite) TestAddCoins() { 431 cases := []struct { 432 inputOne sdk.Coins 433 inputTwo sdk.Coins 434 expected sdk.Coins 435 }{ 436 {sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca2, s.cm2}}, 437 {sdk.Coins{s.ca0, s.cm1}, sdk.Coins{s.ca0, s.cm0}, sdk.Coins{s.cm1}}, 438 {sdk.Coins{s.ca2}, sdk.Coins{s.cm0}, sdk.Coins{s.ca2}}, 439 {sdk.Coins{s.ca1}, sdk.Coins{s.ca1, s.cm2}, sdk.Coins{s.ca2, s.cm2}}, 440 {sdk.Coins{s.ca0, s.cm0}, sdk.Coins{s.ca0, s.cm0}, sdk.Coins(nil)}, 441 } 442 443 for tcIndex, tc := range cases { 444 res := tc.inputOne.Add(tc.inputTwo...) 445 s.Require().True(res.IsValid()) 446 s.Require().Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) 447 } 448 } 449 450 func (s *coinTestSuite) TestSubCoins() { 451 testCases := []struct { 452 inputOne sdk.Coins 453 inputTwo sdk.Coins 454 expected sdk.Coins 455 shouldPanic bool 456 }{ 457 // denoms are not sorted - should panic 458 {sdk.Coins{s.ca2}, sdk.Coins{s.cm2, s.ca1}, sdk.Coins{}, true}, 459 {sdk.Coins{s.cm2, s.ca2}, sdk.Coins{s.ca1}, sdk.Coins{}, true}, 460 // test cases for sorted denoms 461 {sdk.Coins{s.ca2}, sdk.Coins{s.ca1, s.cm2}, sdk.Coins{s.ca1, s.cm2}, true}, 462 {sdk.Coins{s.ca2}, sdk.Coins{s.cm0}, sdk.Coins{s.ca2}, false}, 463 {sdk.Coins{s.ca1}, sdk.Coins{s.cm0}, sdk.Coins{s.ca1}, false}, 464 {sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca1}, sdk.Coins{s.cm1}, false}, 465 {sdk.Coins{s.ca1, s.cm1}, sdk.Coins{s.ca2}, sdk.Coins{}, true}, 466 } 467 468 assert := s.Assert() 469 for i, tc := range testCases { 470 tc := tc 471 if tc.shouldPanic { 472 assert.Panics(func() { tc.inputOne.Sub(tc.inputTwo) }) 473 } else { 474 res := tc.inputOne.Sub(tc.inputTwo) 475 assert.True(res.IsValid()) 476 assert.Equal(tc.expected, res, "sum of coins is incorrect, tc #%d", i) 477 } 478 } 479 } 480 481 func (s *coinTestSuite) TestCoins_Validate() { 482 testCases := []struct { 483 name string 484 coins sdk.Coins 485 expPass bool 486 }{ 487 { 488 "valid lowercase coins", 489 sdk.Coins{ 490 {"gas", sdk.OneInt()}, 491 {"mineral", sdk.OneInt()}, 492 {"tree", sdk.OneInt()}, 493 }, 494 true, 495 }, 496 { 497 "valid uppercase coins", 498 sdk.Coins{ 499 {"GAS", sdk.OneInt()}, 500 {"MINERAL", sdk.OneInt()}, 501 {"TREE", sdk.OneInt()}, 502 }, 503 true, 504 }, 505 { 506 "valid uppercase coin", 507 sdk.Coins{ 508 {"ATOM", sdk.OneInt()}, 509 }, 510 true, 511 }, 512 { 513 "valid lower and uppercase coins (1)", 514 sdk.Coins{ 515 {"GAS", sdk.OneInt()}, 516 {"gAs", sdk.OneInt()}, 517 }, 518 true, 519 }, 520 { 521 "valid lower and uppercase coins (2)", 522 sdk.Coins{ 523 {"ATOM", sdk.OneInt()}, 524 {"Atom", sdk.OneInt()}, 525 {"atom", sdk.OneInt()}, 526 }, 527 true, 528 }, 529 { 530 "mixed case (1)", 531 sdk.Coins{ 532 {"MineraL", sdk.OneInt()}, 533 {"TREE", sdk.OneInt()}, 534 {"gAs", sdk.OneInt()}, 535 }, 536 true, 537 }, 538 { 539 "mixed case (2)", 540 sdk.Coins{ 541 {"gAs", sdk.OneInt()}, 542 {"mineral", sdk.OneInt()}, 543 }, 544 true, 545 }, 546 { 547 "mixed case (3)", 548 sdk.Coins{ 549 {"gAs", sdk.OneInt()}, 550 }, 551 true, 552 }, 553 { 554 "unicode letters and numbers", 555 sdk.Coins{ 556 {"𐀀𐀆𐀉Ⅲ", sdk.OneInt()}, 557 }, 558 false, 559 }, 560 { 561 "emojis", 562 sdk.Coins{ 563 {"🤑😋🤔", sdk.OneInt()}, 564 }, 565 false, 566 }, 567 { 568 "IBC denominations (ADR 001)", 569 sdk.Coins{ 570 {"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.OneInt()}, 571 {"ibc/876563AAAACF739EB061C67CDB5EDF2B7C9FD4AA9D876450CC21210807C2820A", sdk.NewInt(2)}, 572 }, 573 true, 574 }, 575 { 576 "empty (1)", 577 sdk.NewCoins(), 578 true, 579 }, 580 { 581 "empty (2)", 582 sdk.Coins{}, 583 true, 584 }, 585 { 586 "invalid denomination (1)", 587 sdk.Coins{ 588 {"MineraL", sdk.OneInt()}, 589 {"0TREE", sdk.OneInt()}, 590 {"gAs", sdk.OneInt()}, 591 }, 592 false, 593 }, 594 { 595 "invalid denomination (2)", 596 sdk.Coins{ 597 {"-GAS", sdk.OneInt()}, 598 {"gAs", sdk.OneInt()}, 599 }, 600 false, 601 }, 602 { 603 "bad sort (1)", 604 sdk.Coins{ 605 {"tree", sdk.OneInt()}, 606 {"gas", sdk.OneInt()}, 607 {"mineral", sdk.OneInt()}, 608 }, 609 false, 610 }, 611 { 612 "bad sort (2)", 613 sdk.Coins{ 614 {"gas", sdk.OneInt()}, 615 {"tree", sdk.OneInt()}, 616 {"mineral", sdk.OneInt()}, 617 }, 618 false, 619 }, 620 { 621 "non-positive amount (1)", 622 sdk.Coins{ 623 {"gas", sdk.OneInt()}, 624 {"tree", sdk.NewInt(0)}, 625 {"mineral", sdk.OneInt()}, 626 }, 627 false, 628 }, 629 { 630 "non-positive amount (2)", 631 sdk.Coins{ 632 {"gas", sdk.NewInt(-1)}, 633 {"tree", sdk.OneInt()}, 634 {"mineral", sdk.OneInt()}, 635 }, 636 false, 637 }, 638 { 639 "duplicate denomination", 640 sdk.Coins{ 641 {"gas", sdk.OneInt()}, 642 {"gas", sdk.OneInt()}, 643 {"mineral", sdk.OneInt()}, 644 }, 645 false, 646 }, 647 } 648 649 for _, tc := range testCases { 650 err := tc.coins.Validate() 651 if tc.expPass { 652 s.Require().NoError(err, tc.name) 653 } else { 654 s.Require().Error(err, tc.name) 655 } 656 } 657 } 658 659 func (s *coinTestSuite) TestMinMax() { 660 one := sdk.OneInt() 661 two := sdk.NewInt(2) 662 663 cases := []struct { 664 name string 665 input1 sdk.Coins 666 input2 sdk.Coins 667 min sdk.Coins 668 max sdk.Coins 669 }{ 670 {"zero-zero", sdk.Coins{}, sdk.Coins{}, sdk.Coins{}, sdk.Coins{}}, 671 {"zero-one", sdk.Coins{}, sdk.Coins{{testDenom1, one}}, sdk.Coins{}, sdk.Coins{{testDenom1, one}}}, 672 {"two-zero", sdk.Coins{{testDenom2, two}}, sdk.Coins{}, sdk.Coins{}, sdk.Coins{{testDenom2, two}}}, 673 {"disjoint", sdk.Coins{{testDenom1, one}}, sdk.Coins{{testDenom2, two}}, sdk.Coins{}, sdk.Coins{{testDenom1, one}, {testDenom2, two}}}, 674 {"disjoint2", sdk.Coins{{testDenom2, one}}, sdk.Coins{{testDenom1, two}}, sdk.Coins{}, sdk.Coins{{testDenom1, two}, {testDenom2, one}}}, 675 { 676 "overlap", 677 sdk.Coins{{testDenom1, one}, {testDenom2, two}}, 678 sdk.Coins{{testDenom1, two}, {testDenom2, one}}, 679 sdk.Coins{{testDenom1, one}, {testDenom2, one}}, 680 sdk.Coins{{testDenom1, two}, {testDenom2, two}}, 681 }, 682 } 683 684 for _, tc := range cases { 685 min := tc.input1.Min(tc.input2) 686 max := tc.input1.Max(tc.input2) 687 s.Require().True(min.IsEqual(tc.min), tc.name) 688 s.Require().True(max.IsEqual(tc.max), tc.name) 689 } 690 } 691 692 func (s *coinTestSuite) TestCoinsGT() { 693 one := sdk.OneInt() 694 two := sdk.NewInt(2) 695 696 s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{})) 697 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{})) 698 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}})) 699 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}})) 700 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom2, one}})) 701 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom2, two}})) 702 } 703 704 func (s *coinTestSuite) TestCoinsLT() { 705 one := sdk.OneInt() 706 two := sdk.NewInt(2) 707 708 s.Require().False(sdk.Coins{}.IsAllLT(sdk.Coins{})) 709 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{})) 710 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom1, one}})) 711 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLT(sdk.Coins{{testDenom2, one}})) 712 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, one}})) 713 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom2, two}})) 714 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 715 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(sdk.Coins{{testDenom1, two}, {testDenom2, two}})) 716 s.Require().True(sdk.Coins{}.IsAllLT(sdk.Coins{{testDenom1, one}})) 717 } 718 719 func (s *coinTestSuite) TestCoinsLTE() { 720 one := sdk.OneInt() 721 two := sdk.NewInt(2) 722 723 s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{})) 724 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{})) 725 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}})) 726 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}})) 727 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, one}})) 728 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom2, two}})) 729 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 730 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 731 s.Require().True(sdk.Coins{}.IsAllLTE(sdk.Coins{{testDenom1, one}})) 732 } 733 734 func (s *coinTestSuite) TestParseCoins() { 735 one := sdk.OneInt() 736 737 cases := []struct { 738 input string 739 valid bool // if false, we expect an error on parse 740 expected sdk.Coins // if valid is true, make sure this is returned 741 }{ 742 {"", true, nil}, 743 {"0stake", true, sdk.Coins{}}, // remove zero coins 744 {"0stake,1foo,99bar", true, sdk.Coins{{"bar", sdk.NewInt(99)}, {"foo", one}}}, // remove zero coins 745 {"1foo", true, sdk.Coins{{"foo", one}}}, 746 {"10btc,1atom,20btc", false, nil}, 747 {"10bar", true, sdk.Coins{{"bar", sdk.NewInt(10)}}}, 748 {"99bar,1foo", true, sdk.Coins{{"bar", sdk.NewInt(99)}, {"foo", one}}}, 749 {"98 bar , 1 foo ", true, sdk.Coins{{"bar", sdk.NewInt(98)}, {"foo", one}}}, 750 {" 55\t \t bling\n", true, sdk.Coins{{"bling", sdk.NewInt(55)}}}, 751 {"2foo, 97 bar", true, sdk.Coins{{"bar", sdk.NewInt(97)}, {"foo", sdk.NewInt(2)}}}, 752 {"5 mycoin,", false, nil}, // no empty coins in a list 753 {"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name 754 {"11me coin, 12you coin", false, nil}, // no spaces in coin names 755 {"1.2btc", true, sdk.Coins{{"btc", sdk.NewInt(1)}}}, // amount can be decimal, will get truncated 756 {"5foo:bar", false, nil}, // invalid separator 757 {"10atom10", true, sdk.Coins{{"atom10", sdk.NewInt(10)}}}, 758 {"200transfer/channelToA/uatom", true, sdk.Coins{{"transfer/channelToA/uatom", sdk.NewInt(200)}}}, 759 {"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, sdk.Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", sdk.NewInt(50)}}}, 760 {"120000000000000000000000000000000000000000000000000000000000000000000000000000btc", false, nil}, 761 } 762 763 for tcIndex, tc := range cases { 764 res, err := sdk.ParseCoinsNormalized(tc.input) 765 if !tc.valid { 766 s.Require().Error(err, "%s: %#v. tc #%d", tc.input, res, tcIndex) 767 } else if s.Assert().Nil(err, "%s: %+v", tc.input, err) { 768 s.Require().Equal(tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex) 769 } 770 } 771 } 772 773 func (s *coinTestSuite) TestSortCoins() { 774 good := sdk.Coins{ 775 sdk.NewInt64Coin("gas", 1), 776 sdk.NewInt64Coin("mineral", 1), 777 sdk.NewInt64Coin("tree", 1), 778 } 779 empty := sdk.Coins{ 780 sdk.NewInt64Coin("gold", 0), 781 } 782 badSort1 := sdk.Coins{ 783 sdk.NewInt64Coin("tree", 1), 784 sdk.NewInt64Coin("gas", 1), 785 sdk.NewInt64Coin("mineral", 1), 786 } 787 badSort2 := sdk.Coins{ // both are after the first one, but the second and third are in the wrong order 788 sdk.NewInt64Coin("gas", 1), 789 sdk.NewInt64Coin("tree", 1), 790 sdk.NewInt64Coin("mineral", 1), 791 } 792 badAmt := sdk.Coins{ 793 sdk.NewInt64Coin("gas", 1), 794 sdk.NewInt64Coin("tree", 0), 795 sdk.NewInt64Coin("mineral", 1), 796 } 797 dup := sdk.Coins{ 798 sdk.NewInt64Coin("gas", 1), 799 sdk.NewInt64Coin("gas", 1), 800 sdk.NewInt64Coin("mineral", 1), 801 } 802 803 cases := []struct { 804 name string 805 coins sdk.Coins 806 validBefore, 807 validAfter bool 808 }{ 809 {"valid coins", good, true, true}, 810 {"empty coins", empty, false, false}, 811 {"bad sort (1)", badSort1, false, true}, 812 {"bad sort (2)", badSort2, false, true}, 813 {"zero value coin", badAmt, false, false}, 814 {"duplicate coins", dup, false, false}, 815 } 816 817 for _, tc := range cases { 818 err := tc.coins.Validate() 819 if tc.validBefore { 820 s.Require().NoError(err, tc.name) 821 } else { 822 s.Require().Error(err, tc.name) 823 } 824 825 tc.coins.Sort() 826 827 err = tc.coins.Validate() 828 if tc.validAfter { 829 s.Require().NoError(err, tc.name) 830 } else { 831 s.Require().Error(err, tc.name) 832 } 833 } 834 } 835 836 func (s *coinTestSuite) TestAmountOf() { 837 case0 := sdk.Coins{} 838 case1 := sdk.Coins{ 839 sdk.NewInt64Coin("gold", 0), 840 } 841 case2 := sdk.Coins{ 842 sdk.NewInt64Coin("gas", 1), 843 sdk.NewInt64Coin("mineral", 1), 844 sdk.NewInt64Coin("tree", 1), 845 } 846 case3 := sdk.Coins{ 847 sdk.NewInt64Coin("mineral", 1), 848 sdk.NewInt64Coin("tree", 1), 849 } 850 case4 := sdk.Coins{ 851 sdk.NewInt64Coin("gas", 8), 852 } 853 854 cases := []struct { 855 coins sdk.Coins 856 amountOf int64 857 amountOfSpace int64 858 amountOfGAS int64 859 amountOfMINERAL int64 860 amountOfTREE int64 861 }{ 862 {case0, 0, 0, 0, 0, 0}, 863 {case1, 0, 0, 0, 0, 0}, 864 {case2, 0, 0, 1, 1, 1}, 865 {case3, 0, 0, 0, 1, 1}, 866 {case4, 0, 0, 8, 0, 0}, 867 } 868 869 for _, tc := range cases { 870 s.Require().Equal(sdk.NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas")) 871 s.Require().Equal(sdk.NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral")) 872 s.Require().Equal(sdk.NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree")) 873 } 874 875 s.Require().Panics(func() { cases[0].coins.AmountOf("10Invalid") }) 876 } 877 878 func (s *coinTestSuite) TestCoinsIsAnyGTE() { 879 one := sdk.OneInt() 880 two := sdk.NewInt(2) 881 882 s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{})) 883 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{})) 884 s.Require().False(sdk.Coins{}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) 885 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, two}})) 886 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}})) 887 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) 888 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) 889 s.Require().True(sdk.Coins{{testDenom1, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}})) 890 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 891 s.Require().True(sdk.Coins{{testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 892 s.Require().False(sdk.Coins{{testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 893 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 894 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 895 s.Require().True(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 896 } 897 898 func (s *coinTestSuite) TestCoinsIsAllGT() { 899 one := sdk.OneInt() 900 two := sdk.NewInt(2) 901 902 s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{})) 903 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{})) 904 s.Require().False(sdk.Coins{}.IsAllGT(sdk.Coins{{testDenom1, one}})) 905 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, two}})) 906 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom2, one}})) 907 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) 908 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}})) 909 s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGT(sdk.Coins{{testDenom1, one}})) 910 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 911 s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 912 s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 913 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 914 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 915 s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 916 } 917 918 func (s *coinTestSuite) TestCoinsIsAllGTE() { 919 one := sdk.OneInt() 920 two := sdk.NewInt(2) 921 922 s.Require().True(sdk.Coins{}.IsAllGTE(sdk.Coins{})) 923 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{})) 924 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}})) 925 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom2, two}})) 926 s.Require().False(sdk.Coins{}.IsAllGTE(sdk.Coins{{testDenom1, one}})) 927 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, two}})) 928 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom2, one}})) 929 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, two}, {testDenom2, one}})) 930 s.Require().True(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}})) 931 s.Require().True(sdk.Coins{{testDenom1, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}})) 932 s.Require().False(sdk.Coins{{testDenom1, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 933 s.Require().False(sdk.Coins{{testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 934 s.Require().False(sdk.Coins{{testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 935 s.Require().True(sdk.Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, one}})) 936 s.Require().False(sdk.Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(sdk.Coins{{testDenom1, one}, {testDenom2, two}})) 937 s.Require().False(sdk.Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(sdk.Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 938 } 939 940 func (s *coinTestSuite) TestNewCoins() { 941 tenatom := sdk.NewInt64Coin("atom", 10) 942 tenbtc := sdk.NewInt64Coin("btc", 10) 943 zeroeth := sdk.NewInt64Coin("eth", 0) 944 invalidCoin := sdk.Coin{"0ETH", sdk.OneInt()} 945 tests := []struct { 946 name string 947 coins sdk.Coins 948 want sdk.Coins 949 wantPanic bool 950 }{ 951 {"empty args", []sdk.Coin{}, sdk.Coins{}, false}, 952 {"one coin", []sdk.Coin{tenatom}, sdk.Coins{tenatom}, false}, 953 {"sort after create", []sdk.Coin{tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false}, 954 {"sort and remove zeroes", []sdk.Coin{zeroeth, tenbtc, tenatom}, sdk.Coins{tenatom, tenbtc}, false}, 955 {"panic on dups", []sdk.Coin{tenatom, tenatom}, sdk.Coins{}, true}, 956 {"panic on invalid coin", []sdk.Coin{invalidCoin, tenatom}, sdk.Coins{}, true}, 957 } 958 for _, tt := range tests { 959 if tt.wantPanic { 960 s.Require().Panics(func() { sdk.NewCoins(tt.coins...) }) 961 continue 962 } 963 got := sdk.NewCoins(tt.coins...) 964 s.Require().True(got.IsEqual(tt.want)) 965 } 966 } 967 968 func (s *coinTestSuite) TestCoinsIsAnyGT() { 969 twoAtom := sdk.NewInt64Coin("atom", 2) 970 fiveAtom := sdk.NewInt64Coin("atom", 5) 971 threeEth := sdk.NewInt64Coin("eth", 3) 972 sixEth := sdk.NewInt64Coin("eth", 6) 973 twoBtc := sdk.NewInt64Coin("btc", 2) 974 975 tests := []struct { 976 name string 977 coinsA sdk.Coins 978 coinsB sdk.Coins 979 expPass bool 980 }{ 981 {"{} ≤ {}", sdk.Coins{}, sdk.Coins{}, false}, 982 {"{} ≤ 5atom", sdk.Coins{}, sdk.Coins{fiveAtom}, false}, 983 {"5atom > 2atom", sdk.Coins{fiveAtom}, sdk.Coins{twoAtom}, true}, 984 {"2atom ≤ 5atom", sdk.Coins{twoAtom}, sdk.Coins{fiveAtom}, false}, 985 {"2atom,6eth > 2btc,5atom,3eth", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom, threeEth}, true}, 986 {"2btc,2atom,3eth ≤ 5atom,6eth", sdk.Coins{twoBtc, twoAtom, threeEth}, sdk.Coins{fiveAtom, sixEth}, false}, 987 {"2atom,6eth ≤ 2btc,5atom", sdk.Coins{twoAtom, sixEth}, sdk.Coins{twoBtc, fiveAtom}, false}, 988 } 989 990 for _, tc := range tests { 991 s.Require().True(tc.expPass == tc.coinsA.IsAnyGT(tc.coinsB), tc.name) 992 } 993 } 994 995 func (s *coinTestSuite) TestCoinsIsAnyNil() { 996 twoAtom := sdk.NewInt64Coin("atom", 2) 997 fiveAtom := sdk.NewInt64Coin("atom", 5) 998 threeEth := sdk.NewInt64Coin("eth", 3) 999 nilAtom := sdk.Coin{Denom: "atom"} 1000 1001 s.Require().True(sdk.Coins{twoAtom, fiveAtom, threeEth, nilAtom}.IsAnyNil()) 1002 s.Require().True(sdk.Coins{twoAtom, nilAtom, fiveAtom, threeEth}.IsAnyNil()) 1003 s.Require().True(sdk.Coins{nilAtom, twoAtom, fiveAtom, threeEth}.IsAnyNil()) 1004 s.Require().False(sdk.Coins{twoAtom, fiveAtom, threeEth}.IsAnyNil()) 1005 } 1006 1007 func (s *coinTestSuite) TestMarshalJSONCoins() { 1008 cdc := codec.NewLegacyAmino() 1009 sdk.RegisterLegacyAminoCodec(cdc) 1010 1011 testCases := []struct { 1012 name string 1013 input sdk.Coins 1014 strOutput string 1015 }{ 1016 {"nil coins", nil, `[]`}, 1017 {"empty coins", sdk.Coins{}, `[]`}, 1018 {"non-empty coins", sdk.NewCoins(sdk.NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`}, 1019 } 1020 1021 for _, tc := range testCases { 1022 bz, err := cdc.MarshalJSON(tc.input) 1023 s.Require().NoError(err) 1024 s.Require().Equal(tc.strOutput, string(bz)) 1025 1026 var newCoins sdk.Coins 1027 s.Require().NoError(cdc.UnmarshalJSON(bz, &newCoins)) 1028 1029 if tc.input.Empty() { 1030 s.Require().Nil(newCoins) 1031 } else { 1032 s.Require().Equal(tc.input, newCoins) 1033 } 1034 } 1035 } 1036 1037 func (s *coinTestSuite) TestCoinAminoEncoding() { 1038 cdc := codec.NewLegacyAmino() 1039 c := sdk.NewInt64Coin(testDenom1, 5) 1040 1041 bz1, err := cdc.Marshal(c) 1042 s.Require().NoError(err) 1043 1044 bz2, err := cdc.MarshalLengthPrefixed(c) 1045 s.Require().NoError(err) 1046 1047 bz3, err := c.Marshal() 1048 s.Require().NoError(err) 1049 s.Require().Equal(bz1, bz3) 1050 s.Require().Equal(bz2[1:], bz3) 1051 }