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