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) Len() int {
 49	return len(t.Exits)
 50}
 51
 52func genTestExits(
 53	commonExits []common.ExitInfo,
 54	tokens []historydb.TokenWithUSD,
 55	accs []common.Account,
 56	usrIdxs []string,
 57) (usrExits, allExits []testExit) {
 58	allExits = []testExit{}
 59	for _, exit := range commonExits {
 60		token := getTokenByIdx(exit.AccountIdx, tokens, accs)
 61		siblings := []string{}
 62		for i := 0; i < len(exit.MerkleProof.Siblings); i++ {
 63			siblings = append(siblings, exit.MerkleProof.Siblings[i].String())
 64		}
 65		allExits = append(allExits, testExit{
 66			BatchNum:   exit.BatchNum,
 67			AccountIdx: idxToHez(exit.AccountIdx, token.Symbol),
 68			MerkleProof: testCVP{
 69				Root:     exit.MerkleProof.Root.String(),
 70				Siblings: siblings,
 71				OldKey:   exit.MerkleProof.OldKey.String(),
 72				OldValue: exit.MerkleProof.OldValue.String(),
 73				IsOld0:   exit.MerkleProof.IsOld0,
 74				Key:      exit.MerkleProof.Key.String(),
 75				Value:    exit.MerkleProof.Value.String(),
 76				Fnc:      exit.MerkleProof.Fnc,
 77			},
 78			Balance:                exit.Balance.String(),
 79			InstantWithdrawn:       exit.InstantWithdrawn,
 80			DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
 81			DelayedWithdrawn:       exit.DelayedWithdrawn,
 82			Token:                  token,
 83		})
 84	}
 85	usrExits = []testExit{}
 86	for _, exit := range allExits {
 87		for _, idx := range usrIdxs {
 88			if idx == exit.AccountIdx {
 89				usrExits = append(usrExits, exit)
 90				break
 91			}
 92		}
 93	}
 94	return usrExits, allExits
 95}
 96
 97func TestGetExits(t *testing.T) {
 98	endpoint := apiURL + "exits"
 99	fetchedExits := []testExit{}
100	appendIter := func(intr interface{}) {
101		for i := 0; i < len(intr.(*testExitsResponse).Exits); i++ {
102			tmp, err := copystructure.Copy(intr.(*testExitsResponse).Exits[i])
103			if err != nil {
104				panic(err)
105			}
106			fetchedExits = append(fetchedExits, tmp.(testExit))
107		}
108	}
109	// Get all (no filters)
110	limit := 8
111	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
112	err := doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
113	assert.NoError(t, err)
114	assertExitAPIs(t, tc.exits, fetchedExits)
115
116	// Get by ethAddr
117	fetchedExits = []testExit{}
118	limit = 7
119	account := tc.accounts[3]
120	path = fmt.Sprintf(
121		"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
122		endpoint, account.EthAddr, limit,
123	)
124	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
125	assert.NoError(t, err)
126	accountIdx := fetchedExits[0].AccountIdx
127	var hermezEthExits []testExit
128	for i := range tc.usrExits {
129		if tc.usrExits[i].AccountIdx == accountIdx {
130			hermezEthExits = append(hermezEthExits, tc.usrExits[i])
131		}
132	}
133	assertExitAPIs(t, hermezEthExits, fetchedExits)
134	// Get by bjj
135	fetchedExits = []testExit{}
136	limit = 6
137	path = fmt.Sprintf(
138		"%s?BJJ=%s&limit=%d&fromItem=",
139		endpoint, account.PublicKey, limit,
140	)
141	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
142	assert.NoError(t, err)
143	assertExitAPIs(t, hermezEthExits, fetchedExits)
144	// Get by tokenID
145	fetchedExits = []testExit{}
146	limit = 5
147	tokenID := tc.exits[0].Token.TokenID
148	path = fmt.Sprintf(
149		"%s?tokenId=%d&limit=%d&fromItem=",
150		endpoint, tokenID, limit,
151	)
152	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
153	assert.NoError(t, err)
154	tokenIDExits := []testExit{}
155	for i := 0; i < len(tc.exits); i++ {
156		if tc.exits[i].Token.TokenID == tokenID {
157			tokenIDExits = append(tokenIDExits, tc.exits[i])
158		}
159	}
160	assertExitAPIs(t, tokenIDExits, fetchedExits)
161	// idx
162	fetchedExits = []testExit{}
163	limit = 4
164	idx := tc.exits[0].AccountIdx
165	path = fmt.Sprintf(
166		"%s?accountIndex=%s&limit=%d&fromItem=",
167		endpoint, idx, limit,
168	)
169	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
170	assert.NoError(t, err)
171	idxExits := []testExit{}
172	for i := 0; i < len(tc.exits); i++ {
173		if tc.exits[i].AccountIdx[6:] == idx[6:] {
174			idxExits = append(idxExits, tc.exits[i])
175		}
176	}
177	assertExitAPIs(t, idxExits, fetchedExits)
178	// batchNum
179	fetchedExits = []testExit{}
180	limit = 3
181	batchNum := tc.exits[0].BatchNum
182	path = fmt.Sprintf(
183		"%s?batchNum=%d&limit=%d&fromItem=",
184		endpoint, batchNum, limit,
185	)
186	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
187	assert.NoError(t, err)
188	batchNumExits := []testExit{}
189	for i := 0; i < len(tc.exits); i++ {
190		if tc.exits[i].BatchNum == batchNum {
191			batchNumExits = append(batchNumExits, tc.exits[i])
192		}
193	}
194	assertExitAPIs(t, batchNumExits, fetchedExits)
195	// OnlyPendingWithdraws
196	fetchedExits = []testExit{}
197	limit = 7
198	path = fmt.Sprintf(
199		"%s?&onlyPendingWithdraws=%t&limit=%d&fromItem=",
200		endpoint, true, limit,
201	)
202	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
203	assert.NoError(t, err)
204	pendingExits := []testExit{}
205	for i := 0; i < len(tc.exits); i++ {
206		if tc.exits[i].InstantWithdrawn == nil && tc.exits[i].DelayedWithdrawn == nil {
207			pendingExits = append(pendingExits, tc.exits[i])
208		}
209	}
210	assertExitAPIs(t, pendingExits, fetchedExits)
211	// Multiple filters
212	fetchedExits = []testExit{}
213	limit = 1
214	path = fmt.Sprintf(
215		"%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
216		endpoint, batchNum, tokenID, limit,
217	)
218	err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
219	assert.NoError(t, err)
220	mixedExits := []testExit{}
221	flipedExits := []testExit{}
222	for i := 0; i < len(tc.exits); i++ {
223		if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
224			mixedExits = append(mixedExits, tc.exits[i])
225		}
226		flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
227	}
228	assertExitAPIs(t, mixedExits, fetchedExits)
229	// All, in reverse order
230	fetchedExits = []testExit{}
231	limit = 5
232	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
233	err = doGoodReqPaginated(path, historydb.OrderDesc, &testExitsResponse{}, appendIter)
234	assert.NoError(t, err)
235	assertExitAPIs(t, flipedExits, fetchedExits)
236	// 400
237	path = fmt.Sprintf(
238		"%s?accountIndex=%s&hermezEthereumAddress=%s",
239		endpoint, idx, tc.usrAddr,
240	)
241	err = doBadReq("GET", path, nil, 400)
242	assert.NoError(t, err)
243	path = fmt.Sprintf("%s?tokenId=X", endpoint)
244	err = doBadReq("GET", path, nil, 400)
245	assert.NoError(t, err)
246	// 404
247	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
248	err = doBadReq("GET", path, nil, 404)
249	assert.NoError(t, err)
250	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
251	err = doBadReq("GET", path, nil, 404)
252	assert.NoError(t, err)
253}
254
255func TestGetExit(t *testing.T) {
256	// Get all txs by their ID
257	endpoint := apiURL + "exits/"
258	fetchedExits := []testExit{}
259	for _, exit := range tc.exits {
260		fetchedExit := testExit{}
261		assert.NoError(
262			t, doGoodReq(
263				"GET",
264				fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
265				nil, &fetchedExit,
266			),
267		)
268		fetchedExits = append(fetchedExits, fetchedExit)
269	}
270	assertExitAPIs(t, tc.exits, fetchedExits)
271	// 400
272	err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
273	assert.NoError(t, err)
274	err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
275	assert.NoError(t, err)
276	// 404
277	err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
278	assert.NoError(t, err)
279}
280
281func assertExitAPIs(t *testing.T, expected, actual []testExit) {
282	require.Equal(t, len(expected), len(actual))
283	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
284		actual[i].ItemID = 0
285		actual[i].Token.ItemID = 0
286		if expected[i].Token.USDUpdate == nil {
287			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
288		} else {
289			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
290			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
291		}
292		assert.Equal(t, expected[i], actual[i])
293	}
294}