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 // ni, err := api.h.GetNodeInfoAPI()
103 // assert.NoError(t, err)
104 emptySlot := api.getEmptyTestSlot(slotNum, 0, tc.auctionVars)
105 assertSlot(t, emptySlot, fetchedSlot)
106
107 // Invalid slotNum
108 path := endpoint + strconv.Itoa(-2)
109 err := doBadReq("GET", path, nil, 400)
110 assert.NoError(t, err)
111}
112
113func TestGetSlots(t *testing.T) {
114 endpoint := apiURL + "slots"
115 fetchedSlots := []testSlot{}
116 appendIter := func(intr interface{}) {
117 for i := 0; i < len(intr.(*testSlotsResponse).Slots); i++ {
118 tmp, err := copystructure.Copy(intr.(*testSlotsResponse).Slots[i])
119 if err != nil {
120 panic(err)
121 }
122 fetchedSlots = append(fetchedSlots, tmp.(testSlot))
123 }
124 }
125 // All slots with maxSlotNum filter
126 maxSlotNum := tc.slots[len(tc.slots)-1].SlotNum + 5
127 limit := 1
128 path := fmt.Sprintf("%s?maxSlotNum=%d&limit=%d", endpoint, maxSlotNum, limit)
129 err := doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
130 assert.NoError(t, err)
131 allSlots := tc.slots
132 // ni, err := api.h.GetNodeInfoAPI()
133 // assert.NoError(t, err)
134 for i := tc.slots[len(tc.slots)-1].SlotNum; i < maxSlotNum; i++ {
135 emptySlot := api.getEmptyTestSlot(i+1, 0, tc.auctionVars)
136 allSlots = append(allSlots, emptySlot)
137 }
138 assertSlots(t, allSlots, fetchedSlots)
139
140 // All slots with maxSlotNum filter, in reverse order
141 fetchedSlots = []testSlot{}
142 limit = 3
143 path = fmt.Sprintf("%s?maxSlotNum=%d&limit=%d", endpoint, maxSlotNum, limit)
144 err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
145 assert.NoError(t, err)
146
147 flippedAllSlots := []testSlot{}
148 for i := len(allSlots) - 1; i >= 0; i-- {
149 flippedAllSlots = append(flippedAllSlots, allSlots[i])
150 }
151 assertSlots(t, flippedAllSlots, fetchedSlots)
152
153 // maxSlotNum & wonByEthereumAddress
154 fetchedSlots = []testSlot{}
155 limit = 1
156 var bidderAddr ethCommon.Address
157 for i := 0; i < len(tc.slots); i++ {
158 if tc.slots[i].WinnerBid != nil {
159 bidderAddr = tc.slots[i].WinnerBid.Bidder
160 }
161 }
162 path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d", endpoint, maxSlotNum, bidderAddr.String(), limit)
163 err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
164 assert.NoError(t, err)
165 bidderAddressSlots := []testSlot{}
166 for i := 0; i < len(tc.slots); i++ {
167 if tc.slots[i].WinnerBid != nil {
168 if tc.slots[i].WinnerBid.Bidder == bidderAddr {
169 bidderAddressSlots = append(bidderAddressSlots, tc.slots[i])
170 }
171 }
172 }
173 assertSlots(t, bidderAddressSlots, fetchedSlots)
174
175 // maxSlotNum & wonByEthereumAddress, in reverse order
176 fetchedSlots = []testSlot{}
177 limit = 1
178 path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d", endpoint, maxSlotNum, bidderAddr.String(), limit)
179 err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
180 assert.NoError(t, err)
181 flippedBidderAddressSlots := []testSlot{}
182 for i := len(bidderAddressSlots) - 1; i >= 0; i-- {
183 flippedBidderAddressSlots = append(flippedBidderAddressSlots, bidderAddressSlots[i])
184 }
185 assertSlots(t, flippedBidderAddressSlots, fetchedSlots)
186
187 // finishedAuction
188 fetchedSlots = []testSlot{}
189 limit = 15
190 path = fmt.Sprintf("%s?finishedAuction=%t&limit=%d", endpoint, true, limit)
191 err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
192 assert.NoError(t, err)
193
194 currentSlot := api.getCurrentSlot(tc.blocks[len(tc.blocks)-1].Num)
195 finishedAuctionSlots := []testSlot{}
196 for i := 0; i < len(tc.slots); i++ {
197 finishAuction := currentSlot + int64(tc.auctionVars.ClosedAuctionSlots)
198 if tc.slots[i].SlotNum <= finishAuction {
199 finishedAuctionSlots = append(finishedAuctionSlots, tc.slots[i])
200 } else {
201 break
202 }
203 }
204 assertSlots(t, finishedAuctionSlots, fetchedSlots)
205
206 //minSlot + maxSlot
207 limit = 10
208 minSlotNum := tc.slots[3].SlotNum
209 maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum - 1
210 fetchedSlots = []testSlot{}
211 path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d", endpoint, maxSlotNum, minSlotNum, limit)
212 err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
213 assert.NoError(t, err)
214 minMaxBatchNumSlots := []testSlot{}
215 for i := 0; i < len(tc.slots); i++ {
216 if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum {
217 minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i])
218 }
219 }
220 assertSlots(t, minMaxBatchNumSlots, fetchedSlots)
221
222 //minSlot + maxSlot
223 limit = 15
224 minSlotNum = tc.slots[0].SlotNum
225 maxSlotNum = tc.slots[0].SlotNum
226 fetchedSlots = []testSlot{}
227 path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d", endpoint, maxSlotNum, minSlotNum, limit)
228 err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
229 assert.NoError(t, err)
230 minMaxBatchNumSlots = []testSlot{}
231 for i := 0; i < len(tc.slots); i++ {
232 if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum {
233 minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i])
234 }
235 }
236 assertSlots(t, minMaxBatchNumSlots, fetchedSlots)
237
238 // Only empty Slots
239 limit = 2
240 minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1
241 maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5
242 fetchedSlots = []testSlot{}
243 path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d", endpoint, maxSlotNum, minSlotNum, limit)
244 err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter)
245 assert.NoError(t, err)
246 emptySlots := []testSlot{}
247 for i := 0; i < len(allSlots); i++ {
248 if allSlots[i].SlotNum >= minSlotNum && allSlots[i].SlotNum <= maxSlotNum {
249 emptySlots = append(emptySlots, allSlots[i])
250 }
251 }
252 assertSlots(t, emptySlots, fetchedSlots)
253
254 // Only empty Slots, in reverse order
255 limit = 4
256 minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1
257 maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5
258 fetchedSlots = []testSlot{}
259 path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d", endpoint, maxSlotNum, minSlotNum, limit)
260 err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter)
261 assert.NoError(t, err)
262 flippedEmptySlots := []testSlot{}
263 for i := 0; i < len(flippedAllSlots); i++ {
264 if flippedAllSlots[i].SlotNum >= minSlotNum && flippedAllSlots[i].SlotNum <= maxSlotNum {
265 flippedEmptySlots = append(flippedEmptySlots, flippedAllSlots[i])
266 }
267 }
268 assertSlots(t, flippedEmptySlots, fetchedSlots)
269
270 // 400
271 // No filters
272 path = fmt.Sprintf("%s?limit=%d", endpoint, limit)
273 err = doBadReq("GET", path, nil, 400)
274 assert.NoError(t, err)
275 // Invalid maxSlotNum
276 path = fmt.Sprintf("%s?maxSlotNum=%d", endpoint, -2)
277 err = doBadReq("GET", path, nil, 400)
278 assert.NoError(t, err)
279 // Invalid wonByEthereumAddress
280 path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s", endpoint, maxSlotNum, "0xG0000001")
281 err = doBadReq("GET", path, nil, 400)
282 assert.NoError(t, err)
283 // Invalid minSlotNum / maxSlotNum (minSlotNum > maxSlotNum)
284 maxSlotNum = tc.slots[1].SlotNum
285 minSlotNum = tc.slots[4].SlotNum
286 path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d", endpoint, maxSlotNum, minSlotNum, limit)
287 err = doBadReq("GET", path, nil, 400)
288 assert.NoError(t, err)
289 // 404
290 maxSlotNum = tc.slots[1].SlotNum
291 path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d", endpoint, maxSlotNum, tc.coordinators[3].Bidder.String(), limit)
292 err = doBadReq("GET", path, nil, 404)
293 assert.NoError(t, err)
294}
295
296func assertSlots(t *testing.T, expected, actual []testSlot) {
297 assert.Equal(t, len(expected), len(actual))
298 for i := 0; i < len(expected); i++ {
299 assertSlot(t, expected[i], actual[i])
300 }
301}
302
303func assertSlot(t *testing.T, expected, actual testSlot) {
304 if actual.WinnerBid != nil {
305 assert.Equal(t, expected.WinnerBid.Timestamp.Unix(), actual.WinnerBid.Timestamp.Unix())
306 expected.WinnerBid.Timestamp = actual.WinnerBid.Timestamp
307 actual.WinnerBid.ItemID = expected.WinnerBid.ItemID
308 }
309 actual.ItemID = expected.ItemID
310 assert.Equal(t, expected, actual)
311}