API add slots endpointsfeature/sql-semaphore1
@ -0,0 +1,346 @@ |
|||
package api |
|||
|
|||
import ( |
|||
"database/sql" |
|||
"errors" |
|||
"net/http" |
|||
|
|||
"github.com/gin-gonic/gin" |
|||
"github.com/hermeznetwork/hermez-node/common" |
|||
"github.com/hermeznetwork/hermez-node/db" |
|||
"github.com/hermeznetwork/hermez-node/db/historydb" |
|||
) |
|||
|
|||
// SlotAPI is a repesentation of a slot information
|
|||
type SlotAPI struct { |
|||
ItemID int `json:"itemId"` |
|||
SlotNum int64 `json:"slotNum"` |
|||
FirstBlock int64 `json:"firstBlock"` |
|||
LastBlock int64 `json:"lastBlock"` |
|||
OpenAuction bool `json:"openAuction"` |
|||
WinnerBid *historydb.BidAPI `json:"winnerBid"` |
|||
TotalItems int `json:"-"` |
|||
FirstItem int `json:"-"` |
|||
LastItem int `json:"-"` |
|||
} |
|||
|
|||
func getFirstLastBlock(slotNum int64) (int64, int64) { |
|||
genesisBlock := cg.AuctionConstants.GenesisBlockNum |
|||
blocksPerSlot := int64(cg.AuctionConstants.BlocksPerSlot) |
|||
firstBlock := slotNum*blocksPerSlot + genesisBlock |
|||
lastBlock := (slotNum+1)*blocksPerSlot + genesisBlock - 1 |
|||
return firstBlock, lastBlock |
|||
} |
|||
|
|||
func getCurrentSlot(currentBlock int64) int64 { |
|||
genesisBlock := cg.AuctionConstants.GenesisBlockNum |
|||
blocksPerSlot := int64(cg.AuctionConstants.BlocksPerSlot) |
|||
currentSlot := (currentBlock - genesisBlock) / blocksPerSlot |
|||
return currentSlot |
|||
} |
|||
|
|||
func isOpenAuction(currentBlock, slotNum int64, auctionVars common.AuctionVariables) bool { |
|||
currentSlot := getCurrentSlot(currentBlock) |
|||
closedAuctionSlots := currentSlot + int64(auctionVars.ClosedAuctionSlots) |
|||
openAuctionSlots := int64(auctionVars.OpenAuctionSlots) |
|||
if slotNum > closedAuctionSlots && slotNum <= (closedAuctionSlots+openAuctionSlots) { |
|||
return true |
|||
} |
|||
return false |
|||
} |
|||
|
|||
func getPagination(totalItems int, minSlotNum, maxSlotNum *int64) *db.Pagination { |
|||
// itemID is slotNum
|
|||
firstItem := *minSlotNum |
|||
lastItem := *maxSlotNum |
|||
pagination := &db.Pagination{ |
|||
TotalItems: int(totalItems), |
|||
FirstItem: int(firstItem), |
|||
LastItem: int(lastItem), |
|||
} |
|||
return pagination |
|||
} |
|||
|
|||
func newSlotAPI(slotNum, currentBlockNum int64, bid *historydb.BidAPI, auctionVars *common.AuctionVariables) SlotAPI { |
|||
firstBlock, lastBlock := getFirstLastBlock(slotNum) |
|||
openAuction := isOpenAuction(currentBlockNum, slotNum, *auctionVars) |
|||
slot := SlotAPI{ |
|||
ItemID: int(slotNum), |
|||
SlotNum: slotNum, |
|||
FirstBlock: firstBlock, |
|||
LastBlock: lastBlock, |
|||
OpenAuction: openAuction, |
|||
WinnerBid: bid, |
|||
} |
|||
return slot |
|||
} |
|||
|
|||
func newSlotsAPIFromWinnerBids(fromItem *uint, order string, bids []historydb.BidAPI, currentBlockNum int64, auctionVars *common.AuctionVariables) (slots []SlotAPI) { |
|||
for i := range bids { |
|||
slotNum := bids[i].SlotNum |
|||
slot := newSlotAPI(slotNum, currentBlockNum, &bids[i], auctionVars) |
|||
if order == historydb.OrderAsc { |
|||
if slot.ItemID >= int(*fromItem) { |
|||
slots = append(slots, slot) |
|||
} |
|||
} else { |
|||
if slot.ItemID <= int(*fromItem) { |
|||
slots = append(slots, slot) |
|||
} |
|||
} |
|||
} |
|||
return slots |
|||
} |
|||
|
|||
func addEmptySlot(slots []SlotAPI, slotNum int64, currentBlockNum int64, auctionVars *common.AuctionVariables, fromItem *uint, order string) ([]SlotAPI, error) { |
|||
emptySlot := newSlotAPI(slotNum, currentBlockNum, nil, auctionVars) |
|||
if order == historydb.OrderAsc { |
|||
if emptySlot.ItemID >= int(*fromItem) { |
|||
slots = append(slots, emptySlot) |
|||
} |
|||
} else { |
|||
if emptySlot.ItemID <= int(*fromItem) { |
|||
slots = append([]SlotAPI{emptySlot}, slots...) |
|||
} |
|||
} |
|||
return slots, nil |
|||
} |
|||
|
|||
func getSlot(c *gin.Context) { |
|||
slotNumUint, err := parseParamUint("slotNum", nil, 0, maxUint32, c) |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
currentBlock, err := h.GetLastBlock() |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
auctionVars, err := h.GetAuctionVars() |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
|
|||
slotNum := int64(*slotNumUint) |
|||
bid, err := h.GetBestBidAPI(&slotNum) |
|||
if err != nil && err != sql.ErrNoRows { |
|||
retSQLErr(err, c) |
|||
return |
|||
} |
|||
|
|||
var slot SlotAPI |
|||
if err == sql.ErrNoRows { |
|||
slot = newSlotAPI(slotNum, currentBlock.EthBlockNum, nil, auctionVars) |
|||
} else { |
|||
slot = newSlotAPI(bid.SlotNum, currentBlock.EthBlockNum, &bid, auctionVars) |
|||
} |
|||
|
|||
// JSON response
|
|||
c.JSON(http.StatusOK, slot) |
|||
} |
|||
|
|||
func getLimits(minSlotNum, maxSlotNum *int64, fromItem, limit *uint, order string) (int64, int64) { |
|||
var minLim, maxLim int64 |
|||
if fromItem != nil { |
|||
if order == historydb.OrderAsc { |
|||
if int64(*fromItem) > *minSlotNum { |
|||
minLim = int64(*fromItem) |
|||
} else { |
|||
minLim = *minSlotNum |
|||
} |
|||
if (minLim + int64(*limit-1)) < *maxSlotNum { |
|||
maxLim = minLim + int64(*limit-1) |
|||
} else { |
|||
maxLim = *maxSlotNum |
|||
} |
|||
} else { |
|||
if int64(*fromItem) < *maxSlotNum { |
|||
maxLim = int64(*fromItem) |
|||
} else { |
|||
maxLim = *maxSlotNum |
|||
} |
|||
if (maxLim - int64(*limit-1)) < *minSlotNum { |
|||
minLim = *minSlotNum |
|||
} else { |
|||
minLim = maxLim - int64(*limit-1) |
|||
} |
|||
} |
|||
} |
|||
return minLim, maxLim |
|||
} |
|||
|
|||
func getLimitsWithAddr(minSlotNum, maxSlotNum *int64, fromItem, limit *uint, order string) (int64, int64) { |
|||
var minLim, maxLim int64 |
|||
if fromItem != nil { |
|||
if order == historydb.OrderAsc { |
|||
maxLim = *maxSlotNum |
|||
if int64(*fromItem) > *minSlotNum { |
|||
minLim = int64(*fromItem) |
|||
} else { |
|||
minLim = *minSlotNum |
|||
} |
|||
} else { |
|||
minLim = *minSlotNum |
|||
if int64(*fromItem) < *maxSlotNum { |
|||
maxLim = int64(*fromItem) |
|||
} else { |
|||
maxLim = *maxSlotNum |
|||
} |
|||
} |
|||
} |
|||
return minLim, maxLim |
|||
} |
|||
|
|||
func getSlots(c *gin.Context) { |
|||
var slots []SlotAPI |
|||
minSlotNumDflt := int64(0) |
|||
|
|||
// Get filters
|
|||
minSlotNum, maxSlotNum, wonByEthereumAddress, finishedAuction, err := parseSlotFilters(c) |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
|
|||
// Pagination
|
|||
fromItem, order, limit, err := parsePagination(c) |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
|
|||
currentBlock, err := h.GetLastBlock() |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
auctionVars, err := h.GetAuctionVars() |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
|
|||
// Check filters
|
|||
if maxSlotNum == nil && finishedAuction == nil { |
|||
retBadReq(errors.New("It is necessary to add maxSlotNum filter"), c) |
|||
return |
|||
} else if finishedAuction != nil { |
|||
if maxSlotNum == nil && !*finishedAuction { |
|||
retBadReq(errors.New("It is necessary to add maxSlotNum filter"), c) |
|||
return |
|||
} else if *finishedAuction { |
|||
currentBlock, err := h.GetLastBlock() |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
currentSlot := getCurrentSlot(currentBlock.EthBlockNum) |
|||
auctionVars, err := h.GetAuctionVars() |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
closedAuctionSlots := currentSlot + int64(auctionVars.ClosedAuctionSlots) |
|||
if maxSlotNum == nil { |
|||
maxSlotNum = &closedAuctionSlots |
|||
} else if closedAuctionSlots < *maxSlotNum { |
|||
maxSlotNum = &closedAuctionSlots |
|||
} |
|||
} |
|||
} else if maxSlotNum != nil && minSlotNum != nil { |
|||
if *minSlotNum > *maxSlotNum { |
|||
retBadReq(errors.New("It is necessary to add valid filter (minSlotNum <= maxSlotNum)"), c) |
|||
return |
|||
} |
|||
} |
|||
if minSlotNum == nil { |
|||
minSlotNum = &minSlotNumDflt |
|||
} |
|||
|
|||
// Get bids and pagination according to filters
|
|||
var slotMinLim, slotMaxLim int64 |
|||
var bids []historydb.BidAPI |
|||
var pag *db.Pagination |
|||
totalItems := 0 |
|||
if wonByEthereumAddress == nil { |
|||
slotMinLim, slotMaxLim = getLimits(minSlotNum, maxSlotNum, fromItem, limit, order) |
|||
// Get best bids in range maxSlotNum - minSlotNum
|
|||
bids, _, err = h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, nil, order) |
|||
if err != nil && err != sql.ErrNoRows { |
|||
retSQLErr(err, c) |
|||
return |
|||
} |
|||
totalItems = int(*maxSlotNum) - int(*minSlotNum) + 1 |
|||
} else { |
|||
slotMinLim, slotMaxLim = getLimitsWithAddr(minSlotNum, maxSlotNum, fromItem, limit, order) |
|||
bids, pag, err = h.GetBestBidsAPI(&slotMinLim, &slotMaxLim, wonByEthereumAddress, limit, order) |
|||
if err != nil && err != sql.ErrNoRows { |
|||
retSQLErr(err, c) |
|||
return |
|||
} |
|||
if len(bids) > 0 { |
|||
totalItems = pag.TotalItems |
|||
*maxSlotNum = int64(pag.LastItem) |
|||
*minSlotNum = int64(pag.FirstItem) |
|||
} |
|||
} |
|||
|
|||
// Build the slot information with previous bids
|
|||
var slotsBids []SlotAPI |
|||
if len(bids) > 0 { |
|||
slotsBids = newSlotsAPIFromWinnerBids(fromItem, order, bids, currentBlock.EthBlockNum, auctionVars) |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
} |
|||
|
|||
// Build the other slots
|
|||
if wonByEthereumAddress == nil { |
|||
// Build hte information of the slots with bids or not
|
|||
for i := slotMinLim; i <= slotMaxLim; i++ { |
|||
found := false |
|||
for j := range slotsBids { |
|||
if slotsBids[j].SlotNum == i { |
|||
found = true |
|||
if order == historydb.OrderAsc { |
|||
if slotsBids[j].ItemID >= int(*fromItem) { |
|||
slots = append(slots, slotsBids[j]) |
|||
} |
|||
} else { |
|||
if slotsBids[j].ItemID <= int(*fromItem) { |
|||
slots = append([]SlotAPI{slotsBids[j]}, slots...) |
|||
} |
|||
} |
|||
break |
|||
} |
|||
} |
|||
if !found { |
|||
slots, err = addEmptySlot(slots, i, currentBlock.EthBlockNum, auctionVars, fromItem, order) |
|||
if err != nil { |
|||
retBadReq(err, c) |
|||
return |
|||
} |
|||
} |
|||
} |
|||
} else if len(slotsBids) > 0 { |
|||
slots = slotsBids |
|||
} |
|||
|
|||
if len(slots) == 0 { |
|||
retSQLErr(sql.ErrNoRows, c) |
|||
return |
|||
} |
|||
|
|||
// Build succesfull response
|
|||
type slotsResponse struct { |
|||
Slots []SlotAPI `json:"slots"` |
|||
Pagination *db.Pagination `json:"pagination"` |
|||
} |
|||
c.JSON(http.StatusOK, &slotsResponse{ |
|||
Slots: slots, |
|||
Pagination: getPagination(totalItems, minSlotNum, maxSlotNum), |
|||
}) |
|||
} |
@ -0,0 +1,305 @@ |
|||
package api |
|||
|
|||
import ( |
|||
"fmt" |
|||
"strconv" |
|||
"testing" |
|||
|
|||
"github.com/hermeznetwork/hermez-node/common" |
|||
"github.com/hermeznetwork/hermez-node/db" |
|||
"github.com/hermeznetwork/hermez-node/db/historydb" |
|||
"github.com/mitchellh/copystructure" |
|||
"github.com/stretchr/testify/assert" |
|||
) |
|||
|
|||
type testSlot struct { |
|||
ItemID int `json:"itemId"` |
|||
SlotNum int64 `json:"slotNum"` |
|||
FirstBlock int64 `json:"firstBlock"` |
|||
LastBlock int64 `json:"lastBlock"` |
|||
OpenAuction bool `json:"openAuction"` |
|||
WinnerBid *testBid `json:"winnerBid"` |
|||
} |
|||
|
|||
type testSlotsResponse struct { |
|||
Slots []testSlot `json:"slots"` |
|||
Pagination *db.Pagination `json:"pagination"` |
|||
} |
|||
|
|||
func (t testSlotsResponse) GetPagination() *db.Pagination { |
|||
if t.Slots[0].ItemID < t.Slots[len(t.Slots)-1].ItemID { |
|||
t.Pagination.FirstReturnedItem = int(t.Slots[0].ItemID) |
|||
t.Pagination.LastReturnedItem = int(t.Slots[len(t.Slots)-1].ItemID) |
|||
} else { |
|||
t.Pagination.LastReturnedItem = int(t.Slots[0].ItemID) |
|||
t.Pagination.FirstReturnedItem = int(t.Slots[len(t.Slots)-1].ItemID) |
|||
} |
|||
return t.Pagination |
|||
} |
|||
|
|||
func (t testSlotsResponse) Len() int { |
|||
return len(t.Slots) |
|||
} |
|||
|
|||
func genTestSlots(nSlots int, lastBlockNum int64, bids []testBid, auctionVars common.AuctionVariables) []testSlot { |
|||
tSlots := []testSlot{} |
|||
bestBids := make(map[int64]testBid) |
|||
// It's assumed that bids for each slot will be received in increasing order
|
|||
for i := range bids { |
|||
bestBids[bids[i].SlotNum] = bids[i] |
|||
} |
|||
|
|||
for i := int64(0); i < int64(nSlots); i++ { |
|||
bid, ok := bestBids[i] |
|||
firstBlock, lastBlock := getFirstLastBlock(int64(i)) |
|||
tSlot := testSlot{ |
|||
SlotNum: int64(i), |
|||
FirstBlock: firstBlock, |
|||
LastBlock: lastBlock, |
|||
OpenAuction: isOpenAuction(lastBlockNum, int64(i), auctionVars), |
|||
} |
|||
if ok { |
|||
tSlot.WinnerBid = &bid |
|||
} |
|||
tSlots = append(tSlots, tSlot) |
|||
} |
|||
return tSlots |
|||
} |
|||
|
|||
func getEmptyTestSlot(slotNum int64) testSlot { |
|||
firstBlock, lastBlock := getFirstLastBlock(slotNum) |
|||
slot := testSlot{ |
|||
SlotNum: slotNum, |
|||
FirstBlock: firstBlock, |
|||
LastBlock: lastBlock, |
|||
OpenAuction: false, |
|||
WinnerBid: nil, |
|||
} |
|||
return slot |
|||
} |
|||
|
|||
func TestGetSlot(t *testing.T) { |
|||
endpoint := apiURL + "slots/" |
|||
for _, slot := range tc.slots { |
|||
fetchedSlot := testSlot{} |
|||
assert.NoError( |
|||
t, doGoodReq( |
|||
"GET", |
|||
endpoint+strconv.Itoa(int(slot.SlotNum)), |
|||
nil, &fetchedSlot, |
|||
), |
|||
) |
|||
assertSlot(t, slot, fetchedSlot) |
|||
} |
|||
|
|||
// Slot with WinnerBid == nil
|
|||
slotNum := int64(15) |
|||
fetchedSlot := testSlot{} |
|||
assert.NoError( |
|||
t, doGoodReq( |
|||
"GET", |
|||
endpoint+strconv.Itoa(int(slotNum)), |
|||
nil, &fetchedSlot, |
|||
), |
|||
) |
|||
emptySlot := getEmptyTestSlot(slotNum) |
|||
assertSlot(t, emptySlot, fetchedSlot) |
|||
|
|||
// Invalid slotNum
|
|||
path := endpoint + strconv.Itoa(-2) |
|||
err := doBadReq("GET", path, nil, 400) |
|||
assert.NoError(t, err) |
|||
} |
|||
|
|||
func TestGetSlots(t *testing.T) { |
|||
endpoint := apiURL + "slots" |
|||
fetchedSlots := []testSlot{} |
|||
appendIter := func(intr interface{}) { |
|||
for i := 0; i < len(intr.(*testSlotsResponse).Slots); i++ { |
|||
tmp, err := copystructure.Copy(intr.(*testSlotsResponse).Slots[i]) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
fetchedSlots = append(fetchedSlots, tmp.(testSlot)) |
|||
} |
|||
} |
|||
// All slots with maxSlotNum filter
|
|||
maxSlotNum := tc.slots[len(tc.slots)-1].SlotNum + 5 |
|||
limit := 1 |
|||
path := fmt.Sprintf("%s?maxSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, limit) |
|||
err := doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
allSlots := tc.slots |
|||
for i := tc.slots[len(tc.slots)-1].SlotNum; i < maxSlotNum; i++ { |
|||
emptySlot := getEmptyTestSlot(i + 1) |
|||
allSlots = append(allSlots, emptySlot) |
|||
} |
|||
assertSlots(t, allSlots, fetchedSlots) |
|||
|
|||
// All slots with maxSlotNum filter, in reverse order
|
|||
fetchedSlots = []testSlot{} |
|||
limit = 3 |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
|
|||
flippedAllSlots := []testSlot{} |
|||
for i := len(allSlots) - 1; i >= 0; i-- { |
|||
flippedAllSlots = append(flippedAllSlots, allSlots[i]) |
|||
} |
|||
assertSlots(t, flippedAllSlots, fetchedSlots) |
|||
|
|||
// maxSlotNum & wonByEthereumAddress
|
|||
fetchedSlots = []testSlot{} |
|||
limit = 1 |
|||
bidderAddr := tc.coordinators[2].Bidder |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, bidderAddr.String(), limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
bidderAddressSlots := []testSlot{} |
|||
for i := 0; i < len(tc.slots); i++ { |
|||
if tc.slots[i].WinnerBid != nil { |
|||
if tc.slots[i].WinnerBid.Bidder == bidderAddr { |
|||
bidderAddressSlots = append(bidderAddressSlots, tc.slots[i]) |
|||
} |
|||
} |
|||
} |
|||
assertSlots(t, bidderAddressSlots, fetchedSlots) |
|||
|
|||
// maxSlotNum & wonByEthereumAddress, in reverse order
|
|||
fetchedSlots = []testSlot{} |
|||
limit = 1 |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, bidderAddr.String(), limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
flippedBidderAddressSlots := []testSlot{} |
|||
for i := len(bidderAddressSlots) - 1; i >= 0; i-- { |
|||
flippedBidderAddressSlots = append(flippedBidderAddressSlots, bidderAddressSlots[i]) |
|||
} |
|||
assertSlots(t, flippedBidderAddressSlots, fetchedSlots) |
|||
|
|||
// finishedAuction
|
|||
fetchedSlots = []testSlot{} |
|||
limit = 15 |
|||
path = fmt.Sprintf("%s?finishedAuction=%t&limit=%d&fromItem=", endpoint, true, limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
|
|||
currentSlot := getCurrentSlot(tc.blocks[len(tc.blocks)-1].EthBlockNum) |
|||
finishedAuctionSlots := []testSlot{} |
|||
for i := 0; i < len(tc.slots); i++ { |
|||
finishAuction := currentSlot + int64(tc.auctionVars.ClosedAuctionSlots) |
|||
if tc.slots[i].SlotNum <= finishAuction { |
|||
finishedAuctionSlots = append(finishedAuctionSlots, tc.slots[i]) |
|||
} else { |
|||
break |
|||
} |
|||
} |
|||
assertSlots(t, finishedAuctionSlots, fetchedSlots) |
|||
|
|||
//minSlot + maxSlot
|
|||
limit = 10 |
|||
minSlotNum := tc.slots[3].SlotNum |
|||
maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum - 1 |
|||
fetchedSlots = []testSlot{} |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
minMaxBatchNumSlots := []testSlot{} |
|||
for i := 0; i < len(tc.slots); i++ { |
|||
if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum { |
|||
minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i]) |
|||
} |
|||
} |
|||
assertSlots(t, minMaxBatchNumSlots, fetchedSlots) |
|||
|
|||
//minSlot + maxSlot
|
|||
limit = 15 |
|||
minSlotNum = tc.slots[0].SlotNum |
|||
maxSlotNum = tc.slots[0].SlotNum |
|||
fetchedSlots = []testSlot{} |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
minMaxBatchNumSlots = []testSlot{} |
|||
for i := 0; i < len(tc.slots); i++ { |
|||
if tc.slots[i].SlotNum >= minSlotNum && tc.slots[i].SlotNum <= maxSlotNum { |
|||
minMaxBatchNumSlots = append(minMaxBatchNumSlots, tc.slots[i]) |
|||
} |
|||
} |
|||
assertSlots(t, minMaxBatchNumSlots, fetchedSlots) |
|||
|
|||
// Only empty Slots
|
|||
limit = 2 |
|||
minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1 |
|||
maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5 |
|||
fetchedSlots = []testSlot{} |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderAsc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
emptySlots := []testSlot{} |
|||
for i := 0; i < len(allSlots); i++ { |
|||
if allSlots[i].SlotNum >= minSlotNum && allSlots[i].SlotNum <= maxSlotNum { |
|||
emptySlots = append(emptySlots, allSlots[i]) |
|||
} |
|||
} |
|||
assertSlots(t, emptySlots, fetchedSlots) |
|||
|
|||
// Only empty Slots, in reverse order
|
|||
limit = 4 |
|||
minSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 1 |
|||
maxSlotNum = tc.slots[len(tc.slots)-1].SlotNum + 5 |
|||
fetchedSlots = []testSlot{} |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit) |
|||
err = doGoodReqPaginated(path, historydb.OrderDesc, &testSlotsResponse{}, appendIter) |
|||
assert.NoError(t, err) |
|||
flippedEmptySlots := []testSlot{} |
|||
for i := 0; i < len(flippedAllSlots); i++ { |
|||
if flippedAllSlots[i].SlotNum >= minSlotNum && flippedAllSlots[i].SlotNum <= maxSlotNum { |
|||
flippedEmptySlots = append(flippedEmptySlots, flippedAllSlots[i]) |
|||
} |
|||
} |
|||
assertSlots(t, flippedEmptySlots, fetchedSlots) |
|||
|
|||
// 400
|
|||
// No filters
|
|||
path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit) |
|||
err = doBadReq("GET", path, nil, 400) |
|||
assert.NoError(t, err) |
|||
// Invalid maxSlotNum
|
|||
path = fmt.Sprintf("%s?maxSlotNum=%d", endpoint, -2) |
|||
err = doBadReq("GET", path, nil, 400) |
|||
assert.NoError(t, err) |
|||
// Invalid wonByEthereumAddress
|
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s", endpoint, maxSlotNum, "0xG0000001") |
|||
err = doBadReq("GET", path, nil, 400) |
|||
assert.NoError(t, err) |
|||
// Invalid minSlotNum / maxSlotNum (minSlotNum > maxSlotNum)
|
|||
maxSlotNum = tc.slots[1].SlotNum |
|||
minSlotNum = tc.slots[4].SlotNum |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&minSlotNum=%d&limit=%d&fromItem=", endpoint, maxSlotNum, minSlotNum, limit) |
|||
err = doBadReq("GET", path, nil, 400) |
|||
assert.NoError(t, err) |
|||
// 404
|
|||
maxSlotNum = tc.slots[1].SlotNum |
|||
path = fmt.Sprintf("%s?maxSlotNum=%d&wonByEthereumAddress=%s&limit=%d&fromItem=", endpoint, maxSlotNum, tc.coordinators[3].Bidder.String(), limit) |
|||
err = doBadReq("GET", path, nil, 404) |
|||
assert.NoError(t, err) |
|||
} |
|||
|
|||
func assertSlots(t *testing.T, expected, actual []testSlot) { |
|||
assert.Equal(t, len(expected), len(actual)) |
|||
for i := 0; i < len(expected); i++ { |
|||
assertSlot(t, expected[i], actual[i]) |
|||
} |
|||
} |
|||
|
|||
func assertSlot(t *testing.T, expected, actual testSlot) { |
|||
if actual.WinnerBid != nil { |
|||
assert.Equal(t, expected.WinnerBid.Timestamp.Unix(), actual.WinnerBid.Timestamp.Unix()) |
|||
expected.WinnerBid.Timestamp = actual.WinnerBid.Timestamp |
|||
actual.WinnerBid.ItemID = expected.WinnerBid.ItemID |
|||
} |
|||
actual.ItemID = expected.ItemID |
|||
assert.Equal(t, expected, actual) |
|||
} |