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