github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/std/coin_test.go (about)

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