github.com/cryptohub-digital/blockbook@v0.3.5-0.20240403155730-99ab40b9104c/tests/sync/sync.go (about) 1 //go:build integration 2 3 package sync 4 5 import ( 6 "encoding/json" 7 "errors" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "testing" 12 13 "github.com/cryptohub-digital/blockbook/bchain" 14 "github.com/cryptohub-digital/blockbook/common" 15 "github.com/cryptohub-digital/blockbook/db" 16 ) 17 18 var testMap = map[string]func(t *testing.T, th *TestHandler){ 19 "ConnectBlocks": testConnectBlocks, 20 "ConnectBlocksParallel": testConnectBlocksParallel, 21 "HandleFork": testHandleFork, 22 } 23 24 type TestHandler struct { 25 Coin string 26 Chain bchain.BlockChain 27 TestData *TestData 28 } 29 30 type Range struct { 31 Lower uint32 `json:"lower"` 32 Upper uint32 `json:"upper"` 33 } 34 35 type TestData struct { 36 ConnectBlocks struct { 37 SyncRanges []Range `json:"syncRanges"` 38 Blocks map[uint32]BlockInfo `json:"blocks"` 39 } `json:"connectBlocks"` 40 HandleFork struct { 41 SyncRanges []Range `json:"syncRanges"` 42 FakeBlocks map[uint32]BlockID `json:"fakeBlocks"` 43 RealBlocks map[uint32]BlockID `json:"realBlocks"` 44 } `json:"handleFork"` 45 } 46 47 type BlockID struct { 48 Height uint32 `json:"height"` 49 Hash string `json:"hash"` 50 } 51 52 type BlockInfo struct { 53 BlockID 54 NoTxs uint32 `json:"noTxs"` 55 TxDetails []*bchain.Tx `json:"txDetails"` 56 } 57 58 func IntegrationTest(t *testing.T, coin string, chain bchain.BlockChain, mempool bchain.Mempool, testConfig json.RawMessage) { 59 tests, err := getTests(testConfig) 60 if err != nil { 61 t.Fatalf("Failed loading of test list: %s", err) 62 } 63 64 parser := chain.GetChainParser() 65 td, err := loadTestData(coin, parser) 66 if err != nil { 67 t.Fatalf("Failed loading of test data: %s", err) 68 } 69 70 for _, test := range tests { 71 if f, found := testMap[test]; found { 72 h := TestHandler{Coin: coin, Chain: chain, TestData: td} 73 t.Run(test, func(t *testing.T) { f(t, &h) }) 74 } else { 75 t.Errorf("%s: test not found", test) 76 continue 77 } 78 } 79 } 80 81 func getTests(cfg json.RawMessage) ([]string, error) { 82 var v []string 83 err := json.Unmarshal(cfg, &v) 84 if err != nil { 85 return nil, err 86 } 87 if len(v) == 0 { 88 return nil, errors.New("No tests declared") 89 } 90 return v, nil 91 } 92 93 func loadTestData(coin string, parser bchain.BlockChainParser) (*TestData, error) { 94 path := filepath.Join("sync/testdata", coin+".json") 95 b, err := ioutil.ReadFile(path) 96 if err != nil { 97 return nil, err 98 } 99 var v TestData 100 err = json.Unmarshal(b, &v) 101 if err != nil { 102 return nil, err 103 } 104 105 for _, b := range v.ConnectBlocks.Blocks { 106 for _, tx := range b.TxDetails { 107 // convert amounts in test json to bit.Int and clear the temporary JsonValue 108 for i := range tx.Vout { 109 vout := &tx.Vout[i] 110 vout.ValueSat, err = parser.AmountToBigInt(vout.JsonValue) 111 if err != nil { 112 return nil, err 113 } 114 vout.JsonValue = "" 115 } 116 117 // get addresses parsed 118 err := setTxAddresses(parser, tx) 119 if err != nil { 120 return nil, err 121 } 122 } 123 } 124 125 return &v, nil 126 } 127 128 func setTxAddresses(parser bchain.BlockChainParser, tx *bchain.Tx) error { 129 for i := range tx.Vout { 130 ad, err := parser.GetAddrDescFromVout(&tx.Vout[i]) 131 if err != nil { 132 return err 133 } 134 a, s, err := parser.GetAddressesFromAddrDesc(ad) 135 if err == nil && s { 136 tx.Vout[i].ScriptPubKey.Addresses = a 137 } 138 } 139 return nil 140 } 141 142 func makeRocksDB(parser bchain.BlockChainParser, m *common.Metrics, is *common.InternalState) (*db.RocksDB, func(), error) { 143 p, err := ioutil.TempDir("", "sync_test") 144 if err != nil { 145 return nil, nil, err 146 } 147 148 d, err := db.NewRocksDB(p, 1<<17, 1<<14, parser, m, false) 149 if err != nil { 150 return nil, nil, err 151 } 152 153 d.SetInternalState(is) 154 155 closer := func() { 156 d.Close() 157 os.RemoveAll(p) 158 } 159 160 return d, closer, nil 161 } 162 163 var metricsRegistry = map[string]*common.Metrics{} 164 165 func getMetrics(name string) (*common.Metrics, error) { 166 if m, found := metricsRegistry[name]; found { 167 return m, nil 168 } else { 169 m, err := common.GetMetrics(name) 170 if err != nil { 171 return nil, err 172 } 173 metricsRegistry[name] = m 174 return m, nil 175 } 176 } 177 178 func withRocksDBAndSyncWorker(t *testing.T, h *TestHandler, startHeight uint32, fn func(*db.RocksDB, *db.SyncWorker, chan os.Signal)) { 179 m, err := getMetrics(h.Coin) 180 if err != nil { 181 t.Fatal(err) 182 } 183 is := &common.InternalState{} 184 185 d, closer, err := makeRocksDB(h.Chain.GetChainParser(), m, is) 186 if err != nil { 187 t.Fatal(err) 188 } 189 defer closer() 190 191 ch := make(chan os.Signal) 192 193 sw, err := db.NewSyncWorker(d, h.Chain, 8, 0, int(startHeight), false, ch, m, is) 194 if err != nil { 195 t.Fatal(err) 196 } 197 198 fn(d, sw, ch) 199 }