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.

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