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