cli: add tests for NEP5 balance querying

This commit is contained in:
Evgenii Stratonikov 2020-08-29 14:41:33 +03:00
parent 282b55494b
commit 468f7ee650
6 changed files with 340 additions and 4 deletions

148
cli/executor_test.go Normal file
View file

@ -0,0 +1,148 @@
package main
import (
"bytes"
"errors"
"io"
"testing"
"github.com/nspcc-dev/neo-go/cli/input"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/rpc/server"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"go.uber.org/zap"
"go.uber.org/zap/zaptest"
)
const (
validatorAddr = "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK"
validatorWallet = "testdata/wallet1_solo.json"
)
var validatorHash, _ = address.StringToUint160(validatorAddr)
// executor represents context for a test instance.
// It can be safely used in multiple tests, but not in parallel.
type executor struct {
// CLI is a cli application to test.
CLI *cli.App
// Chain is a blockchain instance (can be empty).
Chain *core.Blockchain
// RPC is an RPC server to query (can be empty).
RPC *server.Server
// NetSrv is a network server (can be empty).
NetSrv *network.Server
// Out contains command output.
Out *bytes.Buffer
// Err contains command errors.
Err *bytes.Buffer
}
func newTestChain(t *testing.T) (*core.Blockchain, *server.Server, *network.Server) {
configPath := "../config/protocol.unit_testnet.single.yml"
cfg, err := config.LoadFile(configPath)
require.NoError(t, err, "could not load config")
memoryStore := storage.NewMemoryStore()
logger := zaptest.NewLogger(t)
chain, err := core.NewBlockchain(memoryStore, cfg.ProtocolConfiguration, logger)
require.NoError(t, err, "could not create chain")
go chain.Run()
serverConfig := network.NewServerConfig(cfg)
netSrv, err := network.NewServer(serverConfig, chain, zap.NewNop())
require.NoError(t, err)
go netSrv.Start(make(chan error, 1))
rpcServer := server.New(chain, cfg.ApplicationConfiguration.RPC, netSrv, logger)
errCh := make(chan error, 2)
rpcServer.Start(errCh)
return chain, &rpcServer, netSrv
}
func newExecutor(t *testing.T, needChain bool) *executor {
e := &executor{
CLI: newApp(),
Out: bytes.NewBuffer(nil),
Err: bytes.NewBuffer(nil),
}
e.CLI.Writer = e.Out
e.CLI.ErrWriter = e.Err
if needChain {
e.Chain, e.RPC, e.NetSrv = newTestChain(t)
}
return e
}
func (e *executor) Close(t *testing.T) {
input.Terminal = nil
if e.RPC != nil {
require.NoError(t, e.RPC.Shutdown())
}
if e.NetSrv != nil {
e.NetSrv.Shutdown()
}
if e.Chain != nil {
e.Chain.Close()
}
}
func (e *executor) checkNextLine(t *testing.T, expected string) {
line, err := e.Out.ReadString('\n')
require.NoError(t, err)
e.checkLine(t, line, expected)
}
func (e *executor) checkLine(t *testing.T, line, expected string) {
require.Regexp(t, expected, line)
}
func (e *executor) checkEOF(t *testing.T) {
_, err := e.Out.ReadString('\n')
require.True(t, errors.Is(err, io.EOF))
}
func setExitFunc() <-chan int {
ch := make(chan int, 1)
cli.OsExiter = func(code int) {
ch <- code
}
return ch
}
func checkExit(t *testing.T, ch <-chan int, code int) {
select {
case c := <-ch:
require.Equal(t, code, c)
default:
if code != 0 {
require.Fail(t, "no exit was called")
}
}
}
// RunWithError runs command and checks that is exits with error.
func (e *executor) RunWithError(t *testing.T, args ...string) {
ch := setExitFunc()
require.Error(t, e.run(args...))
checkExit(t, ch, 1)
}
// Run runs command and checks that there were no errors.
func (e *executor) Run(t *testing.T, args ...string) {
ch := setExitFunc()
require.NoError(t, e.run(args...))
checkExit(t, ch, 0)
}
func (e *executor) run(args ...string) error {
e.Out.Reset()
e.Err.Reset()
return e.CLI.Run(args)
}

View file

