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 // Multiple filters
194 fetchedExits = []testExit{}
195 limit = 1
196 path = fmt.Sprintf(
197 "%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
198 endpoint, batchNum, tokenID, limit,
199 )
200 err = doGoodReqPaginated(path, historydb.OrderAsc, &testExitsResponse{}, appendIter)
201 assert.NoError(t, err)
202 mixedExits := []testExit{}
203 flipedExits := []testExit{}
204 for i := 0; i < len(tc.exits); i++ {
205 if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
206 mixedExits = append(mixedExits, tc.exits[i])
207 }
208 flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
209 }
210 assertExitAPIs(t, mixedExits, fetchedExits)
211 // All, in reverse order
212 fetchedExits = []testExit{}
213 limit = 5
214 path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
215 err = doGoodReqPaginated(path, historydb.OrderDesc, &testExitsResponse{}, appendIter)
216 assert.NoError(t, err)
217 assertExitAPIs(t, flipedExits, fetchedExits)
218 // 400
219 path = fmt.Sprintf(
220 "%s?accountIndex=%s&hermezEthereumAddress=%s",
221 endpoint, idx, tc.usrAddr,
222 )
223 err = doBadReq("GET", path, nil, 400)
224 assert.NoError(t, err)
225 path = fmt.Sprintf("%s?tokenId=X", endpoint)
226 err = doBadReq("GET", path, nil, 400)
227 assert.NoError(t, err)
228 // 404
229 path = fmt.Sprintf("%s?batchNum=999999", endpoint)
230 err = doBadReq("GET", path, nil, 404)
231 assert.NoError(t, err)
232 path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
233 err = doBadReq("GET", path, nil, 404)
234 assert.NoError(t, err)
235}
236
237func TestGetExit(t *testing.T) {
238 // Get all txs by their ID
239 endpoint := apiURL + "exits/"
240 fetchedExits := []testExit{}
241 for _, exit := range tc.exits {
242 fetchedExit := testExit{}
243 assert.NoError(
244 t, doGoodReq(
245 "GET",
246 fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
247 nil, &fetchedExit,
248 ),
249 )
250 fetchedExits = append(fetchedExits, fetchedExit)
251 }
252 assertExitAPIs(t, tc.exits, fetchedExits)
253 // 400
254 err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
255 assert.NoError(t, err)
256 err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
257 assert.NoError(t, err)
258 // 404
259 err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
260 assert.NoError(t, err)
261}
262
263func assertExitAPIs(t *testing.T, expected, actual []testExit) {
264 require.Equal(t, len(expected), len(actual))
265 for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
266 actual[i].ItemID = 0
267 actual[i].Token.ItemID = 0
268 if expected[i].Token.USDUpdate == nil {
269 assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
270 } else {
271 assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
272 expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
273 }
274 assert.Equal(t, expected[i], actual[i])
275 }
276}