arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
hermez-node/api/api_test.go
api/api_test.goRaw
   1package api
   2
   3import (
   4	"context"
   5	"encoding/json"
   6	"errors"
   7	"fmt"
   8	"io"
   9	"io/ioutil"
  10	"math"
  11	"math/big"
  12	"net/http"
  13	"os"
  14	"sort"
  15	"strconv"
  16	"testing"
  17	"time"
  18
  19	ethCommon "github.com/ethereum/go-ethereum/common"
  20	swagger "github.com/getkin/kin-openapi/openapi3filter"
  21	"github.com/gin-gonic/gin"
  22	"github.com/hermeznetwork/hermez-node/common"
  23	"github.com/hermeznetwork/hermez-node/db"
  24	"github.com/hermeznetwork/hermez-node/db/historydb"
  25	"github.com/hermeznetwork/hermez-node/db/l2db"
  26	"github.com/hermeznetwork/hermez-node/db/statedb"
  27	"github.com/hermeznetwork/hermez-node/eth"
  28	"github.com/hermeznetwork/hermez-node/log"
  29	"github.com/hermeznetwork/hermez-node/test"
  30	"github.com/iden3/go-iden3-crypto/babyjub"
  31	"github.com/mitchellh/copystructure"
  32	"github.com/stretchr/testify/assert"
  33	"github.com/stretchr/testify/require"
  34)
  35
  36const apiPort = ":4010"
  37const apiURL = "http://localhost" + apiPort + "/"
  38
  39type testCommon struct {
  40	blocks   []common.Block
  41	tokens   []tokenAPI
  42	batches  []common.Batch
  43	usrAddr  string
  44	usrBjj   string
  45	accs     []common.Account
  46	usrTxs   []historyTxAPI
  47	allTxs   []historyTxAPI
  48	exits    []exitAPI
  49	usrExits []exitAPI
  50	router   *swagger.Router
  51}
  52
  53// TxSortFields represents the fields needed to sort L1 and L2 transactions
  54type txSortFields struct {
  55	BatchNum *common.BatchNum
  56	Position int
  57}
  58
  59// TxSortFielder is a interface that allows sorting L1 and L2 transactions in a combined way
  60type txSortFielder interface {
  61	SortFields() txSortFields
  62	L1() *common.L1Tx
  63	L2() *common.L2Tx
  64}
  65
  66// TxsSort array of TxSortFielder
  67type txsSort []txSortFielder
  68
  69func (t txsSort) Len() int      { return len(t) }
  70func (t txsSort) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
  71func (t txsSort) Less(i, j int) bool {
  72	// i not forged yet
  73	isf := t[i].SortFields()
  74	jsf := t[j].SortFields()
  75	if isf.BatchNum == nil {
  76		if jsf.BatchNum != nil { // j is already forged
  77			return false
  78		}
  79		// Both aren't forged, is i in a smaller position?
  80		return isf.Position < jsf.Position
  81	}
  82	// i is forged
  83	if jsf.BatchNum == nil {
  84		return false // j is not forged
  85	}
  86	// Both are forged
  87	if *isf.BatchNum == *jsf.BatchNum {
  88		// At the same batch,  is i in a smaller position?
  89		return isf.Position < jsf.Position
  90	}
  91	// At different batches, is i in a smaller batch?
  92	return *isf.BatchNum < *jsf.BatchNum
  93}
  94
  95type wrappedL1 common.L1Tx
  96
  97// SortFields implements TxSortFielder
  98func (tx *wrappedL1) SortFields() txSortFields {
  99	return txSortFields{
 100		BatchNum: tx.BatchNum,
 101		Position: tx.Position,
 102	}
 103}
 104
 105// L1 implements TxSortFielder
 106func (tx *wrappedL1) L1() *common.L1Tx {
 107	l1tx := common.L1Tx(*tx)
 108	return &l1tx
 109}
 110
 111// L2 implements TxSortFielder
 112func (tx *wrappedL1) L2() *common.L2Tx { return nil }
 113
 114type wrappedL2 common.L2Tx
 115
 116// SortFields implements TxSortFielder
 117func (tx *wrappedL2) SortFields() txSortFields {
 118	return txSortFields{
 119		BatchNum: &tx.BatchNum,
 120		Position: tx.Position,
 121	}
 122}
 123
 124// L1 implements TxSortFielder
 125func (tx *wrappedL2) L1() *common.L1Tx { return nil }
 126
 127// L2 implements TxSortFielder
 128func (tx *wrappedL2) L2() *common.L2Tx {
 129	l2tx := common.L2Tx(*tx)
 130	return &l2tx
 131}
 132
 133var tc testCommon
 134var config configAPI
 135
 136func TestMain(m *testing.M) {
 137	// Init swagger
 138	router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
 139	// Init DBs
 140	// HistoryDB
 141	pass := os.Getenv("POSTGRES_PASS")
 142	database, err := db.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
 143	if err != nil {
 144		panic(err)
 145	}
 146	hdb := historydb.NewHistoryDB(database)
 147	err = hdb.Reorg(-1)
 148	if err != nil {
 149		panic(err)
 150	}
 151	// StateDB
 152	dir, err := ioutil.TempDir("", "tmpdb")
 153	if err != nil {
 154		panic(err)
 155	}
 156	defer func() {
 157		if err := os.RemoveAll(dir); err != nil {
 158			panic(err)
 159		}
 160	}()
 161	sdb, err := statedb.NewStateDB(dir, statedb.TypeTxSelector, 0)
 162	if err != nil {
 163		panic(err)
 164	}
 165	// L2DB
 166	l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
 167	test.CleanL2DB(l2DB.DB())
 168
 169	config.RollupConstants.ExchangeMultiplier = eth.RollupConstExchangeMultiplier
 170	config.RollupConstants.ExitIdx = eth.RollupConstExitIDx
 171	config.RollupConstants.ReservedIdx = eth.RollupConstReservedIDx
 172	config.RollupConstants.LimitLoadAmount, _ = new(big.Int).SetString("340282366920938463463374607431768211456", 10)
 173	config.RollupConstants.LimitL2TransferAmount, _ = new(big.Int).SetString("6277101735386680763835789423207666416102355444464034512896", 10)
 174	config.RollupConstants.LimitTokens = eth.RollupConstLimitTokens
 175	config.RollupConstants.L1CoordinatorTotalBytes = eth.RollupConstL1CoordinatorTotalBytes
 176	config.RollupConstants.L1UserTotalBytes = eth.RollupConstL1UserTotalBytes
 177	config.RollupConstants.MaxL1UserTx = eth.RollupConstMaxL1UserTx
 178	config.RollupConstants.MaxL1Tx = eth.RollupConstMaxL1Tx
 179	config.RollupConstants.InputSHAConstantBytes = eth.RollupConstInputSHAConstantBytes
 180	config.RollupConstants.NumBuckets = eth.RollupConstNumBuckets
 181	config.RollupConstants.MaxWithdrawalDelay = eth.RollupConstMaxWithdrawalDelay
 182	var rollupPublicConstants eth.RollupPublicConstants
 183	rollupPublicConstants.AbsoluteMaxL1L2BatchTimeout = 240
 184	rollupPublicConstants.HermezAuctionContract = ethCommon.HexToAddress("0x500D1d6A4c7D8Ae28240b47c8FCde034D827fD5e")
 185	rollupPublicConstants.HermezGovernanceDAOAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
 186	rollupPublicConstants.SafetyAddress = ethCommon.HexToAddress("0xE5904695748fe4A84b40b3fc79De2277660BD1D3")
 187	rollupPublicConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
 188	rollupPublicConstants.WithdrawDelayerContract = ethCommon.HexToAddress("0xD6C850aeBFDC46D7F4c207e445cC0d6B0919BDBe")
 189	var verifier eth.RollupVerifierStruct
 190	verifier.MaxTx = 512
 191	verifier.NLevels = 32
 192	rollupPublicConstants.Verifiers = append(rollupPublicConstants.Verifiers, verifier)
 193
 194	var auctionConstants eth.AuctionConstants
 195	auctionConstants.BlocksPerSlot = 40
 196	auctionConstants.GenesisBlockNum = 100
 197	auctionConstants.GovernanceAddress = ethCommon.HexToAddress("0xeAD9C93b79Ae7C1591b1FB5323BD777E86e150d4")
 198	auctionConstants.InitialMinimalBidding, _ = new(big.Int).SetString("10000000000000000000", 10)
 199	auctionConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
 200	auctionConstants.TokenHEZ = ethCommon.HexToAddress("0xf784709d2317D872237C4bC22f867d1BAe2913AB")
 201
 202	var wdelayerConstants eth.WDelayerConstants
 203	wdelayerConstants.HermezRollup = ethCommon.HexToAddress("0xEa960515F8b4C237730F028cBAcF0a28E7F45dE0")
 204	wdelayerConstants.MaxEmergencyModeTime = uint64(1000000)
 205	wdelayerConstants.MaxWithdrawalDelay = uint64(10000000)
 206
 207	config.RollupConstants.PublicConstants = rollupPublicConstants
 208	config.AuctionConstants = auctionConstants
 209	config.WDelayerConstants = wdelayerConstants
 210
 211	// Init API
 212	api := gin.Default()
 213	if err := SetAPIEndpoints(
 214		true,
 215		true,
 216		api,
 217		hdb,
 218		sdb,
 219		l2DB,
 220		&config,
 221	); err != nil {
 222		panic(err)
 223	}
 224	// Start server
 225	server := &http.Server{Addr: apiPort, Handler: api}
 226	go func() {
 227		if err := server.ListenAndServe(); err != nil &&
 228			err != http.ErrServerClosed {
 229			panic(err)
 230		}
 231	}()
 232
 233	// Populate DBs
 234	// Clean DB
 235	err = h.Reorg(0)
 236	if err != nil {
 237		panic(err)
 238	}
 239	// Gen blocks and add them to DB
 240	const nBlocks = 5
 241	blocks := test.GenBlocks(1, nBlocks+1)
 242	err = h.AddBlocks(blocks)
 243	if err != nil {
 244		panic(err)
 245	}
 246	// Gen tokens and add them to DB
 247	const nTokens = 10
 248	tokens := test.GenTokens(nTokens, blocks)
 249	err = h.AddTokens(tokens)
 250	if err != nil {
 251		panic(err)
 252	}
 253	// Set token value
 254	tokensUSD := []tokenAPI{}
 255	for i, tkn := range tokens {
 256		token := tokenAPI{
 257			TokenID:     tkn.TokenID,
 258			EthBlockNum: tkn.EthBlockNum,
 259			EthAddr:     tkn.EthAddr,
 260			Name:        tkn.Name,
 261			Symbol:      tkn.Symbol,
 262			Decimals:    tkn.Decimals,
 263		}
 264		// Set value of 50% of the tokens
 265		if i%2 != 0 {
 266			value := float64(i) * 1.234567
 267			now := time.Now().UTC()
 268			token.USD = &value
 269			token.USDUpdate = &now
 270			err = h.UpdateTokenValue(token.Symbol, value)
 271			if err != nil {
 272				panic(err)
 273			}
 274		}
 275		tokensUSD = append(tokensUSD, token)
 276	}
 277	// Gen batches and add them to DB
 278	const nBatches = 10
 279	batches := test.GenBatches(nBatches, blocks)
 280	err = h.AddBatches(batches)
 281	if err != nil {
 282		panic(err)
 283	}
 284	// Gen accounts and add them to DB
 285	const totalAccounts = 40
 286	const userAccounts = 4
 287	usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
 288	privK := babyjub.NewRandPrivKey()
 289	usrBjj := privK.Public()
 290	accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
 291	err = h.AddAccounts(accs)
 292	if err != nil {
 293		panic(err)
 294	}
 295	// Gen exits and add them to DB
 296	const totalExits = 40
 297	exits := test.GenExitTree(totalExits, batches, accs)
 298	err = h.AddExitTree(exits)
 299	if err != nil {
 300		panic(err)
 301	}
 302	// Gen L1Txs and add them to DB
 303	const totalL1Txs = 40
 304	const userL1Txs = 4
 305	usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
 306	// Gen L2Txs and add them to DB
 307	const totalL2Txs = 20
 308	const userL2Txs = 4
 309	usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
 310	// Order txs
 311	sortedTxs := []txSortFielder{}
 312	for i := 0; i < len(usrL1Txs); i++ {
 313		wL1 := wrappedL1(usrL1Txs[i])
 314		sortedTxs = append(sortedTxs, &wL1)
 315	}
 316	for i := 0; i < len(othrL1Txs); i++ {
 317		wL1 := wrappedL1(othrL1Txs[i])
 318		sortedTxs = append(sortedTxs, &wL1)
 319	}
 320	for i := 0; i < len(usrL2Txs); i++ {
 321		wL2 := wrappedL2(usrL2Txs[i])
 322		sortedTxs = append(sortedTxs, &wL2)
 323	}
 324	for i := 0; i < len(othrL2Txs); i++ {
 325		wL2 := wrappedL2(othrL2Txs[i])
 326		sortedTxs = append(sortedTxs, &wL2)
 327	}
 328	sort.Sort(txsSort(sortedTxs))
 329	// Add txs to DB and prepare them for test commons
 330	usrTxs := []historyTxAPI{}
 331	allTxs := []historyTxAPI{}
 332	getTimestamp := func(blockNum int64) time.Time {
 333		for i := 0; i < len(blocks); i++ {
 334			if blocks[i].EthBlockNum == blockNum {
 335				return blocks[i].Timestamp
 336			}
 337		}
 338		panic("timesamp not found")
 339	}
 340	getToken := func(id common.TokenID) tokenAPI {
 341		for i := 0; i < len(tokensUSD); i++ {
 342			if tokensUSD[i].TokenID == id {
 343				return tokensUSD[i]
 344			}
 345		}
 346		panic("token not found")
 347	}
 348	getTokenByIdx := func(idx common.Idx) tokenAPI {
 349		for _, acc := range accs {
 350			if idx == acc.Idx {
 351				return getToken(acc.TokenID)
 352			}
 353		}
 354		panic("token not found")
 355	}
 356	usrIdxs := []string{}
 357	for _, acc := range accs {
 358		if acc.EthAddr == usrAddr || acc.PublicKey == usrBjj {
 359			for _, token := range tokens {
 360				if token.TokenID == acc.TokenID {
 361					usrIdxs = append(usrIdxs, idxToHez(acc.Idx, token.Symbol))
 362				}
 363			}
 364		}
 365	}
 366	isUsrTx := func(tx historyTxAPI) bool {
 367		for _, idx := range usrIdxs {
 368			if tx.FromIdx != nil && *tx.FromIdx == idx {
 369				return true
 370			}
 371			if tx.ToIdx == idx {
 372				return true
 373			}
 374		}
 375		return false
 376	}
 377	for _, genericTx := range sortedTxs {
 378		l1 := genericTx.L1()
 379		l2 := genericTx.L2()
 380		if l1 != nil {
 381			// Add L1 tx to DB
 382			err = h.AddL1Txs([]common.L1Tx{*l1})
 383			if err != nil {
 384				panic(err)
 385			}
 386			// L1Tx ==> historyTxAPI
 387			token := getToken(l1.TokenID)
 388			tx := historyTxAPI{
 389				IsL1:      "L1",
 390				TxID:      l1.TxID.String(),
 391				Type:      l1.Type,
 392				Position:  l1.Position,
 393				ToIdx:     idxToHez(l1.ToIdx, token.Symbol),
 394				Amount:    l1.Amount.String(),
 395				BatchNum:  l1.BatchNum,
 396				Timestamp: getTimestamp(l1.EthBlockNum),
 397				L1Info: &l1Info{
 398					ToForgeL1TxsNum: l1.ToForgeL1TxsNum,
 399					UserOrigin:      l1.UserOrigin,
 400					FromEthAddr:     ethAddrToHez(l1.FromEthAddr),
 401					FromBJJ:         bjjToString(l1.FromBJJ),
 402					LoadAmount:      l1.LoadAmount.String(),
 403					EthBlockNum:     l1.EthBlockNum,
 404				},
 405				Token: token,
 406			}
 407			if l1.FromIdx != 0 {
 408				idxStr := idxToHez(l1.FromIdx, token.Symbol)
 409				tx.FromIdx = &idxStr
 410			}
 411			if token.USD != nil {
 412				af := new(big.Float).SetInt(l1.Amount)
 413				amountFloat, _ := af.Float64()
 414				usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals))
 415				tx.HistoricUSD = &usd
 416				laf := new(big.Float).SetInt(l1.LoadAmount)
 417				loadAmountFloat, _ := laf.Float64()
 418				loadUSD := *token.USD * loadAmountFloat / math.Pow(10, float64(token.Decimals))
 419				tx.L1Info.HistoricLoadAmountUSD = &loadUSD
 420			}
 421			allTxs = append(allTxs, tx)
 422			if isUsrTx(tx) {
 423				usrTxs = append(usrTxs, tx)
 424			}
 425		} else {
 426			// Add L2 tx to DB
 427			err = h.AddL2Txs([]common.L2Tx{*l2})
 428			if err != nil {
 429				panic(err)
 430			}
 431			// L2Tx ==> historyTxAPI
 432			var tokenID common.TokenID
 433			found := false
 434			for _, acc := range accs {
 435				if acc.Idx == l2.FromIdx {
 436					found = true
 437					tokenID = acc.TokenID
 438					break
 439				}
 440			}
 441			if !found {
 442				panic("tokenID not found")
 443			}
 444			token := getToken(tokenID)
 445			tx := historyTxAPI{
 446				IsL1:      "L2",
 447				TxID:      l2.TxID.String(),
 448				Type:      l2.Type,
 449				Position:  l2.Position,
 450				ToIdx:     idxToHez(l2.ToIdx, token.Symbol),
 451				Amount:    l2.Amount.String(),
 452				BatchNum:  &l2.BatchNum,
 453				Timestamp: getTimestamp(l2.EthBlockNum),
 454				L2Info: &l2Info{
 455					Nonce: l2.Nonce,
 456					Fee:   l2.Fee,
 457				},
 458				Token: token,
 459			}
 460			if l2.FromIdx != 0 {
 461				idxStr := idxToHez(l2.FromIdx, token.Symbol)
 462				tx.FromIdx = &idxStr
 463			}
 464			if token.USD != nil {
 465				af := new(big.Float).SetInt(l2.Amount)
 466				amountFloat, _ := af.Float64()
 467				usd := *token.USD * amountFloat / math.Pow(10, float64(token.Decimals))
 468				tx.HistoricUSD = &usd
 469				feeUSD := usd * l2.Fee.Percentage()
 470				tx.HistoricUSD = &usd
 471				tx.L2Info.HistoricFeeUSD = &feeUSD
 472			}
 473			allTxs = append(allTxs, tx)
 474			if isUsrTx(tx) {
 475				usrTxs = append(usrTxs, tx)
 476			}
 477		}
 478	}
 479	// Transform exits to API
 480	exitsToAPIExits := func(exits []common.ExitInfo, accs []common.Account, tokens []common.Token) []exitAPI {
 481		historyExits := []historydb.HistoryExit{}
 482		for _, exit := range exits {
 483			token := getTokenByIdx(exit.AccountIdx)
 484			historyExits = append(historyExits, historydb.HistoryExit{
 485				BatchNum:               exit.BatchNum,
 486				AccountIdx:             exit.AccountIdx,
 487				MerkleProof:            exit.MerkleProof,
 488				Balance:                exit.Balance,
 489				InstantWithdrawn:       exit.InstantWithdrawn,
 490				DelayedWithdrawRequest: exit.DelayedWithdrawRequest,
 491				DelayedWithdrawn:       exit.DelayedWithdrawn,
 492				TokenID:                token.TokenID,
 493				TokenEthBlockNum:       token.EthBlockNum,
 494				TokenEthAddr:           token.EthAddr,
 495				TokenName:              token.Name,
 496				TokenSymbol:            token.Symbol,
 497				TokenDecimals:          token.Decimals,
 498				TokenUSD:               token.USD,
 499				TokenUSDUpdate:         token.USDUpdate,
 500			})
 501		}
 502		return historyExitsToAPI(historyExits)
 503	}
 504	apiExits := exitsToAPIExits(exits, accs, tokens)
 505	// sort.Sort(apiExits)
 506	usrExits := []exitAPI{}
 507	for _, exit := range apiExits {
 508		for _, idx := range usrIdxs {
 509			if idx == exit.AccountIdx {
 510				usrExits = append(usrExits, exit)
 511			}
 512		}
 513	}
 514	tc = testCommon{
 515		blocks:   blocks,
 516		tokens:   tokensUSD,
 517		batches:  batches,
 518		usrAddr:  ethAddrToHez(usrAddr),
 519		usrBjj:   bjjToString(usrBjj),
 520		accs:     accs,
 521		usrTxs:   usrTxs,
 522		allTxs:   allTxs,
 523		exits:    apiExits,
 524		usrExits: usrExits,
 525		router:   router,
 526	}
 527	// Run tests
 528	result := m.Run()
 529	// Stop server
 530	if err := server.Shutdown(context.Background()); err != nil {
 531		panic(err)
 532	}
 533	if err := database.Close(); err != nil {
 534		panic(err)
 535	}
 536	os.Exit(result)
 537}
 538
 539func TestGetHistoryTxs(t *testing.T) {
 540	endpoint := apiURL + "transactions-history"
 541	fetchedTxs := []historyTxAPI{}
 542	appendIter := func(intr interface{}) {
 543		for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
 544			tmp, err := copystructure.Copy(intr.(*historyTxsAPI).Txs[i])
 545			if err != nil {
 546				panic(err)
 547			}
 548			fetchedTxs = append(fetchedTxs, tmp.(historyTxAPI))
 549		}
 550	}
 551	// Get all (no filters)
 552	limit := 8
 553	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 554	err := doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 555	assert.NoError(t, err)
 556	assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
 557	// Uncomment once tx generation for tests is fixed
 558	// // Get by ethAddr
 559	// fetchedTxs = []historyTxAPI{}
 560	// limit = 7
 561	// path = fmt.Sprintf(
 562	// 	"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
 563	// 	endpoint, tc.usrAddr, limit,
 564	// )
 565	// err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 566	// assert.NoError(t, err)
 567	// assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
 568	// // Get by bjj
 569	// fetchedTxs = []historyTxAPI{}
 570	// limit = 6
 571	// path = fmt.Sprintf(
 572	// 	"%s?BJJ=%s&limit=%d&fromItem=",
 573	// 	endpoint, tc.usrBjj, limit,
 574	// )
 575	// err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 576	// assert.NoError(t, err)
 577	// assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
 578	// Get by tokenID
 579	fetchedTxs = []historyTxAPI{}
 580	limit = 5
 581	tokenID := tc.allTxs[0].Token.TokenID
 582	path = fmt.Sprintf(
 583		"%s?tokenId=%d&limit=%d&fromItem=",
 584		endpoint, tokenID, limit,
 585	)
 586	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 587	assert.NoError(t, err)
 588	tokenIDTxs := []historyTxAPI{}
 589	for i := 0; i < len(tc.allTxs); i++ {
 590		if tc.allTxs[i].Token.TokenID == tokenID {
 591			tokenIDTxs = append(tokenIDTxs, tc.allTxs[i])
 592		}
 593	}
 594	assertHistoryTxAPIs(t, tokenIDTxs, fetchedTxs)
 595	// idx
 596	fetchedTxs = []historyTxAPI{}
 597	limit = 4
 598	idx := tc.allTxs[0].ToIdx
 599	path = fmt.Sprintf(
 600		"%s?accountIndex=%s&limit=%d&fromItem=",
 601		endpoint, idx, limit,
 602	)
 603	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 604	assert.NoError(t, err)
 605	idxTxs := []historyTxAPI{}
 606	for i := 0; i < len(tc.allTxs); i++ {
 607		if (tc.allTxs[i].FromIdx != nil && (*tc.allTxs[i].FromIdx)[6:] == idx[6:]) ||
 608			tc.allTxs[i].ToIdx[6:] == idx[6:] {
 609			idxTxs = append(idxTxs, tc.allTxs[i])
 610		}
 611	}
 612	assertHistoryTxAPIs(t, idxTxs, fetchedTxs)
 613	// batchNum
 614	fetchedTxs = []historyTxAPI{}
 615	limit = 3
 616	batchNum := tc.allTxs[0].BatchNum
 617	path = fmt.Sprintf(
 618		"%s?batchNum=%d&limit=%d&fromItem=",
 619		endpoint, *batchNum, limit,
 620	)
 621	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 622	assert.NoError(t, err)
 623	batchNumTxs := []historyTxAPI{}
 624	for i := 0; i < len(tc.allTxs); i++ {
 625		if tc.allTxs[i].BatchNum != nil &&
 626			*tc.allTxs[i].BatchNum == *batchNum {
 627			batchNumTxs = append(batchNumTxs, tc.allTxs[i])
 628		}
 629	}
 630	assertHistoryTxAPIs(t, batchNumTxs, fetchedTxs)
 631	// type
 632	txTypes := []common.TxType{
 633		// Uncomment once test gen is fixed
 634		// common.TxTypeExit,
 635		// common.TxTypeTransfer,
 636		// common.TxTypeDeposit,
 637		common.TxTypeCreateAccountDeposit,
 638		// common.TxTypeCreateAccountDepositTransfer,
 639		// common.TxTypeDepositTransfer,
 640		common.TxTypeForceTransfer,
 641		// common.TxTypeForceExit,
 642		// common.TxTypeTransferToEthAddr,
 643		// common.TxTypeTransferToBJJ,
 644	}
 645	for _, txType := range txTypes {
 646		fetchedTxs = []historyTxAPI{}
 647		limit = 2
 648		path = fmt.Sprintf(
 649			"%s?type=%s&limit=%d&fromItem=",
 650			endpoint, txType, limit,
 651		)
 652		err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 653		assert.NoError(t, err)
 654		txTypeTxs := []historyTxAPI{}
 655		for i := 0; i < len(tc.allTxs); i++ {
 656			if tc.allTxs[i].Type == txType {
 657				txTypeTxs = append(txTypeTxs, tc.allTxs[i])
 658			}
 659		}
 660		assertHistoryTxAPIs(t, txTypeTxs, fetchedTxs)
 661	}
 662	// Multiple filters
 663	fetchedTxs = []historyTxAPI{}
 664	limit = 1
 665	path = fmt.Sprintf(
 666		"%s?batchNum=%d&tokenId=%d&limit=%d&fromItem=",
 667		endpoint, *batchNum, tokenID, limit,
 668	)
 669	err = doGoodReqPaginated(path, historydb.OrderAsc, &historyTxsAPI{}, appendIter)
 670	assert.NoError(t, err)
 671	mixedTxs := []historyTxAPI{}
 672	for i := 0; i < len(tc.allTxs); i++ {
 673		if tc.allTxs[i].BatchNum != nil {
 674			if *tc.allTxs[i].BatchNum == *batchNum && tc.allTxs[i].Token.TokenID == tokenID {
 675				mixedTxs = append(mixedTxs, tc.allTxs[i])
 676			}
 677		}
 678	}
 679	assertHistoryTxAPIs(t, mixedTxs, fetchedTxs)
 680	// All, in reverse order
 681	fetchedTxs = []historyTxAPI{}
 682	limit = 5
 683	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 684	err = doGoodReqPaginated(path, historydb.OrderDesc, &historyTxsAPI{}, appendIter)
 685	assert.NoError(t, err)
 686	flipedTxs := []historyTxAPI{}
 687	for i := 0; i < len(tc.allTxs); i++ {
 688		flipedTxs = append(flipedTxs, tc.allTxs[len(tc.allTxs)-1-i])
 689	}
 690	assertHistoryTxAPIs(t, flipedTxs, fetchedTxs)
 691	// 400
 692	path = fmt.Sprintf(
 693		"%s?accountIndex=%s&hermezEthereumAddress=%s",
 694		endpoint, idx, tc.usrAddr,
 695	)
 696	err = doBadReq("GET", path, nil, 400)
 697	assert.NoError(t, err)
 698	path = fmt.Sprintf("%s?tokenId=X", endpoint)
 699	err = doBadReq("GET", path, nil, 400)
 700	assert.NoError(t, err)
 701	// 404
 702	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
 703	err = doBadReq("GET", path, nil, 404)
 704	assert.NoError(t, err)
 705	path = fmt.Sprintf("%s?limit=1000&fromItem=999999", endpoint)
 706	err = doBadReq("GET", path, nil, 404)
 707	assert.NoError(t, err)
 708}
 709
 710func TestGetHistoryTx(t *testing.T) {
 711	// Get all txs by their ID
 712	endpoint := apiURL + "transactions-history/"
 713	fetchedTxs := []historyTxAPI{}
 714	for _, tx := range tc.allTxs {
 715		fetchedTx := historyTxAPI{}
 716		assert.NoError(t, doGoodReq("GET", endpoint+tx.TxID, nil, &fetchedTx))
 717		fetchedTxs = append(fetchedTxs, fetchedTx)
 718	}
 719	assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
 720	// 400
 721	err := doBadReq("GET", endpoint+"0x001", nil, 400)
 722	assert.NoError(t, err)
 723	// 404
 724	err = doBadReq("GET", endpoint+"0x00000000000001e240004700", nil, 404)
 725	assert.NoError(t, err)
 726}
 727
 728func assertHistoryTxAPIs(t *testing.T, expected, actual []historyTxAPI) {
 729	require.Equal(t, len(expected), len(actual))
 730	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
 731		actual[i].ItemID = 0
 732		assert.Equal(t, expected[i].Timestamp.Unix(), actual[i].Timestamp.Unix())
 733		expected[i].Timestamp = actual[i].Timestamp
 734		if expected[i].Token.USDUpdate == nil {
 735			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
 736		} else {
 737			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
 738			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
 739		}
 740		test.AssertUSD(t, expected[i].HistoricUSD, actual[i].HistoricUSD)
 741		if expected[i].L2Info != nil {
 742			test.AssertUSD(t, expected[i].L2Info.HistoricFeeUSD, actual[i].L2Info.HistoricFeeUSD)
 743		} else {
 744			test.AssertUSD(t, expected[i].L1Info.HistoricLoadAmountUSD, actual[i].L1Info.HistoricLoadAmountUSD)
 745		}
 746		assert.Equal(t, expected[i], actual[i])
 747	}
 748}
 749
 750func TestGetExits(t *testing.T) {
 751	endpoint := apiURL + "exits"
 752	fetchedExits := []exitAPI{}
 753	appendIter := func(intr interface{}) {
 754		for i := 0; i < len(intr.(*exitsAPI).Exits); i++ {
 755			tmp, err := copystructure.Copy(intr.(*exitsAPI).Exits[i])
 756			if err != nil {
 757				panic(err)
 758			}
 759			fetchedExits = append(fetchedExits, tmp.(exitAPI))
 760		}
 761	}
 762	// Get all (no filters)
 763	limit := 8
 764	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 765	err := doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 766	assert.NoError(t, err)
 767	assertExitAPIs(t, tc.exits, fetchedExits)
 768
 769	// Get by ethAddr
 770	fetchedExits = []exitAPI{}
 771	limit = 7
 772	path = fmt.Sprintf(
 773		"%s?hermezEthereumAddress=%s&limit=%d&fromItem=",
 774		endpoint, tc.usrAddr, limit,
 775	)
 776	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 777	assert.NoError(t, err)
 778	assertExitAPIs(t, tc.usrExits, fetchedExits)
 779	// Get by bjj
 780	fetchedExits = []exitAPI{}
 781	limit = 6
 782	path = fmt.Sprintf(
 783		"%s?BJJ=%s&limit=%d&fromItem=",
 784		endpoint, tc.usrBjj, limit,
 785	)
 786	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 787	assert.NoError(t, err)
 788	assertExitAPIs(t, tc.usrExits, fetchedExits)
 789	// Get by tokenID
 790	fetchedExits = []exitAPI{}
 791	limit = 5
 792	tokenID := tc.exits[0].Token.TokenID
 793	path = fmt.Sprintf(
 794		"%s?tokenId=%d&limit=%d&fromItem=",
 795		endpoint, tokenID, limit,
 796	)
 797	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 798	assert.NoError(t, err)
 799	tokenIDExits := []exitAPI{}
 800	for i := 0; i < len(tc.exits); i++ {
 801		if tc.exits[i].Token.TokenID == tokenID {
 802			tokenIDExits = append(tokenIDExits, tc.exits[i])
 803		}
 804	}
 805	assertExitAPIs(t, tokenIDExits, fetchedExits)
 806	// idx
 807	fetchedExits = []exitAPI{}
 808	limit = 4
 809	idx := tc.exits[0].AccountIdx
 810	path = fmt.Sprintf(
 811		"%s?accountIndex=%s&limit=%d&fromItem=",
 812		endpoint, idx, limit,
 813	)
 814	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 815	assert.NoError(t, err)
 816	idxExits := []exitAPI{}
 817	for i := 0; i < len(tc.exits); i++ {
 818		if tc.exits[i].AccountIdx[6:] == idx[6:] {
 819			idxExits = append(idxExits, tc.exits[i])
 820		}
 821	}
 822	assertExitAPIs(t, idxExits, fetchedExits)
 823	// batchNum
 824	fetchedExits = []exitAPI{}
 825	limit = 3
 826	batchNum := tc.exits[0].BatchNum
 827	path = fmt.Sprintf(
 828		"%s?batchNum=%d&limit=%d&fromItem=",
 829		endpoint, batchNum, limit,
 830	)
 831	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 832	assert.NoError(t, err)
 833	batchNumExits := []exitAPI{}
 834	for i := 0; i < len(tc.exits); i++ {
 835		if tc.exits[i].BatchNum == batchNum {
 836			batchNumExits = append(batchNumExits, tc.exits[i])
 837		}
 838	}
 839	assertExitAPIs(t, batchNumExits, fetchedExits)
 840	// Multiple filters
 841	fetchedExits = []exitAPI{}
 842	limit = 1
 843	path = fmt.Sprintf(
 844		"%s?batchNum=%d&tokeId=%d&limit=%d&fromItem=",
 845		endpoint, batchNum, tokenID, limit,
 846	)
 847	err = doGoodReqPaginated(path, historydb.OrderAsc, &exitsAPI{}, appendIter)
 848	assert.NoError(t, err)
 849	mixedExits := []exitAPI{}
 850	flipedExits := []exitAPI{}
 851	for i := 0; i < len(tc.exits); i++ {
 852		if tc.exits[i].BatchNum == batchNum && tc.exits[i].Token.TokenID == tokenID {
 853			mixedExits = append(mixedExits, tc.exits[i])
 854		}
 855		flipedExits = append(flipedExits, tc.exits[len(tc.exits)-1-i])
 856	}
 857	assertExitAPIs(t, mixedExits, fetchedExits)
 858	// All, in reverse order
 859	fetchedExits = []exitAPI{}
 860	limit = 5
 861	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 862	err = doGoodReqPaginated(path, historydb.OrderDesc, &exitsAPI{}, appendIter)
 863	assert.NoError(t, err)
 864	assertExitAPIs(t, flipedExits, fetchedExits)
 865	// 400
 866	path = fmt.Sprintf(
 867		"%s?accountIndex=%s&hermezEthereumAddress=%s",
 868		endpoint, idx, tc.usrAddr,
 869	)
 870	err = doBadReq("GET", path, nil, 400)
 871	assert.NoError(t, err)
 872	path = fmt.Sprintf("%s?tokenId=X", endpoint)
 873	err = doBadReq("GET", path, nil, 400)
 874	assert.NoError(t, err)
 875	// 404
 876	path = fmt.Sprintf("%s?batchNum=999999", endpoint)
 877	err = doBadReq("GET", path, nil, 404)
 878	assert.NoError(t, err)
 879	path = fmt.Sprintf("%s?limit=1000&fromItem=1000", endpoint)
 880	err = doBadReq("GET", path, nil, 404)
 881	assert.NoError(t, err)
 882}
 883
 884func TestGetExit(t *testing.T) {
 885	// Get all txs by their ID
 886	endpoint := apiURL + "exits/"
 887	fetchedExits := []exitAPI{}
 888	for _, exit := range tc.exits {
 889		fetchedExit := exitAPI{}
 890		assert.NoError(
 891			t, doGoodReq(
 892				"GET",
 893				fmt.Sprintf("%s%d/%s", endpoint, exit.BatchNum, exit.AccountIdx),
 894				nil, &fetchedExit,
 895			),
 896		)
 897		fetchedExits = append(fetchedExits, fetchedExit)
 898	}
 899	assertExitAPIs(t, tc.exits, fetchedExits)
 900	// 400
 901	err := doBadReq("GET", endpoint+"1/haz:BOOM:1", nil, 400)
 902	assert.NoError(t, err)
 903	err = doBadReq("GET", endpoint+"-1/hez:BOOM:1", nil, 400)
 904	assert.NoError(t, err)
 905	// 404
 906	err = doBadReq("GET", endpoint+"494/hez:XXX:1", nil, 404)
 907	assert.NoError(t, err)
 908}
 909
 910func assertExitAPIs(t *testing.T, expected, actual []exitAPI) {
 911	require.Equal(t, len(expected), len(actual))
 912	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
 913		actual[i].ItemID = 0
 914		if expected[i].Token.USDUpdate == nil {
 915			assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
 916		} else {
 917			assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
 918			expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
 919		}
 920		assert.Equal(t, expected[i], actual[i])
 921	}
 922}
 923
 924func TestGetToken(t *testing.T) {
 925	// Get all txs by their ID
 926	endpoint := apiURL + "tokens/"
 927	fetchedTokens := []tokenAPI{}
 928	for _, token := range tc.tokens {
 929		fetchedToken := tokenAPI{}
 930		assert.NoError(t, doGoodReq("GET", endpoint+strconv.Itoa(int(token.TokenID)), nil, &fetchedToken))
 931		fetchedTokens = append(fetchedTokens, fetchedToken)
 932	}
 933	assertTokensAPIs(t, tc.tokens, fetchedTokens)
 934}
 935
 936func TestGetTokens(t *testing.T) {
 937	endpoint := apiURL + "tokens"
 938	fetchedTokens := []tokenAPI{}
 939	appendIter := func(intr interface{}) {
 940		for i := 0; i < len(intr.(*tokensAPI).Tokens); i++ {
 941			tmp, err := copystructure.Copy(intr.(*tokensAPI).Tokens[i])
 942			if err != nil {
 943				panic(err)
 944			}
 945			fetchedTokens = append(fetchedTokens, tmp.(tokenAPI))
 946		}
 947	}
 948	// Get all (no filters)
 949	limit := 8
 950	path := fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
 951	err := doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
 952	assert.NoError(t, err)
 953	assertTokensAPIs(t, tc.tokens, fetchedTokens)
 954
 955	// Get by tokenIds
 956	fetchedTokens = []tokenAPI{}
 957	limit = 7
 958	stringIds := strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID))
 959	path = fmt.Sprintf(
 960		"%s?ids=%s&limit=%d&fromItem=",
 961		endpoint, stringIds, limit,
 962	)
 963	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
 964	assert.NoError(t, err)
 965	var tokensFiltered []tokenAPI
 966	tokensFiltered = append(tokensFiltered, tc.tokens[2])
 967	tokensFiltered = append(tokensFiltered, tc.tokens[5])
 968	tokensFiltered = append(tokensFiltered, tc.tokens[6])
 969	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
 970
 971	// Get by symbols
 972	fetchedTokens = []tokenAPI{}
 973	limit = 7
 974	stringSymbols := tc.tokens[1].Symbol + "," + tc.tokens[3].Symbol
 975	path = fmt.Sprintf(
 976		"%s?symbols=%s&limit=%d&fromItem=",
 977		endpoint, stringSymbols, limit,
 978	)
 979	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
 980	assert.NoError(t, err)
 981	tokensFiltered = nil
 982	tokensFiltered = append(tokensFiltered, tc.tokens[1])
 983	tokensFiltered = append(tokensFiltered, tc.tokens[3])
 984	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
 985
 986	// Get by name
 987	fetchedTokens = []tokenAPI{}
 988	limit = 5
 989	stringName := tc.tokens[8].Name[4:5]
 990	path = fmt.Sprintf(
 991		"%s?name=%s&limit=%d&fromItem=",
 992		endpoint, stringName, limit,
 993	)
 994	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
 995	assert.NoError(t, err)
 996	tokensFiltered = nil
 997	tokensFiltered = append(tokensFiltered, tc.tokens[8])
 998	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
 999
