github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/tests/integration.go (about)

     1  // +build integration
     2  
     3  package tests
     4  
     5  import (
     6  	"blockbook/bchain"
     7  	"blockbook/bchain/coins"
     8  	"blockbook/build/tools"
     9  	"blockbook/tests/rpc"
    10  	"blockbook/tests/sync"
    11  	"encoding/json"
    12  	"errors"
    13  	"fmt"
    14  	"io/ioutil"
    15  	"net"
    16  	"os"
    17  	"path/filepath"
    18  	"reflect"
    19  	"sort"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/jakm/btcutil/chaincfg"
    24  )
    25  
    26  type TestFunc func(t *testing.T, coin string, chain bchain.BlockChain, testConfig json.RawMessage)
    27  
    28  var integrationTests = map[string]TestFunc{
    29  	"rpc":  rpc.IntegrationTest,
    30  	"sync": sync.IntegrationTest,
    31  }
    32  
    33  var notConnectedError = errors.New("Not connected to backend server")
    34  
    35  func runIntegrationTests(t *testing.T) {
    36  	tests, err := loadTests("tests.json")
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  
    41  	keys := make([]string, 0, len(tests))
    42  	for k := range tests {
    43  		keys = append(keys, k)
    44  	}
    45  	sort.Strings(keys)
    46  
    47  	for _, coin := range keys {
    48  		cfg := tests[coin]
    49  		name := getMatchableName(coin)
    50  		t.Run(name, func(t *testing.T) { runTests(t, coin, cfg) })
    51  
    52  	}
    53  }
    54  
    55  func loadTests(path string) (map[string]map[string]json.RawMessage, error) {
    56  	b, err := ioutil.ReadFile(path)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	v := make(map[string]map[string]json.RawMessage)
    61  	err = json.Unmarshal(b, &v)
    62  	return v, err
    63  }
    64  
    65  func getMatchableName(coin string) string {
    66  	if idx := strings.Index(coin, "_testnet"); idx != -1 {
    67  		return coin[:idx] + "=test"
    68  	} else {
    69  		return coin + "=main"
    70  	}
    71  }
    72  
    73  func runTests(t *testing.T, coin string, cfg map[string]json.RawMessage) {
    74  	if cfg == nil || len(cfg) == 0 {
    75  		t.Skip("No tests to run")
    76  	}
    77  	defer chaincfg.ResetParams()
    78  
    79  	bc, err := makeBlockChain(coin)
    80  	if err != nil {
    81  		if err == notConnectedError {
    82  			t.Fatal(err)
    83  		}
    84  		t.Fatalf("Cannot make blockchain config: %s", err)
    85  	}
    86  
    87  	for test, c := range cfg {
    88  		if fn, found := integrationTests[test]; found {
    89  			t.Run(test, func(t *testing.T) { fn(t, coin, bc, c) })
    90  		} else {
    91  			t.Errorf("Test not found: %s", test)
    92  		}
    93  	}
    94  }
    95  
    96  func makeBlockChain(coin string) (bchain.BlockChain, error) {
    97  	c, err := build.LoadConfig("../configs", coin)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	outputDir, err := ioutil.TempDir("", "integration_test")
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	defer os.RemoveAll(outputDir)
   107  
   108  	err = build.GeneratePackageDefinitions(c, "../build/templates", outputDir)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  
   113  	b, err := ioutil.ReadFile(filepath.Join(outputDir, "blockbook", "blockchaincfg.json"))
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	var cfg json.RawMessage
   119  	err = json.Unmarshal(b, &cfg)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	coinName, err := getName(cfg)
   125  	if err != nil {
   126  		return nil, err
   127  	}
   128  
   129  	return initBlockChain(coinName, cfg)
   130  }
   131  
   132  func getName(raw json.RawMessage) (string, error) {
   133  	var cfg map[string]interface{}
   134  	err := json.Unmarshal(raw, &cfg)
   135  	if err != nil {
   136  		return "", err
   137  	}
   138  	if n, found := cfg["coin_name"]; found {
   139  		switch n := n.(type) {
   140  		case string:
   141  			return n, nil
   142  		default:
   143  			return "", fmt.Errorf("Unexpected type of field `name`: %s", reflect.TypeOf(n))
   144  		}
   145  	} else {
   146  		return "", errors.New("Missing field `name`")
   147  	}
   148  }
   149  
   150  func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, error) {
   151  	factory, found := coins.BlockChainFactories[coinName]
   152  	if !found {
   153  		return nil, fmt.Errorf("Factory function not found")
   154  	}
   155  
   156  	cli, err := factory(cfg, func(_ bchain.NotificationType) {})
   157  	if err != nil {
   158  		if isNetError(err) {
   159  			return nil, notConnectedError
   160  		}
   161  		return nil, fmt.Errorf("Factory function failed: %s", err)
   162  	}
   163  
   164  	err = cli.Initialize()
   165  	if err != nil {
   166  		if isNetError(err) {
   167  			return nil, notConnectedError
   168  		}
   169  		return nil, fmt.Errorf("BlockChain initialization failed: %s", err)
   170  	}
   171  
   172  	return cli, nil
   173  }
   174  
   175  func isNetError(err error) bool {
   176  	if _, ok := err.(net.Error); ok {
   177  		return true
   178  	}
   179  	return false
   180  }