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 "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/hermeznetwork/hermez-node/test/til"
29 "github.com/iden3/go-iden3-crypto/babyjub"
30)
31
32// Pendinger is an interface that allows getting last returned item ID and PendingItems to be used for building fromItem
33// when testing paginated endpoints.
34type Pendinger interface {
35 GetPending() (pendingItems, lastItemID uint64)
36 Len() int
37}
38
39const apiPort = ":4010"
40const apiURL = "http://localhost" + apiPort + "/"
41
42var SetBlockchain = `
43 Type: Blockchain
44
45 AddToken(1)
46 AddToken(2)
47 AddToken(3)
48 AddToken(4)
49
50 // Coordinator accounts, Idxs: 256, 257
51 CreateAccountCoordinator(0) Coord
52 CreateAccountCoordinator(1) Coord
53
54 // close Block:0, Batch:0
55 > batch
56
57 CreateAccountDeposit(0) A: 500
58 CreateAccountDeposit(1) C: 0
59 CreateAccountCoordinator(0) C
60
61 // close Block:0, Batch:1
62 > batchL1
63 // Expected balances:
64 // Coord(0): 0, Coord(1): 0
65 // C(0): 0
66
67 CreateAccountDeposit(1) A: 500
68
69 // close Block:0, Batch:2
70 > batchL1
71
72 // close Block:0, Batch:3
73 > batchL1
74
75 CreateAccountDepositTransfer(0) B-A: 500, 100
76
77 // close Block:0, Batch:4
78 > batchL1
79 CreateAccountDeposit(0) D: 800
80
81 // close Block:0, Batch:5
82 > batchL1
83
84 CreateAccountCoordinator(1) B
85
86 Transfer(1) A-B: 200 (126)
87 Transfer(0) B-C: 100 (126)
88
89 // close Block:0, Batch:6
90 > batchL1 // forge L1User{1}, forge L1Coord{2}, forge L2{2}
91
92 Deposit(0) C: 500
93 DepositTransfer(0) C-D: 400, 100
94
95 Transfer(0) A-B: 100 (126)
96 Transfer(0) C-A: 50 (126)
97 Transfer(1) B-C: 100 (126)
98 Exit(0) A: 100 (126)
99
100 ForceTransfer(0) D-B: 200
101 ForceExit(0) B: 100
102
103 // close Block:0, Batch:7
104 > batchL1
105 > block
106
107 AddToken(5)
108 AddToken(6)
109 AddToken(7)
110 AddToken(8)
111
112 Transfer(0) D-A: 300 (126)
113 Transfer(0) B-D: 100 (126)
114
115 // close Block:1, Batch:0
116 > batchL1
117
118 CreateAccountCoordinator(0) F
119
120 CreateAccountCoordinator(0) G
121 CreateAccountCoordinator(0) H
122 CreateAccountCoordinator(0) I
123 CreateAccountCoordinator(0) J
124 CreateAccountCoordinator(0) K
125 CreateAccountCoordinator(0) L
126 CreateAccountCoordinator(0) M
127 CreateAccountCoordinator(0) N
128 CreateAccountCoordinator(0) O
129 CreateAccountCoordinator(0) P
130
131 CreateAccountCoordinator(5) G
132 CreateAccountCoordinator(5) H
133 CreateAccountCoordinator(5) I
134 CreateAccountCoordinator(5) J
135 CreateAccountCoordinator(5) K
136 CreateAccountCoordinator(5) L
137 CreateAccountCoordinator(5) M
138 CreateAccountCoordinator(5) N
139 CreateAccountCoordinator(5) O
140 CreateAccountCoordinator(5) P
141
142 CreateAccountCoordinator(2) G
143 CreateAccountCoordinator(2) H
144 CreateAccountCoordinator(2) I
145 CreateAccountCoordinator(2) J
146 CreateAccountCoordinator(2) K
147 CreateAccountCoordinator(2) L
148 CreateAccountCoordinator(2) M
149 CreateAccountCoordinator(2) N
150 CreateAccountCoordinator(2) O
151 CreateAccountCoordinator(2) P
152
153
154 > batch
155 > block
156 > batch
157 > block
158 > batch
159 > block
160`
161
162type testCommon struct {
163 blocks []common.Block
164 tokens []historydb.TokenWithUSD
165 batches []testBatch
166 fullBatches []testFullBatch
167 coordinators []historydb.CoordinatorAPI
168 accounts []testAccount
169 usrAddr string
170 usrBjj string
171 accs []common.Account
172 usrTxs []testTx
173 allTxs []testTx
174 exits []testExit
175 usrExits []testExit
176 poolTxsToSend []testPoolTxSend
177 poolTxsToReceive []testPoolTxReceive
178 auths []testAuth
179 router *swagger.Router
180 bids []testBid
181 slots []testSlot
182 auctionVars common.AuctionVariables
183 rollupVars common.RollupVariables
184 wdelayerVars common.WDelayerVariables
185}
186
187var tc testCommon
188var config configAPI
189var api *API
190
191// TestMain initializes the API server, and fill HistoryDB and StateDB with fake data,
192// emulating the task of the synchronizer in order to have data to be returned
193// by the API endpoints that will be tested
194func TestMain(m *testing.M) {
195 /*
196 til update considerations:
197 1. Two instructions sets should be enough (one for L2 another for historydb)
198 2. FillBlocksExtra function must be used, there is a coment on top of the function that explains which data is setted
199 3. Some data will not be generated by til nor FillBlocksExtra, test.GenXXX will still be required to cover this cases
200 4. Most of the historydb inserts should be replaced with nBlocks calls to AddBlockSCData
201 5. When defining til instructions, there is no need to have 100s of entries for each table, but it's interesting to
202 cover all different cases (for instance all tx types)
203 */
204
205 // Initializations
206 // Swagger
207 router := swagger.NewRouter().WithSwaggerFromFile("./swagger.yml")
208 // HistoryDB
209 pass := os.Getenv("POSTGRES_PASS")
210
211 database, err := db.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
212 if err != nil {
213 panic(err)
214 }
215 hdb := historydb.NewHistoryDB(database)
216 if err != nil {
217 panic(err)
218 }
219 // StateDB
220 dir, err := ioutil.TempDir("", "tmpdb")
221 if err != nil {
222 panic(err)
223 }
224 defer func() {
225 if err := os.RemoveAll(dir); err != nil {
226 panic(err)
227 }
228 }()
229 sdb, err := statedb.NewStateDB(dir, statedb.TypeTxSelector, 0)
230 if err != nil {
231 panic(err)
232 }
233 // L2DB
234 l2DB := l2db.NewL2DB(database, 10, 100, 24*time.Hour)
235 test.WipeDB(l2DB.DB()) // this will clean HistoryDB and L2DB
236
237 // Config (smart contract constants)
238 config = getConfigTest()
239
240 // API
241 apiGin := gin.Default()
242 api, err = NewAPI(
243 true,
244 true,
245 apiGin,
246 hdb,
247 sdb,
248 l2DB,
249 &config,
250 )
251 if err != nil {
252 panic(err)
253 }
254 // Start server
255 server := &http.Server{Addr: apiPort, Handler: apiGin}
256 go func() {
257 if err := server.ListenAndServe(); err != nil &&
258 err != http.ErrServerClosed {
259 panic(err)
260 }
261 }()
262
263 // Fill HistoryDB and StateDB with fake data
264 // Gen blocks and add them to DB
265 const nBlocks = 5
266 // TODO: UPDATE with til
267 blocks := test.GenBlocks(1, nBlocks+1)
268 err = api.h.AddBlocks(blocks)
269 if err != nil {
270 panic(err)
271 }
272 lastBlockNum := blocks[nBlocks-1].EthBlockNum
273
274 // Gen tokens and add them to DB
275 const nTokens = 10
276 // TODO: UPDATE with til
277 tokens, ethToken := test.GenTokens(nTokens, blocks)
278 err = api.h.AddTokens(tokens)
279 if err != nil {
280 panic(err)
281 }
282 tokens = append([]common.Token{ethToken}, tokens...)
283 // Set token value
284 tokensUSD := []historydb.TokenWithUSD{}
285 for i, tkn := range tokens {
286 token := historydb.TokenWithUSD{
287 TokenID: tkn.TokenID,
288 EthBlockNum: tkn.EthBlockNum,
289 EthAddr: tkn.EthAddr,
290 Name: tkn.Name,
291 Symbol: tkn.Symbol,
292 Decimals: tkn.Decimals,
293 }
294 // Set value of 50% of the tokens
295 if i%2 != 0 {
296 value := float64(i) * 1.234567
297 now := time.Now().UTC()
298 token.USD = &value
299 token.USDUpdate = &now
300 err = api.h.UpdateTokenValue(token.Symbol, value)
301 if err != nil {
302 panic(err)
303 }
304 }
305 tokensUSD = append(tokensUSD, token)
306 }
307 // Gen batches and add them to DB
308 const nBatches = 10
309 // TODO: UPDATE with til
310 batches := test.GenBatches(nBatches, blocks)
311 err = api.h.AddBatches(batches)
312 if err != nil {
313 panic(err)
314 }
315 // Gen accounts and add them to HistoryDB and StateDB
316 const totalAccounts = 40
317 const userAccounts = 4
318 usrAddr := ethCommon.BigToAddress(big.NewInt(4896847))
319 privK := babyjub.NewRandPrivKey()
320 usrBjj := privK.Public()
321 // TODO: UPDATE with til
322 accs := test.GenAccounts(totalAccounts, userAccounts, tokens, &usrAddr, usrBjj, batches)
323 err = api.h.AddAccounts(accs)
324 if err != nil {
325 panic(err)
326 }
327
328 // api.s.CreateAccount called in new part with til
329 /* for i := 0; i < len(accs); i++ {
330 if _, err := api.s.CreateAccount(accs[i].Idx, &accs[i]); err != nil {
331 panic(err)
332 }
333 } */
334
335 // helper to vinculate user related resources
336 usrIdxs := []string{}
337 for _, acc := range accs {
338 if acc.EthAddr == usrAddr || acc.PublicKey == usrBjj {
339 for _, token := range tokens {
340 if token.TokenID == acc.TokenID {
341 usrIdxs = append(usrIdxs, idxToHez(acc.Idx, token.Symbol))
342 }
343 }
344 }
345 }
346 // Gen exits and add them to DB
347 const totalExits = 40
348 // TODO: UPDATE with til
349 exits := test.GenExitTree(totalExits, batches, accs, blocks)
350 err = api.h.AddExitTree(exits)
351 if err != nil {
352 panic(err)
353 }
354
355 // L1 and L2 txs need to be sorted in a combined way
356 // Gen L1Txs
357 const totalL1Txs = 40
358 const userL1Txs = 4
359 // TODO: UPDATE with til
360 usrL1Txs, othrL1Txs := test.GenL1Txs(256, totalL1Txs, userL1Txs, &usrAddr, accs, tokens, blocks, batches)
361 // Gen L2Txs
362 const totalL2Txs = 20
363 const userL2Txs = 4
364 // TODO: UPDATE with til
365 usrL2Txs, othrL2Txs := test.GenL2Txs(256+totalL1Txs, totalL2Txs, userL2Txs, &usrAddr, accs, tokens, blocks, batches)
366 // Sort txs
367 sortedTxs := []txSortFielder{}
368 for i := 0; i < len(usrL1Txs); i++ {
369 wL1 := wrappedL1(usrL1Txs[i])
370 sortedTxs = append(sortedTxs, &wL1)
371 }
372 for i := 0; i < len(othrL1Txs); i++ {
373 wL1 := wrappedL1(othrL1Txs[i])
374 sortedTxs = append(sortedTxs, &wL1)
375 }
376 for i := 0; i < len(usrL2Txs); i++ {
377 wL2 := wrappedL2(usrL2Txs[i])
378 sortedTxs = append(sortedTxs, &wL2)
379 }
380 for i := 0; i < len(othrL2Txs); i++ {
381 wL2 := wrappedL2(othrL2Txs[i])
382 sortedTxs = append(sortedTxs, &wL2)
383 }
384 sort.Sort(txsSort(sortedTxs))
385 // Store txs to DB
386 for _, genericTx := range sortedTxs {
387 l1 := genericTx.L1()
388 l2 := genericTx.L2()
389 if l1 != nil {
390 err = api.h.AddL1Txs([]common.L1Tx{*l1})
391 if err != nil {
392 panic(err)
393 }
394 } else if l2 != nil {
395 err = api.h.AddL2Txs([]common.L2Tx{*l2})
396 if err != nil {
397 panic(err)
398 }
399 } else {
400 panic("should be l1 or l2")
401 }
402 }
403
404 // Coordinators
405 const nCoords = 10
406 coords := test.GenCoordinators(nCoords, blocks)
407 err = api.h.AddCoordinators(coords)
408 if err != nil {
409 panic(err)
410 }
411 fromItem := uint(0)
412 limit := uint(99999)
413 coordinators, _, err := api.h.GetCoordinatorsAPI(&fromItem, &limit, historydb.OrderAsc)
414 if err != nil {
415 panic(err)
416 }
417
418 // Bids
419 const nBids = 20
420 bids := test.GenBids(nBids, blocks, coords)
421 err = api.h.AddBids(bids)
422 if err != nil {
423 panic(err)
424 }
425 testBids := genTestBids(blocks, coordinators, bids)
426
427 // Vars
428 var defaultSlotSetBid [6]*big.Int = [6]*big.Int{big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10), big.NewInt(10)}
429 auctionVars := common.AuctionVariables{
430 EthBlockNum: int64(2),
431 DonationAddress: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
432 DefaultSlotSetBid: defaultSlotSetBid,
433 Outbidding: uint16(1),
434 SlotDeadline: uint8(20),
435 BootCoordinator: ethCommon.HexToAddress("0x1111111111111111111111111111111111111111"),
436 ClosedAuctionSlots: uint16(2),
437 OpenAuctionSlots: uint16(5),
438 }
439
440 rollupVars := common.RollupVariables{
441 EthBlockNum: int64(3),
442 FeeAddToken: big.NewInt(100),
443 ForgeL1L2BatchTimeout: int64(44),
444 WithdrawalDelay: uint64(3000),
445 }
446
447 wdelayerVars := common.WDelayerVariables{
448 WithdrawalDelay: uint64(3000),
449 }
450
451 err = api.h.AddAuctionVars(&auctionVars)
452 if err != nil {
453 panic(err)
454 }
455
456 const nSlots = 20
457
458 // Set testCommon
459 usrTxs, allTxs := genTestTxs(sortedTxs, usrIdxs, accs, tokensUSD, blocks)
460 poolTxsToSend, poolTxsToReceive := genTestPoolTx(accs, []babyjub.PrivateKey{privK}, tokensUSD) // NOTE: pool txs are not inserted to the DB here. In the test they will be posted and getted.
461 testBatches, fullBatches := genTestBatches(blocks, batches, allTxs)
462 /* usrExits, allExits*/ _, _ = genTestExits(exits, tokensUSD, accs, usrIdxs)
463
464 // NEW WITH TIL
465 // Reset DB
466 test.WipeDB(api.h.DB())
467
468 tcc := til.NewContext(common.RollupConstMaxL1UserTx)
469 tilCfgExtra := til.ConfigExtra{
470 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
471 CoordUser: "A",
472 }
473 blocksData, err := tcc.GenerateBlocks(SetBlockchain)
474 if err != nil {
475 panic(err)
476 }
477 err = tcc.FillBlocksExtra(blocksData, &tilCfgExtra)
478 if err != nil {
479 panic(err)
480 }
481 // poolL2Txs, err := tcc.GeneratePoolL2Txs(til.SetPoolL2MinimumFlow0)
482
483 var blocksTc []common.Block
484 var batchesTc []common.Batch
485 var tokensTc []common.Token
486 var accountsTc []common.Account
487 var exitTreeTc []common.ExitInfo
488 var allL1TxsTc []common.L1Tx
489 var allL2TxsTc []common.L2Tx
490
491 AddAditionalInformation(blocksData)
492
493 for _, block := range blocksData {
494 // Insert block
495 err := api.h.AddBlockSCData(&block)
496 if err != nil {
497 panic(err)
498 }
499 blocksTc = append(blocksTc, block.Block)
500 tokensTc = append(tokensTc, block.Rollup.AddedTokens...)
501 allL1TxsTc = append(allL1TxsTc, block.Rollup.L1UserTxs...)
502 for _, batch := range block.Rollup.Batches {
503 allL2TxsTc = append(allL2TxsTc, batch.L2Txs...)
504 accountsTc = append(accountsTc, batch.CreatedAccounts...)
505 batchesTc = append(batchesTc, batch.Batch)
506 exitTreeTc = append(exitTreeTc, batch.ExitTree...)
507 allL1TxsTc = append(allL1TxsTc, batch.L1CoordinatorTxs...)
508 }
509 }
510 // lastBlockNum2 := blocksData[len(blocksData)-1].Block.EthBlockNum
511
512 tokensTc = append([]common.Token{ethToken}, tokensTc...)
513 tokensUSDTc := []historydb.TokenWithUSD{}
514 for i, tkn := range tokensTc {
515 token := historydb.TokenWithUSD{
516 TokenID: tkn.TokenID,
517 EthBlockNum: tkn.EthBlockNum,
518 EthAddr: tkn.EthAddr,
519 Name: tkn.Name,
520 Symbol: tkn.Symbol,
521 Decimals: tkn.Decimals,
522 }
523 // Set value of 50% of the tokens
524 if i%2 != 0 {
525 value := float64(i) * 1.234567
526 now := time.Now().UTC()
527 token.USD = &value
528 token.USDUpdate = &now
529 err = api.h.UpdateTokenValue(token.Symbol, value)
530 if err != nil {
531 panic(err)
532 }
533 }
534 tokensUSDTc = append(tokensUSDTc, token)
535 }
536
537 for i := 0; i < len(accountsTc); i++ {
538 if _, err := api.s.CreateAccount(accountsTc[i].Idx, &accountsTc[i]); err != nil {
539 panic(err)
540 }
541 }
542
543 usrIdxsTc := []string{}
544 for _, acc := range accountsTc {
545 for _, token := range tokensTc {
546 if token.TokenID == acc.TokenID {
547 usrIdxsTc = append(usrIdxsTc, idxToHez(acc.Idx, token.Symbol))
548 }
549 }
550 }
551
552 // Sort txs
553 sortedTxsTc := []txSortFielder{}
554 for i := 0; i < len(allL1TxsTc); i++ {
555 wL1 := wrappedL1(allL1TxsTc[i])
556 sortedTxsTc = append(sortedTxsTc, &wL1)
557 }
558 for i := 0; i < len(allL2TxsTc); i++ {
559 wL2 := wrappedL2(allL2TxsTc[i])
560 sortedTxsTc = append(sortedTxsTc, &wL2)
561 }
562 sort.Sort(txsSort(sortedTxsTc))
563
564 // Coordinators
565 const nCoordsTc = 10
566 coordsTc := test.GenCoordinators(nCoordsTc, blocksTc)
567 err = api.h.AddCoordinators(coordsTc)
568 if err != nil {
569 panic(err)
570 }
571 coordinatorsTc, _, err := api.h.GetCoordinatorsAPI(&fromItem, &limit, historydb.OrderAsc)
572 if err != nil {
573 panic(err)
574 }
575
576 // Bids
577 const nBidsTc = 20
578 bidsTc := test.GenBids(nBidsTc, blocksTc, coordsTc)
579 err = api.h.AddBids(bidsTc)
580 if err != nil {
581 panic(err)
582 }
583 testBidsTc := genTestBids(blocksTc, coordinatorsTc, bidsTc)
584 usrExitsTc, allExitsTc := genTestExits(exitTreeTc, tokensUSDTc, accountsTc, usrIdxsTc)
585 _, allTxsTc := genTestTxs(sortedTxsTc, usrIdxsTc, accountsTc, tokensUSDTc, blocksTc)
586 fmt.Println(allTxsTc)
587 // allTxsTc == allTxs
588 // testBatchesTc, fullBatchesTc := genTestBatches(blocksTc, batchesTc, allTxsTc)
589
590 tc = testCommon{
591 blocks: blocksTc,
592 tokens: tokensUSDTc,
593 batches: testBatches,
594 fullBatches: fullBatches,
595 coordinators: coordinatorsTc,
596 accounts: genTestAccounts(accountsTc, tokensUSDTc),
597 usrAddr: ethAddrToHez(usrAddr),
598 usrBjj: bjjToString(usrBjj),
599 accs: accountsTc,
600 usrTxs: usrTxs,
601 allTxs: allTxs,
602 exits: allExitsTc,
603 usrExits: usrExitsTc,
604 poolTxsToSend: poolTxsToSend,
605 poolTxsToReceive: poolTxsToReceive,
606 auths: genTestAuths(test.GenAuths(5)),
607 router: router,
608 bids: testBidsTc,
609 slots: api.genTestSlots(nSlots, lastBlockNum, testBids, auctionVars),
610 auctionVars: auctionVars,
611 rollupVars: rollupVars,
612 wdelayerVars: wdelayerVars,
613 }
614
615 /* tc = testCommon{
616 blocks: blocks,
617 tokens: tokensUSD,
618 batches: testBatches,
619 fullBatches: fullBatches,
620 coordinators: coordinators,
621 accounts: genTestAccounts(accs, tokensUSD),
622 usrAddr: ethAddrToHez(usrAddr),
623 usrBjj: bjjToString(usrBjj),
624 accs: accs,
625 usrTxs: usrTxs,
626 allTxs: allTxs,
627 exits: allExits,
628 usrExits: usrExits,
629 poolTxsToSend: poolTxsToSend,
630 poolTxsToReceive: poolTxsToReceive,
631 auths: genTestAuths(test.GenAuths(5)),
632 router: router,
633 bids: testBids,
634 slots: api.genTestSlots(nSlots, lastBlockNum, testBids, auctionVars),
635 auctionVars: auctionVars,
636 rollupVars: rollupVars,
637 wdelayerVars: wdelayerVars,
638 } */
639
640 // Fake server
641 if os.Getenv("FAKE_SERVER") == "yes" {
642 for {
643 log.Info("Running fake server at " + apiURL + " until ^C is received")
644 time.Sleep(30 * time.Second)
645 }
646 }
647 // Run tests
648 result := m.Run()
649 // Stop server
650 if err := server.Shutdown(context.Background()); err != nil {
651 panic(err)
652 }
653 if err := database.Close(); err != nil {
654 panic(err)
655 }
656 if err := os.RemoveAll(dir); err != nil {
657 panic(err)
658 }
659 os.Exit(result)
660}
661
662func doGoodReqPaginated(
663 path, order string,
664 iterStruct Pendinger,
665 appendIter func(res interface{}),
666) error {
667 var next uint64
668 firstIte := true
669 expectedTotal := 0
670 totalReceived := 0
671 for {
672 // Calculate fromItem
673 iterPath := path
674 if firstIte {
675 if order == historydb.OrderDesc {
676 // Fetch first item in reverse order
677 iterPath += "99999" // Asumption that for testing there won't be any itemID > 99999
678 } else {
679 iterPath += "0"
680 }
681 } else {
682 iterPath += strconv.Itoa(int(next))
683 }
684 // Call API to get this iteration items
685 if err := doGoodReq("GET", iterPath+"&order="+order, nil, iterStruct); err != nil {
686 return err
687 }
688 appendIter(iterStruct)
689 // Keep iterating?
690 remaining, lastID := iterStruct.GetPending()
691 if remaining == 0 {
692 break
693 }
694 if order == historydb.OrderDesc {
695 next = lastID - 1
696 } else {
697 next = lastID + 1
698 }
699 // Check that the expected amount of items is consistent across iterations
700 totalReceived += iterStruct.Len()
701 if firstIte {
702 firstIte = false
703 expectedTotal = totalReceived + int(remaining)
704 }
705 if expectedTotal != totalReceived+int(remaining) {
706 panic(fmt.Sprintf(
707 "pagination error, totalReceived + remaining should be %d, but is %d",
708 expectedTotal, totalReceived+int(remaining),
709 ))
710 }
711 }
712 return nil
713}
714
715func doGoodReq(method, path string, reqBody io.Reader, returnStruct interface{}) error {
716 ctx := context.Background()
717 client := &http.Client{}
718 httpReq, err := http.NewRequest(method, path, reqBody)
719 if err != nil {
720 return err
721 }
722 if reqBody != nil {
723 httpReq.Header.Add("Content-Type", "application/json")
724 }
725 route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
726 if err != nil {
727 return err
728 }
729 // Validate request against swagger spec
730 requestValidationInput := &swagger.RequestValidationInput{
731 Request: httpReq,
732 PathParams: pathParams,
733 Route: route,
734 }
735 if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
736 return err
737 }
738 // Do API call
739 resp, err := client.Do(httpReq)
740 if err != nil {
741 return err
742 }
743 if resp.Body == nil && returnStruct != nil {
744 return errors.New("Nil body")
745 }
746 //nolint
747 defer resp.Body.Close()
748 body, err := ioutil.ReadAll(resp.Body)
749 if err != nil {
750 return err
751 }
752 if resp.StatusCode != 200 {
753 return fmt.Errorf("%d response. Body: %s", resp.StatusCode, string(body))
754 }
755 if returnStruct == nil {
756 return nil
757 }
758 // Unmarshal body into return struct
759 if err := json.Unmarshal(body, returnStruct); err != nil {
760 log.Error("invalid json: " + string(body))
761 return err
762 }
763 // Validate response against swagger spec
764 responseValidationInput := &swagger.ResponseValidationInput{
765 RequestValidationInput: requestValidationInput,
766 Status: resp.StatusCode,
767 Header: resp.Header,
768 }
769 responseValidationInput = responseValidationInput.SetBodyBytes(body)
770 return swagger.ValidateResponse(ctx, responseValidationInput)
771}
772
773func doBadReq(method, path string, reqBody io.Reader, expectedResponseCode int) error {
774 ctx := context.Background()
775 client := &http.Client{}
776 httpReq, _ := http.NewRequest(method, path, reqBody)
777 route, pathParams, err := tc.router.FindRoute(httpReq.Method, httpReq.URL)
778 if err != nil {
779 return err
780 }
781 // Validate request against swagger spec
782 requestValidationInput := &swagger.RequestValidationInput{
783 Request: httpReq,
784 PathParams: pathParams,
785 Route: route,
786 }
787 if err := swagger.ValidateRequest(ctx, requestValidationInput); err != nil {
788 if expectedResponseCode != 400 {
789 return err
790 }
791 log.Warn("The request does not match the API spec")
792 }
793 // Do API call
794 resp, err := client.Do(httpReq)
795 if err != nil {
796 return err
797 }
798 if resp.Body == nil {
799 return errors.New("Nil body")
800 }
801 //nolint
802 defer resp.Body.Close()
803 body, err := ioutil.ReadAll(resp.Body)
804 if err != nil {
805 return err
806 }
807 if resp.StatusCode != expectedResponseCode {
808 return fmt.Errorf("Unexpected response code: %d. Body: %s", resp.StatusCode, string(body))
809 }
810 // Validate response against swagger spec
811 responseValidationInput := &swagger.ResponseValidationInput{
812 RequestValidationInput: requestValidationInput,
813 Status: resp.StatusCode,
814 Header: resp.Header,
815 }
816 responseValidationInput = responseValidationInput.SetBodyBytes(body)
817 return swagger.ValidateResponse(ctx, responseValidationInput)
818}
819
820// test helpers
821
822func getTimestamp(blockNum int64, blocks []common.Block) time.Time {
823 for i := 0; i < len(blocks); i++ {
824 if blocks[i].EthBlockNum == blockNum {
825 return blocks[i].Timestamp
826 }
827 }
828 panic("timesamp not found")
829}
830
831func getTokenByID(id common.TokenID, tokens []historydb.TokenWithUSD) historydb.TokenWithUSD {
832 for i := 0; i < len(tokens); i++ {
833 if tokens[i].TokenID == id {
834 return tokens[i]
835 }
836 }
837 panic("token not found")
838}
839
840func getTokenByIdx(idx common.Idx, tokens []historydb.TokenWithUSD, accs []common.Account) historydb.TokenWithUSD {
841 for _, acc := range accs {
842 if idx == acc.Idx {
843 return getTokenByID(acc.TokenID, tokens)
844 }
845 }
846 panic("token not found")
847}
848
849func getAccountByIdx(idx common.Idx, accs []common.Account) *common.Account {
850 for _, acc := range accs {
851 if acc.Idx == idx {
852 return &acc
853 }
854 }
855 panic("account not found")
856}
857
858func getBlockByNum(ethBlockNum int64, blocks []common.Block) common.Block {
859 for _, b := range blocks {
860 if b.EthBlockNum == ethBlockNum {
861 return b
862 }
863 }
864 panic("block not found")
865}
866
867func getCoordinatorByBidder(bidder ethCommon.Address, coordinators []historydb.CoordinatorAPI) historydb.CoordinatorAPI {
868 for _, c := range coordinators {
869 if c.Bidder == bidder {
870 return c
871 }
872 }
873 panic("coordinator not found")
874}