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.

305 lines
10 KiB

  1. package api
  2. import (
  3. "fmt"
  4. "strconv"
  5. "testing"
  6. "github.com/hermeznetwork/hermez-node/common"
  7. "github.com/hermeznetwork/hermez-node/db"
  8. "github.com/hermeznetwork/hermez-node/db/historydb"
  9. "github.com/mitchellh/copystructure"
  10. "github.com/stretchr/testify/assert"
  11. )
  12. type testSlot struct {
  13. ItemID int `json:"itemId"`
  14. SlotNum int64 `json:"slotNum"`
  15. FirstBlock int64 `json:"firstBlock"`
  16. LastBlock int64 `json:"lastBlock"`
  17. OpenAuction bool `json:"openAuction"`
  18. WinnerBid *testBid `json:"winnerBid"`
  19. }
  20. type testSlotsResponse struct {
  21. Slots []testSlot `json:"slots"`
  22. Pagination *db.Pagination `json:"pagination"`
  23. }
  24. func (t testSlotsResponse) GetPagination() *db.Pagination {
  25. if t.Slots[0].ItemID < t.Slots[len(t.Slots)-1].ItemID {
  26. t.Pagination.FirstReturnedItem = int(t.Slots[0].ItemID)
  27. t.Pagination.LastReturnedItem = int(t.Slots[len(t.Slots)-1].ItemID)
  28. } else {
  29. t.Pagination.LastReturnedItem = int(t.Slots[0].ItemID)
  30. t.Pagination.FirstReturnedItem = int(t.Slots[len(t.Slots)-1].ItemID)
  31. }
  32. return t.Pagination
  33. }
  34. func (t testSlotsResponse) Len() int {
  35. return len(t.Slots)
  36. }
  37. func genTestSlots(nSlots int, lastBlockNum int64, bids []testBid, auctionVars common.AuctionVariables) []testSlot {
  38. tSlots := []testSlot{}
  39. bestBids := make(map[int64]testBid)
  40. // It's assumed that bids for each slot will be received in increasing order
  41. for i := range bids {
  42. bestBids[bids[i].SlotNum] = bids[i]
  43. }
  44. for i := int64(0); i < int64(nSlots); i++ {
  45. bid, ok := bestBids[i]
  46. firstBlock, lastBlock := getFirstLastBlock(int64(i))
  47. tSlot := testSlot{
  48. SlotNum: int64(i),
  49. FirstBlock: firstBlock,
  50. LastBlock: lastBlock,
  51. OpenAuction: isOpenAuction(lastBlockNum, int64(i), auctionVars),
  52. }
  53. if ok {
  54. tSlot.WinnerBid = &bid
  55. }
  56. tSlots = append(tSlots, tSlot)
  57. }
  58. return tSlots
  59. }
  60. func getEmptyTestSlot(slotNum int64) testSlot {
  61. firstBlock, lastBlock := getFirstLastBlock(slotNum)
  62. slot := testSlot{
  63. SlotNum: slotNum,
  64. FirstBlock: firstBlock,
  65. LastBlock: lastBlock,
  66. OpenAuction: false,
  67. WinnerBid: nil,
  68. }
  69. return slot
  70. }
  71. func TestGetSlot(t *testing.T) {
  72. endpoint := apiURL + "slots/"
  73. for _, slot := range tc.slots {
  74. fetchedSlot := testSlot{}
  75. assert.NoError(
  76. t, doGoodReq(
  77. "GET",
  78. endpoint+strconv.Itoa(int(slot.SlotNum)),
  79. nil, &fetchedSlot,
  80. ),
  81. )
  82. assertSlot(t, slot, fetchedSlot)
  83. }
  84. // Slot with WinnerBid == nil
  85. slotNum := int64(15)
  86. fetchedSlot := testSlot{}
  87. assert.NoError(
  88. t, doGoodReq(
  89. "GET",
  90. endpoint+strconv.Itoa(int(slotNum)),
  91. nil, &fetchedSlot,
  92. ),
  93. )
  94. emptySlot := getEmptyTestSlot(slotNum)
  95. assertSlot(t, emptySlot, fetchedSlot)
  96. // Invalid slotNum
  97. path := endpoint + strconv.Itoa(-2)
  98. err := doBadReq("GET", path, nil, 400)
  99. assert.NoError(t, err)
  100. }
  101. func TestGetSlots(t *testing.T) {
  102. endpoint := apiURL + "slots"
  103. fetchedSlots := []testSlot{}
  104. appendIter := func(intr interface{}) {
  105. for i := 0; i < len(intr.(*testSlotsResponse).Slots); i++ {
  106. tmp, err := copystructure.Copy(intr.(*testSlotsResponse).Slots[i])
  107. if err != nil {
  108. panic(err)
  109. }
  110. fetchedSlots = append(fetchedSlots, tmp.(testSlot))
  111. }
  112. }
  113. // All slots with maxSlotNum filter
  114. maxSlotNum := tc.slots[len(tc.slots)-1].SlotNum + 5
  115. limit := 1
  116. path := fmt.Sprintf("%s?maxSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, limit)
  117. err := doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  118. assert.NoError(t, err)
  119. allSlots := tc.slots
  120. for i := tc.slots[len(tc.slots)-1].SlotNum; i < maxSlotNum; i++ {
  121. emptySlot := getEmptyTestSlot(i + 1)
  122. allSlots = append(allSlots, emptySlot)
  123. }
  124. assertSlots(t, allSlots, fetchedSlots)
  125. // All slots with maxSlotNum filter, in reverse order
  126. fetchedSlots = []testSlot{}
  127. limit = 3
  128. path = fmt.Sprintf("%s?maxSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, limit)
  129. err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
  130. assert.NoError(t, err)
  131. flippedAllSlots := []testSlot{}
  132. for i := len(allSlots) - 1; i >= 0; i-- {
  133. flippedAllSlots = append(flippedAllSlots, allSlots[i])
  134. }
  135. assertSlots(t, flippedAllSlots, fetchedSlots)
  136. // maxSlotNum & wonByEthereumAddress
  137. fetchedSlots = []testSlot{}
  138. limit = 1
  139. bidderAddr := tc.coordinators[2].Bidder
  140. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, bidderAddr.String(), limit)
  141. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  142. assert.NoError(t, err)
  143. bidderAddressSlots := []testSlot{}
  144. for i := 0; i < len(tc.slots); i++ {
  145. if tc.slots[i].WinnerBid != nil {
  146. if tc.slots[i].WinnerBid.Bidder == bidderAddr {
  147. bidderAddressSlots = append(bidderAddressSlots, tc.slots[i])
  148. }
  149. }
  150. }
  151. assertSlots(t, bidderAddressSlots, fetchedSlots)
  152. // maxSlotNum & wonByEthereumAddress, in reverse order
  153. fetchedSlots = []testSlot{}
  154. limit = 1
  155. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, bidderAddr.String(), limit)
  156. err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
  157. assert.NoError(t, err)
  158. flippedBidderAddressSlots := []testSlot{}
  159. for i := len(bidderAddressSlots) - 1; i >= 0; i-- {
  160. flippedBidderAddressSlots = append(flippedBidderAddressSlots, bidderAddressSlots[i])
  161. }
  162. assertSlots(t, flippedBidderAddressSlots, fetchedSlots)
  163. // finishedAuction
  164. fetchedSlots = []testSlot{}
  165. limit = 15
  166. path = fmt.Sprintf("%s?finishedAuction=%t&limit=%d&fromItem=", endpoint, true, limit)
  167. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  168. assert.NoError(t, err)
  169. currentSlot := getCurrentSlot(tc.blocks[len(tc.blocks)-1].EthBlockNum)
  170. finishedAuctionSlots := []testSlot{}
  171. for i := 0; i < len(tc.slots); i++ {
  172. finishAuction := currentSlot + int64(tc.auctionVars.ClosedAuctionSlots)
  173. if tc.slots[i].SlotNum <= finishAuction {
  174. finishedAuctionSlots = append(finishedAuctionSlots, tc.slots[i])
  175. } else {
  176. break
  177. }
  178. }
  179. assertSlots(t, finishedAuctionSlots, fetchedSlots)
  180. //minSlot + maxSlot
  181. limit = 10
  182. minSlotNum := tc.slots[3].SlotNum
  183. maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum - 1
  184. fetchedSlots = []testSlot{}
  185. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  186. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  187. assert.NoError(t, err)
  188. minMaxBatchNumSlots := []testSlot{}
  189. for i := 0; i < len(tc.slots); i++ {
  190. if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum {
  191. minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i])
  192. }
  193. }
  194. assertSlots(t, minMaxBatchNumSlots, fetchedSlots)
  195. //minSlot + maxSlot
  196. limit = 15
  197. minSlotNum = tc.slots[0].SlotNum
  198. maxSlotNum = tc.slots[0].SlotNum
  199. fetchedSlots = []testSlot{}
  200. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  201. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  202. assert.NoError(t, err)
  203. minMaxBatchNumSlots = []testSlot{}
  204. for i := 0; i < len(tc.slots); i++ {
  205. if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum {
  206. minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i])
  207. }
  208. }
  209. assertSlots(t, minMaxBatchNumSlots, fetchedSlots)
  210. // Only empty Slots
  211. limit = 2
  212. minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1
  213. maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5
  214. fetchedSlots = []testSlot{}
  215. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  216. err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
  217. assert.NoError(t, err)
  218. emptySlots := []testSlot{}
  219. for i := 0; i < len(allSlots); i++ {
  220. if allSlots[i].SlotNum >= minSlotNum && allSlots[i].SlotNum <= maxSlotNum {
  221. emptySlots = append(emptySlots, allSlots[i])
  222. }
  223. }
  224. assertSlots(t, emptySlots, fetchedSlots)
  225. // Only empty Slots, in reverse order
  226. limit = 4
  227. minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1
  228. maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5
  229. fetchedSlots = []testSlot{}
  230. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  231. err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
  232. assert.NoError(t, err)
  233. flippedEmptySlots := []testSlot{}
  234. for i := 0; i < len(flippedAllSlots); i++ {
  235. if flippedAllSlots[i].SlotNum >= minSlotNum && flippedAllSlots[i].SlotNum <= maxSlotNum {
  236. flippedEmptySlots = append(flippedEmptySlots, flippedAllSlots[i])
  237. }
  238. }
  239. assertSlots(t, flippedEmptySlots, fetchedSlots)
  240. // 400
  241. // No filters
  242. path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
  243. err = doBadReq("GET", path, nil, 400)
  244. assert.NoError(t, err)
  245. // Invalid maxSlotNum
  246. path = fmt.Sprintf("%s?maxSlotNum=%d", endpoint, -2)
  247. err = doBadReq("GET", path, nil, 400)
  248. assert.NoError(t, err)
  249. // Invalid wonByEthereumAddress
  250. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s", endpoint, maxSlotNum, "0xG0000001")
  251. err = doBadReq("GET", path, nil, 400)
  252. assert.NoError(t, err)
  253. // Invalid minSlotNum / maxSlotNum (minSlotNum > maxSlotNum)
  254. maxSlotNum = tc.slots[1].SlotNum
  255. minSlotNum = tc.slots[4].SlotNum
  256. path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit)
  257. err = doBadReq("GET", path, nil, 400)
  258. assert.NoError(t, err)
  259. // 404
  260. maxSlotNum = tc.slots[1].SlotNum
  261. path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, tc.coordinators[3].Bidder.String(), limit)
  262. err = doBadReq("GET", path, nil, 404)
  263. assert.NoError(t, err)
  264. }
  265. func assertSlots(t *testing.T, expected, actual []testSlot) {
  266. assert.Equal(t, len(expected), len(actual))
  267. for i := 0; i < len(expected); i++ {
  268. assertSlot(t, expected[i], actual[i])
  269. }
  270. }
  271. func assertSlot(t *testing.T, expected, actual testSlot) {
  272. if actual.WinnerBid != nil {
  273. assert.Equal(t, expected.WinnerBid.Timestamp.Unix(), actual.WinnerBid.Timestamp.Unix())
  274. expected.WinnerBid.Timestamp = actual.WinnerBid.Timestamp
  275. actual.WinnerBid.ItemID = expected.WinnerBid.ItemID
  276. }
  277. actual.ItemID = expected.ItemID
  278. assert.Equal(t, expected, actual)
  279. }