diff --git a/cli/flags/address_test.go b/cli/flags/address_test.go new file mode 100644 index 000000000..a5019216d --- /dev/null +++ b/cli/flags/address_test.go @@ -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"})) +} diff --git a/cli/flags/fixed8_test.go b/cli/flags/fixed8_test.go new file mode 100644 index 000000000..62004b1c4 --- /dev/null +++ b/cli/flags/fixed8_test.go @@ -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"})) +} diff --git a/cli/flags/util_test.go b/cli/flags/util_test.go new file mode 100644 index 000000000..fb47b697f --- /dev/null +++ b/cli/flags/util_test.go @@ -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) +} diff --git a/cli/options/options_test.go b/cli/options/options_test.go new file mode 100644 index 000000000..bd92c6416 --- /dev/null +++ b/cli/options/options_test.go @@ -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) + }) + +} diff --git a/cli/server/dump_test.go b/cli/server/dump_test.go new file mode 100644 index 000000000..d118125c7 --- /dev/null +++ b/cli/server/dump_test.go @@ -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) +} diff --git a/cli/server/server_test.go b/cli/server/server_test.go new file mode 100644 index 000000000..dbbfe44c9 --- /dev/null +++ b/cli/server/server_test.go @@ -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) + }) +} diff --git a/cli/smartcontract/smart_contract_test.go b/cli/smartcontract/smart_contract_test.go new file mode 100644 index 000000000..7ed50ae0e --- /dev/null +++ b/cli/smartcontract/smart_contract_test.go @@ -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) + } +}