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 int `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}