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	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}