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