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