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