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.

716 lines
20 KiB

  1. package api
  2. import (
  3. "context"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "math/big"
  10. "net/http"
  11. "os"
  12. "sort"
  13. "strconv"
  14. "testing"
  15. "time"
  16. ethCommon "github.com/ethereum/go-ethereum/common"
  17. swagger "github.com/getkin/kin-openapi/openapi3filter"
  18. "github.com/gin-gonic/gin"
  19. "github.com/hermeznetwork/hermez-node/common"
  20. dbUtils "github.com/hermeznetwork/hermez-node/db"
  21. "github.com/hermeznetwork/hermez-node/db/historydb"
  22. "github.com/hermeznetwork/hermez-node/db/l2db"
  23. "github.com/hermeznetwork/hermez-node/db/statedb"
  24. "github.com/hermeznetwork/hermez-node/log"
  25. "github.com/hermeznetwork/hermez-node/test"
  26. "github.com/iden3/go-iden3-crypto/babyjub"
  27. "github.com/mitchellh/copystructure"
  28. "github.com/stretchr/testify/assert"
  29. "github.com/stretchr/testify/require"
  30. )
  31. const apiPort = ":4010"
  32. const apiURL = "http://localhost" + apiPort + "/"
  33. type testCommon struct {
  34. blocks []common.Block
  35. tokens []historydb.TokenRead
  36. batches []common.Batch
  37. usrAddr string
  38. usrBjj string
  39. accs []common.Account
  40. usrTxs historyTxAPIs
  41. othrTxs historyTxAPIs
  42. allTxs historyTxAPIs
  43. router *swagger.Router
  44. }
  45. type historyTxAPIs []historyTxAPI
  46. func (h historyTxAPIs) Len() int { return len(h) }
  47. func (h historyTxAPIs) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
  48. func (h historyTxAPIs) Less(i, j int) bool {
  49. // i not forged yet
  50. if h[i].BatchNum == nil {
  51. if h[j].BatchNum != nil { // j is already forged
  52. return false
  53. }
  54. // Both aren't forged, is i in a smaller position?
  55. return h[i].Position < h[j].Position
  56. }
  57. // i is forged
  58. if h[j].BatchNum == nil {
  59. return true // j is not forged
  60. }
  61. // Both are forged
  62. if *h[i].BatchNum == *h[j].BatchNum {
  63. // At the same batch, is i in a smaller position?
  64. return h[i].Position < h[j].Position
  65. }
  66. // At different batches, is i in a smaller batch?
  67. return *h[i].BatchNum < *h[j].BatchNum
  68. }
  69. var tc testCommon
  70. func TestMain(m *testing.M) {
  71. // Init swagger
  72. router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
  73. // Init DBs
  74. // HistoryDB
  75. pass := os.Getenv("POSTGRES_PASS")
  76. db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
  77. if err != nil {
  78. panic(err)
  79. }
  80. hdb := historydb.NewHistoryDB(db)
  81. err = hdb.Reorg(-1)
  82. if err != nil {
  83. panic(err)
  84. }
  85. // StateDB
  86. dir, err := ioutil.TempDir("", "tmpdb")
  87. if err != nil {
  88. panic(err)
  89. }
  90. sdb, err := statedb.NewStateDB(dir, statedb.TypeTxSelector, 0)
  91. if err != nil {
  92. panic(err)
  93. }
  94. // L2DB
  95. l2DB := l2db.NewL2DB(db, 10, 100, 24*time.Hour)
  96. test.CleanL2DB(l2DB.DB())
  97. // Init API
  98. api := gin.Default()
  99. if err := SetAPIEndpoints(
  100. true,
  101. true,
  102. api,
  103. hdb,
  104. sdb,
  105. l2DB,
  106. ); err != nil {
  107. panic(err)
  108. }
  109. // Start server
  110. server := &http.Server{Addr: apiPort, Handler: api}
  111. go func() {
  112. if err := server.ListenAndServe(); err != nil &&
  113. err != http.ErrServerClosed {
  114. panic(err)
  115. }
  116. }()
  117. // Populate DBs
  118. // Clean DB
  119. err = h.Reorg(0)
  120. if err != nil {
  121. panic(err)
  122. }
  123. // Gen blocks and add them to DB
  124. const nBlocks = 5
  125. blocks := test.GenBlocks(1, nBlocks+1)
  126. err = h.AddBlocks(blocks)
  127. if err != nil {
  128. panic(err)
  129. }
  130. // Gen tokens and add them to DB
  131. const nTokens = 10
  132. tokens := test.GenTokens(nTokens, blocks)
  133. err = h.AddTokens(tokens)
  134. if err != nil {
  135. panic(err)
  136. }
  137. // Set token value
  138. tokensUSD := []historydb.TokenRead{}
  139. for i, tkn := range tokens {
  140. token := historydb.TokenRead{
  141. TokenID: tkn.TokenID,
  142. EthBlockNum: tkn.EthBlockNum,
  143. EthAddr: tkn.EthAddr,
  144. Name: tkn.Name,
  145. Symbol: tkn.Symbol,
  146. Decimals: tkn.Decimals,
  147. }
  148. // Set value of 50% of the tokens
  149. if i%2 != 0 {
  150. value := float64(i) * 1.234567
  151. now := time.Now().UTC()
  152. token.USD = &value
  153. token.USDUpdate = &now
  154. err = h.UpdateTokenValue(token.Symbol, value)
  155. if err != nil {
  156. panic(err)
  157. }
  158. }
  159. tokensUSD = append(tokensUSD, token)
  160. }
  161. // Gen batches and add them to DB
  162. const nBatches = 10
  163. batches := test.GenBatches(nBatches, blocks)
  164. err = h.AddBatches(batches)
  165. if err != nil {
  166. panic(err)
  167. }
  168. // Gen accounts and add them to DB
  169. const totalAccounts = 40
  170. const userAccounts = 4
  171. usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
  172. privK := babyjub.NewRandPrivKey()
  173. usrBjj := privK.Public()
  174. accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
  175. err = h.AddAccounts(accs)
  176. if err != nil {
  177. panic(err)
  178. }
  179. // Gen L1Txs and add them to DB
  180. const totalL1Txs = 40
  181. const userL1Txs = 4
  182. usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
  183. var l1Txs []common.L1Tx
  184. l1Txs = append(l1Txs, usrL1Txs...)
  185. l1Txs = append(l1Txs, othrL1Txs...)
  186. err = h.AddL1Txs(l1Txs)
  187. if err != nil {
  188. panic(err)
  189. }
  190. // Gen L2Txs and add them to DB
  191. const totalL2Txs = 20
  192. const userL2Txs = 4
  193. usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
  194. var l2Txs []common.L2Tx
  195. l2Txs = append(l2Txs, usrL2Txs...)
  196. l2Txs = append(l2Txs, othrL2Txs...)
  197. err = h.AddL2Txs(l2Txs)
  198. if err != nil {
  199. panic(err)
  200. }
  201. // Set test commons
  202. txsToAPITxs := func(l1Txs []common.L1Tx, l2Txs []common.L2Tx, blocks []common.Block, tokens []historydb.TokenRead) historyTxAPIs {
  203. /* TODO: stop using l1tx.Tx() & l2tx.Tx()
  204. // Transform L1Txs and L2Txs to generic Txs
  205. genericTxs := []*common.Tx{}
  206. for _, l1tx := range l1Txs {
  207. genericTxs = append(genericTxs, l1tx.Tx())
  208. }
  209. for _, l2tx := range l2Txs {
  210. genericTxs = append(genericTxs, l2tx.Tx())
  211. }
  212. // Transform generic Txs to HistoryTx
  213. historyTxs := []historydb.HistoryTx{}
  214. for _, genericTx := range genericTxs {
  215. // find timestamp
  216. var timestamp time.Time
  217. for i := 0; i < len(blocks); i++ {
  218. if blocks[i].EthBlockNum == genericTx.EthBlockNum {
  219. timestamp = blocks[i].Timestamp
  220. break
  221. }
  222. }
  223. // find token
  224. var token historydb.TokenRead
  225. if genericTx.IsL1 {
  226. tokenID := genericTx.TokenID
  227. found := false
  228. for i := 0; i < len(tokens); i++ {
  229. if tokens[i].TokenID == tokenID {
  230. token = tokens[i]
  231. found = true
  232. break
  233. }
  234. }
  235. if !found {
  236. panic("Token not found")
  237. }
  238. } else {
  239. var id common.TokenID
  240. found := false
  241. for _, acc := range accs {
  242. if acc.Idx == genericTx.FromIdx {
  243. found = true
  244. id = acc.TokenID
  245. break
  246. }
  247. }
  248. if !found {
  249. panic("tokenID not found")
  250. }
  251. found = false
  252. for i := 0; i < len(tokensUSD); i++ {
  253. if tokensUSD[i].TokenID == id {
  254. token = tokensUSD[i]
  255. found = true
  256. break
  257. }
  258. }
  259. if !found {
  260. panic("tokenID not found")
  261. }
  262. }
  263. var usd, loadUSD, feeUSD *float64
  264. if token.USD != nil {
  265. noDecimalsUSD := *token.USD / math.Pow(10, float64(token.Decimals))
  266. usd = new(float64)
  267. *usd = noDecimalsUSD * genericTx.AmountFloat
  268. if genericTx.IsL1 {
  269. loadUSD = new(float64)
  270. *loadUSD = noDecimalsUSD * *genericTx.LoadAmountFloat
  271. } else {
  272. feeUSD = new(float64)
  273. *feeUSD = *usd * genericTx.Fee.Percentage()
  274. }
  275. }
  276. historyTx := &historydb.HistoryTx{
  277. IsL1: genericTx.IsL1,
  278. TxID: genericTx.TxID,
  279. Type: genericTx.Type,
  280. Position: genericTx.Position,
  281. ToIdx: genericTx.ToIdx,
  282. Amount: genericTx.Amount,
  283. HistoricUSD: usd,
  284. BatchNum: genericTx.BatchNum,
  285. EthBlockNum: genericTx.EthBlockNum,
  286. ToForgeL1TxsNum: genericTx.ToForgeL1TxsNum,
  287. UserOrigin: genericTx.UserOrigin,
  288. FromBJJ: genericTx.FromBJJ,
  289. LoadAmount: genericTx.LoadAmount,
  290. HistoricLoadAmountUSD: loadUSD,
  291. Fee: genericTx.Fee,
  292. HistoricFeeUSD: feeUSD,
  293. Nonce: genericTx.Nonce,
  294. Timestamp: timestamp,
  295. TokenID: token.TokenID,
  296. TokenEthBlockNum: token.EthBlockNum,
  297. TokenEthAddr: token.EthAddr,
  298. TokenName: token.Name,
  299. TokenSymbol: token.Symbol,
  300. TokenDecimals: token.Decimals,
  301. TokenUSD: token.USD,
  302. TokenUSDUpdate: token.USDUpdate,
  303. }
  304. if genericTx.FromIdx != 0 {
  305. historyTx.FromIdx = &genericTx.FromIdx
  306. }
  307. if !bytes.Equal(genericTx.FromEthAddr.Bytes(), common.EmptyAddr.Bytes()) {
  308. historyTx.FromEthAddr = &genericTx.FromEthAddr
  309. }
  310. historyTxs = append(historyTxs, historyTx)
  311. }
  312. return historyTxAPIs(historyTxsToAPI(historyTxs))
  313. */
  314. return nil
  315. }
  316. usrTxs := txsToAPITxs(usrL1Txs, usrL2Txs, blocks, tokensUSD)
  317. sort.Sort(usrTxs)
  318. othrTxs := txsToAPITxs(othrL1Txs, othrL2Txs, blocks, tokensUSD)
  319. sort.Sort(othrTxs)
  320. allTxs := append(usrTxs, othrTxs...)
  321. sort.Sort(allTxs)
  322. tc = testCommon{
  323. blocks: blocks,
  324. tokens: tokensUSD,
  325. batches: batches,
  326. usrAddr: "hez:" + usrAddr.String(),
  327. usrBjj: bjjToString(usrBjj),
  328. accs: accs,
  329. usrTxs: usrTxs,
  330. othrTxs: othrTxs,
  331. allTxs: allTxs,
  332. router: router,
  333. }
  334. // Run tests
  335. result := m.Run()
  336. // Stop server
  337. if err := server.Shutdown(context.Background()); err != nil {
  338. panic(err)
  339. }
  340. if err := db.Close(); err != nil {
  341. panic(err)
  342. }
  343. os.Exit(result)
  344. }
  345. func TestGetHistoryTxs(t *testing.T) {
  346. return
  347. //nolint:govet this is a temp patch to avoid running the test
  348. endpoint := apiURL + "transactions-history"
  349. fetchedTxs := historyTxAPIs{}
  350. appendIter := func(intr interface{}) {
  351. for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
  352. tmp, err := copystructure.Copy(intr.(*historyTxsAPI).Txs[i])
  353. if err != nil {
  354. panic(err)
  355. }
  356. fetchedTxs = append(fetchedTxs, tmp.(historyTxAPI))
  357. }
  358. }
  359. // Get all (no filters)
  360. limit := 8
  361. path := fmt.Sprintf("%s?limit=%d&offset=", endpoint, limit)
  362. err := doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  363. assert.NoError(t, err)
  364. assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
  365. // Get by ethAddr
  366. fetchedTxs = historyTxAPIs{}
  367. limit = 7
  368. path = fmt.Sprintf(
  369. "%s?hermezEthereumAddress=%s&limit=%d&offset=",
  370. endpoint, tc.usrAddr, limit,
  371. )
  372. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  373. assert.NoError(t, err)
  374. assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
  375. // Get by bjj
  376. fetchedTxs = historyTxAPIs{}
  377. limit = 6
  378. path = fmt.Sprintf(
  379. "%s?BJJ=%s&limit=%d&offset=",
  380. endpoint, tc.usrBjj, limit,
  381. )
  382. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  383. assert.NoError(t, err)
  384. assertHistoryTxAPIs(t, tc.usrTxs, fetchedTxs)
  385. // Get by tokenID
  386. fetchedTxs = historyTxAPIs{}
  387. limit = 5
  388. tokenID := tc.allTxs[0].Token.TokenID
  389. path = fmt.Sprintf(
  390. "%s?tokenId=%d&limit=%d&offset=",
  391. endpoint, tokenID, limit,
  392. )
  393. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  394. assert.NoError(t, err)
  395. tokenIDTxs := historyTxAPIs{}
  396. for i := 0; i < len(tc.allTxs); i++ {
  397. if tc.allTxs[i].Token.TokenID == tokenID {
  398. tokenIDTxs = append(tokenIDTxs, tc.allTxs[i])
  399. }
  400. }
  401. assertHistoryTxAPIs(t, tokenIDTxs, fetchedTxs)
  402. // idx
  403. fetchedTxs = historyTxAPIs{}
  404. limit = 4
  405. idx := tc.allTxs[0].ToIdx
  406. path = fmt.Sprintf(
  407. "%s?accountIndex=%s&limit=%d&offset=",
  408. endpoint, idx, limit,
  409. )
  410. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  411. assert.NoError(t, err)
  412. idxTxs := historyTxAPIs{}
  413. for i := 0; i < len(tc.allTxs); i++ {
  414. if (tc.allTxs[i].FromIdx != nil && (*tc.allTxs[i].FromIdx)[6:] == idx[6:]) ||
  415. tc.allTxs[i].ToIdx[6:] == idx[6:] {
  416. idxTxs = append(idxTxs, tc.allTxs[i])
  417. }
  418. }
  419. assertHistoryTxAPIs(t, idxTxs, fetchedTxs)
  420. // batchNum
  421. fetchedTxs = historyTxAPIs{}
  422. limit = 3
  423. batchNum := tc.allTxs[0].BatchNum
  424. path = fmt.Sprintf(
  425. "%s?batchNum=%d&limit=%d&offset=",
  426. endpoint, *batchNum, limit,
  427. )
  428. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  429. assert.NoError(t, err)
  430. batchNumTxs := historyTxAPIs{}
  431. for i := 0; i < len(tc.allTxs); i++ {
  432. if tc.allTxs[i].BatchNum != nil &&
  433. *tc.allTxs[i].BatchNum == *batchNum {
  434. batchNumTxs = append(batchNumTxs, tc.allTxs[i])
  435. }
  436. }
  437. assertHistoryTxAPIs(t, batchNumTxs, fetchedTxs)
  438. // type
  439. txTypes := []common.TxType{
  440. common.TxTypeExit,
  441. common.TxTypeTransfer,
  442. common.TxTypeDeposit,
  443. common.TxTypeCreateAccountDeposit,
  444. common.TxTypeCreateAccountDepositTransfer,
  445. common.TxTypeDepositTransfer,
  446. common.TxTypeForceTransfer,
  447. common.TxTypeForceExit,
  448. common.TxTypeTransferToEthAddr,
  449. common.TxTypeTransferToBJJ,
  450. }
  451. for _, txType := range txTypes {
  452. fetchedTxs = historyTxAPIs{}
  453. limit = 2
  454. path = fmt.Sprintf(
  455. "%s?type=%s&limit=%d&offset=",
  456. endpoint, txType, limit,
  457. )
  458. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  459. assert.NoError(t, err)
  460. txTypeTxs := historyTxAPIs{}
  461. for i := 0; i < len(tc.allTxs); i++ {
  462. if tc.allTxs[i].Type == txType {
  463. txTypeTxs = append(txTypeTxs, tc.allTxs[i])
  464. }
  465. }
  466. assertHistoryTxAPIs(t, txTypeTxs, fetchedTxs)
  467. }
  468. // Multiple filters
  469. fetchedTxs = historyTxAPIs{}
  470. limit = 1
  471. path = fmt.Sprintf(
  472. "%s?batchNum=%d&tokeId=%d&limit=%d&offset=",
  473. endpoint, *batchNum, tokenID, limit,
  474. )
  475. err = doGoodReqPaginated(path, &historyTxsAPI{}, appendIter)
  476. assert.NoError(t, err)
  477. mixedTxs := historyTxAPIs{}
  478. for i := 0; i < len(tc.allTxs); i++ {
  479. if tc.allTxs[i].BatchNum != nil {
  480. if *tc.allTxs[i].BatchNum == *batchNum && tc.allTxs[i].Token.TokenID == tokenID {
  481. mixedTxs = append(mixedTxs, tc.allTxs[i])
  482. }
  483. }
  484. }
  485. assertHistoryTxAPIs(t, mixedTxs, fetchedTxs)
  486. // All, in reverse order
  487. fetchedTxs = historyTxAPIs{}
  488. limit = 5
  489. path = fmt.Sprintf("%s?", endpoint)
  490. appendIterRev := func(intr interface{}) {
  491. tmpAll := historyTxAPIs{}
  492. for i := 0; i < len(intr.(*historyTxsAPI).Txs); i++ {
  493. tmp, err := copystructure.Copy(intr.(*historyTxsAPI).Txs[i])
  494. if err != nil {
  495. panic(err)
  496. }
  497. tmpAll = append(tmpAll, tmp.(historyTxAPI))
  498. }
  499. fetchedTxs = append(tmpAll, fetchedTxs...)
  500. }
  501. err = doGoodReqPaginatedReverse(path, &historyTxsAPI{}, appendIterRev, limit)
  502. assert.NoError(t, err)
  503. assertHistoryTxAPIs(t, tc.allTxs, fetchedTxs)
  504. // 400
  505. path = fmt.Sprintf(
  506. "%s?accountIndex=%s&hermezEthereumAddress=%s",
  507. endpoint, idx, tc.usrAddr,
  508. )
  509. err = doBadReq("GET", path, nil, 400)
  510. assert.NoError(t, err)
  511. path = fmt.Sprintf("%s?tokenId=X", endpoint)
  512. err = doBadReq("GET", path, nil, 400)
  513. assert.NoError(t, err)
  514. // 404
  515. path = fmt.Sprintf("%s?batchNum=999999", endpoint)
  516. err = doBadReq("GET", path, nil, 404)
  517. assert.NoError(t, err)
  518. path = fmt.Sprintf("%s?limit=1000&offset=1000", endpoint)
  519. err = doBadReq("GET", path, nil, 404)
  520. assert.NoError(t, err)
  521. }
  522. //nolint:govet this is a temp patch to avoid running the test
  523. func assertHistoryTxAPIs(t *testing.T, expected, actual historyTxAPIs) {
  524. require.Equal(t, len(expected), len(actual))
  525. for i := 0; i < len(actual); i++ { //nolint len(actual) won't change within the loop
  526. assert.Equal(t, expected[i].Timestamp.Unix(), actual[i].Timestamp.Unix())
  527. expected[i].Timestamp = actual[i].Timestamp
  528. if expected[i].Token.USDUpdate == nil {
  529. assert.Equal(t, expected[i].Token.USDUpdate, actual[i].Token.USDUpdate)
  530. } else {
  531. assert.Equal(t, expected[i].Token.USDUpdate.Unix(), actual[i].Token.USDUpdate.Unix())
  532. expected[i].Token.USDUpdate = actual[i].Token.USDUpdate
  533. }
  534. test.AssertUSD(t, expected[i].HistoricUSD, actual[i].HistoricUSD)
  535. if expected[i].L2Info != nil {
  536. test.AssertUSD(t, expected[i].L2Info.HistoricFeeUSD, actual[i].L2Info.HistoricFeeUSD)
  537. } else {
  538. test.AssertUSD(t, expected[i].L1Info.HistoricLoadAmountUSD, actual[i].L1Info.HistoricLoadAmountUSD)
  539. }
  540. assert.Equal(t, expected[i], actual[i])
  541. }
  542. }
  543. //nolint:govet this is a temp patch to avoid running the test
  544. func doGoodReqPaginated(
  545. path string,
  546. iterStruct paginationer,
  547. appendIter func(res interface{}),
  548. ) error {
  549. next := 0
  550. for {
  551. // Call API to get this iteration items
  552. if err := doGoodReq("GET", path+strconv.Itoa(next), nil, iterStruct); err != nil {
  553. return err
  554. }
  555. appendIter(iterStruct)
  556. // Keep iterating?
  557. pag := iterStruct.GetPagination()
  558. if pag.LastReturnedItem == pag.TotalItems-1 { // No
  559. break
  560. } else { // Yes
  561. next = int(pag.LastReturnedItem + 1)
  562. }
  563. }
  564. return nil
  565. }
  566. //nolint:govet this is a temp patch to avoid running the test
  567. func doGoodReqPaginatedReverse(
  568. path string,
  569. iterStruct paginationer,
  570. appendIter func(res interface{}),
  571. limit int,
  572. ) error {
  573. next := 0
  574. first := true
  575. for {
  576. // Call API to get this iteration items
  577. if first {
  578. first = false
  579. pagQuery := fmt.Sprintf("last=true&limit=%d", limit)
  580. if err := doGoodReq("GET", path+pagQuery, nil, iterStruct); err != nil {
  581. return err
  582. }
  583. } else {
  584. pagQuery := fmt.Sprintf("offset=%d&limit=%d", next, limit)
  585. if err := doGoodReq("GET", path+pagQuery, nil, iterStruct); err != nil {
  586. return err
  587. }
  588. }
  589. appendIter(iterStruct)
  590. // Keep iterating?
  591. pag := iterStruct.GetPagination()
  592. if iterStruct.Len() == pag.TotalItems || pag.LastReturnedItem-iterStruct.Len() == -1 { // No
  593. break
  594. } else { // Yes
  595. prevOffset := next
  596. next = pag.LastReturnedItem - iterStruct.Len() - limit + 1
  597. if next < 0 {
  598. next = 0
  599. limit = prevOffset
  600. }
  601. }
  602. }
  603. return nil
  604. }
  605. //nolint:govet this is a temp patch to avoid running the test
  606. func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
  607. ctx := context.Background()
  608. client := &http.Client{}
  609. httpReq, _ := http.NewRequest(method, path, reqBody)
  610. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  611. if err != nil {
  612. return err
  613. }
  614. // Validate request against swagger spec
  615. requestValidationInput := &swagger.RequestValidationInput{
  616. Request: httpReq,
  617. PathParams: pathParams,
  618. Route: route,
  619. }
  620. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  621. return err
  622. }
  623. // Do API call
  624. resp, err := client.Do(httpReq)
  625. if err != nil {
  626. return err
  627. }
  628. if resp.Body == nil {
  629. return errors.New("Nil body")
  630. }
  631. //nolint
  632. defer resp.Body.Close()
  633. body, err := ioutil.ReadAll(resp.Body)
  634. if err != nil {
  635. return err
  636. }
  637. if resp.StatusCode != 200 {
  638. return fmt.Errorf("%d response: %s", resp.StatusCode, string(body))
  639. }
  640. // Unmarshal body into return struct
  641. if err := json.Unmarshal(body, returnStruct); err != nil {
  642. return err
  643. }
  644. // Validate response against swagger spec
  645. responseValidationInput := &swagger.ResponseValidationInput{
  646. RequestValidationInput: requestValidationInput,
  647. Status: resp.StatusCode,
  648. Header: resp.Header,
  649. }
  650. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  651. return swagger.ValidateResponse(ctx, responseValidationInput)
  652. }
  653. //nolint:govet this is a temp patch to avoid running the test
  654. func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
  655. ctx := context.Background()
  656. client := &http.Client{}
  657. httpReq, _ := http.NewRequest(method, path, reqBody)
  658. route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
  659. if err != nil {
  660. return err
  661. }
  662. // Validate request against swagger spec
  663. requestValidationInput := &swagger.RequestValidationInput{
  664. Request: httpReq,
  665. PathParams: pathParams,
  666. Route: route,
  667. }
  668. if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
  669. if expectedResponseCode != 400 {
  670. return err
  671. }
  672. log.Warn("The request does not match the API spec")
  673. }
  674. // Do API call
  675. resp, err := client.Do(httpReq)
  676. if err != nil {
  677. return err
  678. }
  679. if resp.Body == nil {
  680. return errors.New("Nil body")
  681. }
  682. //nolint
  683. defer resp.Body.Close()
  684. body, err := ioutil.ReadAll(resp.Body)
  685. if err != nil {
  686. return err
  687. }
  688. if resp.StatusCode != expectedResponseCode {
  689. return fmt.Errorf("Unexpected response code: %d", resp.StatusCode)
  690. }
  691. // Validate response against swagger spec
  692. responseValidationInput := &swagger.ResponseValidationInput{
  693. RequestValidationInput: requestValidationInput,
  694. Status: resp.StatusCode,
  695. Header: resp.Header,
  696. }
  697. responseValidationInput = responseValidationInput.SetBodyBytes(body)
  698. return swagger.ValidateResponse(ctx, responseValidationInput)
  699. }