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