arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/exits_test.go
api/exits_test.goRaw
  1package api
  2
  3import (
  4	"fmt"
  5	"testing"
  6
  7	"github.com/hermeznetwork/hermez-node/common"
  8	"github.com/hermeznetwork/hermez-node/db/historydb"
  9	"github.com/mitchellh/copystructure"
 10	"github.com/stretchr/testify/assert"
 11	"github.com/stretchr/testify/require"
 12)
 13
 14type testCVP struct {
 15	Root     string
 16	Siblings []string
 17	OldKey   string
 18	OldValue string
 19	IsOld0   bool
 20	Key      string
 21	Value    string
 22	Fnc      int
 23}
 24
 25type testExit struct {
 26	ItemID                 uint64                 `json:"itemId"`
 27	BatchNum               common.BatchNum        `json:"batchNum"`
 28	AccountIdx             string                 `json:"accountIndex"`
 29	MerkleProof            testCVP                `json:"merkleProof"`
 30	Balance                string                 `json:"balance"`
 31	InstantWithdrawn       *int64                 `json:"instantWithdrawn"`
 32	DelayedWithdrawRequest *int64                 `json:"delayedWithdrawRequest"`
 33	DelayedWithdrawn       *int64                 `json:"delayedWithdrawn"`
 34	Token                  historydb.TokenWithUSD `json:"token"`
 35}
 36
 37type testExitsResponse struct {
 38	Exits        []testExit `json:"exits"`
 39	PendingItems uint64     `json:"pendingItems"`
 40}
 41
 42func (t testExitsResponse) GetPending() (pendingItems, lastItemID uint64) {
 43	pendingItems = t.PendingItems
 44	lastItemID = t.Exits[len(t.Exits)-1].ItemID
 45	return pendingItems, lastItemID
 46}
 47
 48func (t testExitsResponse) New() Pendinger { return &testExitsResponse{} }
 49
 50func (t *testExitsResponse) Len() int {
 51	return len(t.Exits)
 52}
 53
 54func genTestExits(
 55	commonExits []common.ExitInfo,
 56	tokens []historydb.TokenWithUSD,
 57	accs []common.Account,
 58) []testExit {
 59	allExits := []testExit{}
 60	for _, exit := range commonExits {
 61		token := getTokenByIdx(exit.AccountIdx, tokens, accs)
 62		siblings := []string{}
 63		for i := 0; i < len(exit.MerkleProof.Siblings); i++ {
 64			siblings = append(siblings, exit.MerkleProof.Siblings[i].String())
 65		}
 66		allExits = append(allExits, testExit{
 67			BatchNum:   exit.BatchNum,
 68			AccountIdx: idxToHez(exit.AccountIdx, token.Symbol),
 69			MerkleProof: testCVP{
 70				Root:     exit.MerkleProof.Root.String(),
 71				Siblings: siblings,
 72				OldKey:   exit.MerkleProof.OldKey.String(),
 73				OldValue: exit.MerkleProof.OldValue.String(),
 74				IsOld0:   exit.MerkleProof.IsOld0,
 75				Key:      exit.MerkleProof.Key.String(),
 76				Value:    exit.MerkleProof.Value.String(),
 77				Fnc:      exit.MerkleProof.Fnc,
 78			},
 79			Balance:                exit.Balance.String(),
 80			InstantWithdrawn:       exit.InstantWithdrawn,
 81			DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
 82			DelayedWithdrawn:       exit.DelayedWithdrawn,
 83			Token:                  token,
 84		})
 85	}
 86	return allExits
 87}
 88
 89func TestGetExits(t *testing.T) {
 90	endpoint := apiURL + "exits"
 91	fetchedExits := []testExit{}
 92	appendIter := func(intr interface{}) {
 93		for i := 0; i < len(intr.(*testExitsResponse).Exits); i++ {
 94			tmp, err := copystructure.Copy(intr.(*testExitsResponse).Exits[i])
 95			if err != nil {
 96				panic(err)
 97			}
 98			fetchedExits = append(fetchedExits, tmp.(testExit))
 99		}
100	}
101	// Get all (no filters)
102	limit := 8
103	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
104	err := doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
105	assert.NoError(t, err)
106	assertExitAPIs(t, tc.exits, fetchedExits)
107
108	// Get by ethAddr
109	fetchedExits = []testExit{}
110	limit = 7
111	var account testAccount
112	for _, tx := range tc.txs {
113		found := false
114		if tx.Type == common.TxTypeExit {
115			for i := 0; i < len(tc.accounts); i++ {
116				if tx.FromIdx != nil && string(tc.accounts[i].Idx) == *tx.FromIdx {
117					account = tc.accounts[i]
118					break
119				}
120			}
121		}
122		if found {
123			break
124		}
125	}
126	path = fmt.Sprintf(
127		"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
128		endpoint, account.EthAddr, limit,
129	)
130	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
131	assert.NoError(t, err)
132	var accountExits []testExit
133	for i := range tc.exits {
134		for _, acc := range tc.accounts {
135			if string(acc.Idx) == tc.exits[i].AccountIdx {
136				if acc.EthAddr == account.EthAddr {
137					accountExits = append(accountExits, tc.exits[i])
138				}
139			}
140		}
141	}
142	assertExitAPIs(t, accountExits, fetchedExits)
143	// Get by bjj
144	fetchedExits = []testExit{}
145	limit = 6
146	path = fmt.Sprintf(
147		"%s?BJJ=%s&limit=%d&fromItem=",
148		endpoint, account.PublicKey, limit,
149	)
150	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
151	assert.NoError(t, err)
152	assertExitAPIs(t, accountExits, fetchedExits)
153	// Get by tokenID
154	fetchedExits = []testExit{}
155	limit = 5
156	tokenID := tc.exits[0].Token.TokenID
157	path = fmt.Sprintf(
158		"%s?tokenId=%d&limit=%d&fromItem=",
159		endpoint, tokenID, limit,
160	)
161	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
162	assert.NoError(t, err)
163	tokenIDExits := []testExit{}
164	for i := 0; i < len(tc.exits); i++ {
165		if tc.exits[i].Token.TokenID == tokenID {
166			tokenIDExits = append(tokenIDExits, tc.exits[i])
167		}
168	}
169	assertExitAPIs(t, tokenIDExits, fetchedExits)
170	// idx
171	fetchedExits = []testExit{}
172	limit = 4
173	idx := tc.exits[0].AccountIdx
174	path = fmt.Sprintf(
175		"%s?accountIndex=%s&limit=%d&fromItem=",
176		endpoint, idx, limit,
177	)
178	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
179	assert.NoError(t, err)
180	idxExits := []testExit{}
181	for i := 0; i < len(tc.exits); i++ {
182		if tc.exits[i].AccountIdx[6:] == idx[6:] {
183			idxExits = append(idxExits, tc.exits[i])
184		}
185	}
186	assertExitAPIs(t, idxExits, fetchedExits)
187	// batchNum
188	fetchedExits = []testExit{}
189	limit = 3
190	batchNum := tc.exits[0].BatchNum
191	path = fmt.Sprintf(
192		"%s?batchNum=%d&limit=%d&fromItem=",
193		endpoint, batchNum, limit,
194	)
195	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
196	assert.NoError(t, err)
197	batchNumExits := []testExit{}
198	for i := 0; i < len(tc.exits); i++ {
199		if tc.exits[i].BatchNum == batchNum {
200			batchNumExits = append(batchNumExits, tc.exits[i])
201		}
202	}
203	assertExitAPIs(t, batchNumExits, fetchedExits)
204	// OnlyPendingWithdraws
205	fetchedExits = []testExit{}
206	limit = 7
207	path = fmt.Sprintf(
208		"%s?&onlyPendingWithdraws=%t&limit=%d&fromItem=",
209		endpoint, true, limit,
210	)
211	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
212	assert.NoError(t, err)
213	pendingExits := []testExit{}
214	for i := 0; i < len(tc.exits); i++ {
215		if tc.exits[i].InstantWithdrawn == nil && tc.exits[i].DelayedWithdrawn == nil {
216			pendingExits = append(pendingExits, tc.exits[i])
217		}
218	}
219	assertExitAPIs(t, pendingExits, fetchedExits)
220	// Multiple filters
221	fetchedExits = []testExit{}
222	limit = 1
223	path = fmt.Sprintf(
224		"%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
225		endpoint, batchNum, tokenID, limit,
226	)
227	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
228	assert.NoError(t, err)
229	mixedExits := []testExit{}
230	flipedExits := []testExit{}
231	for i := 0; i < len(tc.exits); i++ {
232		if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
233			mixedExits = append(mixedExits, tc.exits[i])
234		}
235		flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
236	}
237	assertExitAPIs(t, mixedExits, fetchedExits)
238	// All, in reverse order
239	fetchedExits = []testExit{}
240	limit = 5
241	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
242	err = doGoodReqPaginated(path, historydb.OrderDesc, &testExitsResponse{}, appendIter)
243	assert.NoError(t, err)
244	assertExitAPIs(t, flipedExits, fetchedExits)
245	// 400
246	path = fmt.Sprintf(
247		"%s?accountIndex=%s&hermezEthereumAddress=%s",
248		endpoint, idx, account.EthAddr,
249	)
250	err = doBadReq("GET", path, nil, 400)
251	assert.NoError(t, err)
252	path = fmt.Sprintf("%s?tokenId=X", endpoint)
253	err = doBadReq("GET", path, nil, 400)
254	assert.NoError(t, err)
255	// 404
256	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
257	err = doBadReq("GET", path, nil, 404)
258	assert.NoError(t, err)
259	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
260	err = doBadReq("GET", path, nil, 404)
261	assert.NoError(t, err)
262}
263
264func TestGetExit(t *testing.T) {
265	// Get all txs by their ID
266	endpoint := apiURL + "exits/"
267	fetchedExits := []testExit{}
268	for _, exit := range tc.exits {
269		fetchedExit := testExit{}
270		assert.NoError(
271			t, doGoodReq(
272				"GET",
273				fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
274				nil, &fetchedExit,
275			),
276		)
277		fetchedExits = append(fetchedExits, fetchedExit)
278	}
279	assertExitAPIs(t, tc.exits, fetchedExits)
280	// 400
281	err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
282	assert.NoError(t, err)
283	err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
284	assert.NoError(t, err)
285	// 404
286	err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
287	assert.NoError(t, err)
288}
289
290func assertExitAPIs(t *testing.T, expected, actual []testExit) {
291	require.Equal(t, len(expected), len(actual))
292	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
293		actual[i].ItemID = 0
294		actual[i].Token.ItemID = 0
295		if expected[i].Token.USDUpdate == nil {
296			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
297		} else {
298			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
299			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
300		}
301		assert.Equal(t, expected[i], actual[i])
302	}
303}