Added documentaiton of sync tests

This commit is contained in:
Jakub Matys 2018-10-10 09:37:14 +02:00
parent a16ef2904e
commit 3b35981af1
2 changed files with 70 additions and 42 deletions

View File

@ -81,7 +81,7 @@ In section *blockbook* update information how to build and configure Blockbook s
Update *package_maintainer* and *package_maintainer_email* options in section *meta*. Update *package_maintainer* and *package_maintainer_email* options in section *meta*.
Execute script *go run contrib/scripts/check-and-generate-port-registry.go -w* that checks mandatory ports and Execute script *go run contrib/scripts/check-and-generate-port-registry.go -w* that checks mandatory ports and
uniqueness of ports and updates registry of ports *docs/ports.md*. uniqueness of ports and updates registry of ports *docs/ports.md*.
Now you can try generate package definitions as described above in order to check outputs. Now you can try generate package definitions as described above in order to check outputs.
@ -167,7 +167,7 @@ different approach for address representation than Bitcoin.
#### Add tests #### Add tests
Add unit tests and integration tests. Tests are described [here](/docs/testing.md). Add unit tests and integration tests. PR without passing tests won't be accepted. Tests are described [here](/docs/testing.md).
#### Deploy public server #### Deploy public server

View File

@ -4,16 +4,22 @@ There are two kinds of tests in Blockbook unit tests and integration tests.
back-end daemon so they can't be executed at every build. Blockbook's build system uses conditional compilation to back-end daemon so they can't be executed at every build. Blockbook's build system uses conditional compilation to
distinguish which tests should be executed. distinguish which tests should be executed.
To execute unit tests run `make test`. To execute unit tests and integration tests run `make test-all`. You can use There are several ways to run tests:
Go's flag *-run* to filter which tests should be executed. Use *ARGS* variable, e.g.
* `make test` run unit tests only (note that `make deb*` and `make all*` commands always run also *test* target)
* `make test-integration` run integration tests only
* `make test-all` run all tests above
You can use Go's flag *-run* to filter which tests should be executed. Use *ARGS* variable, e.g.
`make test-all ARGS="-run TestBitcoin"`. `make test-all ARGS="-run TestBitcoin"`.
## Unit tests ## Unit tests
Unit test file must start with constraint `// +build unittest` followed by blank line (constraints are described Unit test file must start with constraint `// +build unittest` followed by blank line (constraints are described
[here](https://golang.org/pkg/go/build/#hdr-Build_Constraints)). [here](https://golang.org/pkg/go/build/#hdr-Build_Constraints)).
Every coin implementation should have unit tests. At least for parser. Usual test suite define real transaction data Every coin implementation must have unit tests. At least for parser. Usual test suite define real transaction data
and try pack and unpack them. Specialities of particular coin are tested too. See examples in and try pack and unpack them. Specialities of particular coin are tested too. See examples in
[bcashparser_test.go](/bchain/coins/bch/bcashparser_test.go), [bcashparser_test.go](/bchain/coins/bch/bcashparser_test.go),
[bitcoinparser_test.go](/bchain/coins/btc/bitcoinparser_test.go) and [bitcoinparser_test.go](/bchain/coins/btc/bitcoinparser_test.go) and
@ -22,50 +28,72 @@ and try pack and unpack them. Specialities of particular coin are tested too. Se
## Integration tests ## Integration tests
Integration test file must start with constraint `// +build integration` followed by blank line (constraints are Integration tests test interface between either Blockbook's components or back-end services. Integration tests are
described [here](https://golang.org/pkg/go/build/#hdr-Build_Constraints)). located in *tests* directory and every test suite has its own package. Because RPC and synchronization are crucial
components of Blockbook, it is mandatory that coin implementations have these integration tests defined. They are
implemented in packages `blockbook/tests/rpc` and `blockbook/tests/sync` and both of them are declarative. For each coin
there are test definition that enables particular tests of test suite and *testdata* file that contains test fixtures.
> Tests that cannot connect back-end service are skipped. `go test` doesn't show any information about skipped test, Not every coin implementation support full set of back-end API so it is necessary define which tests of test suite
> so you must run it in verbose mode with flag *-v*. are able to run. That is done in test definition file *blockbook/tests/tests.json*. Configuration is hierarchical and
test implementations call each level as separate subtest. Go's *test* command allows filter tests to run by `-run` flag.
It perfectly fits with layered test definitions. For example, you can:
### Blockbook integration tests * run tests for single coin `make test-integration ARGS="-run=TestIntegration/bitcoin/"`
* run single test suite `make test-integration ARGS="-run=TestIntegration//sync/"`
* run single test `make test-integration ARGS="-run=TestIntegration//sync/HandleFork"`
* run tests for set of coins `make test-integration ARGS="-run='TestIntegration/(bcash|bgold|bitcoin|dash|dogecoin|litecoin|vertcoin|zcash)/'"`
TODO Test fixtures are defined in *testdata* directory in package of particular test suite. They are separate JSON files named
by coin. File schemes are very similar with verbose results of CLI tools and are described below. Integration tests
follow the same concept, use live component or service and verify their results with fixtures.
For simplicity, URLs and credentials of back-end services, where are tests going to connect, are loaded
from *blockbook/configs/coins*, the same place from where are production configuration files generated. There are general
URLs that link to *localhost*. If you need run tests against remote servers, there are few options how to do it:
* temporarily change config
* SSH tunneling `ssh -nNT -L 8030:localhost:8030 remote-server`
* HTTP proxy
### Synchronization integration tests
Synchronization is crucial part of Blockbook and these tests test whether it is doing well. They sync few blocks from
blockchain and verify them against fixtures. Ranges of blocks to sync are defined in fixtures.
* `ConnectBlocks` Calls *db.SyncWorker.connectBlocks*, a single-thread method that is called when a new block is detected.
Sync blocks and checks whether blocks and transactions from fixtures are indexed.
* `ConnectBlocksParallel` Calls *db.SyncWorker.ConnectBlocksParallel*, a multi-thread method that is used during initial
synchronization. Uses the same fixtures as ConnectBlocks.
* `HandleFork` Calls *db.SyncWorker.HandleFork* method that rolls back blockchain if a fork is detected. Test uses two
sets of blocks with the same heights in fixtures. First set with fake blocks is synced initially, than *HandleFork*
method is called and finally it is checked that index contain only blocks from second set the real blocks.
### Back-end RPC integration tests ### Back-end RPC integration tests
This kind of tests test *bchain.BlockChain* implementation and its capability to communicate with back-end RPC. Tests This kind of tests test *bchain.BlockChain* implementation and its capability to communicate with back-end RPC.
of most of coins are similar so there is single generalized implementation in package *blockbook/bchain/tests/rpc*. Test
functions of particular coin implementation can just initialize test object and call its methods. Configuration of tests
is stored in *blockbook/bchain/tests/rpc/config.json* and consists of back-end URL and credentials. Every test suite also
has fixtures stored in *blockbook/bchain/tests/rpc/testdata*. Content is obvious from existing files.
Tests listed below just call back-end RPC methods with parameters from fixture file and check results against same Tests listed below just call back-end RPC methods with parameters from fixture file and check results against same
fixture file. So data in fixture file must be related together. fixture file. So data in fixture file must be related together.
* TestGetBlockHash Calls *BlockChain.GetBlockHash* with height and checks returned hash. * `GetBlockHash` Calls *BlockChain.GetBlockHash* with height and checks returned hash.
* TestGetBlockHeader Calls *BlockChain.GetBlockHeader* with hash and check returned header. Note that only fields * `GetBlockHeader` Calls *BlockChain.GetBlockHeader* with hash and check returned header. Note that only fields
that are significant are *Hash* and *Height*, they are checked against fixtures. that are significant are *Hash* and *Height*, they are checked against fixtures. Scheme of transaction data in fixtures
* TestGetBlock Calls *BlockChain.GetBlock* with hash and checks returned block (actually number of transactions and is very similar to verbose result of *getrawtransaction* command of CLI tools and can be copy-pasted with few
their txids). modifications.
* TestGetTransaction Calls *BlockChain.GetTransaction* with txid and checks result against transaction object, where * `GetBlock` Calls *BlockChain.GetBlock* with hash and checks returned block (actually number of transactions and
*txid* is key and* **transaction object* is value of *txDetails* object in fixture file. their txids).
* TestGetTransactionForMempool Calls *BlockChain.GetTransactionForMempool* that should be version of * `GetTransaction` Calls *BlockChain.GetTransaction* with txid and checks result against transaction object, where
*BlockChain.GetTransaction* optimized for mempool. Implementation of test is similar. *txid* is key and* **transaction object* is value of *txDetails* object in fixture file.
* TestGetMempoolEntry Calls *BlockChain.GetMempoolEntry* and checks result. Because mempool is living structure it * `GetTransactionForMempool` Calls *BlockChain.GetTransactionForMempool* that should be version of
tries to load entry for random transaction in mempool repeatedly. *BlockChain.GetTransaction* optimized for mempool. Implementation of test is similar.
* TestEstimateSmartFee Calls *BlockChain.EstimateSmartFee* for few numbers of blocks and checks if returned fee is * `GetMempoolEntry` Calls *BlockChain.GetMempoolEntry* and checks result. Because mempool is living structure it
non-negative. tries to load entry for random transaction in mempool repeatedly.
* TestEstimateFee Calls *BlockChain.EstimateFee*; implementation is same as *TestEstimateSmartFee*. * `EstimateSmartFee` Calls *BlockChain.EstimateSmartFee* for few numbers of blocks and checks if returned fee is
* TestGetBestBlockHash Calls *BlockChain.GetBestBlockHash* and verifies that returned hash matches the really last non-negative.
block. * `EstimateFee` Calls *BlockChain.EstimateFee*; implementation is same as *EstimateSmartFee*.
* TestGetBestBlockHeight Calls *BlockChain.GetBestBlockHeight* and verifies that returned height matches the really * `GetBestBlockHash` Calls *BlockChain.GetBestBlockHash* and verifies that returned hash matches the really last
last block. block.
* `GetBestBlockHeight` Calls *BlockChain.GetBestBlockHeight* and verifies that returned height matches the really
TODO: TestMempoolSync should be "Blockbook integration test" last block.
* `MempoolSync` Synchronize *BlockChain*'s mempool and verify if sync was successful.
* TestMempoolSync Synchronize *BlockChain*'s mempool and verify if sync was successful.
For example see [bitcoinrpc_test.go](/bchain/coins/btc/bitcoinrpc_test.go) and
[implementation](/bchain/tests/rpc/rpc.go) of test suite.