@ -13,6 +13,14 @@ import (
)
func main() {
ctl := newApp()
if err := ctl.Run(os.Args); err != nil {
panic(err)
}
}
func newApp() *cli.App {
ctl := cli.NewApp()
ctl.Name = "neo-go"
ctl.Version = config.Version
@ -23,8 +31,5 @@ func main() {
ctl.Commands = append(ctl.Commands, wallet.NewCommands()...)
ctl.Commands = append(ctl.Commands, vm.NewCommands()...)
ctl.Commands = append(ctl.Commands, util.NewCommands()...)
if err := ctl.Run(os.Args); err != nil {
panic(err)
}
return ctl
}

13
cli/main_test.go Normal file
View file

@ -0,0 +1,13 @@
package main
import (
"testing"
)
func TestCLIVersion(t *testing.T) {
e := newExecutor(t, false)
defer e.Close(t)
e.Run(t, "neo-go", "--version")
e.checkNextLine(t, "^neo-go version")
e.checkEOF(t)
}

46
cli/nep5_test.go Normal file
View file

@ -0,0 +1,46 @@
package main
import (
"strconv"
"testing"
"github.com/nspcc-dev/neo-go/pkg/util"
)
func TestNEP5Balance(t *testing.T) {
e := newExecutor(t, true)
defer e.Close(t)
cmd := []string{
"neo-go", "wallet", "nep5", "balance",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", validatorWallet,
"--addr", validatorAddr,
}
t.Run("NEO", func(t *testing.T) {
b, index := e.Chain.GetGoverningTokenBalance(validatorHash)
checkResult := func(t *testing.T) {
e.checkNextLine(t, "^\\s*TokenHash:\\s*"+e.Chain.GoverningTokenHash().StringLE())
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+b.String())
e.checkNextLine(t, "^\\s*Updated\\s*:\\s*"+strconv.FormatUint(uint64(index), 10))
e.checkEOF(t)
}
t.Run("Alias", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "neo")...)
checkResult(t)
})
t.Run("Hash", func(t *testing.T) {
e.Run(t, append(cmd, "--token", e.Chain.GoverningTokenHash().StringLE())...)
checkResult(t)
})
})
t.Run("GAS", func(t *testing.T) {
e.Run(t, append(cmd, "--token", "gas")...)
e.checkNextLine(t, "^\\s*TokenHash:\\s*"+e.Chain.UtilityTokenHash().StringLE())
b := e.Chain.GetUtilityTokenBalance(validatorHash)
e.checkNextLine(t, "^\\s*Amount\\s*:\\s*"+util.Fixed8(b.Int64()).String())
})
t.Run("Invalid", func(t *testing.T) {
e.RunWithError(t, append(cmd, "--token", "kek")...)
})
return
}

72
cli/testdata/wallet1_solo.json vendored Normal file
View file

@ -0,0 +1,72 @@
{
"version": "3.0",
"accounts": [
{
"address": "NbTiM6h8r99kpRtb428XcsUk1TzKed2gTc",
"key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux",
"label": "",
"contract": {
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcILQZVEDXg=",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isdefault": false
},
{
"address": "NUVPACMnKFhpuHjsRjhUvXz1XhqfGZYVtY",
"key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFAtBE43vrw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
},
{
"name": "parameter1",
"type": "Signature"
},
{
"name": "parameter2",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isdefault": false
},
{
"address": "NVNvVRW5Q5naSx2k2iZm7xRgtRNGuZppAK",
"key": "6PYN7LvaWqBNw7Xb7a52LSbPnP91kyuzYi3HncGvQwQoYAY2W8DncTgpux",
"label": "",
"contract": {
"script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEQtBE43vrw==",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isdefault": false
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
}
}

View file

@ -0,0 +1,52 @@
ProtocolConfiguration:
Magic: 42
SecondsPerBlock: 1
MemPoolSize: 100
StandbyCommittee:
- 02b3622bf4017bdfe317c58aed5f4c753f206b7db896046fa7d774bbc4bf7f8dc2
ValidatorsCount: 1
VerifyBlocks: true
VerifyTransactions: true
ApplicationConfiguration:
# LogPath could be set up in case you need stdout logs to some proper file.
# LogPath: "./log/neogo.log"
DBConfiguration:
Type: "inmemory" #other options: 'inmemory','redis','boltdb', 'badgerdb'.
# DB type options. Uncomment those you need in case you want to switch DB type.
# LevelDBOptions:
# DataDirectoryPath: "./chains/unit_testnet"
# RedisDBOptions:
# Addr: "localhost:6379"
# Password: ""
# DB: 0
# BoltDBOptions:
# FilePath: "./chains/unit_testnet.bolt"
# BadgerDBOptions:
# BadgerDir: "./chains/unit_testnet.badger"
# Uncomment in order to set up custom address for node.
# Address: 127.0.0.1
NodePort: 0
Relay: true
DialTimeout: 3
ProtoTickInterval: 2
PingInterval: 30
PingTimeout: 90
MinPeers: 0
MaxPeers: 10
AttemptConnPeers: 5
UnlockWallet:
Path: "testdata/wallet1_solo.json"
Password: "one"
RPC:
Address: 127.0.0.1
MaxGasInvoke: 10
Enabled: true
EnableCORSWorkaround: false
Port: 0 # let the system choose port dynamically
Prometheus:
Enabled: false #since it's not useful for unit tests.
Port: 2112
Pprof:
Enabled: false #since it's not useful for unit tests.
Port: 2113