mirror of
https://github.com/trezor/blockbook.git
synced 2026-03-03 06:14:27 +01:00
169 lines
4.4 KiB
Go
169 lines
4.4 KiB
Go
//go:build integration
|
|
|
|
package connectivity
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
"github.com/trezor/blockbook/bchain"
|
|
"github.com/trezor/blockbook/bchain/coins"
|
|
)
|
|
|
|
const connectivityTimeout = 10 * time.Second
|
|
|
|
type connectivityCfg struct {
|
|
CoinName string `json:"coin_name"`
|
|
RpcUrl string `json:"rpc_url"`
|
|
RpcUrlWs string `json:"rpc_url_ws"`
|
|
RpcUser string `json:"rpc_user"`
|
|
RpcPass string `json:"rpc_pass"`
|
|
}
|
|
|
|
// IntegrationTest runs connectivity checks for the requested modes (e.g., ["http","ws"]).
|
|
// HTTP checks verify the backend responds (UTXO uses getblockchaininfo, EVM uses web3_clientVersion).
|
|
// WS checks verify web3_clientVersion and a newHeads subscription over the WS endpoint.
|
|
func IntegrationTest(t *testing.T, coin string, _ bchain.BlockChain, _ bchain.Mempool, testConfig json.RawMessage) {
|
|
t.Helper()
|
|
|
|
modes, err := parseConnectivityModes(testConfig)
|
|
if err != nil {
|
|
t.Fatalf("invalid connectivity config for %s: %v", coin, err)
|
|
}
|
|
|
|
for _, mode := range modes {
|
|
switch mode {
|
|
case "http":
|
|
HTTPIntegrationTest(t, coin, nil, nil, nil)
|
|
case "ws":
|
|
WSIntegrationTest(t, coin, nil, nil, nil)
|
|
default:
|
|
t.Fatalf("unsupported connectivity mode %q for %s", mode, coin)
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseConnectivityModes(testConfig json.RawMessage) ([]string, error) {
|
|
var modes []string
|
|
if err := json.Unmarshal(testConfig, &modes); err != nil {
|
|
return nil, err
|
|
}
|
|
if len(modes) == 0 {
|
|
return nil, errors.New("empty connectivity list")
|
|
}
|
|
return modes, nil
|
|
}
|
|
|
|
func HTTPIntegrationTest(t *testing.T, coin string, _ bchain.BlockChain, _ bchain.Mempool, _ json.RawMessage) {
|
|
t.Helper()
|
|
|
|
rawCfg, cfg := loadConnectivityCfg(t, coin)
|
|
if cfg.RpcUrl == "" {
|
|
t.Fatalf("empty rpc_url for %s", coin)
|
|
}
|
|
|
|
if isUTXO(cfg) {
|
|
if cfg.CoinName == "" {
|
|
t.Fatalf("empty coin_name for %s", coin)
|
|
}
|
|
factory, ok := coins.BlockChainFactories[cfg.CoinName]
|
|
if !ok {
|
|
t.Fatalf("blockchain factory not found for %s", cfg.CoinName)
|
|
}
|
|
chain, err := factory(rawCfg, func(bchain.NotificationType) {})
|
|
if err != nil {
|
|
t.Fatalf("init chain %s: %v", cfg.CoinName, err)
|
|
}
|
|
if _, err := chain.GetChainInfo(); err != nil {
|
|
t.Fatalf("GetChainInfo %s: %v", cfg.CoinName, err)
|
|
}
|
|
return
|
|
}
|
|
|
|
evmHTTPConnectivity(t, cfg.RpcUrl)
|
|
}
|
|
|
|
func WSIntegrationTest(t *testing.T, coin string, _ bchain.BlockChain, _ bchain.Mempool, _ json.RawMessage) {
|
|
t.Helper()
|
|
|
|
_, cfg := loadConnectivityCfg(t, coin)
|
|
if cfg.RpcUrlWs == "" {
|
|
t.Fatalf("empty rpc_url_ws for %s", coin)
|
|
}
|
|
|
|
evmWSConnectivity(t, cfg.RpcUrlWs)
|
|
}
|
|
|
|
func loadConnectivityCfg(t *testing.T, coin string) (json.RawMessage, connectivityCfg) {
|
|
t.Helper()
|
|
|
|
rawCfg, err := bchain.LoadBlockchainCfgRaw(coin)
|
|
if err != nil {
|
|
t.Fatalf("load blockchain config for %s: %v", coin, err)
|
|
}
|
|
var cfg connectivityCfg
|
|
if err := json.Unmarshal(rawCfg, &cfg); err != nil {
|
|
t.Fatalf("unmarshal blockchain config for %s: %v", coin, err)
|
|
}
|
|
return rawCfg, cfg
|
|
}
|
|
|
|
func isUTXO(cfg connectivityCfg) bool {
|
|
return cfg.RpcUser != "" || cfg.RpcPass != ""
|
|
}
|
|
|
|
func evmHTTPConnectivity(t *testing.T, httpURL string) {
|
|
t.Helper()
|
|
|
|
rpcClient, err := rpc.DialOptions(context.Background(), httpURL)
|
|
if err != nil {
|
|
t.Fatalf("dial rpc_url %s: %v", httpURL, err)
|
|
}
|
|
defer rpcClient.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), connectivityTimeout)
|
|
defer cancel()
|
|
|
|
var version string
|
|
if err := rpcClient.CallContext(ctx, &version, "web3_clientVersion"); err != nil {
|
|
t.Fatalf("CallContext web3_clientVersion failed: %v", err)
|
|
}
|
|
if version == "" {
|
|
t.Fatalf("empty web3_clientVersion")
|
|
}
|
|
}
|
|
|
|
func evmWSConnectivity(t *testing.T, wsURL string) {
|
|
t.Helper()
|
|
|
|
rpcClient, err := rpc.DialOptions(context.Background(), wsURL, rpc.WithWebsocketMessageSizeLimit(0))
|
|
if err != nil {
|
|
t.Fatalf("dial rpc_url_ws %s: %v", wsURL, err)
|
|
}
|
|
defer rpcClient.Close()
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), connectivityTimeout)
|
|
defer cancel()
|
|
|
|
var version string
|
|
if err := rpcClient.CallContext(ctx, &version, "web3_clientVersion"); err != nil {
|
|
t.Fatalf("CallContext web3_clientVersion failed: %v", err)
|
|
}
|
|
if version == "" {
|
|
t.Fatalf("empty web3_clientVersion")
|
|
}
|
|
|
|
subCtx, subCancel := context.WithTimeout(context.Background(), connectivityTimeout)
|
|
defer subCancel()
|
|
|
|
sub, err := rpcClient.EthSubscribe(subCtx, make(chan interface{}, 1), "newHeads")
|
|
if err != nil {
|
|
t.Fatalf("EthSubscribe newHeads failed: %v", err)
|
|
}
|
|
sub.Unsubscribe()
|
|
}
|