Added rpc unit tests (#107)

* Fixed small incosistencies related to comments and variable name

* For testing purposes we have to move the method newBlockchain from the cli/server/server.go to  pkg/core/blockchain.go. In addition we have to rename it to NewBlockchainLevelDB in order to be able to export it and avoid naming conflicts. In future we still need to think how to switch between different blockchain implementation easily but for the time being this is not possible.

* Added unit tests for the rpc server

* Added unit_testnet chain fixture

* fixed port number

* Added errors handling

* move unit_testnet chain from 'cli/chains' to 'pkg/rpc/chains'
This commit is contained in:
dauTT 2019-01-22 13:14:52 +01:00 committed by Anthony De Meulemeester
parent 77296f6481
commit 1360e1de68
21 changed files with 256 additions and 23 deletions

View file

@ -7,7 +7,6 @@ import (
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/CityOfZion/neo-go/pkg/rpc"
"github.com/pkg/errors"
@ -53,7 +52,7 @@ func startServer(ctx *cli.Context) error {
signal.Notify(interruptChan, os.Interrupt)
serverConfig := network.NewServerConfig(cfg)
chain, err := newBlockchain(cfg)
chain, err := core.NewBlockchainLevelDB(cfg)
if err != nil {
err = fmt.Errorf("could not initialize blockchain: %s", err)
return cli.NewExitError(err, 1)
@ -98,19 +97,6 @@ Main:
return nil
}
func newBlockchain(cfg config.Config) (*core.Blockchain, error) {
// Hardcoded for now.
store, err := storage.NewLevelDBStore(
cfg.ApplicationConfiguration.DataDirectoryPath,
nil,
)
if err != nil {
return nil, err
}
return core.NewBlockchain(store, cfg.ProtocolConfiguration)
}
func logo() string {
return `
_ ____________ __________

View file

@ -17,6 +17,7 @@ const (
ModeMainNet NetMode = 0x00746e41 // 7630401
ModeTestNet NetMode = 0x74746e41 // 1953787457
ModePrivNet NetMode = 56753 // docker privnet
ModeUnitTestNet NetMode = 0
)
var (
@ -78,6 +79,8 @@ func (n NetMode) String() string {
return "testnet"
case ModeMainNet:
return "mainnet"
case ModeUnitTestNet:
return "unit_testnet"
default:
return "net unknown"
}

View file

@ -0,0 +1,26 @@
ProtocolConfiguration:
Magic: 56753
AddressVersion: 23
StandbyValidators:
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
- 02103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e
- 03d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee699
- 02a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd62
SeedList:
- 127.0.0.1:20334
- 127.0.0.1:20335
- 127.0.0.1:20336
SystemFee:
EnrollmentTransaction: 1000
IssueTransaction: 500
PublishTransaction: 500
RegisterTransaction: 10000
ApplicationConfiguration:
DataDirectoryPath: "./chains/unit_testnet"
RPCPort: 20332
NodePort: 20333
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
MaxPeers: 50

View file

@ -139,7 +139,7 @@ func (a *AssetState) EncodeBinary(w io.Writer) error {
return binary.Write(w, binary.LittleEndian, a.IsFrozen)
}
// Get the asset name based on its type.
// GetName returns the asset name based on its type.
func (a *AssetState) GetName() string {
if a.AssetType == transaction.GoverningToken {

View file

@ -9,6 +9,7 @@ import (
"github.com/CityOfZion/neo-go/config"
//"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/pkg/core/storage"
"github.com/CityOfZion/neo-go/pkg/core/transaction"
"github.com/CityOfZion/neo-go/pkg/util"
@ -79,6 +80,19 @@ func NewBlockchain(s storage.Store, cfg config.ProtocolConfiguration) (*Blockcha
return bc, nil
}
// GetBlockchainLevelDB returns blockchain based on configuration
func NewBlockchainLevelDB(cfg config.Config) (*Blockchain, error) {
store, err := storage.NewLevelDBStore(
cfg.ApplicationConfiguration.DataDirectoryPath,
nil,
)
if err != nil {
return nil, err
}
return NewBlockchain(store, cfg.ProtocolConfiguration)
}
func (bc *Blockchain) init() error {
genesisBlock, err := createGenesisBlock(bc.config)
if err != nil {

View file

@ -51,7 +51,7 @@ func (c *Cache) has(h util.Uint256) bool {
return ok
}
// Hash returns whether the cach contains the given hash.
// Has returns whether the cach contains the given hash.
func (c *Cache) Has(h util.Uint256) bool {
c.lock.Lock()
defer c.lock.Unlock()

View file

@ -52,9 +52,9 @@ func NewUnspentCoinState(n int) *UnspentCoinState {
}
// commit writes all unspent coin states to the given Batch.
func (s UnspentCoins) commit(b storage.Batch) error {
func (u UnspentCoins) commit(b storage.Batch) error {
buf := new(bytes.Buffer)
for hash, state := range s {
for hash, state := range u {
if err := state.EncodeBinary(buf); err != nil {
return err
}
@ -78,7 +78,7 @@ func (s *UnspentCoinState) EncodeBinary(w io.Writer) error {
return nil
}
// DecodBinary decodes UnspentCoinState from the given io.Reader.
// DecodeBinary decodes UnspentCoinState from the given io.Reader.
func (s *UnspentCoinState) DecodeBinary(r io.Reader) error {
lenStates := util.ReadVarUint(r)
s.states = make([]CoinState, lenStates)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1 @@
MANIFEST-000025

View file

View file

@ -0,0 +1,82 @@
=============== Nov 26, 2018 (CET) ===============
23:50:38.287098 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
23:50:38.341648 db@open opening
23:50:38.344325 version@stat F·[] S·0B[] Sc·[]
23:50:38.387172 db@janitor F·2 G·0
23:50:38.387311 db@open done T·45.546154ms
23:50:42.538000 memdb@flush N·12831 S·3MiB
23:50:42.617097 memdb@flush created L0@3 N·12831 S·2MiB "\x01\x00\x03..\xaa1\x14,v5970":"\xf0,v1"
23:50:42.617164 version@stat F·[1] S·2MiB[2MiB] Sc·[0.25]
23:50:42.639664 memdb@flush committed F·1 T·101.60375ms
23:50:42.641205 journal@remove removed @1
23:50:51.406280 memdb@flush N·16663 S·3MiB
23:50:51.479245 memdb@flush created L0@5 N·16663 S·3MiB "\x01\x00\x01..\n\xce\xf5,v22520":"\xc1,v12833"
23:50:51.479405 version@stat F·[2] S·5MiB[5MiB] Sc·[0.50]
23:50:51.501823 memdb@flush committed F·1 T·95.469736ms
23:50:51.503989 journal@remove removed @2
23:50:52.558287 table@compaction L0·2 -> L1·0 S·5MiB Q·29522
23:50:52.666973 table@build created L1@6 N·5499 S·2MiB "\x01\x00\x01..\n\xce\xf5,v22520":"\x01q`..\x0e\x88 ,v8859"
23:50:52.735582 table@build created L1@7 N·5499 S·2MiB "\x01qe..\xdfvy,v25919":"\x01\xe4\x15..~e\xd4,v20183"
23:50:52.785741 table@build created L1@8 N·3762 S·990KiB "\x01\xe4\x18..Ew@,v25246":"\xf0,v1"
23:50:52.785895 version@stat F·[0 3] S·4MiB[0B 4MiB] Sc·[0.00 0.05]
23:50:52.808457 table@compaction committed F+1 S-951KiB Ke·0 D·14734 T·250.047198ms
23:50:52.810550 table@remove removed @5
23:50:52.812603 table@remove removed @3
23:51:55.424408 memdb@flush N·25534 S·3MiB
23:51:55.607413 memdb@flush created L0@10 N·25534 S·3MiB "\x01\x00\x03..\xaa1\x14,v36636":"\xc1,v39129"
23:51:55.607695 version@stat F·[1 3] S·8MiB[3MiB 4MiB] Sc·[0.25 0.05]
23:51:55.629931 memdb@flush committed F·1 T·205.445709ms
23:51:55.633098 journal@remove removed @4
23:51:56.911824 table@compaction L0·1 -> L1·3 S·8MiB Q·55136
23:51:57.016224 table@build created L1@11 N·5568 S·2MiB "\x01\x00\x01..\n\xce\xf5,v22520":"\x01r\xb9..;\xcfa,v36196"
23:51:57.085097 table@build created L1@12 N·5559 S·2MiB "\x01r\xb9..\x8c^^,v22386":"\x01\xe6\xe0..\xcf\xf3\xdc,v34688"
23:51:57.149174 table@build created L1@13 N·16401 S·1MiB "\x01\xe6\xe0.. \xa6\xca,v50877":"\xf0,v1"
23:51:57.149348 version@stat F·[0 3] S·5MiB[0B 5MiB] Sc·[0.00 0.06]
23:51:57.171727 table@compaction committed F-1 S-2MiB Ke·0 D·12766 T·259.819169ms
23:51:57.173227 table@remove removed @10
23:51:57.174685 table@remove removed @6
23:51:57.176587 table@remove removed @7
23:51:57.177884 table@remove removed @8
=============== Dec 21, 2018 (CET) ===============
21:48:53.302835 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:48:53.322817 version@stat F·[0 3] S·5MiB[0B 5MiB] Sc·[0.00 0.06]
21:48:53.322841 db@open opening
21:48:53.322906 journal@recovery F·1
21:48:53.323457 journal@recovery recovering @9
21:48:53.494374 memdb@flush created L0@14 N·24266 S·2MiB "\x01\x00\x01..\n\xce\xf5,v67975":"\xc1,v61538"
21:48:53.500940 version@stat F·[1 3] S·8MiB[2MiB 5MiB] Sc·[0.25 0.06]
21:48:53.541457 db@janitor F·6 G·0
21:48:53.541547 db@open done T·218.687106ms
=============== Dec 21, 2018 (CET) ===============
21:49:14.115818 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:49:14.116034 version@stat F·[1 3] S·8MiB[2MiB 5MiB] Sc·[0.25 0.06]
21:49:14.116045 db@open opening
21:49:14.116086 journal@recovery F·1
21:49:14.116495 journal@recovery recovering @15
21:49:14.256619 memdb@flush created L0@17 N·700 S·149KiB "\x01\x00/..\x02&\xab,v79538":"\xc1,v79297"
21:49:14.257851 version@stat F·[2 3] S·8MiB[2MiB 5MiB] Sc·[0.50 0.06]
21:49:14.539567 db@janitor F·7 G·0
21:49:14.539642 db@open done T·423.57991ms
21:49:14.579206 table@compaction L0·2 -> L1·3 S·8MiB Q·79996
=============== Dec 21, 2018 (CET) ===============
21:54:32.133909 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:54:32.134127 version@stat F·[2 3] S·8MiB[2MiB 5MiB] Sc·[0.50 0.06]
21:54:32.134137 db@open opening
21:54:32.134181 journal@recovery F·1
21:54:32.134631 journal@recovery recovering @18
21:54:32.224030 memdb@flush created L0@20 N·700 S·149KiB "\x01\x00/..\x02&\xab,v80239":"\xc1,v79998"
21:54:32.225414 version@stat F·[3 3] S·8MiB[3MiB 5MiB] Sc·[0.75 0.06]
21:54:32.269397 db@janitor F·8 G·0
21:54:32.269495 db@open done T·135.337131ms
21:54:32.307525 table@compaction L0·3 -> L1·3 S·8MiB Q·80697
=============== Dec 21, 2018 (CET) ===============
21:57:38.775640 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
21:57:38.775891 version@stat F·[3 3] S·8MiB[3MiB 5MiB] Sc·[0.75 0.06]
21:57:38.775902 db@open opening
21:57:38.775949 journal@recovery F·1
21:57:38.776389 journal@recovery recovering @21
21:57:38.797001 memdb@flush created L0@23 N·700 S·149KiB "\x01\x00/..\x02&\xab,v80940":"\xc1,v80699"
21:57:38.797463 version@stat F·[4 3] S·8MiB[3MiB 5MiB] Sc·[1.00 0.06]
21:57:38.843345 db@janitor F·9 G·0
21:57:38.843446 db@open done T·67.525965ms
21:57:38.843705 table@compaction L0·4 -> L1·3 S·8MiB Q·81398

Binary file not shown.

121
pkg/rpc/server_test.go Normal file
View file

@ -0,0 +1,121 @@
package rpc
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"github.com/CityOfZion/neo-go/pkg/core"
"github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/network"
"github.com/stretchr/testify/assert"
)
func TestHandler(t *testing.T) {
// setup rpcServer server
net := config.ModeUnitTestNet
configPath := "../../config"
cfg, err := config.Load(configPath, net)
if err != nil {
t.Errorf("could not load configuration file")
}
chain, err := core.NewBlockchainLevelDB(cfg)
if err != nil {
t.Errorf("could not create levelDB chain")
}
serverConfig := network.NewServerConfig(cfg)
server := network.NewServer(serverConfig, chain)
rpcServer := NewServer(chain, cfg.ApplicationConfiguration.RPCPort, server)
// setup handler
handler := http.HandlerFunc(rpcServer.requestHandler)
testCases := []struct {
rpcCall string
method string
expectedResult string
}{
{`{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
"getassetstate_1",
`{"jsonrpc":"2.0","result":{"assetId":"0x602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7","assetType":1,"name":"NEOGas","amount":"100000000","available":"0","precision":8,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"AWKECj9RD8rS8RPcpCgYVjk1DeYyHwxZm3","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`},
{`{ "jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["c56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b"] }`,
"getassetstate_2",
`{"jsonrpc":"2.0","result":{"assetId":"0xc56f33fc6ecfcd0c225c4ab356fee59390af8560be0e930faebe74a6daff7c9b","assetType":0,"name":"NEO","amount":"100000000","available":"0","precision":0,"fee":0,"address":"0x0000000000000000000000000000000000000000","owner":"00","admin":"Abf2qMs1pzQb8kYk9RuxtUb9jtRKJVuBJt","issuer":"AFmseVrdL9f9oyCzZefL9tG6UbvhPbdYzM","expiration":0,"is_frozen":false},"id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": ["62c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"] }`,
"getassetstate_3",
`{"jsonrpc":"2.0","result":"Invalid assetid","id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getassetstate", "params": [123] }`,
"getassetstate_4",
`{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params","data":"Param need to be a string"},"id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [10] }`,
"getblockhash_1",
`{"jsonrpc":"2.0","result":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e","id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockhash", "params": [-2] }`,
"getblockhash_2",
`{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid Params","data":"Param at index 0 should be greater than or equal to 0 and less then or equal to current block height, got: -2"},"id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getblock", "params": [10] }`,
"getblock",
`{"jsonrpc":"2.0","result":{"version":0,"previousblockhash":"0x7c5b4c8a70336bf68e8679be7c9a2a15f85c0f6d0e14389019dcc3edfab2bb4b","merkleroot":"0xc027979ad29226b7d34523b1439a64a6cf57fe3f4e823e9d3e90d43934783d26","time":1529926220,"height":10,"nonce":8313828522725096825,"next_consensus":"0xbe48d3a3f5d10013ab9ffee489706078714f1ea2","script":{"invocation":"40ac828e1c2a214e4d356fd2eccc7c7be9ef426f8e4ea67a50464e90ca4367e611c4c5247082b85a7d5ed985cfb90b9af2f1195531038f49c63fb6894b517071ea40b22b83d9457ca5c4c5bb2d8d7e95333820611d447bb171ce7b8af3b999d0a5a61c2301cdd645a33a47defd09c0f237a0afc86e9a84c2fe675d701e4015c0302240a6899296660c612736edc22f8d630927649d4ef1301868079032d80aae6cc1e21622f256497a84a71d7afeeef4c124135f611db24a0f7ab3d2a6886f15db7865","verification":"532102103a7f7dd016558597f7960d27c516a4394fd968b9e65155eb4b013e4040406e2102a7bc55fe8684e0119768d104ba30795bdcc86619e864add26156723ed185cd622102b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc22103d90c07df63e690ce77912e10ab51acc944b66860237b608c4f8f8309e71ee69954ae"},"tx":[{"type":0,"version":0,"attributes":null,"vin":null,"vout":null,"scripts":null}],"confirmations":12338,"nextblockhash":"0x2b1c78633dae7ab81f64362e0828153079a17b018d779d0406491f84c27b086f","hash":"0xd69e7a1f62225a35fed91ca578f33447d93fa0fd2b2f662b957e19c38c1dab1e"},"id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getblockcount", "params": [] }`,
"getblockcount",
`{"jsonrpc":"2.0","result":12349,"id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getconnectioncount", "params": [] }`,
"getconnectioncount",
`{"jsonrpc":"2.0","result":0,"id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getversion", "params": [] }`,
"getversion",
fmt.Sprintf(`{"jsonrpc":"2.0","result":{"port":20333,"nonce":%s,"useragent":"/NEO-GO:/"},"id":1}`, strconv.FormatUint(uint64(server.ID()), 10))},
{`{"jsonrpc": "2.0", "id": 1, "method": "getbestblockhash", "params": [] }`,
"getbestblockhash",
`{"jsonrpc":"2.0","result":"877f5f2084181b85ce4726ab0a86bea6cc82cdbcb6f2eb59e6b04d27fd10929c","id":1}`},
{`{"jsonrpc": "2.0", "id": 1, "method": "getpeers", "params": [] }`,
"getpeers",
`{"jsonrpc":"2.0","result":{"unconnected":[],"connected":[],"bad":[]},"id":1}`},
}
for _, tc := range testCases {
t.Run(fmt.Sprintf("method: %s, rpc call: %s", tc.method, tc.rpcCall), func(t *testing.T) {
jsonStr := []byte(tc.rpcCall)
req := httptest.NewRequest("POST", "http://0.0.0.0:20333/", bytes.NewBuffer(jsonStr))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
handler(w, req)
resp := w.Result()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Errorf("could not read response from the request: %s", tc.rpcCall)
}
assert.Equal(t, tc.expectedResult, string(bytes.TrimSpace(body)))
})
}
}