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