Merge pull request #1356 from nspcc-dev/cli/tests

cli: add non-integrational tests for cli packages
This commit is contained in:
Roman Khimov 2020-08-26 13:03:50 +03:00 committed by GitHub
commit 5b30389d33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 646 additions and 2 deletions

99
cli/flags/address_test.go Normal file
View file

@ -0,0 +1,99 @@
package flags
import (
"flag"
"io/ioutil"
"testing"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestAddress_String(t *testing.T) {
value := util.Uint160{1, 2, 3}
addr := Address{
IsSet: true,
Value: value,
}
require.Equal(t, address.Uint160ToString(value), addr.String())
}
func TestAddress_Set(t *testing.T) {
value := util.Uint160{1, 2, 3}
addr := Address{}
t.Run("bad address", func(t *testing.T) {
require.Error(t, addr.Set("not an address"))
})
t.Run("positive", func(t *testing.T) {
require.NoError(t, addr.Set(address.Uint160ToString(value)))
require.Equal(t, true, addr.IsSet)
require.Equal(t, value, addr.Value)
})
}
func TestAddress_Uint160(t *testing.T) {
value := util.Uint160{4, 5, 6}
addr := Address{}
t.Run("not set", func(t *testing.T) {
require.Panics(t, func() { addr.Uint160() })
})
t.Run("success", func(t *testing.T) {
addr.IsSet = true
addr.Value = value
require.Equal(t, value, addr.Uint160())
})
}
func TestAddressFlag_IsSet(t *testing.T) {
flag := AddressFlag{}
t.Run("not set", func(t *testing.T) {
require.False(t, flag.IsSet())
})
t.Run("set", func(t *testing.T) {
flag.Value.IsSet = true
require.True(t, flag.IsSet())
})
}
func TestAddressFlag_String(t *testing.T) {
flag := AddressFlag{
Name: "myFlag",
Usage: "Address to pass",
Value: Address{},
}
require.Equal(t, "--myFlag value\tAddress to pass", flag.String())
}
func TestAddress_getNameHelp(t *testing.T) {
require.Equal(t, "-f value", getNameHelp("f"))
require.Equal(t, "--flag value", getNameHelp("flag"))
}
func TestAddressFlag_GetName(t *testing.T) {
flag := AddressFlag{
Name: "my flag",
}
require.Equal(t, "my flag", flag.GetName())
}
func TestAddress(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(ioutil.Discard) // don't pollute test output
addr := AddressFlag{Name: "addr, a"}
addr.Apply(f)
require.NoError(t, f.Parse([]string{"--addr", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String())
require.NoError(t, f.Parse([]string{"-a", "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR"}))
require.Equal(t, "NRHkiY2hLy5ypD32CKZtL6pNwhbFMqDEhR", f.Lookup("a").Value.String())
require.Error(t, f.Parse([]string{"--addr", "kek"}))
}

66
cli/flags/fixed8_test.go Normal file
View file

@ -0,0 +1,66 @@
package flags
import (
"flag"
"io/ioutil"
"testing"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
func TestFixed8_String(t *testing.T) {
value := util.Fixed8(123)
f := Fixed8{
Value: value,
}
require.Equal(t, "0.00000123", f.String())
}
func TestFixed8_Set(t *testing.T) {
value := util.Fixed8(123)
f := Fixed8{}
require.Error(t, f.Set("not-a-fixed8"))
require.NoError(t, f.Set("0.00000123"))
require.Equal(t, value, f.Value)
}
func TestFixed8_Fixed8(t *testing.T) {
f := Fixed8{
Value: util.Fixed8(123),
}
require.Equal(t, util.Fixed8(123), f.Fixed8())
}
func TestFixed8Flag_String(t *testing.T) {
flag := Fixed8Flag{
Name: "myFlag",
Usage: "Gas amount",
}
require.Equal(t, "--myFlag value\tGas amount", flag.String())
}
func TestFixed8Flag_GetName(t *testing.T) {
flag := Fixed8Flag{
Name: "myFlag",
}
require.Equal(t, "myFlag", flag.GetName())
}
func TestFixed8(t *testing.T) {
f := flag.NewFlagSet("", flag.ContinueOnError)
f.SetOutput(ioutil.Discard) // don't pollute test output
gas := Fixed8Flag{Name: "gas, g"}
gas.Apply(f)
require.NoError(t, f.Parse([]string{"--gas", "0.123"}))
require.Equal(t, "0.123", f.Lookup("g").Value.String())
require.NoError(t, f.Parse([]string{"-g", "0.456"}))
require.Equal(t, "0.456", f.Lookup("g").Value.String())
require.Error(t, f.Parse([]string{"--gas", "kek"}))
}

17
cli/flags/util_test.go Normal file
View file

@ -0,0 +1,17 @@
package flags
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestEachName(t *testing.T) {
expected := "*one*two*three"
actual := ""
eachName(" one,two ,three", func(s string) {
actual += "*" + s
})
require.Equal(t, expected, actual)
}

View file

@ -0,0 +1,76 @@
package options
import (
"flag"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestGetNetwork(t *testing.T) {
t.Run("privnet", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.Equal(t, netmode.PrivNet, GetNetwork(ctx))
})
t.Run("testnet", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Bool("testnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.Equal(t, netmode.TestNet, GetNetwork(ctx))
})
t.Run("mainnet", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Bool("mainnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.Equal(t, netmode.MainNet, GetNetwork(ctx))
})
}
func TestGetTimeoutContext(t *testing.T) {
t.Run("default", func(t *testing.T) {
start := time.Now()
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
actualCtx, _ := GetTimeoutContext(ctx)
end := time.Now()
dl, _ := actualCtx.Deadline()
require.True(t, start.Before(dl) && dl.Before(end.Add(DefaultTimeout)))
})
t.Run("set", func(t *testing.T) {
start := time.Now()
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Duration("timeout", time.Duration(20), "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
actualCtx, _ := GetTimeoutContext(ctx)
end := time.Now()
dl, _ := actualCtx.Deadline()
require.True(t, start.Before(dl) && dl.Before(end.Add(time.Nanosecond*20)))
})
}
func TestGetRPCClient(t *testing.T) {
t.Run("no endpoint", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := GetTimeoutContext(ctx)
_, ec := GetRPCClient(gctx, ctx)
require.Equal(t, 1, ec.ExitCode())
})
t.Run("success", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String(RPCEndpointFlag, "http://localhost:50333", "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
gctx, _ := GetTimeoutContext(ctx)
_, ec := GetRPCClient(gctx, ctx)
require.Nil(t, ec)
})
}

29
cli/server/dump_test.go Normal file
View file

@ -0,0 +1,29 @@
package server
import (
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/require"
)
func TestGetPath(t *testing.T) {
testPath, err := ioutil.TempDir("./", "")
require.NoError(t, err)
defer func() {
err := os.RemoveAll(testPath)
require.NoError(t, err)
}()
path, err := getPath(testPath, 123)
require.NoError(t, err)
require.Equal(t, testPath+"/BlockStorage_100000/dump-block-1000.json", path)
path, err = getPath(testPath, 1230)
require.NoError(t, err)
require.Equal(t, testPath+"/BlockStorage_100000/dump-block-2000.json", path)
path, err = getPath(testPath, 123000)
require.NoError(t, err)
require.Equal(t, testPath+"/BlockStorage_200000/dump-block-123000.json", path)
}

View file

@ -147,7 +147,7 @@ func initBCWithMetrics(cfg config.Config, log *zap.Logger) (*core.Blockchain, *m
if err != nil {
return nil, nil, nil, cli.NewExitError(err, 1)
}
configureAddresses(cfg.ApplicationConfiguration)
configureAddresses(&cfg.ApplicationConfiguration)
prometheus := metrics.NewPrometheusService(cfg.ApplicationConfiguration.Prometheus, log)
pprof := metrics.NewPprofService(cfg.ApplicationConfiguration.Pprof, log)
@ -392,7 +392,7 @@ Main:
// In case RPC or Prometheus or Pprof Address provided each of them will use it.
// In case global Address (of the node) provided and RPC/Prometheus/Pprof don't have configured addresses they will
// use global one. So Node and RPC and Prometheus and Pprof will run on one address.
func configureAddresses(cfg config.ApplicationConfiguration) {
func configureAddresses(cfg *config.ApplicationConfiguration) {
if cfg.Address != "" {
if cfg.RPC.Address == "" {
cfg.RPC.Address = cfg.Address

231
cli/server/server_test.go Normal file
View file

@ -0,0 +1,231 @@
package server
import (
"flag"
"io/ioutil"
"os"
"testing"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/network/metrics"
"github.com/nspcc-dev/neo-go/pkg/rpc"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
"go.uber.org/zap"
)
func TestGetConfigFromContext(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../config", "")
set.Bool("testnet", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := getConfigFromContext(ctx)
require.NoError(t, err)
require.Equal(t, netmode.TestNet, cfg.ProtocolConfiguration.Magic)
}
func TestHandleLoggingParams(t *testing.T) {
testLog, err := ioutil.TempFile("./", "*.log")
require.NoError(t, err)
defer func() {
require.NoError(t, os.Remove(testLog.Name()))
}()
t.Run("default", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg := config.ApplicationConfiguration{
LogPath: testLog.Name(),
}
logger, err := handleLoggingParams(ctx, cfg)
require.NoError(t, err)
require.True(t, logger.Core().Enabled(zap.InfoLevel))
require.False(t, logger.Core().Enabled(zap.DebugLevel))
})
t.Run("debug", func(t *testing.T) {
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.Bool("debug", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg := config.ApplicationConfiguration{
LogPath: testLog.Name(),
}
logger, err := handleLoggingParams(ctx, cfg)
require.NoError(t, err)
require.True(t, logger.Core().Enabled(zap.InfoLevel))
require.True(t, logger.Core().Enabled(zap.DebugLevel))
})
}
func TestInitBCWithMetrics(t *testing.T) {
d, err := ioutil.TempDir("./", "")
require.NoError(t, err)
os.Chdir(d)
defer func() {
os.Chdir("..")
os.RemoveAll(d)
}()
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../../config", "")
set.Bool("testnet", true, "")
set.Bool("debug", true, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
cfg, err := getConfigFromContext(ctx)
require.NoError(t, err)
logger, err := handleLoggingParams(ctx, cfg.ApplicationConfiguration)
chain, prometheus, pprof, err := initBCWithMetrics(cfg, logger)
require.NoError(t, err)
defer chain.Close()
defer prometheus.ShutDown()
defer pprof.ShutDown()
require.Equal(t, netmode.TestNet, chain.GetConfig().Magic)
}
func TestDumpDB(t *testing.T) {
t.Run("too low chain", func(t *testing.T) {
d, err := ioutil.TempDir("./", "")
require.NoError(t, err)
os.Chdir(d)
defer func() {
os.Chdir("..")
os.RemoveAll(d)
}()
testDump := "file.acc"
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../../config", "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("start", 0, "")
set.Int("count", 5, "")
set.String("out", testDump, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = dumpDB(ctx)
require.Error(t, err)
})
t.Run("positive", func(t *testing.T) {
d, err := ioutil.TempDir("./", "")
require.NoError(t, err)
os.Chdir(d)
defer func() {
os.Chdir("..")
os.RemoveAll(d)
}()
testDump := "file.acc"
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../../config", "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("start", 0, "")
set.Int("count", 1, "")
set.String("out", testDump, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = dumpDB(ctx)
require.NoError(t, err)
})
}
func TestRestoreDB(t *testing.T) {
d, err := ioutil.TempDir("./", "")
require.NoError(t, err)
testDump := "file1.acc"
saveDump := "file2.acc"
os.Chdir(d)
defer func() {
os.Chdir("..")
os.RemoveAll(d)
}()
//dump first
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("config-path", "../../../config", "")
set.Bool("privnet", true, "")
set.Bool("debug", true, "")
set.Int("start", 0, "")
set.Int("count", 1, "")
set.String("out", testDump, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
err = dumpDB(ctx)
require.NoError(t, err)
// and then restore
set.String("in", testDump, "")
set.Int("skip", 0, "")
set.String("dump", saveDump, "")
require.NoError(t, restoreDB(ctx))
}
func TestConfigureAddresses(t *testing.T) {
defaultAddress := "http://127.0.0.1:10333"
customAddress := "http://127.0.0.1:10334"
t.Run("default addresses", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
}
configureAddresses(cfg)
require.Equal(t, defaultAddress, cfg.RPC.Address)
require.Equal(t, defaultAddress, cfg.Prometheus.Address)
require.Equal(t, defaultAddress, cfg.Pprof.Address)
})
t.Run("custom RPC address", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
RPC: rpc.Config{
Address: customAddress,
},
}
configureAddresses(cfg)
require.Equal(t, cfg.RPC.Address, customAddress)
require.Equal(t, cfg.Prometheus.Address, defaultAddress)
require.Equal(t, cfg.Pprof.Address, defaultAddress)
})
t.Run("custom Pprof address", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
Pprof: metrics.Config{
Address: customAddress,
},
}
configureAddresses(cfg)
require.Equal(t, cfg.RPC.Address, defaultAddress)
require.Equal(t, cfg.Prometheus.Address, defaultAddress)
require.Equal(t, cfg.Pprof.Address, customAddress)
})
t.Run("custom Prometheus address", func(t *testing.T) {
cfg := &config.ApplicationConfiguration{
Address: defaultAddress,
Prometheus: metrics.Config{
Address: customAddress,
},
}
configureAddresses(cfg)
require.Equal(t, cfg.RPC.Address, defaultAddress)
require.Equal(t, cfg.Prometheus.Address, customAddress)
require.Equal(t, cfg.Pprof.Address, defaultAddress)
})
}
func TestInitBlockChain(t *testing.T) {
t.Run("bad storage", func(t *testing.T) {
_, err := initBlockChain(config.Config{}, nil)
require.Error(t, err)
})
t.Run("empty logger", func(t *testing.T) {
_, err := initBlockChain(config.Config{
ApplicationConfiguration: config.ApplicationConfiguration{
DBConfiguration: storage.DBConfiguration{
Type: "inmemory",
},
},
}, nil)
require.Error(t, err)
})
}

View file

@ -0,0 +1,123 @@
package smartcontract
import (
"flag"
"io/ioutil"
"os"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
"github.com/urfave/cli"
)
func TestInitSmartContract(t *testing.T) {
d, err := ioutil.TempDir("./", "")
require.NoError(t, err)
os.Chdir(d)
defer func() {
os.Chdir("..")
os.RemoveAll(d)
}()
contractName := "testContract"
set := flag.NewFlagSet("flagSet", flag.ExitOnError)
set.String("name", contractName, "")
ctx := cli.NewContext(cli.NewApp(), set, nil)
require.NoError(t, initSmartContract(ctx))
dirInfo, err := os.Stat(contractName)
require.NoError(t, err)
require.True(t, dirInfo.IsDir())
files, err := ioutil.ReadDir(contractName)
require.NoError(t, err)
require.Equal(t, 2, len(files))
require.Equal(t, "main.go", files[0].Name())
require.Equal(t, "neo-go.yml", files[1].Name())
main, err := ioutil.ReadFile(contractName + "/" + files[0].Name())
require.NoError(t, err)
require.Equal(t,
`package `+contractName+`
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
var notificationName string
// init initializes notificationName before calling any other smart-contract method
func init() {
notificationName = "Hello world!"
}
// RuntimeNotify sends runtime notification with "Hello world!" name
func RuntimeNotify(args []interface{}) {
runtime.Notify(notificationName, args)
}`, string(main))
manifest, err := ioutil.ReadFile(contractName + "/" + files[1].Name())
require.NoError(t, err)
require.Equal(t,
`hasstorage: false
ispayable: false
supportedstandards: []
events:
- name: Hello world!
parameters:
- name: args
type: Array
`, string(manifest))
}
func TestGetFeatures(t *testing.T) {
cfg := ProjectConfig{
IsPayable: true,
HasStorage: true,
}
f := cfg.GetFeatures()
require.Equal(t, smartcontract.IsPayable|smartcontract.HasStorage, f)
}
func TestParseCosigner(t *testing.T) {
acc := util.Uint160{1, 3, 5, 7}
testCases := map[string]transaction.Signer{
acc.StringLE(): {
Account: acc,
Scopes: transaction.Global,
},
"0x" + acc.StringLE(): {
Account: acc,
Scopes: transaction.Global,
},
acc.StringLE() + ":Global": {
Account: acc,
Scopes: transaction.Global,
},
acc.StringLE() + ":CalledByEntry": {
Account: acc,
Scopes: transaction.CalledByEntry,
},
acc.StringLE() + ":FeeOnly": {
Account: acc,
Scopes: transaction.FeeOnly,
},
acc.StringLE() + ":CalledByEntry,CustomContracts": {
Account: acc,
Scopes: transaction.CalledByEntry | transaction.CustomContracts,
},
}
for s, expected := range testCases {
actual, err := parseCosigner(s)
require.NoError(t, err)
require.Equal(t, expected, actual)
}
errorCases := []string{
acc.StringLE() + "0",
acc.StringLE() + ":Unknown",
acc.StringLE() + ":Global,CustomContracts",
acc.StringLE() + ":Global,FeeOnly",
}
for _, s := range errorCases {
_, err := parseCosigner(s)
require.Error(t, err)
}
}

View file

@ -3,6 +3,7 @@ package storage
import (
"encoding/binary"
"errors"
"fmt"
)
// KeyPrefix constants.
@ -91,6 +92,8 @@ func NewStore(cfg DBConfiguration) (Store, error) {
store, err = NewBoltDBStore(cfg.BoltDBOptions)
case "badgerdb":
store, err = NewBadgerDBStore(cfg.BadgerDBOptions)
default:
return nil, fmt.Errorf("unknown storage: %s", cfg.Type)
}
return store, err
}