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.

311 lines
9.3 KiB

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