github.com/aychain/blockbook@v0.1.1-0.20181121092459-6d1fc7e07c5b/server/public_test.go (about) 1 // +build unittest 2 3 package server 4 5 import ( 6 "blockbook/bchain" 7 "blockbook/bchain/coins/btc" 8 "blockbook/common" 9 "blockbook/db" 10 "blockbook/tests/dbtestdata" 11 "io/ioutil" 12 "net/http" 13 "net/http/httptest" 14 "net/url" 15 "os" 16 "strings" 17 "testing" 18 "time" 19 20 "github.com/golang/glog" 21 "github.com/jakm/btcutil/chaincfg" 22 "github.com/martinboehm/golang-socketio" 23 "github.com/martinboehm/golang-socketio/transport" 24 ) 25 26 func TestMain(m *testing.M) { 27 // set the current directory to blockbook root so that ./static/ works 28 if err := os.Chdir(".."); err != nil { 29 glog.Fatal("Chdir error:", err) 30 } 31 c := m.Run() 32 chaincfg.ResetParams() 33 os.Exit(c) 34 } 35 36 func setupRocksDB(t *testing.T, parser bchain.BlockChainParser) (*db.RocksDB, *common.InternalState, string) { 37 tmp, err := ioutil.TempDir("", "testdb") 38 if err != nil { 39 t.Fatal(err) 40 } 41 d, err := db.NewRocksDB(tmp, 100000, -1, parser, nil) 42 if err != nil { 43 t.Fatal(err) 44 } 45 is, err := d.LoadInternalState("fakecoin") 46 if err != nil { 47 t.Fatal(err) 48 } 49 d.SetInternalState(is) 50 // import data 51 if err := d.ConnectBlock(dbtestdata.GetTestUTXOBlock1(parser)); err != nil { 52 t.Fatal(err) 53 } 54 if err := d.ConnectBlock(dbtestdata.GetTestUTXOBlock2(parser)); err != nil { 55 t.Fatal(err) 56 } 57 return d, is, tmp 58 } 59 60 func setupPublicHTTPServer(t *testing.T) (*PublicServer, string) { 61 parser := btc.NewBitcoinParser( 62 btc.GetChainParams("test"), 63 &btc.Configuration{BlockAddressesToKeep: 1}) 64 65 d, is, path := setupRocksDB(t, parser) 66 // setup internal state and match BestHeight to test data 67 is.Coin = "Fakecoin" 68 is.CoinLabel = "Fake Coin" 69 is.CoinShortcut = "FAKE" 70 is.BestHeight = 225494 71 72 metrics, err := common.GetMetrics("Fakecoin") 73 if err != nil { 74 glog.Fatal("metrics: ", err) 75 } 76 77 chain, err := dbtestdata.NewFakeBlockChain(parser) 78 if err != nil { 79 glog.Fatal("fakechain: ", err) 80 } 81 82 // caching is switched off because test transactions do not have hex data 83 txCache, err := db.NewTxCache(d, chain, metrics, is, false) 84 if err != nil { 85 glog.Fatal("txCache: ", err) 86 } 87 88 // s.Run is never called, binding can be to any port 89 s, err := NewPublicServer("localhost:12345", "", d, chain, txCache, "", metrics, is, false) 90 if err != nil { 91 t.Fatal(err) 92 } 93 return s, path 94 } 95 96 func closeAndDestroyPublicServer(t *testing.T, s *PublicServer, dbpath string) { 97 // destroy db 98 if err := s.db.Close(); err != nil { 99 t.Fatal(err) 100 } 101 os.RemoveAll(dbpath) 102 } 103 104 func newGetRequest(u string) *http.Request { 105 r, err := http.NewRequest("GET", u, nil) 106 if err != nil { 107 glog.Fatal(err) 108 } 109 return r 110 } 111 112 func newPostFormRequest(u string, formdata ...string) *http.Request { 113 form := url.Values{} 114 for i := 0; i < len(formdata)-1; i += 2 { 115 form.Add(formdata[i], formdata[i+1]) 116 } 117 r, err := http.NewRequest("POST", u, strings.NewReader(form.Encode())) 118 if err != nil { 119 glog.Fatal(err) 120 } 121 r.Header.Add("Content-Type", "application/x-www-form-urlencoded") 122 return r 123 } 124 125 func newPostRequest(u string, body string) *http.Request { 126 r, err := http.NewRequest("POST", u, strings.NewReader(body)) 127 if err != nil { 128 glog.Fatal(err) 129 } 130 r.Header.Add("Content-Type", "application/octet-stream") 131 return r 132 } 133 134 func httpTests(t *testing.T, ts *httptest.Server) { 135 tests := []struct { 136 name string 137 r *http.Request 138 status int 139 contentType string 140 body []string 141 }{ 142 { 143 name: "explorerTx", 144 r: newGetRequest(ts.URL + "/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"), 145 status: http.StatusOK, 146 contentType: "text/html; charset=utf-8", 147 body: []string{ 148 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 149 `<h1>Transaction</h1>`, 150 `<span class="data">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</span>`, 151 `td class="data">0 FAKE</td>`, 152 `<a href="/address/mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj">mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj</a>`, 153 `13.60030331 FAKE`, 154 `<td><span class="float-left">No Inputs (Newly Generated Coins)</span></td>`, 155 `</html>`, 156 }, 157 }, 158 { 159 name: "explorerAddress", 160 r: newGetRequest(ts.URL + "/address/mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"), 161 status: http.StatusOK, 162 contentType: "text/html; charset=utf-8", 163 body: []string{ 164 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 165 `<h1>Address`, 166 `<small class="text-muted">0 FAKE</small>`, 167 `<span class="data">mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz</span>`, 168 `<td class="data">0.00012345 FAKE</td>`, 169 `<a href="/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25">7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25</a>`, 170 `3172.83951061 FAKE <a class="text-danger" href="/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0" title="Spent">➡</a></span>`, 171 `<td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`, 172 `td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`, 173 `9172.83951061 FAKE <span class="text-success" title="Unspent"> <b>×</b></span></span>`, 174 `<a href="/tx/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840">00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840</a>`, 175 `</html>`, 176 }, 177 }, 178 { 179 name: "explorerSpendingTx", 180 r: newGetRequest(ts.URL + "/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0"), 181 status: http.StatusOK, 182 contentType: "text/html; charset=utf-8", 183 body: []string{ 184 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 185 `<h1>Transaction</h1>`, 186 `<span class="data">3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71</span>`, 187 `<td class="data">0.00000062 FAKE</td>`, 188 `</html>`, 189 }, 190 }, 191 { 192 name: "explorerSpendingTx - not found", 193 r: newGetRequest(ts.URL + "/spending/123be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0"), 194 status: http.StatusOK, 195 contentType: "text/html; charset=utf-8", 196 body: []string{ 197 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 198 `<h1>Error</h1>`, 199 `<h4>Transaction not found</h4>`, 200 `</html>`, 201 }, 202 }, 203 { 204 name: "explorerBlocks", 205 r: newGetRequest(ts.URL + "/blocks"), 206 status: http.StatusOK, 207 contentType: "text/html; charset=utf-8", 208 body: []string{ 209 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 210 `<h1>Blocks`, 211 `<td><a href="/block/225494">225494</a></td>`, 212 `<td class="ellipsis">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</td>`, 213 `<td class="ellipsis">0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997</td>`, 214 `<td class="text-right">2</td>`, 215 `<td class="text-right">1234567</td>`, 216 `</html>`, 217 }, 218 }, 219 { 220 name: "explorerBlock", 221 r: newGetRequest(ts.URL + "/block/225494"), 222 status: http.StatusOK, 223 contentType: "text/html; charset=utf-8", 224 body: []string{ 225 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 226 `<h1>Block 225494</h1>`, 227 `<span class="data">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</span>`, 228 `<td class="data">4</td>`, // number of transactions 229 `<a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a>`, 230 `9172.83951061 FAKE`, 231 `<a href="/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</a>`, 232 `</html>`, 233 }, 234 }, 235 { 236 name: "explorerIndex", 237 r: newGetRequest(ts.URL + "/"), 238 status: http.StatusOK, 239 contentType: "text/html; charset=utf-8", 240 body: []string{ 241 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 242 `<h1>Application status</h1>`, 243 `<h3 class="bg-warning text-white" style="padding: 20px;">Synchronization with backend is disabled, the state of index is not up to date.</h3>`, 244 `<a href="/block/225494">225494</a>`, 245 `<td class="data">/Fakecoin:0.0.1/</td>`, 246 `</html>`, 247 }, 248 }, 249 { 250 name: "explorerSearch block height", 251 r: newGetRequest(ts.URL + "/search?q=225494"), 252 status: http.StatusOK, 253 contentType: "text/html; charset=utf-8", 254 body: []string{ 255 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 256 `<h1>Block 225494</h1>`, 257 `<span class="data">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</span>`, 258 `<td class="data">4</td>`, // number of transactions 259 `<a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a>`, 260 `9172.83951061 FAKE`, 261 `<a href="/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</a>`, 262 `</html>`, 263 }, 264 }, 265 { 266 name: "explorerSearch block hash", 267 r: newGetRequest(ts.URL + "/search?q=00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"), 268 status: http.StatusOK, 269 contentType: "text/html; charset=utf-8", 270 body: []string{ 271 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 272 `<h1>Block 225494</h1>`, 273 `<span class="data">00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6</span>`, 274 `<td class="data">4</td>`, // number of transactions 275 `<a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a>`, 276 `9172.83951061 FAKE`, 277 `<a href="/tx/fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</a>`, 278 `</html>`, 279 }, 280 }, 281 { 282 name: "explorerSearch tx", 283 r: newGetRequest(ts.URL + "/search?q=fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db"), 284 status: http.StatusOK, 285 contentType: "text/html; charset=utf-8", 286 body: []string{ 287 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 288 `<h1>Transaction</h1>`, 289 `<span class="data">fdd824a780cbb718eeb766eb05d83fdefc793a27082cd5e67f856d69798cf7db</span>`, 290 `td class="data">0 FAKE</td>`, 291 `<a href="/address/mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj">mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj</a>`, 292 `13.60030331 FAKE`, 293 `<td><span class="float-left">No Inputs (Newly Generated Coins)</span></td>`, 294 `</html>`, 295 }, 296 }, 297 { 298 name: "explorerSearch address", 299 r: newGetRequest(ts.URL + "/search?q=mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"), 300 status: http.StatusOK, 301 contentType: "text/html; charset=utf-8", 302 body: []string{ 303 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 304 `<h1>Address`, 305 `<small class="text-muted">0 FAKE</small>`, 306 `<span class="data">mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz</span>`, 307 `<td class="data">0.00012345 FAKE</td>`, 308 `<a href="/tx/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25">7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25</a>`, 309 `3172.83951061 FAKE <a class="text-danger" href="/spending/7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25/0" title="Spent">➡</a></span>`, 310 `<td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`, 311 `td><span class="ellipsis float-left"><a href="/address/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL">mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL</a></span><span class="float-right">`, 312 `9172.83951061 FAKE <span class="text-success" title="Unspent"> <b>×</b></span></span>`, 313 `<a href="/tx/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840">00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840</a>`, 314 `</html>`, 315 }, 316 }, 317 { 318 name: "explorerSearch not found", 319 r: newGetRequest(ts.URL + "/search?q=1234"), 320 status: http.StatusOK, 321 contentType: "text/html; charset=utf-8", 322 body: []string{ 323 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 324 `<h1>Error</h1>`, 325 `<h4>No matching records found for '1234'</h4>`, 326 `</html>`, 327 }, 328 }, 329 { 330 name: "explorerSendTx", 331 r: newGetRequest(ts.URL + "/sendtx"), 332 status: http.StatusOK, 333 contentType: "text/html; charset=utf-8", 334 body: []string{ 335 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 336 `<h1>Send Raw Transaction</h1>`, 337 `<textarea class="form-control" rows="8" name="hex"></textarea>`, 338 `</html>`, 339 }, 340 }, 341 { 342 name: "explorerSendTx POST", 343 r: newPostFormRequest(ts.URL+"/sendtx", "hex", "12341234"), 344 status: http.StatusOK, 345 contentType: "text/html; charset=utf-8", 346 body: []string{ 347 `<a href="/" class="nav-link">Fake Coin Explorer</a>`, 348 `<h1>Send Raw Transaction</h1>`, 349 `<textarea class="form-control" rows="8" name="hex">12341234</textarea>`, 350 `<div class="alert alert-danger">Invalid data</div>`, 351 `</html>`, 352 }, 353 }, 354 { 355 name: "apiIndex", 356 r: newGetRequest(ts.URL + "/api"), 357 status: http.StatusOK, 358 contentType: "application/json; charset=utf-8", 359 body: []string{ 360 `{"blockbook":{"coin":"Fakecoin"`, 361 `"bestHeight":225494`, 362 `"backend":{"chain":"fakecoin","blocks":2,"headers":2,"bestblockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"`, 363 `"version":"001001","subversion":"/Fakecoin:0.0.1/"`, 364 }, 365 }, 366 { 367 name: "apiBlockIndex", 368 r: newGetRequest(ts.URL + "/api/block-index/"), 369 status: http.StatusOK, 370 contentType: "application/json; charset=utf-8", 371 body: []string{ 372 `{"blockHash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6"}`, 373 }, 374 }, 375 { 376 name: "apiTx", 377 r: newGetRequest(ts.URL + "/api/tx/05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"), 378 status: http.StatusOK, 379 contentType: "application/json; charset=utf-8", 380 body: []string{ 381 `{"txid":"05e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07","vin":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","vout":2,"n":0,"scriptSig":{"hex":""},"addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"],"value":"0.00009876"}],"vout":[{"value":"0.00009","n":0,"scriptPubKey":{"hex":"a914e921fc4912a315078f370d959f2c4f7b6d2a683c87","addresses":["2NEVv9LJmAnY99W1pFoc5UJjVdypBqdnvu1"]},"spent":false}],"blockhash":"00000000eb0443fd7dc4a1ed5c686a8e995057805f9a161d9a5a77a95e72b7b6","blockheight":225494,"confirmations":1,"time":22549400002,"blocktime":22549400002,"valueOut":"0.00009","valueIn":"0.00009876","fees":"0.00000876","hex":""}`, 382 }, 383 }, 384 { 385 name: "apiTx - not found", 386 r: newGetRequest(ts.URL + "/api/tx/1232e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b07"), 387 status: http.StatusBadRequest, 388 contentType: "application/json; charset=utf-8", 389 body: []string{ 390 `{"error":"Tx not found, Not found"}`, 391 }, 392 }, 393 { 394 name: "apiTxSpecific", 395 r: newGetRequest(ts.URL + "/api/tx-specific/00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840"), 396 status: http.StatusOK, 397 contentType: "application/json; charset=utf-8", 398 body: []string{ 399 `{"hex":"","txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","version":0,"locktime":0,"vin":null,"vout":[{"ValueSat":100000000,"value":0,"n":0,"scriptPubKey":{"hex":"76a914010d39800f86122416e28f485029acf77507169288ac","addresses":null}},{"ValueSat":12345,"value":0,"n":1,"scriptPubKey":{"hex":"76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac","addresses":null}}],"confirmations":2,"time":22549300000,"blocktime":22549300000}`, 400 }, 401 }, 402 { 403 name: "apiAddress", 404 r: newGetRequest(ts.URL + "/api/address/mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw"), 405 status: http.StatusOK, 406 contentType: "application/json; charset=utf-8", 407 body: []string{ 408 `{"page":1,"totalPages":1,"itemsOnPage":1000,"addrStr":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","balance":"0","totalReceived":"12345.67890123","totalSent":"12345.67890123","unconfirmedBalance":"0","unconfirmedTxApperances":0,"txApperances":2,"transactions":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"]}`, 409 }, 410 }, 411 { 412 name: "apiAddressUtxo", 413 r: newGetRequest(ts.URL + "/api/utxo/mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"), 414 status: http.StatusOK, 415 contentType: "application/json; charset=utf-8", 416 body: []string{ 417 `[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","vout":1,"amount":"9172.83951061","satoshis":917283951061,"height":225494,"confirmations":1}]`, 418 }, 419 }, 420 { 421 name: "apiSendTx", 422 r: newGetRequest(ts.URL + "/api/sendtx/1234567890"), 423 status: http.StatusBadRequest, 424 contentType: "application/json; charset=utf-8", 425 body: []string{ 426 `{"error":"Invalid data"}`, 427 }, 428 }, 429 { 430 name: "apiSendTx POST", 431 r: newPostRequest(ts.URL+"/api/sendtx/", "123456"), 432 status: http.StatusOK, 433 contentType: "application/json; charset=utf-8", 434 body: []string{ 435 `{"result":"9876"}`, 436 }, 437 }, 438 { 439 name: "apiSendTx POST empty", 440 r: newPostRequest(ts.URL+"/api/sendtx", ""), 441 status: http.StatusBadRequest, 442 contentType: "application/json; charset=utf-8", 443 body: []string{ 444 `{"error":"Missing tx blob"}`, 445 }, 446 }, 447 { 448 name: "apiEstimateFee", 449 r: newGetRequest(ts.URL + "/api/estimatefee/123?conservative=false"), 450 status: http.StatusOK, 451 contentType: "application/json; charset=utf-8", 452 body: []string{ 453 `{"result":"0.00012299"}`, 454 }, 455 }, 456 } 457 458 for _, tt := range tests { 459 t.Run(tt.name, func(t *testing.T) { 460 resp, err := http.DefaultClient.Do(tt.r) 461 if err != nil { 462 t.Fatal(err) 463 } 464 defer resp.Body.Close() 465 if resp.StatusCode != tt.status { 466 t.Errorf("StatusCode = %v, want %v", resp.StatusCode, tt.status) 467 } 468 if resp.Header["Content-Type"][0] != tt.contentType { 469 t.Errorf("Content-Type = %v, want %v", resp.Header["Content-Type"][0], tt.contentType) 470 } 471 bb, err := ioutil.ReadAll(resp.Body) 472 if err != nil { 473 t.Fatal(err) 474 } 475 b := string(bb) 476 for _, c := range tt.body { 477 if !strings.Contains(b, c) { 478 t.Errorf("Page body does not contain %v, body %v", c, b) 479 break 480 } 481 } 482 }) 483 } 484 } 485 486 func socketioTests(t *testing.T, ts *httptest.Server) { 487 type socketioReq struct { 488 Method string `json:"method"` 489 Params []interface{} `json:"params"` 490 } 491 492 url := strings.Replace(ts.URL, "http://", "ws://", 1) + "/socket.io/" 493 s, err := gosocketio.Dial(url, transport.GetDefaultWebsocketTransport()) 494 if err != nil { 495 t.Fatal(err) 496 } 497 defer s.Close() 498 499 tests := []struct { 500 name string 501 req socketioReq 502 want string 503 }{ 504 { 505 name: "getInfo", 506 req: socketioReq{"getInfo", []interface{}{}}, 507 want: `{"result":{"blocks":225494,"testnet":true,"network":"fakecoin","subversion":"/Fakecoin:0.0.1/","coin_name":"Fakecoin","about":"Blockbook - blockchain indexer for TREZOR wallet https://trezor.io/. Do not use for any other purpose."}}`, 508 }, 509 { 510 name: "estimateFee", 511 req: socketioReq{"estimateFee", []interface{}{17}}, 512 want: `{"result":0.000034}`, 513 }, 514 { 515 name: "estimateSmartFee", 516 req: socketioReq{"estimateSmartFee", []interface{}{19, true}}, 517 want: `{"result":0.000019}`, 518 }, 519 { 520 name: "getAddressTxids", 521 req: socketioReq{"getAddressTxids", []interface{}{ 522 []string{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"}, 523 map[string]interface{}{ 524 "start": 2000000, 525 "end": 0, 526 "queryMempool": false, 527 }, 528 }}, 529 want: `{"result":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840"]}`, 530 }, 531 { 532 name: "getAddressTxids limited range", 533 req: socketioReq{"getAddressTxids", []interface{}{ 534 []string{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"}, 535 map[string]interface{}{ 536 "start": 225494, 537 "end": 225494, 538 "queryMempool": false, 539 }, 540 }}, 541 want: `{"result":["7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25"]}`, 542 }, 543 { 544 name: "getAddressHistory", 545 req: socketioReq{"getAddressHistory", []interface{}{ 546 []string{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"}, 547 map[string]interface{}{ 548 "start": 2000000, 549 "end": 0, 550 "queryMempool": false, 551 "from": 0, 552 "to": 5, 553 }, 554 }}, 555 want: `{"result":{"totalCount":2,"items":[{"addresses":{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz":{"inputIndexes":[1],"outputIndexes":[]}},"satoshis":-12345,"confirmations":1,"tx":{"hex":"","height":225494,"blockTimestamp":22549400000,"version":0,"hash":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","inputs":[{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","outputIndex":0,"script":"","sequence":0,"address":"mv9uLThosiEnGRbVPS7Vhyw6VssbVRsiAw","satoshis":1234567890123},{"txid":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","outputIndex":1,"script":"","sequence":0,"address":"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz","satoshis":12345}],"inputSatoshis":1234567902468,"outputs":[{"satoshis":317283951061,"script":"76a914ccaaaf374e1b06cb83118453d102587b4273d09588ac","address":"mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX"},{"satoshis":917283951061,"script":"76a9148d802c045445df49613f6a70ddd2e48526f3701f88ac","address":"mtR97eM2HPWVM6c8FGLGcukgaHHQv7THoL"}],"outputSatoshis":1234567902122,"feeSatoshis":346}},{"addresses":{"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz":{"inputIndexes":[],"outputIndexes":[1]}},"satoshis":12345,"confirmations":2,"tx":{"hex":"","height":225493,"blockTimestamp":22549300000,"version":0,"hash":"00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840","inputs":[],"outputs":[{"satoshis":100000000,"script":"76a914010d39800f86122416e28f485029acf77507169288ac","address":"mfcWp7DB6NuaZsExybTTXpVgWz559Np4Ti"},{"satoshis":12345,"script":"76a9148bdf0aa3c567aa5975c2e61321b8bebbe7293df688ac","address":"mtGXQvBowMkBpnhLckhxhbwYK44Gs9eEtz"}],"outputSatoshis":100012345}}]}}`, 556 }, 557 { 558 name: "getBlockHeader", 559 req: socketioReq{"getBlockHeader", []interface{}{225493}}, 560 want: `{"result":{"hash":"0000000076fbbed90fd75b0e18856aa35baa984e9c9d444cf746ad85e94e2997","version":0,"confirmations":0,"height":0,"chainWork":"","nextHash":"","merkleRoot":"","time":0,"medianTime":0,"nonce":0,"bits":"","difficulty":0}}`, 561 }, 562 { 563 name: "getDetailedTransaction", 564 req: socketioReq{"getDetailedTransaction", []interface{}{"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71"}}, 565 want: `{"result":{"hex":"","height":225494,"blockTimestamp":22549400001,"version":0,"hash":"3d90d15ed026dc45e19ffb52875ed18fa9e8012ad123d7f7212176e2b0ebdb71","inputs":[{"txid":"7c3be24063f268aaa1ed81b64776798f56088757641a34fb156c4f51ed2e9d25","outputIndex":0,"script":"","sequence":0,"address":"mzB8cYrfRwFRFAGTDzV8LkUQy5BQicxGhX","satoshis":317283951061},{"txid":"effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75","outputIndex":1,"script":"","sequence":0,"address":"2Mz1CYoppGGsLNUGF2YDhTif6J661JitALS","satoshis":1}],"inputSatoshis":317283951062,"outputs":[{"satoshis":118641975500,"script":"76a914b434eb0c1a3b7a02e8a29cc616e791ef1e0bf51f88ac","address":"mwwoKQE5Lb1G4picHSHDQKg8jw424PF9SC"},{"satoshis":198641975500,"script":"76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac","address":"mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP"}],"outputSatoshis":317283951000,"feeSatoshis":62}}`, 566 }, 567 { 568 name: "sendTransaction", 569 req: socketioReq{"sendTransaction", []interface{}{"010000000001019d64f0c72a0d206001decbffaa722eb1044534c"}}, 570 want: `{"error":{"message":"Invalid data"}}`, 571 }, 572 } 573 574 for _, tt := range tests { 575 t.Run(tt.name, func(t *testing.T) { 576 resp, err := s.Ack("message", tt.req, time.Second*3) 577 if err != nil { 578 t.Errorf("Socketio error %v", err) 579 } 580 if resp != tt.want { 581 t.Errorf("Socketio resp %v, want %v", resp, tt.want) 582 } 583 }) 584 } 585 } 586 587 func Test_PublicServer_UTXO(t *testing.T) { 588 s, dbpath := setupPublicHTTPServer(t) 589 defer closeAndDestroyPublicServer(t, s, dbpath) 590 s.ConnectFullPublicInterface() 591 // take the handler of the public server and pass it to the test server 592 ts := httptest.NewServer(s.https.Handler) 593 defer ts.Close() 594 595 httpTests(t, ts) 596 socketioTests(t, ts) 597 598 }