1000	// Multiple filters
1001	fetchedTokens = []tokenAPI{}
1002	limit = 5
1003	stringSymbols = tc.tokens[2].Symbol + "," + tc.tokens[6].Symbol
1004	stringIds = strconv.Itoa(int(tc.tokens[2].TokenID)) + "," + strconv.Itoa(int(tc.tokens[5].TokenID)) + "," + strconv.Itoa(int(tc.tokens[6].TokenID))
1005	path = fmt.Sprintf(
1006		"%s?symbols=%s&ids=%s&limit=%d&fromItem=",
1007		endpoint, stringSymbols, stringIds, limit,
1008	)
1009	err = doGoodReqPaginated(path, historydb.OrderAsc, &tokensAPI{}, appendIter)
1010	assert.NoError(t, err)
1011
1012	tokensFiltered = nil
1013	tokensFiltered = append(tokensFiltered, tc.tokens[2])
1014	tokensFiltered = append(tokensFiltered, tc.tokens[6])
1015	assertTokensAPIs(t, tokensFiltered, fetchedTokens)
1016
1017	// All, in reverse order
1018	fetchedTokens = []tokenAPI{}
1019	limit = 5
1020	path = fmt.Sprintf("%s?limit=%d&fromItem=", endpoint, limit)
1021	err = doGoodReqPaginated(path, historydb.OrderDesc, &tokensAPI{}, appendIter)
1022	assert.NoError(t, err)
1023	flipedTokens := []tokenAPI{}
1024	for i := 0; i < len(tc.tokens); i++ {
1025		flipedTokens = append(flipedTokens, tc.tokens[len(tc.tokens)-1-i])
1026	}
1027	assertTokensAPIs(t, flipedTokens, fetchedTokens)
1028}
1029
1030func assertTokensAPIs(t *testing.T, expected, actual []tokenAPI) {
1031	require.Equal(t, len(expected), len(actual))
1032	for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
1033		actual[i].ItemID = 0
1034		if expected[i].USDUpdate == nil {
1035			assert.Equal(t, expected[i].USDUpdate, actual[i].USDUpdate)
1036		} else {
1037			assert.Equal(t, expected[i].USDUpdate.Unix(), actual[i].USDUpdate.Unix())
1038			expected[i].USDUpdate = actual[i].USDUpdate
1039		}
1040		assert.Equal(t, expected[i], actual[i])
1041	}
1042}
1043
1044func TestGetConfig(t *testing.T) {
1045	endpoint := apiURL + "config"
1046	var configTest configAPI
1047	assert.NoError(t, doGoodReq("GET", endpoint, nil, &configTest))
1048	assert.Equal(t, config, configTest)
1049	assert.Equal(t, cg, &configTest)
1050}
1051
1052func doGoodReqPaginated(
1053	path, order string,
1054	iterStruct db.Paginationer,
1055	appendIter func(res interface{}),
1056) error {
1057	next := 0
1058	for {
1059		// Call API to get this iteration items
1060		iterPath := path
1061		if next == 0 && order == historydb.OrderDesc {
1062			// Fetch first item in reverse order
1063			iterPath += "99999"
1064		} else {
1065			// Fetch from next item or 0 if it's ascending order
1066			iterPath += strconv.Itoa(next)
1067		}
1068		if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
1069			return err
1070		}
1071		appendIter(iterStruct)
1072		// Keep iterating?
1073		pag := iterStruct.GetPagination()
1074		if order == historydb.OrderAsc {
1075			if pag.LastReturnedItem == pag.LastItem { // No
1076				break
1077			} else { // Yes
1078				next = pag.LastReturnedItem + 1
1079			}
1080		} else {
1081			if pag.FirstReturnedItem == pag.FirstItem { // No
1082				break
1083			} else { // Yes
1084				next = pag.FirstReturnedItem - 1
1085			}
1086		}
1087	}
1088	return nil
1089}
1090
1091func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
1092	ctx := context.Background()
1093	client := &http.Client{}
1094	httpReq, _ := http.NewRequest(method, path, reqBody)
1095	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1096	if err != nil {
1097		return err
1098	}
1099	// Validate request against swagger spec
1100	requestValidationInput := &swagger.RequestValidationInput{
1101		Request:    httpReq,
1102		PathParams: pathParams,
1103		Route:      route,
1104	}
1105	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1106		return err
1107	}
1108	// Do API call
1109	resp, err := client.Do(httpReq)
1110	if err != nil {
1111		return err
1112	}
1113	if resp.Body == nil {
1114		return errors.New("Nil body")
1115	}
1116	//nolint
1117	defer resp.Body.Close()
1118	body, err := ioutil.ReadAll(resp.Body)
1119	if err != nil {
1120		return err
1121	}
1122	if resp.StatusCode != 200 {
1123		return fmt.Errorf("%d response: %s", resp.StatusCode, string(body))
1124	}
1125	// Unmarshal body into return struct
1126	if err := json.Unmarshal(body, returnStruct); err != nil {
1127		return err
1128	}
1129	// Validate response against swagger spec
1130	responseValidationInput := &swagger.ResponseValidationInput{
1131		RequestValidationInput: requestValidationInput,
1132		Status:                 resp.StatusCode,
1133		Header:                 resp.Header,
1134	}
1135	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1136	return swagger.ValidateResponse(ctx, responseValidationInput)
1137}
1138
1139func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
1140	ctx := context.Background()
1141	client := &http.Client{}
1142	httpReq, _ := http.NewRequest(method, path, reqBody)
1143	route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
1144	if err != nil {
1145		return err
1146	}
1147	// Validate request against swagger spec
1148	requestValidationInput := &swagger.RequestValidationInput{
1149		Request:    httpReq,
1150		PathParams: pathParams,
1151		Route:      route,
1152	}
1153	if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
1154		if expectedResponseCode != 400 {
1155			return err
1156		}
1157		log.Warn("The request does not match the API spec")
1158	}
1159	// Do API call
1160	resp, err := client.Do(httpReq)
1161	if err != nil {
1162		return err
1163	}
1164	if resp.Body == nil {
1165		return errors.New("Nil body")
1166	}
1167	//nolint
1168	defer resp.Body.Close()
1169	body, err := ioutil.ReadAll(resp.Body)
1170	if err != nil {
1171		return err
1172	}
1173	if resp.StatusCode != expectedResponseCode {
1174		return fmt.Errorf("Unexpected response code: %d", resp.StatusCode)
1175	}
1176	// Validate response against swagger spec
1177	responseValidationInput := &swagger.ResponseValidationInput{
1178		RequestValidationInput: requestValidationInput,
1179		Status:                 resp.StatusCode,
1180		Header:                 resp.Header,
1181	}
1182	responseValidationInput = responseValidationInput.SetBodyBytes(body)
1183	return swagger.ValidateResponse(ctx, responseValidationInput)
1184}