forked from TrueCloudLab/neoneo-go
cli: add tests for NEP5 balance querying
This commit is contained in:
parent
282b55494b
commit
468f7ee650
6 changed files with 340 additions and 4 deletions
148
cli/executor_test.go
Normal file
148
cli/executor_test.go
Normal 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)
|
||||
}
|
13
cli/main.go
13
cli/main.go
|
@ -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
13
cli/main_test.go
Normal 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
46
cli/nep5_test.go
Normal 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
72
cli/testdata/wallet1_solo.json
vendored
Normal 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
|
||||
}
|
||||
}
|
52
config/protocol.unit_testnet.single.yml
Normal file
52
config/protocol.unit_testnet.single.yml
Normal 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
|
Loading…
Reference in a new issue