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