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