cosmos-sdk/types/coin_test.go

925 lines
28 KiB
Go
Raw Normal View History

2018-01-10 20:11:44 -08:00
package types
2016-04-01 15:19:07 -07:00
import (
"fmt"
"strings"
2016-04-01 15:19:07 -07:00
"testing"
2017-02-22 14:30:50 -08:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/codec"
2016-04-01 15:19:07 -07:00
)
var (
testDenom1 = "atom"
testDenom2 = "muon"
)
// ----------------------------------------------------------------------------
// Coin tests
2018-03-25 10:35:45 -07:00
func TestCoin(t *testing.T) {
require.Panics(t, func() { NewInt64Coin(testDenom1, -1) })
require.Panics(t, func() { NewCoin(testDenom1, NewInt(-1)) })
require.Equal(t, NewInt(10), NewInt64Coin(strings.ToUpper(testDenom1), 10).Amount)
require.Equal(t, NewInt(10), NewCoin(strings.ToUpper(testDenom1), NewInt(10)).Amount)
require.Equal(t, NewInt(5), NewInt64Coin(testDenom1, 5).Amount)
require.Equal(t, NewInt(5), NewCoin(testDenom1, NewInt(5)).Amount)
2018-03-25 10:35:45 -07:00
}
func TestCoin_String(t *testing.T) {
coin := NewCoin(testDenom1, NewInt(10))
require.Equal(t, fmt.Sprintf("10%s", testDenom1), coin.String())
}
func TestIsEqualCoin(t *testing.T) {
2018-03-25 10:35:45 -07:00
cases := []struct {
inputOne Coin
inputTwo Coin
expected bool
panics bool
2018-03-25 10:35:45 -07:00
}{
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), true, false},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true},
2019-03-14 07:32:46 -07:00
{NewInt64Coin("stake", 1), NewInt64Coin("stake", 10), false, false},
2018-03-25 10:35:45 -07:00
}
for tcIndex, tc := range cases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.panics {
require.Panics(t, func() { tc.inputOne.IsEqual(tc.inputTwo) })
} else {
res := tc.inputOne.IsEqual(tc.inputTwo)
require.Equal(t, tc.expected, res, "coin equality relation is incorrect, tc #%d", tcIndex)
}
2018-03-25 10:35:45 -07:00
}
}
func TestCoinIsValid(t *testing.T) {
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.
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.
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.`
loremIpsum = strings.ReplaceAll(loremIpsum, " ", "")
loremIpsum = strings.ReplaceAll(loremIpsum, ".", "")
loremIpsum = strings.ReplaceAll(loremIpsum, ",", "")
cases := []struct {
coin Coin
expectPass bool
}{
{Coin{testDenom1, NewInt(-1)}, false},
{Coin{testDenom1, NewInt(0)}, true},
{Coin{testDenom1, OneInt()}, true},
{Coin{"Atom", OneInt()}, true},
{Coin{"ATOM", OneInt()}, true},
{Coin{"a", OneInt()}, false},
{Coin{loremIpsum, OneInt()}, false},
{Coin{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", OneInt()}, true},
{Coin{"atOm", OneInt()}, true},
{Coin{" ", OneInt()}, false},
}
for i, tc := range cases {
require.Equal(t, tc.expectPass, tc.coin.IsValid(), "unexpected result for IsValid, tc #%d", i)
}
}
func TestAddCoin(t *testing.T) {
cases := []struct {
inputOne Coin
inputTwo Coin
expected Coin
shouldPanic bool
}{
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 2), false},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom1, 1), false},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), NewInt64Coin(testDenom1, 1), true},
}
for tcIndex, tc := range cases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.shouldPanic {
require.Panics(t, func() { tc.inputOne.Add(tc.inputTwo) })
} else {
res := tc.inputOne.Add(tc.inputTwo)
require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex)
}
}
}
func TestSubCoin(t *testing.T) {
2018-03-25 10:35:45 -07:00
cases := []struct {
inputOne Coin
inputTwo Coin
expected Coin
shouldPanic bool
2018-03-25 10:35:45 -07:00
}{
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), NewInt64Coin(testDenom1, 1), true},
{NewInt64Coin(testDenom1, 10), NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 9), false},
{NewInt64Coin(testDenom1, 5), NewInt64Coin(testDenom1, 3), NewInt64Coin(testDenom1, 2), false},
{NewInt64Coin(testDenom1, 5), NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom1, 5), false},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 5), Coin{}, true},
2018-03-25 10:35:45 -07:00
}
for tcIndex, tc := range cases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.shouldPanic {
require.Panics(t, func() { tc.inputOne.Sub(tc.inputTwo) })
} else {
res := tc.inputOne.Sub(tc.inputTwo)
require.Equal(t, tc.expected, res, "difference of coins is incorrect, tc #%d", tcIndex)
}
2018-03-25 10:35:45 -07:00
}
tc := struct {
inputOne Coin
inputTwo Coin
expected int64
}{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), 0}
res := tc.inputOne.Sub(tc.inputTwo)
require.Equal(t, tc.expected, res.Amount.Int64())
2018-03-25 10:35:45 -07:00
}
func TestIsGTECoin(t *testing.T) {
2018-03-25 10:35:45 -07:00
cases := []struct {
inputOne Coin
inputTwo Coin
expected bool
panics bool
2018-03-25 10:35:45 -07:00
}{
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), true, false},
{NewInt64Coin(testDenom1, 2), NewInt64Coin(testDenom1, 1), true, false},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true},
2018-03-25 10:35:45 -07:00
}
for tcIndex, tc := range cases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.panics {
require.Panics(t, func() { tc.inputOne.IsGTE(tc.inputTwo) })
} else {
res := tc.inputOne.IsGTE(tc.inputTwo)
require.Equal(t, tc.expected, res, "coin GTE relation is incorrect, tc #%d", tcIndex)
}
2018-03-25 10:35:45 -07:00
}
}
func TestIsLTCoin(t *testing.T) {
2018-03-25 10:35:45 -07:00
cases := []struct {
inputOne Coin
inputTwo Coin
expected bool
panics bool
2018-03-25 10:35:45 -07:00
}{
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), false, false},
{NewInt64Coin(testDenom1, 2), NewInt64Coin(testDenom1, 1), false, false},
{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1), false, true},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom2, 1), false, true},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 1), false, false},
{NewInt64Coin(testDenom1, 1), NewInt64Coin(testDenom1, 2), true, false},
2018-03-25 10:35:45 -07:00
}
for tcIndex, tc := range cases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.panics {
require.Panics(t, func() { tc.inputOne.IsLT(tc.inputTwo) })
} else {
res := tc.inputOne.IsLT(tc.inputTwo)
require.Equal(t, tc.expected, res, "coin LT relation is incorrect, tc #%d", tcIndex)
}
2018-03-25 10:35:45 -07:00
}
}
func TestCoinIsZero(t *testing.T) {
coin := NewInt64Coin(testDenom1, 0)
res := coin.IsZero()
require.True(t, res)
coin = NewInt64Coin(testDenom1, 1)
res = coin.IsZero()
require.False(t, res)
2018-03-25 10:35:45 -07:00
}
func TestFilteredZeroCoins(t *testing.T) {
cases := []struct {
name string
input Coins
original string
expected string
}{
{
name: "all greater than zero",
input: Coins{
{"testa", OneInt()},
{"testb", NewInt(2)},
{"testc", NewInt(3)},
{"testd", NewInt(4)},
{"teste", NewInt(5)},
},
original: "1testa,2testb,3testc,4testd,5teste",
expected: "1testa,2testb,3testc,4testd,5teste",
},
{
name: "zero coin in middle",
input: Coins{
{"testa", OneInt()},
{"testb", NewInt(2)},
{"testc", NewInt(0)},
{"testd", NewInt(4)},
{"teste", NewInt(5)},
},
original: "1testa,2testb,0testc,4testd,5teste",
expected: "1testa,2testb,4testd,5teste",
},
{
name: "zero coin end (unordered)",
input: Coins{
{"teste", NewInt(5)},
{"testc", NewInt(3)},
{"testa", OneInt()},
{"testd", NewInt(4)},
{"testb", NewInt(0)},
},
original: "5teste,3testc,1testa,4testd,0testb",
expected: "1testa,3testc,4testd,5teste",
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
undertest := NewCoins(tt.input...)
require.Equal(t, tt.expected, undertest.String(), "NewCoins must return expected results")
require.Equal(t, tt.original, tt.input.String(), "input must be unmodified and match original")
})
}
}
// ----------------------------------------------------------------------------
// Coins tests
func TestCoins_String(t *testing.T) {
cases := []struct {
name string
input Coins
expected string
}{
{
"empty coins",
Coins{},
"",
},
{
"single coin",
Coins{{"tree", OneInt()}},
"1tree",
},
{
"multiple coins",
Coins{
{"tree", OneInt()},
{"gas", OneInt()},
{"mineral", OneInt()},
},
"1tree,1gas,1mineral",
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
require.Equal(t, tt.expected, tt.input.String())
})
}
}
func TestIsZeroCoins(t *testing.T) {
cases := []struct {
inputOne Coins
expected bool
}{
{Coins{}, true},
{Coins{NewInt64Coin(testDenom1, 0)}, true},
{Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 0)}, true},
{Coins{NewInt64Coin(testDenom1, 1)}, false},
{Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, false},
}
for _, tc := range cases {
res := tc.inputOne.IsZero()
require.Equal(t, tc.expected, res)
}
}
func TestEqualCoins(t *testing.T) {
cases := []struct {
inputOne Coins
inputTwo Coins
expected bool
panics bool
}{
{Coins{}, Coins{}, true, false},
{Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 0)}, true, false},
{Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, true, false},
{Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom2, 0)}, false, true},
{Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 1)}, false, false},
{Coins{NewInt64Coin(testDenom1, 0)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, false, false},
{Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, Coins{NewInt64Coin(testDenom1, 0), NewInt64Coin(testDenom2, 1)}, true, false},
}
for tcnum, tc := range cases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.panics {
require.Panics(t, func() { tc.inputOne.IsEqual(tc.inputTwo) })
} else {
res := tc.inputOne.IsEqual(tc.inputTwo)
require.Equal(t, tc.expected, res, "Equality is differed from exported. tc #%d, expected %b, actual %b.", tcnum, tc.expected, res)
}
}
}
func TestAddCoins(t *testing.T) {
zero := NewInt(0)
one := OneInt()
two := NewInt(2)
cases := []struct {
inputOne Coins
inputTwo Coins
expected Coins
}{
{Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, two}, {testDenom2, two}}},
{Coins{{testDenom1, zero}, {testDenom2, one}}, Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins{{testDenom2, one}}},
{Coins{{testDenom1, two}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, two}}},
{Coins{{testDenom1, one}}, Coins{{testDenom1, one}, {testDenom2, two}}, Coins{{testDenom1, two}, {testDenom2, two}}},
{Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins{{testDenom1, zero}, {testDenom2, zero}}, Coins(nil)},
}
for tcIndex, tc := range cases {
res := tc.inputOne.Add(tc.inputTwo...)
assert.True(t, res.IsValid())
require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", tcIndex)
}
}
func TestSubCoins(t *testing.T) {
zero := NewInt(0)
one := OneInt()
two := NewInt(2)
testCases := []struct {
inputOne Coins
inputTwo Coins
expected Coins
shouldPanic bool
}{
{Coins{{testDenom1, two}}, Coins{{testDenom1, one}, {testDenom2, two}}, Coins{{testDenom1, one}, {testDenom2, two}}, true},
{Coins{{testDenom1, two}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, two}}, false},
{Coins{{testDenom1, one}}, Coins{{testDenom2, zero}}, Coins{{testDenom1, one}}, false},
{Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, one}}, Coins{{testDenom2, one}}, false},
{Coins{{testDenom1, one}, {testDenom2, one}}, Coins{{testDenom1, two}}, Coins{}, true},
}
for i, tc := range testCases {
2019-10-17 06:47:35 -07:00
tc := tc
if tc.shouldPanic {
require.Panics(t, func() { tc.inputOne.Sub(tc.inputTwo) })
} else {
res := tc.inputOne.Sub(tc.inputTwo)
assert.True(t, res.IsValid())
require.Equal(t, tc.expected, res, "sum of coins is incorrect, tc #%d", i)
}
}
}
2017-02-22 14:30:50 -08:00
func TestCoins_Validate(t *testing.T) {
testCases := []struct {
name string
coins Coins
expPass bool
}{
{
"valid lowercase coins",
Coins{
{"gas", OneInt()},
{"mineral", OneInt()},
{"tree", OneInt()},
},
true,
},
{
"valid uppercase coins",
Coins{
{"GAS", OneInt()},
{"MINERAL", OneInt()},
{"TREE", OneInt()},
},
true,
},
{
"valid uppercase coin",
Coins{
{"ATOM", OneInt()},
},
true,
},
{
"valid lower and uppercase coins (1)",
Coins{
{"GAS", OneInt()},
{"gAs", OneInt()},
},
true,
},
{
"valid lower and uppercase coins (2)",
Coins{
{"ATOM", OneInt()},
{"Atom", OneInt()},
{"atom", OneInt()},
},
true,
},
{
"mixed case (1)",
Coins{
{"MineraL", OneInt()},
{"TREE", OneInt()},
{"gAs", OneInt()},
},
true,
},
{
"mixed case (2)",
Coins{
{"gAs", OneInt()},
{"mineral", OneInt()},
},
true,
},
{
"mixed case (3)",
Coins{
{"gAs", OneInt()},
},
true,
},
{
"unicode letters and numbers",
Coins{
{"𐀀𐀆𐀉Ⅲ", OneInt()},
},
false,
},
{
"emojis",
Coins{
{"🤑😋🤔", OneInt()},
},
false,
},
{
"IBC denominations (ADR 001)",
Coins{
{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", OneInt()},
{"ibc/876563AAAACF739EB061C67CDB5EDF2B7C9FD4AA9D876450CC21210807C2820A", NewInt(2)},
},
true,
},
{
"empty (1)",
NewCoins(),
true,
},
{
"empty (2)",
Coins{},
true,
},
{
"invalid denomination (1)",
Coins{
{"MineraL", OneInt()},
{"0TREE", OneInt()},
{"gAs", OneInt()},
},
false,
},
{
"invalid denomination (2)",
Coins{
{"-GAS", OneInt()},
{"gAs", OneInt()},
},
false,
},
{
"bad sort (1)",
Coins{
{"tree", OneInt()},
{"gas", OneInt()},
{"mineral", OneInt()},
},
false,
},
{
"bad sort (2)",
Coins{
{"gas", OneInt()},
{"tree", OneInt()},
{"mineral", OneInt()},
},
false,
},
{
"non-positive amount (1)",
Coins{
{"gas", OneInt()},
{"tree", NewInt(0)},
{"mineral", OneInt()},
},
false,
},
{
"non-positive amount (2)",
Coins{
{"gas", NewInt(-1)},
{"tree", OneInt()},
{"mineral", OneInt()},
},
false,
}, {
"duplicate denomination",
Coins{
{"gas", OneInt()},
{"gas", OneInt()},
{"mineral", OneInt()},
},
false,
},
2016-04-01 15:19:07 -07:00
}
for _, tc := range testCases {
err := tc.coins.Validate()
if tc.expPass {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
2016-04-01 15:19:07 -07:00
}
}
2017-03-28 13:32:55 -07:00
func TestCoinsGT(t *testing.T) {
one := OneInt()
two := NewInt(2)
assert.False(t, Coins{}.IsAllGT(Coins{}))
assert.True(t, Coins{{testDenom1, one}}.IsAllGT(Coins{}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom2, one}}))
2019-03-07 16:24:05 -08:00
assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(Coins{{testDenom2, two}}))
}
func TestCoinsLT(t *testing.T) {
one := OneInt()
two := NewInt(2)
assert.False(t, Coins{}.IsAllLT(Coins{}))
assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{}))
assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllLT(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom2, two}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom1, one}, {testDenom2, one}}))
2019-03-07 16:24:05 -08:00
assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLT(Coins{{testDenom1, two}, {testDenom2, two}}))
assert.True(t, Coins{}.IsAllLT(Coins{{testDenom1, one}}))
}
func TestCoinsLTE(t *testing.T) {
one := OneInt()
two := NewInt(2)
assert.True(t, Coins{}.IsAllLTE(Coins{}))
assert.False(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{}))
assert.True(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllLTE(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom2, two}}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom1, one}, {testDenom2, one}}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllLTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.True(t, Coins{}.IsAllLTE(Coins{{testDenom1, one}}))
}
func TestParseCoins(t *testing.T) {
one := OneInt()
cases := []struct {
input string
valid bool // if false, we expect an error on parse
expected Coins // if valid is true, make sure this is returned
}{
{"", true, nil},
{"0stake", true, Coins{}}, // remove zero coins
{"0stake,1foo,99bar", true, Coins{{"bar", NewInt(99)}, {"foo", one}}}, // remove zero coins
{"1foo", true, Coins{{"foo", one}}},
{"10btc,1atom,20btc", false, nil},
{"10bar", true, Coins{{"bar", NewInt(10)}}},
{"99bar,1foo", true, Coins{{"bar", NewInt(99)}, {"foo", one}}},
{"98 bar , 1 foo ", true, Coins{{"bar", NewInt(98)}, {"foo", one}}},
{" 55\t \t bling\n", true, Coins{{"bling", NewInt(55)}}},
{"2foo, 97 bar", true, Coins{{"bar", NewInt(97)}, {"foo", NewInt(2)}}},
2017-05-24 08:15:42 -07:00
{"5 mycoin,", false, nil}, // no empty coins in a list
{"2 3foo, 97 bar", false, nil}, // 3foo is invalid coin name
{"11me coin, 12you coin", false, nil}, // no spaces in coin names
{"1.2btc", false, nil}, // amount must be integer
{"5foo:bar", false, nil}, // invalid separator
{"10atom10", true, Coins{{"atom10", NewInt(10)}}},
{"200transfer/channelToA/uatom", true, Coins{{"transfer/channelToA/uatom", NewInt(200)}}},
{"50ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", true, Coins{{"ibc/7F1D3FCF4AE79E1554D670D1AD949A9BA4E4A3C76C63093E17E446A46061A7A2", NewInt(50)}}},
}
2017-03-28 13:32:55 -07:00
for tcIndex, tc := range cases {
res, err := ParseCoins(tc.input)
if !tc.valid {
require.Error(t, err, "%s: %#v. tc #%d", tc.input, res, tcIndex)
2018-01-25 16:30:49 -08:00
} else if assert.Nil(t, err, "%s: %+v", tc.input, err) {
require.Equal(t, tc.expected, res, "coin parsing was incorrect, tc #%d", tcIndex)
}
2017-03-28 13:32:55 -07:00
}
}
func TestSortCoins(t *testing.T) {
good := Coins{
NewInt64Coin("gas", 1),
NewInt64Coin("mineral", 1),
NewInt64Coin("tree", 1),
}
empty := Coins{
NewInt64Coin("gold", 0),
}
badSort1 := Coins{
NewInt64Coin("tree", 1),
NewInt64Coin("gas", 1),
NewInt64Coin("mineral", 1),
}
badSort2 := Coins{ // both are after the first one, but the second and third are in the wrong order
NewInt64Coin("gas", 1),
NewInt64Coin("tree", 1),
NewInt64Coin("mineral", 1),
}
badAmt := Coins{
NewInt64Coin("gas", 1),
NewInt64Coin("tree", 0),
NewInt64Coin("mineral", 1),
}
dup := Coins{
NewInt64Coin("gas", 1),
NewInt64Coin("gas", 1),
NewInt64Coin("mineral", 1),
2017-03-28 13:32:55 -07:00
}
cases := []struct {
name string
coins Coins
validBefore,
validAfter bool
}{
{"valid coins", good, true, true},
{"empty coins", empty, false, false},
{"bad sort (1)", badSort1, false, true},
{"bad sort (2)", badSort2, false, true},
{"zero value coin", badAmt, false, false},
{"duplicate coins", dup, false, false},
}
2017-03-28 13:32:55 -07:00
for _, tc := range cases {
err := tc.coins.Validate()
if tc.validBefore {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
tc.coins.Sort()
err = tc.coins.Validate()
if tc.validAfter {
require.NoError(t, err, tc.name)
} else {
require.Error(t, err, tc.name)
}
2018-01-25 16:30:49 -08:00
}
}
func TestAmountOf(t *testing.T) {
case0 := Coins{}
case1 := Coins{
NewInt64Coin("gold", 0),
2018-01-25 16:30:49 -08:00
}
case2 := Coins{
NewInt64Coin("gas", 1),
NewInt64Coin("mineral", 1),
NewInt64Coin("tree", 1),
2018-01-25 16:30:49 -08:00
}
case3 := Coins{
NewInt64Coin("mineral", 1),
NewInt64Coin("tree", 1),
2018-01-25 16:30:49 -08:00
}
case4 := Coins{
NewInt64Coin("gas", 8),
2018-01-25 16:30:49 -08:00
}
cases := []struct {
coins Coins
amountOf int64
2018-02-04 16:59:11 -08:00
amountOfSpace int64
2018-01-25 16:30:49 -08:00
amountOfGAS int64
amountOfMINERAL int64
amountOfTREE int64
}{
{case0, 0, 0, 0, 0, 0},
{case1, 0, 0, 0, 0, 0},
{case2, 0, 0, 1, 1, 1},
{case3, 0, 0, 0, 1, 1},
{case4, 0, 0, 8, 0, 0},
2018-01-25 16:30:49 -08:00
}
for _, tc := range cases {
assert.Equal(t, NewInt(tc.amountOfGAS), tc.coins.AmountOf("gas"))
assert.Equal(t, NewInt(tc.amountOfMINERAL), tc.coins.AmountOf("mineral"))
assert.Equal(t, NewInt(tc.amountOfTREE), tc.coins.AmountOf("tree"))
}
assert.Panics(t, func() { cases[0].coins.AmountOf("10Invalid") })
2017-03-28 13:32:55 -07:00
}
func TestCoinsIsAnyGTE(t *testing.T) {
one := OneInt()
two := NewInt(2)
assert.False(t, Coins{}.IsAnyGTE(Coins{}))
assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{}))
assert.False(t, Coins{}.IsAnyGTE(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, two}}))
assert.False(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom2, one}}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, two}, {testDenom2, one}}))
assert.True(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, one}}))
assert.True(t, Coins{{testDenom1, two}}.IsAnyGTE(Coins{{testDenom1, one}}))
assert.True(t, Coins{{testDenom1, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.True(t, Coins{{testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{testDenom2, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, one}}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAnyGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.True(t, Coins{{"xxx", one}, {"yyy", one}}.IsAnyGTE(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}}))
}
2019-03-07 00:02:40 -08:00
func TestCoinsIsAllGT(t *testing.T) {
one := OneInt()
2019-03-07 00:02:40 -08:00
two := NewInt(2)
assert.False(t, Coins{}.IsAllGT(Coins{}))
assert.True(t, Coins{{testDenom1, one}}.IsAllGT(Coins{}))
assert.False(t, Coins{}.IsAllGT(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, two}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom1, two}, {testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}}))
assert.True(t, Coins{{testDenom1, two}}.IsAllGT(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{testDenom2, two}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{testDenom2, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGT(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{"xxx", one}, {"yyy", one}}.IsAllGT(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}}))
}
func TestCoinsIsAllGTE(t *testing.T) {
one := OneInt()
2019-03-07 00:02:40 -08:00
two := NewInt(2)
assert.True(t, Coins{}.IsAllGTE(Coins{}))
assert.True(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom2, two}}))
2019-03-07 00:02:40 -08:00
assert.False(t, Coins{}.IsAllGTE(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, two}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(Coins{{testDenom1, two}, {testDenom2, one}}))
assert.True(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, one}}))
assert.True(t, Coins{{testDenom1, two}}.IsAllGTE(Coins{{testDenom1, one}}))
assert.False(t, Coins{{testDenom1, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{testDenom2, two}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{testDenom2, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.True(t, Coins{{testDenom1, one}, {testDenom2, two}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, one}}))
assert.False(t, Coins{{testDenom1, one}, {testDenom2, one}}.IsAllGTE(Coins{{testDenom1, one}, {testDenom2, two}}))
assert.False(t, Coins{{"xxx", one}, {"yyy", one}}.IsAllGTE(Coins{{testDenom2, one}, {"ccc", one}, {"yyy", one}, {"zzz", one}}))
}
2019-03-07 16:55:08 -08:00
func TestNewCoins(t *testing.T) {
tenatom := NewInt64Coin("atom", 10)
tenbtc := NewInt64Coin("btc", 10)
zeroeth := NewInt64Coin("eth", 0)
invalidCoin := Coin{"0ETH", OneInt()}
2019-03-07 16:55:08 -08:00
tests := []struct {
name string
coins Coins
want Coins
wantPanic bool
}{
{"empty args", []Coin{}, Coins{}, false},
{"one coin", []Coin{tenatom}, Coins{tenatom}, false},
{"sort after create", []Coin{tenbtc, tenatom}, Coins{tenatom, tenbtc}, false},
{"sort and remove zeroes", []Coin{zeroeth, tenbtc, tenatom}, Coins{tenatom, tenbtc}, false},
{"panic on dups", []Coin{tenatom, tenatom}, Coins{}, true},
{"panic on invalid coin", []Coin{invalidCoin, tenatom}, Coins{}, true},
2019-03-07 16:55:08 -08:00
}
for _, tt := range tests {
2019-10-17 06:47:35 -07:00
tt := tt
2019-03-07 16:55:08 -08:00
t.Run(tt.name, func(t *testing.T) {
if tt.wantPanic {
require.Panics(t, func() { NewCoins(tt.coins...) })
return
}
got := NewCoins(tt.coins...)
require.True(t, got.IsEqual(tt.want))
})
}
}
2019-05-07 10:10:35 -07:00
func TestCoinsIsAnyGT(t *testing.T) {
twoAtom := NewInt64Coin("atom", 2)
fiveAtom := NewInt64Coin("atom", 5)
threeEth := NewInt64Coin("eth", 3)
sixEth := NewInt64Coin("eth", 6)
twoBtc := NewInt64Coin("btc", 2)
2019-05-08 06:51:30 -07:00
tests := []struct {
name string
coinsA Coins
coinsB Coins
expPass bool
2019-05-08 06:51:30 -07:00
}{
{"{} ≤ {}", Coins{}, Coins{}, false},
{"{} ≤ 5atom", Coins{}, Coins{fiveAtom}, false},
{"5atom > 2atom", Coins{fiveAtom}, Coins{twoAtom}, true},
{"2atom ≤ 5atom", Coins{twoAtom}, Coins{fiveAtom}, false},
{"2atom,6eth > 2btc,5atom,3eth", Coins{twoAtom, sixEth}, Coins{twoBtc, fiveAtom, threeEth}, true},
{"2btc,2atom,3eth ≤ 5atom,6eth", Coins{twoBtc, twoAtom, threeEth}, Coins{fiveAtom, sixEth}, false},
{"2atom,6eth ≤ 2btc,5atom", Coins{twoAtom, sixEth}, Coins{twoBtc, fiveAtom}, false},
2019-05-08 06:51:30 -07:00
}
for _, tc := range tests {
require.True(t, tc.expPass == tc.coinsA.IsAnyGT(tc.coinsB), tc.name)
2019-05-08 06:51:30 -07:00
}
}
func TestMarshalJSONCoins(t *testing.T) {
cdc := codec.NewLegacyAmino()
RegisterLegacyAminoCodec(cdc)
testCases := []struct {
name string
input Coins
strOutput string
}{
{"nil coins", nil, `[]`},
{"empty coins", Coins{}, `[]`},
{"non-empty coins", NewCoins(NewInt64Coin("foo", 50)), `[{"denom":"foo","amount":"50"}]`},
}
for _, tc := range testCases {
2019-10-17 06:47:35 -07:00
tc := tc
t.Run(tc.name, func(t *testing.T) {
bz, err := cdc.MarshalJSON(tc.input)
require.NoError(t, err)
require.Equal(t, tc.strOutput, string(bz))
var newCoins Coins
require.NoError(t, cdc.UnmarshalJSON(bz, &newCoins))
if tc.input.Empty() {
require.Nil(t, newCoins)
} else {
require.Equal(t, tc.input, newCoins)
}
})
}
}
func TestCoinAminoEncoding(t *testing.T) {
c := NewInt64Coin(testDenom1, 5)
bz1, err := cdc.MarshalBinaryBare(c)
require.NoError(t, err)
bz2, err := cdc.MarshalBinaryLengthPrefixed(c)
require.NoError(t, err)
bz3, err := c.Marshal()
require.NoError(t, err)
require.Equal(t, bz1, bz3)
require.Equal(t, bz2[1:], bz3)
}