github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/libs/cosmos-sdk/types/coin_test.go (about) 1 package types 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 "github.com/stretchr/testify/require" 9 10 "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/codec" 11 ) 12 13 var ( 14 testDenom1 = "atom" 15 testDenom2 = "muon" 16 ) 17 18 // ---------------------------------------------------------------------------- 19 // Coin tests 20 21 func TestCoin(t *testing.T) { 22 require.Panics(t, func() { NewInt64Coin(testDenom1, -1) }) 23 require.Panics(t, func() { NewCoin(testDenom1, NewDec(-1)) }) 24 require.Panics(t, func() { NewInt64Coin(strings.ToUpper(testDenom1), 10) }) 25 require.Panics(t, func() { NewCoin(strings.ToUpper(testDenom1), NewDec(10)) }) 26 require.Equal(t, NewDec(5), NewInt64Coin(testDenom1, 5).Amount) 27 require.Equal(t, NewDec(5), NewCoin(testDenom1, NewDec(5)).Amount) 28 } 29 30 func TestIsEqualCoin(t *testing.T) { 31 cases := []struct { 32 inputOne Coin 33 inputTwo Coin 34 expected bool 35 panics bool 36 }{ 37 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), true, false}, 38 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true}, 39 {NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), false, false}, 40 } 41 42 for tcIndex, tc := range cases { 43 tc := tc 44 if tc.panics { 45 require.Panics(t, func() { tc.inputOne.IsEqual(tc.inputTwo) }) 46 } else { 47 res := tc.inputOne.IsEqual(tc.inputTwo) 48 require.Equal(t, tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex) 49 } 50 } 51 } 52 53 func TestCoinIsValid(t *testing.T) { 54 cases := []struct { 55 coin Coin 56 expectPass bool 57 }{ 58 {Coin{testDenom1, NewDec(-1)}, false}, 59 {Coin{testDenom1, NewDec(0)}, true}, 60 {Coin{testDenom1, NewDec(1)}, true}, 61 {Coin{"Atom", NewDec(1)}, false}, 62 {Coin{"a", NewDec(1)}, true}, 63 {Coin{"a very long coin denom", NewDec(1)}, false}, 64 {Coin{"atOm", NewDec(1)}, false}, 65 {Coin{" ", NewDec(1)}, false}, 66 } 67 68 for i, tc := range cases { 69 require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) 70 } 71 } 72 73 func TestCustomValidation(t *testing.T) { 74 75 newDnmRegex := `[\x{1F600}-\x{1F6FF}]` 76 SetCoinDenomRegex(func() string { 77 return newDnmRegex 78 }) 79 80 cases := []struct { 81 coin Coin 82 expectPass bool 83 }{ 84 {Coin{"🙂", NewDec(1)}, false}, 85 {Coin{"😃", NewDec(1)}, false}, 86 {Coin{"😄", NewDec(1)}, false}, 87 {Coin{"🌶", NewDec(1)}, false}, // outside the unicode range listed above 88 {Coin{"asdf", NewDec(1)}, true}, 89 {Coin{"", NewDec(1)}, false}, 90 } 91 92 for i, tc := range cases { 93 require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i) 94 } 95 SetCoinDenomRegex(DefaultCoinDenomRegex) 96 } 97 98 func TestAddCoin(t *testing.T) { 99 cases := []struct { 100 inputOne Coin 101 inputTwo Coin 102 expected Coin 103 shouldPanic bool 104 }{ 105 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 2), false}, 106 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom1, 1), false}, 107 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), NewInt64Coin(testDenom1, 1), true}, 108 } 109 110 for tcIndex, tc := range cases { 111 tc := tc 112 if tc.shouldPanic { 113 require.Panics(t, func() { tc.inputOne.Add(tc.inputTwo) }) 114 } else { 115 res := tc.inputOne.Add(tc.inputTwo) 116 require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) 117 } 118 } 119 } 120 121 func TestSubCoin(t *testing.T) { 122 cases := []struct { 123 inputOne Coin 124 inputTwo Coin 125 expected Coin 126 shouldPanic bool 127 }{ 128 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), NewInt64Coin(testDenom1, 1), true}, 129 {NewInt64Coin(testDenom1, 10), NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 9), false}, 130 {NewInt64Coin(testDenom1, 5), NewInt64Coin(testDenom1, 3), NewInt64Coin(testDenom1, 2), false}, 131 {NewInt64Coin(testDenom1, 5), NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom1, 5), false}, 132 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 5), Coin{}, true}, 133 } 134 135 for tcIndex, tc := range cases { 136 tc := tc 137 if tc.shouldPanic { 138 require.Panics(t, func() { tc.inputOne.Sub(tc.inputTwo) }) 139 } else { 140 res := tc.inputOne.Sub(tc.inputTwo) 141 require.Equal(t, tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex) 142 } 143 } 144 145 tc := struct { 146 inputOne Coin 147 inputTwo Coin 148 expected int64 149 }{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), 0} 150 res := tc.inputOne.Sub(tc.inputTwo) 151 require.Equal(t, tc.expected, res.Amount.Int64()) 152 } 153 154 func TestIsGTECoin(t *testing.T) { 155 cases := []struct { 156 inputOne Coin 157 inputTwo Coin 158 expected bool 159 panics bool 160 }{ 161 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), true, false}, 162 {NewInt64Coin(testDenom1, 2), NewInt64Coin(testDenom1, 1), true, false}, 163 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true}, 164 } 165 166 for tcIndex, tc := range cases { 167 tc := tc 168 if tc.panics { 169 require.Panics(t, func() { tc.inputOne.IsGTE(tc.inputTwo) }) 170 } else { 171 res := tc.inputOne.IsGTE(tc.inputTwo) 172 require.Equal(t, tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex) 173 } 174 } 175 } 176 177 func TestIsLTCoin(t *testing.T) { 178 cases := []struct { 179 inputOne Coin 180 inputTwo Coin 181 expected bool 182 panics bool 183 }{ 184 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), false, false}, 185 {NewInt64Coin(testDenom1, 2), NewInt64Coin(testDenom1, 1), false, false}, 186 {NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1), false, true}, 187 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true}, 188 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), false, false}, 189 {NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 2), true, false}, 190 } 191 192 for tcIndex, tc := range cases { 193 tc := tc 194 if tc.panics { 195 require.Panics(t, func() { tc.inputOne.IsLT(tc.inputTwo) }) 196 } else { 197 res := tc.inputOne.IsLT(tc.inputTwo) 198 require.Equal(t, tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex) 199 } 200 } 201 } 202 203 func TestCoinIsZero(t *testing.T) { 204 coin := NewInt64Coin(testDenom1, 0) 205 res := coin.IsZero() 206 require.True(t, res) 207 208 coin = NewInt64Coin(testDenom1, 1) 209 res = coin.IsZero() 210 require.False(t, res) 211 } 212 213 func TestFilteredZeroCoins(t *testing.T) { 214 cases := []struct { 215 name string 216 input Coins 217 original string 218 expected string 219 }{ 220 { 221 name: "all greater than zero", 222 input: Coins{ 223 NewInt64Coin("testa", 1), 224 NewInt64Coin("testb", 2), 225 NewInt64Coin("testc", 3), 226 NewInt64Coin("testd", 4), 227 NewInt64Coin("teste", 5), 228 }, 229 original: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", 230 expected: "1.000000000000000000testa,2.000000000000000000testb,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", 231 }, 232 { 233 name: "zero coin in middle", 234 input: Coins{ 235 NewInt64Coin("testa", 1), 236 NewInt64Coin("testb", 2), 237 NewInt64Coin("testc", 0), 238 NewInt64Coin("testd", 4), 239 NewInt64Coin("teste", 5), 240 }, 241 original: "1.000000000000000000testa,2.000000000000000000testb,0.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", 242 expected: "1.000000000000000000testa,2.000000000000000000testb,4.000000000000000000testd,5.000000000000000000teste", 243 }, 244 { 245 name: "zero coin end (unordered)", 246 input: Coins{ 247 NewInt64Coin("teste", 5), 248 NewInt64Coin("testc", 3), 249 NewInt64Coin("testa", 1), 250 NewInt64Coin("testd", 4), 251 NewInt64Coin("testb", 0), 252 }, 253 original: "5.000000000000000000teste,3.000000000000000000testc,1.000000000000000000testa,4.000000000000000000testd,0.000000000000000000testb", 254 expected: "1.000000000000000000testa,3.000000000000000000testc,4.000000000000000000testd,5.000000000000000000teste", 255 }, 256 } 257 258 for _, tt := range cases { 259 undertest := NewCoins(tt.input...) 260 require.Equal(t, tt.expected, undertest.String(), "NewCoins must return expected results") 261 require.Equal(t, tt.original, tt.input.String(), "input must be unmodified and match original") 262 } 263 } 264 265 // ---------------------------------------------------------------------------- 266 // Coins tests 267 268 func TestIsZeroCoins(t *testing.T) { 269 cases := []struct { 270 inputOne Coins 271 expected bool 272 }{ 273 {Coins{}, true}, 274 {Coins{NewInt64Coin(testDenom1, 0)}, true}, 275 {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 0)}, true}, 276 {Coins{NewInt64Coin(testDenom1, 1)}, false}, 277 {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, false}, 278 } 279 280 for _, tc := range cases { 281 res := tc.inputOne.IsZero() 282 require.Equal(t, tc.expected, res) 283 } 284 } 285 286 func TestEqualCoins(t *testing.T) { 287 cases := []struct { 288 inputOne Coins 289 inputTwo Coins 290 expected bool 291 panics bool 292 }{ 293 {Coins{}, Coins{}, true, false}, 294 {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 0)}, true, false}, 295 {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, true, false}, 296 {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom2, 0)}, false, true}, 297 {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 1)}, false, false}, 298 {Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, false, false}, 299 {Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, true, false}, 300 } 301 302 for tcnum, tc := range cases { 303 tc := tc 304 if tc.panics { 305 require.Panics(t, func() { tc.inputOne.IsEqual(tc.inputTwo) }) 306 } else { 307 res := tc.inputOne.IsEqual(tc.inputTwo) 308 require.Equal(t, tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res) 309 } 310 } 311 } 312 313 func TestAddCoins(t *testing.T) { 314 zero := NewDec(0) 315 one := NewDec(1) 316 two := NewDec(2) 317 318 cases := []struct { 319 inputOne Coins 320 inputTwo Coins 321 expected Coins 322 }{ 323 {Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, two}, {testDenom2, two}}}, 324 {Coins{{testDenom1, zero}, {testDenom2, one}}, Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins{{testDenom2, one}}}, 325 {Coins{{testDenom1, two}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, two}}}, 326 {Coins{{testDenom1, one}}, Coins{{testDenom1, one}, {testDenom2, two}}, Coins{{testDenom1, two}, {testDenom2, two}}}, 327 {Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins(nil)}, 328 } 329 330 for tcIndex, tc := range cases { 331 res := tc.inputOne.Add(tc.inputTwo...) 332 assert.True(t, res.IsValid()) 333 require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex) 334 } 335 } 336 337 func TestSubCoins(t *testing.T) { 338 zero := NewDec(0) 339 one := NewDec(1) 340 two := NewDec(2) 341 342 testCases := []struct { 343 inputOne Coins 344 inputTwo Coins 345 expected Coins 346 shouldPanic bool 347 }{ 348 {Coins{{testDenom1, two}}, Coins{{testDenom1, one}, {testDenom2, two}}, Coins{{testDenom1, one}, {testDenom2, two}}, true}, 349 {Coins{{testDenom1, two}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, two}}, false}, 350 {Coins{{testDenom1, one}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, one}}, false}, 351 {Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, one}}, Coins{{testDenom2, one}}, false}, 352 {Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, two}}, Coins{}, true}, 353 } 354 355 for i, tc := range testCases { 356 tc := tc 357 if tc.shouldPanic { 358 require.Panics(t, func() { tc.inputOne.Sub(tc.inputTwo) }) 359 } else { 360 res := tc.inputOne.Sub(tc.inputTwo) 361 assert.True(t, res.IsValid()) 362 require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", i) 363 } 364 } 365 } 366 367 func TestCoins(t *testing.T) { 368 good := Coins{ 369 {"gas", NewDec(1)}, 370 {"mineral", NewDec(1)}, 371 {"tree", NewDec(1)}, 372 } 373 mixedCase1 := Coins{ 374 {"gAs", NewDec(1)}, 375 {"MineraL", NewDec(1)}, 376 {"TREE", NewDec(1)}, 377 } 378 mixedCase2 := Coins{ 379 {"gAs", NewDec(1)}, 380 {"mineral", NewDec(1)}, 381 } 382 mixedCase3 := Coins{ 383 {"gAs", NewDec(1)}, 384 } 385 empty := NewCoins() 386 badSort1 := Coins{ 387 {"tree", NewDec(1)}, 388 {"gas", NewDec(1)}, 389 {"mineral", NewDec(1)}, 390 } 391 392 // both are after the first one, but the second and third are in the wrong order 393 badSort2 := Coins{ 394 {"gas", NewDec(1)}, 395 {"tree", NewDec(1)}, 396 {"mineral", NewDec(1)}, 397 } 398 badAmt := Coins{ 399 {"gas", NewDec(1)}, 400 {"tree", NewDec(0)}, 401 {"mineral", NewDec(1)}, 402 } 403 dup := Coins{ 404 {"gas", NewDec(1)}, 405 {"gas", NewDec(1)}, 406 {"mineral", NewDec(1)}, 407 } 408 neg := Coins{ 409 {"gas", NewDec(-1)}, 410 {"mineral", NewDec(1)}, 411 } 412 413 assert.True(t, good.IsValid(), "Coins are valid") 414 assert.False(t, mixedCase1.IsValid(), "Coins denoms contain upper case characters") 415 assert.False(t, mixedCase2.IsValid(), "First Coins denoms contain upper case characters") 416 assert.False(t, mixedCase3.IsValid(), "Single denom in Coins contains upper case characters") 417 assert.True(t, good.IsAllPositive(), "Expected coins to be positive: %v", good) 418 assert.False(t, empty.IsAllPositive(), "Expected coins to not be positive: %v", empty) 419 assert.True(t, good.IsAllGTE(empty), "Expected %v to be >= %v", good, empty) 420 assert.False(t, good.IsAllLT(empty), "Expected %v to be < %v", good, empty) 421 assert.True(t, empty.IsAllLT(good), "Expected %v to be < %v", empty, good) 422 assert.False(t, badSort1.IsValid(), "Coins are not sorted") 423 assert.False(t, badSort2.IsValid(), "Coins are not sorted") 424 assert.False(t, badAmt.IsValid(), "Coins cannot include 0 amounts") 425 assert.False(t, dup.IsValid(), "Duplicate coin") 426 assert.False(t, neg.IsValid(), "Negative first-denom coin") 427 } 428 429 func TestCoinsGT(t *testing.T) { 430 one := NewDec(1) 431 two := NewDec(2) 432 433 assert.False(t, Coins{}.IsAllGT(Coins{})) 434 assert.True(t, Coins{{testDenom1, one}}.IsAllGT(Coins{})) 435 assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}})) 436 assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom2, one}})) 437 assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom2, one}})) 438 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(Coins{{testDenom2, two}})) 439 } 440 441 func TestCoinsLT(t *testing.T) { 442 one := NewDec(1) 443 two := NewDec(2) 444 445 assert.False(t, Coins{}.IsAllLT(Coins{})) 446 assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{})) 447 assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{{testDenom1, one}})) 448 assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{{testDenom2, one}})) 449 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom2, one}})) 450 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom2, two}})) 451 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom1, one}, {testDenom2, one}})) 452 assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom1, two}, {testDenom2, two}})) 453 assert.True(t, Coins{}.IsAllLT(Coins{{testDenom1, one}})) 454 } 455 456 func TestCoinsLTE(t *testing.T) { 457 one := NewDec(1) 458 two := NewDec(2) 459 460 assert.True(t, Coins{}.IsAllLTE(Coins{})) 461 assert.False(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{})) 462 assert.True(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{{testDenom1, one}})) 463 assert.False(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{{testDenom2, one}})) 464 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom2, one}})) 465 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom2, two}})) 466 assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom1, one}, {testDenom2, one}})) 467 assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom1, one}, {testDenom2, two}})) 468 assert.True(t, Coins{}.IsAllLTE(Coins{{testDenom1, one}})) 469 } 470 471 func TestParse(t *testing.T) { 472 one := NewDec(1) 473 474 cases := []struct { 475 input string 476 valid bool // if false, we expect an error on parse 477 expected Coins // if valid is true, make sure this is returned 478 }{ 479 {"", true, nil}, 480 {"1foo", true, Coins{{"foo", one}}}, 481 {"10bar", true, Coins{{"bar", NewDec(10)}}}, 482 {"99bar,1foo", true, Coins{{"bar", NewDec(99)}, {"foo", one}}}, 483 {"98 bar , 1 foo ", true, Coins{{"bar", NewDec(98)}, {"foo", one}}}, 484 {" 55\t \t bling\n", true, Coins{{"bling", NewDec(55)}}}, 485 {"2foo, 97 bar", true, Coins{{"bar", NewDec(97)}, {"foo", NewDec(2)}}}, 486 {"5 mycoin,", false, nil}, // no empty coins in a list 487 {"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name 488 {"11me coin, 12you coin", false, nil}, // no spaces in coin names 489 {"1.2btc", true, Coins{{"btc", MustNewDecFromStr("1.2")}}}, // amount must be integer 490 {"5foo-bar", false, nil}, // once more, only letters in coin name 491 } 492 493 for tcIndex, tc := range cases { 494 res, err := ParseCoins(tc.input) 495 if !tc.valid { 496 require.NotNil(t, err, "%s: %#v. tc #%d", tc.input, res, tcIndex) 497 } else if assert.Nil(t, err, "%s: %+v", tc.input, err) { 498 require.Equal(t, tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex) 499 } 500 } 501 } 502 503 func TestSortCoins(t *testing.T) { 504 good := Coins{ 505 NewInt64Coin("gas", 1), 506 NewInt64Coin("mineral", 1), 507 NewInt64Coin("tree", 1), 508 } 509 empty := Coins{ 510 NewInt64Coin("gold", 0), 511 } 512 badSort1 := Coins{ 513 NewInt64Coin("tree", 1), 514 NewInt64Coin("gas", 1), 515 NewInt64Coin("mineral", 1), 516 } 517 badSort2 := Coins{ // both are after the first one, but the second and third are in the wrong order 518 NewInt64Coin("gas", 1), 519 NewInt64Coin("tree", 1), 520 NewInt64Coin("mineral", 1), 521 } 522 badAmt := Coins{ 523 NewInt64Coin("gas", 1), 524 NewInt64Coin("tree", 0), 525 NewInt64Coin("mineral", 1), 526 } 527 dup := Coins{ 528 NewInt64Coin("gas", 1), 529 NewInt64Coin("gas", 1), 530 NewInt64Coin("mineral", 1), 531 } 532 533 cases := []struct { 534 coins Coins 535 before, after bool // valid before/after sort 536 }{ 537 {good, true, true}, 538 {empty, false, false}, 539 {badSort1, false, true}, 540 {badSort2, false, true}, 541 {badAmt, false, false}, 542 {dup, false, false}, 543 } 544 545 for tcIndex, tc := range cases { 546 require.Equal(t, tc.before, tc.coins.IsValid(), "coin validity is incorrect before sorting, tc #%d", tcIndex) 547 tc.coins.Sort() 548 require.Equal(t, tc.after, tc.coins.IsValid(), "coin validity is incorrect after sorting, tc #%d", tcIndex) 549 } 550 } 551 552 func TestAmountOf(t *testing.T) { 553 case0 := Coins{} 554 case1 := Coins{ 555 NewInt64Coin("gold", 0), 556 } 557 case2 := Coins{ 558 NewInt64Coin("gas", 1), 559 NewInt64Coin("mineral", 1), 560 NewInt64Coin("tree", 1), 561 } 562 case3 := Coins{ 563 NewInt64Coin("mineral", 1), 564 NewInt64Coin("tree", 1), 565 } 566 case4 := Coins{ 567 NewInt64Coin("gas", 8), 568 } 569 570 cases := []struct { 571 coins Coins 572 amountOf int64 573 amountOfSpace int64 574 amountOfGAS int64 575 amountOfMINERAL int64 576 amountOfTREE int64 577 }{ 578 {case0, 0, 0, 0, 0, 0}, 579 {case1, 0, 0, 0, 0, 0}, 580 {case2, 0, 0, 1, 1, 1}, 581 {case3, 0, 0, 0, 1, 1}, 582 {case4, 0, 0, 8, 0, 0}, 583 } 584 585 for _, tc := range cases { 586 assert.Equal(t, NewDec(tc.amountOfGAS), tc.coins.AmountOf("gas")) 587 assert.Equal(t, NewDec(tc.amountOfMINERAL), tc.coins.AmountOf("mineral")) 588 assert.Equal(t, NewDec(tc.amountOfTREE), tc.coins.AmountOf("tree")) 589 } 590 591 assert.Panics(t, func() { cases[0].coins.AmountOf("Invalid") }) 592 } 593 594 func TestCoinsIsAnyGTE(t *testing.T) { 595 one := NewDec(1) 596 two := NewDec(2) 597 598 assert.False(t, Coins{}.IsAnyGTE(Coins{})) 599 assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{})) 600 assert.False(t, Coins{}.IsAnyGTE(Coins{{testDenom1, one}})) 601 assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, two}})) 602 assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom2, one}})) 603 assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, two}, {testDenom2, one}})) 604 assert.True(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, one}})) 605 assert.True(t, Coins{{testDenom1, two}}.IsAnyGTE(Coins{{testDenom1, one}})) 606 assert.True(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 607 assert.True(t, Coins{{testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 608 assert.False(t, Coins{{testDenom2, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 609 assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, one}})) 610 assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 611 assert.True(t, Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 612 } 613 614 func TestCoinsIsAllGT(t *testing.T) { 615 one := NewDec(1) 616 two := NewDec(2) 617 618 assert.False(t, Coins{}.IsAllGT(Coins{})) 619 assert.True(t, Coins{{testDenom1, one}}.IsAllGT(Coins{})) 620 assert.False(t, Coins{}.IsAllGT(Coins{{testDenom1, one}})) 621 assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, two}})) 622 assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom2, one}})) 623 assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom1, two}, {testDenom2, one}})) 624 assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}})) 625 assert.True(t, Coins{{testDenom1, two}}.IsAllGT(Coins{{testDenom1, one}})) 626 assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) 627 assert.False(t, Coins{{testDenom2, two}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) 628 assert.False(t, Coins{{testDenom2, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) 629 assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, one}})) 630 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}})) 631 assert.False(t, Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 632 } 633 634 func TestCoinsIsAllGTE(t *testing.T) { 635 one := NewDec(1) 636 two := NewDec(2) 637 638 assert.True(t, Coins{}.IsAllGTE(Coins{})) 639 assert.True(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{})) 640 assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom2, one}})) 641 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom2, two}})) 642 assert.False(t, Coins{}.IsAllGTE(Coins{{testDenom1, one}})) 643 assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, two}})) 644 assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom2, one}})) 645 assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(Coins{{testDenom1, two}, {testDenom2, one}})) 646 assert.True(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, one}})) 647 assert.True(t, Coins{{testDenom1, two}}.IsAllGTE(Coins{{testDenom1, one}})) 648 assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 649 assert.False(t, Coins{{testDenom2, two}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 650 assert.False(t, Coins{{testDenom2, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 651 assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, one}})) 652 assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}})) 653 assert.False(t, Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}})) 654 } 655 656 func TestNewCoins(t *testing.T) { 657 tenatom := NewInt64Coin("atom", 10) 658 tenbtc := NewInt64Coin("btc", 10) 659 zeroeth := NewInt64Coin("eth", 0) 660 tests := []struct { 661 name string 662 coins Coins 663 want Coins 664 wantPanic bool 665 }{ 666 {"empty args", []Coin{}, Coins{}, false}, 667 {"one coin", []Coin{tenatom}, Coins{tenatom}, false}, 668 {"sort after create", []Coin{tenbtc, tenatom}, Coins{tenatom, tenbtc}, false}, 669 {"sort and remove zeroes", []Coin{zeroeth, tenbtc, tenatom}, Coins{tenatom, tenbtc}, false}, 670 {"panic on dups", []Coin{tenatom, tenatom}, Coins{}, true}, 671 } 672 for _, tt := range tests { 673 tt := tt 674 t.Run(tt.name, func(t *testing.T) { 675 if tt.wantPanic { 676 require.Panics(t, func() { NewCoins(tt.coins...) }) 677 return 678 } 679 got := NewCoins(tt.coins...) 680 require.True(t, got.IsEqual(tt.want)) 681 }) 682 } 683 } 684 685 func TestCoinsIsAnyGT(t *testing.T) { 686 twoAtom := NewInt64Coin("atom", 2) 687 fiveAtom := NewInt64Coin("atom", 5) 688 threeEth := NewInt64Coin("eth", 3) 689 sixEth := NewInt64Coin("eth", 6) 690 twoBtc := NewInt64Coin("btc", 2) 691 692 require.False(t, Coins{}.IsAnyGT(Coins{})) 693 694 require.False(t, Coins{fiveAtom}.IsAnyGT(Coins{})) 695 require.False(t, Coins{}.IsAnyGT(Coins{fiveAtom})) 696 require.True(t, Coins{fiveAtom}.IsAnyGT(Coins{twoAtom})) 697 require.False(t, Coins{twoAtom}.IsAnyGT(Coins{fiveAtom})) 698 699 require.True(t, Coins{twoAtom, sixEth}.IsAnyGT(Coins{twoBtc, fiveAtom, threeEth})) 700 require.False(t, Coins{twoBtc, twoAtom, threeEth}.IsAnyGT(Coins{fiveAtom, sixEth})) 701 require.False(t, Coins{twoAtom, sixEth}.IsAnyGT(Coins{twoBtc, fiveAtom})) 702 } 703 704 func TestFindDup(t *testing.T) { 705 abc := NewInt64Coin("abc", 10) 706 def := NewInt64Coin("def", 10) 707 ghi := NewInt64Coin("ghi", 10) 708 709 type args struct { 710 coins Coins 711 } 712 tests := []struct { 713 name string 714 args args 715 want int 716 }{ 717 {"empty", args{NewCoins()}, -1}, 718 {"one coin", args{NewCoins(NewInt64Coin("xyz", 10))}, -1}, 719 {"no dups", args{Coins{abc, def, ghi}}, -1}, 720 {"dup at first position", args{Coins{abc, abc, def}}, 1}, 721 {"dup after first position", args{Coins{abc, def, def}}, 2}, 722 } 723 for _, tt := range tests { 724 tt := tt 725 t.Run(tt.name, func(t *testing.T) { 726 if got := findDup(tt.args.coins); got != tt.want { 727 t.Errorf("findDup() = %v, want %v", got, tt.want) 728 } 729 }) 730 } 731 } 732 733 func TestMarshalJSONCoins(t *testing.T) { 734 cdc := codec.New() 735 RegisterCodec(cdc) 736 737 testCases := []struct { 738 name string 739 input Coins 740 strOutput string 741 }{ 742 {"nil coins", nil, `[]`}, 743 {"empty coins", Coins{}, `[]`}, 744 {"non-empty coins", NewCoins(NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50.000000000000000000"}]`}, 745 } 746 747 for _, tc := range testCases { 748 tc := tc 749 t.Run(tc.name, func(t *testing.T) { 750 bz, err := cdc.MarshalJSON(tc.input) 751 require.NoError(t, err) 752 require.Equal(t, tc.strOutput, string(bz)) 753 754 var newCoins Coins 755 require.NoError(t, cdc.UnmarshalJSON(bz, &newCoins)) 756 757 if tc.input.Empty() { 758 require.Nil(t, newCoins) 759 } else { 760 require.Equal(t, tc.input, newCoins) 761 } 762 }) 763 } 764 } 765 766 func TestConvertWei2FIBO(t *testing.T) { 767 testCases := []struct { 768 name string 769 input CoinAdapter 770 pass bool 771 cm39StrOut string 772 cm40StrOut string 773 }{ 774 {"invalid coin", NewCoinAdapter(DefaultBondDenom, NewInt(1)), false, "", ""}, 775 {"valid coin with specific output", NewCoinAdapter(DefaultIbcWei, NewInt(1)), true, "0.000000000000000001fibo", "1fibo"}, 776 } 777 for _, ca := range testCases { 778 t.Run(ca.name, func(t *testing.T) { 779 coinAdapters := CoinAdapters{ca.input} 780 coins, err := ConvWei2Tfibo(coinAdapters) 781 if !ca.pass { 782 require.Error(t, err) 783 } else { 784 require.NoError(t, err) 785 cm40Coin := coins[0] 786 require.Equal(t, cm40Coin.Denom, DefaultBondDenom) 787 require.Equal(t, ca.cm40StrOut, cm40Coin.String()) 788 cm39Coin := cm40Coin.ToCoin() 789 require.Equal(t, ca.cm39StrOut, cm39Coin.String()) 790 } 791 }) 792 } 793 }