You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

309 lines
9.3 KiB

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