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