arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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&fromItem=", 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}