blockbook/tests/integration.go

199 lines
4.4 KiB
Go
Raw Permalink Normal View History

// +build integration
2018-09-20 01:39:13 -06:00
package tests
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
"reflect"
2018-10-04 02:41:43 -06:00
"sort"
"strings"
2018-09-20 01:39:13 -06:00
"testing"
"time"
"github.com/martinboehm/btcutil/chaincfg"
2021-04-05 12:59:11 -06:00
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins"
build "spacecruft.org/spacecruft/blockbook/build/tools"
"spacecruft.org/spacecruft/blockbook/tests/rpc"
"spacecruft.org/spacecruft/blockbook/tests/sync"
2018-09-20 01:39:13 -06:00
)
type TestFunc func(t *testing.T, coin string, chain bchain.BlockChain, mempool bchain.Mempool, testConfig json.RawMessage)
2018-09-20 01:39:13 -06:00
var integrationTests = map[string]TestFunc{
"rpc": rpc.IntegrationTest,
"sync": sync.IntegrationTest,
}
var notConnectedError = errors.New("Not connected to backend server")
func runIntegrationTests(t *testing.T) {
tests, err := loadTests("tests.json")
if err != nil {
t.Fatal(err)
}
2018-10-04 02:41:43 -06:00
keys := make([]string, 0, len(tests))
for k := range tests {
keys = append(keys, k)
}
sort.Strings(keys)
for _, coin := range keys {
cfg := tests[coin]
name := getMatchableName(coin)
t.Run(name, func(t *testing.T) { runTests(t, coin, cfg) })
2018-09-20 01:39:13 -06:00
}
}
func loadTests(path string) (map[string]map[string]json.RawMessage, error) {
b, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
v := make(map[string]map[string]json.RawMessage)
err = json.Unmarshal(b, &v)
return v, err
}
func getMatchableName(coin string) string {
if idx := strings.Index(coin, "_testnet"); idx != -1 {
return coin[:idx] + "=test"
} else {
return coin + "=main"
}
}
2018-09-20 01:39:13 -06:00
func runTests(t *testing.T, coin string, cfg map[string]json.RawMessage) {
if cfg == nil || len(cfg) == 0 {
t.Skip("No tests to run")
}
defer chaincfg.ResetParams()
2018-09-20 01:39:13 -06:00
bc, m, err := makeBlockChain(coin)
2018-09-20 01:39:13 -06:00
if err != nil {
if err == notConnectedError {
t.Fatal(err)
}
t.Fatalf("Cannot init blockchain: %s", err)
2018-09-20 01:39:13 -06:00
}
for test, c := range cfg {
if fn, found := integrationTests[test]; found {
t.Run(test, func(t *testing.T) { fn(t, coin, bc, m, c) })
2018-09-20 01:39:13 -06:00
} else {
t.Errorf("Test not found: %s", test)
}
}
}
func makeBlockChain(coin string) (bchain.BlockChain, bchain.Mempool, error) {
2018-09-20 01:39:13 -06:00
c, err := build.LoadConfig("../configs", coin)
if err != nil {
return nil, nil, err
2018-09-20 01:39:13 -06:00
}
outputDir, err := ioutil.TempDir("", "integration_test")
if err != nil {
return nil, nil, err
2018-09-20 01:39:13 -06:00
}
defer os.RemoveAll(outputDir)
err = build.GeneratePackageDefinitions(c, "../build/templates", outputDir)
if err != nil {
return nil, nil, err
2018-09-20 01:39:13 -06:00
}
b, err := ioutil.ReadFile(filepath.Join(outputDir, "blockbook", "blockchaincfg.json"))
if err != nil {
return nil, nil, err
2018-09-20 01:39:13 -06:00
}
var cfg json.RawMessage
err = json.Unmarshal(b, &cfg)
if err != nil {
return nil, nil, err
2018-09-20 01:39:13 -06:00
}
coinName, err := getName(cfg)
if err != nil {
return nil, nil, err
2018-09-20 01:39:13 -06:00
}
return initBlockChain(coinName, cfg)
}
func getName(raw json.RawMessage) (string, error) {
var cfg map[string]interface{}
err := json.Unmarshal(raw, &cfg)
if err != nil {
return "", err
}
if n, found := cfg["coin_name"]; found {
switch n := n.(type) {
case string:
return n, nil
default:
return "", fmt.Errorf("Unexpected type of field `name`: %s", reflect.TypeOf(n))
}
} else {
return "", errors.New("Missing field `name`")
}
}
func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, bchain.Mempool, error) {
2018-09-20 01:39:13 -06:00
factory, found := coins.BlockChainFactories[coinName]
if !found {
return nil, nil, fmt.Errorf("Factory function not found")
2018-09-20 01:39:13 -06:00
}
chain, err := factory(cfg, func(_ bchain.NotificationType) {})
2018-09-20 01:39:13 -06:00
if err != nil {
if isNetError(err) {
return nil, nil, notConnectedError
2018-09-20 01:39:13 -06:00
}
return nil, nil, fmt.Errorf("Factory function failed: %s", err)
2018-09-20 01:39:13 -06:00
}
for i := 0; ; i++ {
err = chain.Initialize()
if err == nil {
break
}
2018-09-20 01:39:13 -06:00
if isNetError(err) {
return nil, nil, notConnectedError
2018-09-20 01:39:13 -06:00
}
// wait max 5 minutes for backend to startup
if i > 5*60 {
return nil, nil, fmt.Errorf("BlockChain initialization failed: %s", err)
}
time.Sleep(time.Millisecond * 1000)
}
mempool, err := chain.CreateMempool(chain)
if err != nil {
return nil, nil, fmt.Errorf("Mempool creation failed: %s", err)
}
err = chain.InitializeMempool(nil, nil, nil)
if err != nil {
return nil, nil, fmt.Errorf("Mempool initialization failed: %s", err)
2018-09-20 01:39:13 -06:00
}
return chain, mempool, nil
2018-09-20 01:39:13 -06:00
}
func isNetError(err error) bool {
if _, ok := err.(net.Error); ok {
return true
}
return false
}