diff --git a/server/public_test.go b/server/public_test.go index c2cc3c5c..3d34be2e 100644 --- a/server/public_test.go +++ b/server/public_test.go @@ -8,10 +8,12 @@ import ( "blockbook/common" "blockbook/db" "blockbook/tests/dbtestdata" + "io" "io/ioutil" - "net" + "net/http" + "net/http/httptest" "os" - "strconv" + "strings" "testing" "github.com/golang/glog" @@ -52,32 +54,15 @@ func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *c return d, is, tmp } -// getFreePort asks the kernel for a free open port that is ready to use -func getFreePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") - if err != nil { - return 0, err - } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil -} - -func setupPublicHTTPServer(t *testing.T) (*PublicServer, string, string) { +func setupPublicHTTPServer(t *testing.T) (*PublicServer, string) { parser := btc.NewBitcoinParser( btc.GetChainParams("test"), &btc.Configuration{BlockAddressesToKeep: 1}) - db, is, path := setupRocksDB(t, parser) - - port, err := getFreePort() - if err != nil { - t.Fatal(err) - } + d, is, path := setupRocksDB(t, parser) + is.Coin = "Testnet" + is.CoinLabel = "Bitcoin Testnet" + is.CoinShortcut = "TEST" metrics, err := common.GetMetrics("Testnet") if err != nil { @@ -86,20 +71,23 @@ func setupPublicHTTPServer(t *testing.T) (*PublicServer, string, string) { chain, err := dbtestdata.NewFakeBlockChain(parser) if err != nil { - glog.Fatal("metrics: ", err) + glog.Fatal("fakechain: ", err) } - binding := "localhost:" + strconv.Itoa(port) + txCache, err := db.NewTxCache(d, chain, metrics, is, true) + if err != nil { + glog.Fatal("txCache: ", err) + } - s, err := NewPublicServer(binding, "", db, chain, nil, "", metrics, is, false) + // s.Run is never called, binding can be to any port + s, err := NewPublicServer("localhost:12345", "", d, chain, txCache, "", metrics, is, false) if err != nil { t.Fatal(err) } - return s, binding, path + return s, path } func closeAndDestroyPublicServer(t *testing.T, s *PublicServer, dbpath string) { - // destroy db if err := s.db.Close(); err != nil { t.Fatal(err) @@ -107,7 +95,104 @@ func closeAndDestroyPublicServer(t *testing.T, s *PublicServer, dbpath string) { os.RemoveAll(dbpath) } -func Test_PublicServer_UTXO(t *testing.T) { - s, _, dbpath := setupPublicHTTPServer(t) - defer closeAndDestroyPublicServer(t, s, dbpath) +func newGetRequest(url string, body io.Reader) *http.Request { + r, err := http.NewRequest("GET", url, body) + if err != nil { + glog.Fatal(err) + } + return r +} + +func Test_PublicServer_UTXO(t *testing.T) { + s, dbpath := setupPublicHTTPServer(t) + defer closeAndDestroyPublicServer(t, s, dbpath) + s.ConnectFullPublicInterface() + // take the handler of the public server and pass it to the test server + ts := httptest.NewServer(s.https.Handler) + defer ts.Close() + + tests := []struct { + name string + r *http.Request + status int + contentType string + body []string + }{ + { + name: "explorerTx", + r: newGetRequest(ts.URL+"/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db", nil), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{ + `Bitcoin Testnet Explorer`, + `

Transaction

`, + `fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db`, + `td class="data">0 TEST`, + `mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj`, + `13.60030331 TEST`, + `No Inputs (Newly Generated Coins)`, + ``, + }, + }, + // explorerSpendingTx + // explorerBlocks + { + name: "explorerBlock", + r: newGetRequest(ts.URL+"/block/225494", nil), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{ + `Bitcoin Testnet Explorer`, + `

Block 225494

