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