You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1184 lines
35 KiB

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