`, + `00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6`, + `4`, // number of transactions + `mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL`, + `9172.83951061 TEST`, + `fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db`, + ``, + }, + }, + { + name: "explorerIndex", + r: newGetRequest(ts.URL+"/", nil), + status: http.StatusOK, + contentType: "text/html; charset=utf-8", + body: []string{ + `Bitcoin Testnet Explorer`, + `

Application status

`, + `

Synchronization with backend is disabled, the state of index is not up to date.

`, + `225494`, + `/Satoshi:0.16.3/`, + ``, + }, + }, + // explorerSearch + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resp, err := http.DefaultClient.Do(tt.r) + if err != nil { + t.Fatal(err) + } + defer resp.Body.Close() + if resp.StatusCode != tt.status { + t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) + } + if resp.Header["Content-Type"][0] != tt.contentType { + t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) + } + bb, err := ioutil.ReadAll(resp.Body) + if err != nil { + t.Fatal(err) + } + b := string(bb) + for _, c := range tt.body { + if !strings.Contains(b, c) { + t.Errorf("Page body does not contain %v, body %v", c, b) + break + } + } + }) + } + } diff --git a/tests/dbtestdata/fakechain.go b/tests/dbtestdata/fakechain.go index 350ece22..e17c64c5 100644 --- a/tests/dbtestdata/fakechain.go +++ b/tests/dbtestdata/fakechain.go @@ -3,6 +3,7 @@ package dbtestdata import ( "blockbook/bchain" "context" + "encoding/json" "errors" "math/big" ) @@ -67,7 +68,7 @@ func (c *fakeBlockChain) GetBlockHash(height uint32) (v string, err error) { if height == b2.BlockHeader.Height { return b2.BlockHeader.Hash, nil } - return "", errors.New("Block not found") + return "", bchain.ErrBlockNotFound } func (c *fakeBlockChain) GetBlockHeader(hash string) (v *bchain.BlockHeader, err error) { @@ -79,7 +80,7 @@ func (c *fakeBlockChain) GetBlockHeader(hash string) (v *bchain.BlockHeader, err if hash == b2.BlockHeader.Hash { return &b2.BlockHeader, nil } - return nil, errors.New("Block not found") + return nil, bchain.ErrBlockNotFound } func (c *fakeBlockChain) GetBlock(hash string, height uint32) (v *bchain.Block, err error) { @@ -91,11 +92,29 @@ func (c *fakeBlockChain) GetBlock(hash string, height uint32) (v *bchain.Block, if hash == b2.BlockHeader.Hash || height == b2.BlockHeader.Height { return b2, nil } - return nil, errors.New("Block not found") + return nil, bchain.ErrBlockNotFound +} + +func getBlockInfo(b *bchain.Block) *bchain.BlockInfo { + bi := &bchain.BlockInfo{ + BlockHeader: b.BlockHeader, + } + for _, tx := range b.Txs { + bi.Txids = append(bi.Txids, tx.Txid) + } + return bi } func (c *fakeBlockChain) GetBlockInfo(hash string) (v *bchain.BlockInfo, err error) { - return nil, errors.New("Not implemented") + b1 := GetTestUTXOBlock1(c.parser) + if hash == b1.BlockHeader.Hash { + return getBlockInfo(b1), nil + } + b2 := GetTestUTXOBlock2(c.parser) + if hash == b2.BlockHeader.Hash { + return getBlockInfo(b2), nil + } + return nil, bchain.ErrBlockNotFound } func (c *fakeBlockChain) GetMempool() (v []string, err error) { @@ -122,6 +141,18 @@ func (c *fakeBlockChain) GetTransaction(txid string) (v *bchain.Tx, err error) { return nil, errors.New("Not implemented") } +func (c *fakeBlockChain) GetTransactionSpecific(txid string) (v json.RawMessage, err error) { + tx, err := c.GetTransaction(txid) + if err != nil { + return nil, err + } + rm, err := json.Marshal(tx) + if err != nil { + return nil, err + } + return json.RawMessage(rm), nil +} + func (c *fakeBlockChain) GetTransactionForMempool(txid string) (v *bchain.Tx, err error) { return nil, errors.New("Not implemented") }