package api
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/hermeznetwork/hermez-node/api/apitypes"
|
|
"github.com/hermeznetwork/hermez-node/common"
|
|
"github.com/hermeznetwork/hermez-node/db/historydb"
|
|
"github.com/mitchellh/copystructure"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
type testCVP struct {
|
|
Root string
|
|
Siblings []string
|
|
OldKey string
|
|
OldValue string
|
|
IsOld0 bool
|
|
Key string
|
|
Value string
|
|
Fnc int
|
|
}
|
|
|
|
type testExit struct {
|
|
ItemID uint64 `json:"itemId"`
|
|
BatchNum common.BatchNum `json:"batchNum"`
|
|
AccountIdx string `json:"accountIndex"`
|
|
BJJ apitypes.HezBJJ `json:"bjj"`
|
|
EthAddr apitypes.HezEthAddr `json:"hezEthereumAddress"`
|
|
MerkleProof testCVP `json:"merkleProof"`
|
|
Balance string `json:"balance"`
|
|
InstantWithdrawn *int64 `json:"instantWithdraw"`
|
|
DelayedWithdrawRequest *int64 `json:"delayedWithdrawRequest"`
|
|
DelayedWithdrawn *int64 `json:"delayedWithdraw"`
|
|
Token historydb.TokenWithUSD `json:"token"`
|
|
}
|
|
|
|
type testExitsResponse struct {
|
|
Exits []testExit `json:"exits"`
|
|
PendingItems uint64 `json:"pendingItems"`
|
|
}
|
|
|
|
func (t testExitsResponse) GetPending() (pendingItems, lastItemID uint64) {
|
|
if len(t.Exits) == 0 {
|
|
return 0, 0
|
|
}
|
|
pendingItems = t.PendingItems
|
|
lastItemID = t.Exits[len(t.Exits)-1].ItemID
|
|
return pendingItems, lastItemID
|
|
}
|
|
|
|
func (t testExitsResponse) New() Pendinger { return &testExitsResponse{} }
|
|
|
|
func (t *testExitsResponse) Len() int {
|
|
return len(t.Exits)
|
|
}
|
|
|
|
func genTestExits(
|
|
commonExits []common.ExitInfo,
|
|
tokens []historydb.TokenWithUSD,
|
|
accs []common.Account,
|
|
) []testExit {
|
|
allExits := []testExit{}
|
|
for _, exit := range commonExits {
|
|
token := getTokenByIdx(exit.AccountIdx, tokens, accs)
|
|
siblings := []string{}
|
|
for i := 0; i < len(exit.MerkleProof.Siblings); i++ {
|
|
siblings = append(siblings, exit.MerkleProof.Siblings[i].String())
|
|
}
|
|
acc := getAccountByIdx(exit.AccountIdx, accs)
|
|
allExits = append(allExits, testExit{
|
|
BatchNum: exit.BatchNum,
|
|
AccountIdx: idxToHez(exit.AccountIdx, token.Symbol),
|
|
BJJ: apitypes.NewHezBJJ(acc.BJJ),
|
|
EthAddr: apitypes.NewHezEthAddr(acc.EthAddr),
|
|
MerkleProof: testCVP{
|
|
Root: exit.MerkleProof.Root.String(),
|
|
Siblings: siblings,
|
|
OldKey: exit.MerkleProof.OldKey.String(),
|
|
OldValue: exit.MerkleProof.OldValue.String(),
|
|
IsOld0: exit.MerkleProof.IsOld0,
|
|
Key: exit.MerkleProof.Key.String(),
|
|
Value: exit.MerkleProof.Value.String(),
|
|
Fnc: exit.MerkleProof.Fnc,
|
|
},
|
|
Balance: exit.Balance.String(),
|
|
InstantWithdrawn: exit.InstantWithdrawn,
|
|
DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
|
|
DelayedWithdrawn: exit.DelayedWithdrawn,
|
|
Token: token,
|
|
})
|
|
}
|
|
return allExits
|
|
}
|
|
|
|
func TestGetExits(t *testing.T) {
|
|
endpoint := apiURL + "exits"
|
|
fetchedExits := []testExit{}
|
|
appendIter := func(intr interface{}) {
|
|
for i := 0; i < len(intr.(*testExitsResponse).Exits); i++ {
|
|
tmp, err := copystructure.Copy(intr.(*testExitsResponse).Exits[i])
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fetchedExits = append(fetchedExits, tmp.(testExit))
|
|
}
|
|
}
|
|
// Get all (no filters)
|
|
limit := 8
|
|
path := fmt.Sprintf("%s?limit=%d", endpoint, limit)
|
|
err := doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
assertExitAPIs(t, tc.exits, fetchedExits)
|
|
|
|
// Get by ethAddr
|
|
fetchedExits = []testExit{}
|
|
limit = 7
|
|
var account testAccount
|
|
for _, tx := range tc.txs {
|
|
found := false
|
|
if tx.Type == common.TxTypeExit {
|
|
for i := 0; i < len(tc.accounts); i++ {
|
|
if tx.FromIdx != nil && string(tc.accounts[i].Idx) == *tx.FromIdx {
|
|
account = tc.accounts[i]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if found {
|
|
break
|
|
}
|
|
}
|
|
path = fmt.Sprintf(
|
|
"%s?hezEthereumAddress=%s&limit=%d",
|
|
endpoint, account.EthAddr, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
var accountExits []testExit
|
|
for i := range tc.exits {
|
|
for _, acc := range tc.accounts {
|
|
if string(acc.Idx) == tc.exits[i].AccountIdx {
|
|
if acc.EthAddr == account.EthAddr {
|
|
accountExits = append(accountExits, tc.exits[i])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
assertExitAPIs(t, accountExits, fetchedExits)
|
|
// Get by bjj
|
|
fetchedExits = []testExit{}
|
|
limit = 6
|
|
path = fmt.Sprintf(
|
|
"%s?BJJ=%s&limit=%d",
|
|
endpoint, account.PublicKey, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
assertExitAPIs(t, accountExits, fetchedExits)
|
|
// Get by tokenID
|
|
fetchedExits = []testExit{}
|
|
limit = 5
|
|
tokenID := tc.exits[0].Token.TokenID
|
|
path = fmt.Sprintf(
|
|
"%s?tokenId=%d&limit=%d",
|
|
endpoint, tokenID, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
tokenIDExits := []testExit{}
|
|
for i := 0; i < len(tc.exits); i++ {
|
|
if tc.exits[i].Token.TokenID == tokenID {
|
|
tokenIDExits = append(tokenIDExits, tc.exits[i])
|
|
}
|
|
}
|
|
assertExitAPIs(t, tokenIDExits, fetchedExits)
|
|
// idx
|
|
fetchedExits = []testExit{}
|
|
limit = 4
|
|
idx := tc.exits[0].AccountIdx
|
|
path = fmt.Sprintf(
|
|
"%s?accountIndex=%s&limit=%d",
|
|
endpoint, idx, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
idxExits := []testExit{}
|
|
for i := 0; i < len(tc.exits); i++ {
|
|
if tc.exits[i].AccountIdx[6:] == idx[6:] {
|
|
idxExits = append(idxExits, tc.exits[i])
|
|
}
|
|
}
|
|
assertExitAPIs(t, idxExits, fetchedExits)
|
|
// batchNum
|
|
fetchedExits = []testExit{}
|
|
limit = 3
|
|
batchNum := tc.exits[0].BatchNum
|
|
path = fmt.Sprintf(
|
|
"%s?batchNum=%d&limit=%d",
|
|
endpoint, batchNum, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
batchNumExits := []testExit{}
|
|
for i := 0; i < len(tc.exits); i++ {
|
|
if tc.exits[i].BatchNum == batchNum {
|
|
batchNumExits = append(batchNumExits, tc.exits[i])
|
|
}
|
|
}
|
|
assertExitAPIs(t, batchNumExits, fetchedExits)
|
|
// OnlyPendingWithdraws
|
|
fetchedExits = []testExit{}
|
|
limit = 7
|
|
path = fmt.Sprintf(
|
|
"%s?&onlyPendingWithdraws=%t&limit=%d",
|
|
endpoint, true, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
pendingExits := []testExit{}
|
|
for i := 0; i < len(tc.exits); i++ {
|
|
if tc.exits[i].InstantWithdrawn == nil && tc.exits[i].DelayedWithdrawn == nil {
|
|
pendingExits = append(pendingExits, tc.exits[i])
|
|
}
|
|
}
|
|
assertExitAPIs(t, pendingExits, fetchedExits)
|
|
// Multiple filters
|
|
fetchedExits = []testExit{}
|
|
limit = 1
|
|
path = fmt.Sprintf(
|
|
"%s?batchNum=%d&tokeId=%d&limit=%d",
|
|
endpoint, batchNum, tokenID, limit,
|
|
)
|
|
err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
mixedExits := []testExit{}
|
|
flipedExits := []testExit{}
|
|
for i := 0; i < len(tc.exits); i++ {
|
|
if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
|
|
mixedExits = append(mixedExits, tc.exits[i])
|
|
}
|
|
flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
|
|
}
|
|
assertExitAPIs(t, mixedExits, fetchedExits)
|
|
// All, in reverse order
|
|
fetchedExits = []testExit{}
|
|
limit = 5
|
|
path = fmt.Sprintf("%s?limit=%d", endpoint, limit)
|
|
err = doGoodReqPaginated(path, historydb.OrderDesc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
assertExitAPIs(t, flipedExits, fetchedExits)
|
|
// Empty array
|
|
fetchedExits = []testExit{}
|
|
path = fmt.Sprintf("%s?batchNum=999999", endpoint)
|
|
err = doGoodReqPaginated(path, historydb.OrderDesc, &testExitsResponse{}, appendIter)
|
|
assert.NoError(t, err)
|
|
assertExitAPIs(t, []testExit{}, fetchedExits)
|
|
// 400
|
|
path = fmt.Sprintf(
|
|
"%s?accountIndex=%s&hezEthereumAddress=%s",
|
|
endpoint, idx, account.EthAddr,
|
|
)
|
|
err = doBadReq("GET", path, nil, 400)
|
|
assert.NoError(t, err)
|
|
path = fmt.Sprintf("%s?tokenId=X", endpoint)
|
|
err = doBadReq("GET", path, nil, 400)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestGetExit(t *testing.T) {
|
|
// Get all txs by their ID
|
|
endpoint := apiURL + "exits/"
|
|
fetchedExits := []testExit{}
|
|
for _, exit := range tc.exits {
|
|
fetchedExit := testExit{}
|
|
assert.NoError(
|
|
t, doGoodReq(
|
|
"GET",
|
|
fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
|
|
nil, &fetchedExit,
|
|
),
|
|
)
|
|
fetchedExits = append(fetchedExits, fetchedExit)
|
|
}
|
|
assertExitAPIs(t, tc.exits, fetchedExits)
|
|
// 400
|
|
err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
|
|
assert.NoError(t, err)
|
|
err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
|
|
assert.NoError(t, err)
|
|
// 404
|
|
err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func assertExitAPIs(t *testing.T, expected, actual []testExit) {
|
|
require.Equal(t, len(expected), len(actual))
|
|
for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
|
|
actual[i].ItemID = 0
|
|
actual[i].Token.ItemID = 0
|
|
if expected[i].Token.USDUpdate == nil {
|
|
assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
|
|
} else {
|
|
assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
|
|
expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
|
|
}
|
|
assert.Equal(t, expected[i], actual[i])
|
|
}
|
|
}
|