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  }