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