331 lines
10 KiB
Go
331 lines
10 KiB
Go
package params_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"gopkg.in/go-playground/validator.v9"
|
|
|
|
"github.com/ethereum/go-ethereum/core"
|
|
gethparams "github.com/ethereum/go-ethereum/params"
|
|
"github.com/status-im/status-go/params"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
var clusterConfigData = []byte(`{
|
|
"staticnodes": [
|
|
"enode://7ab298cedc4185a894d21d8a4615262ec6bdce66c9b6783878258e0d5b31013d30c9038932432f70e5b2b6a5cd323bf820554fcb22fbc7b45367889522e9c449@10.1.1.1:30303",
|
|
"enode://f59e8701f18c79c5cbc7618dc7bb928d44dc2f5405c7d693dad97da2d8585975942ec6fd36d3fe608bfdc7270a34a4dd00f38cfe96b2baa24f7cd0ac28d382a1@10.1.1.2:30303"
|
|
]
|
|
}`)
|
|
|
|
func TestLoadNodeConfigFromNonExistingFile(t *testing.T) {
|
|
_, err := params.LoadNodeConfig(`{
|
|
"NetworkId": 3,
|
|
"DataDir": "/tmp/statusgo",
|
|
"ClusterConfigFile": "/file/does/not.exist"
|
|
}`)
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "no such file or directory")
|
|
}
|
|
|
|
func TestLoadNodeConfigFromFile(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "geth-config-tests")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
|
|
|
// create cluster config file
|
|
clusterFile := filepath.Join(tmpDir, "cluster.json")
|
|
err = ioutil.WriteFile(clusterFile, clusterConfigData, os.ModePerm)
|
|
require.NoError(t, err)
|
|
|
|
c, err := params.LoadNodeConfig(`{
|
|
"NetworkId": 3,
|
|
"DataDir": "` + tmpDir + `",
|
|
"ClusterConfigFile": "` + clusterFile + `"
|
|
}`)
|
|
require.NoError(t, err)
|
|
require.True(t, c.ClusterConfig.Enabled)
|
|
require.Len(t, c.ClusterConfig.StaticNodes, 2)
|
|
}
|
|
|
|
// TestGenerateAndLoadNodeConfig tests creating and loading config
|
|
// exactly as it's done by status-react.
|
|
func TestGenerateAndLoadNodeConfig(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "geth-config-tests")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
|
|
|
var testCases = []struct {
|
|
Name string
|
|
Fleet string // optional; if omitted all fleets will be tested
|
|
NetworkID int // optional; if omitted all networks will be checked
|
|
Update func(*params.NodeConfig)
|
|
Validate func(t *testing.T, dataDir string, c *params.NodeConfig)
|
|
}{
|
|
{
|
|
Name: "default KeyStoreDir",
|
|
Update: func(config *params.NodeConfig) {},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.Equal(t, dataDir, c.DataDir)
|
|
keyStoreDir := filepath.Join(dataDir, params.KeyStoreDir)
|
|
require.Equal(t, keyStoreDir, c.KeyStoreDir)
|
|
},
|
|
},
|
|
{
|
|
Name: "non-default KeyStoreDir",
|
|
Update: func(c *params.NodeConfig) {
|
|
c.KeyStoreDir = "/foo/bar"
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.Equal(t, "/foo/bar", c.KeyStoreDir)
|
|
},
|
|
},
|
|
{
|
|
Name: "custom network and upstream",
|
|
NetworkID: 333,
|
|
Update: func(c *params.NodeConfig) {
|
|
c.UpstreamConfig.Enabled = true
|
|
c.UpstreamConfig.URL = "http://custom.local"
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.Equal(t, uint64(333), c.NetworkID)
|
|
require.True(t, c.UpstreamConfig.Enabled)
|
|
require.Equal(t, "http://custom.local", c.UpstreamConfig.URL)
|
|
},
|
|
},
|
|
{
|
|
Name: "upstream config",
|
|
NetworkID: params.RopstenNetworkID,
|
|
Update: func(c *params.NodeConfig) {
|
|
c.UpstreamConfig.Enabled = true
|
|
c.UpstreamConfig.URL = params.RopstenEthereumNetworkURL
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.True(t, c.UpstreamConfig.Enabled)
|
|
require.Equal(t, params.RopstenEthereumNetworkURL, c.UpstreamConfig.URL)
|
|
},
|
|
},
|
|
{
|
|
Name: "loading LES config",
|
|
NetworkID: params.MainNetworkID,
|
|
Update: func(c *params.NodeConfig) {},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
var genesis core.Genesis
|
|
err := json.Unmarshal([]byte(c.LightEthConfig.Genesis), &genesis)
|
|
require.NoError(t, err)
|
|
|
|
require.Zero(t, genesis.Config.ChainID.Cmp(gethparams.MainnetChainConfig.ChainID))
|
|
require.Zero(t, genesis.Config.HomesteadBlock.Cmp(gethparams.MainnetChainConfig.HomesteadBlock))
|
|
require.Zero(t, genesis.Config.EIP150Block.Cmp(gethparams.MainnetChainConfig.EIP150Block))
|
|
require.Zero(t, genesis.Config.EIP155Block.Cmp(gethparams.MainnetChainConfig.EIP155Block))
|
|
require.Zero(t, genesis.Config.EIP158Block.Cmp(gethparams.MainnetChainConfig.EIP158Block))
|
|
},
|
|
},
|
|
{
|
|
Name: "cluster nodes setup",
|
|
Update: func(c *params.NodeConfig) {},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.True(t, c.ClusterConfig.Enabled)
|
|
require.NotEmpty(t, c.ClusterConfig.BootNodes)
|
|
require.NotEmpty(t, c.ClusterConfig.StaticNodes)
|
|
require.NotEmpty(t, c.ClusterConfig.TrustedMailServers)
|
|
},
|
|
},
|
|
{
|
|
Name: "custom bootnodes",
|
|
Update: func(c *params.NodeConfig) {
|
|
c.ClusterConfig.BootNodes = []string{"a", "b", "c"}
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.True(t, c.ClusterConfig.Enabled)
|
|
require.Equal(t, []string{"a", "b", "c"}, c.ClusterConfig.BootNodes)
|
|
},
|
|
},
|
|
{
|
|
Name: "disabled ClusterConfiguration",
|
|
Update: func(c *params.NodeConfig) {
|
|
c.ClusterConfig.Enabled = false
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.False(t, c.ClusterConfig.Enabled)
|
|
},
|
|
},
|
|
{
|
|
Name: "peers discovery and topics",
|
|
Update: func(c *params.NodeConfig) {},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.NotNil(t, c.RequireTopics)
|
|
require.False(t, c.NoDiscovery)
|
|
require.Contains(t, c.RequireTopics, params.WhisperDiscv5Topic)
|
|
require.Equal(t, params.WhisperDiscv5Limits, c.RequireTopics[params.WhisperDiscv5Topic])
|
|
},
|
|
},
|
|
{
|
|
Name: "verify NoDiscovery preserved",
|
|
Update: func(c *params.NodeConfig) {
|
|
c.NoDiscovery = true
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.True(t, c.NoDiscovery)
|
|
},
|
|
},
|
|
{
|
|
Name: "staging fleet",
|
|
Fleet: params.FleetStaging,
|
|
Update: func(c *params.NodeConfig) {},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
staging, ok := params.ClusterForFleet("eth.staging")
|
|
require.True(t, ok)
|
|
beta, ok := params.ClusterForFleet("eth.beta")
|
|
require.True(t, ok)
|
|
|
|
require.NotEqual(t, staging, beta)
|
|
|
|
// test case asserts
|
|
require.Equal(t, "eth.staging", c.ClusterConfig.Fleet)
|
|
require.Equal(t, staging.BootNodes, c.ClusterConfig.BootNodes)
|
|
},
|
|
},
|
|
{
|
|
Name: "Whisper light client",
|
|
Update: func(c *params.NodeConfig) {
|
|
c.WhisperConfig.LightClient = true
|
|
},
|
|
Validate: func(t *testing.T, dataDir string, c *params.NodeConfig) {
|
|
require.True(t, c.WhisperConfig.LightClient)
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
fleets := []string{params.FleetBeta, params.FleetStaging}
|
|
if tc.Fleet != params.FleetUndefined {
|
|
fleets = []string{tc.Fleet}
|
|
}
|
|
|
|
networks := []int{params.MainNetworkID, params.RinkebyNetworkID, params.RopstenNetworkID}
|
|
if tc.NetworkID != 0 {
|
|
networks = []int{tc.NetworkID}
|
|
}
|
|
|
|
for _, fleet := range fleets {
|
|
for _, networkID := range networks {
|
|
name := fmt.Sprintf("%s_%s_%d", tc.Name, fleet, networkID)
|
|
t.Run(name, func(t *testing.T) {
|
|
// Corresponds to GenerateConfig() binding.
|
|
config, err := params.NewNodeConfig(tmpDir, "", fleet, uint64(networkID))
|
|
require.NoError(t, err)
|
|
|
|
// Corresponds to config update in status-react.
|
|
tc.Update(config)
|
|
configBytes, err := json.Marshal(config)
|
|
require.NoError(t, err)
|
|
|
|
// Corresponds to starting node and loading config from JSON blob.
|
|
loadedConfig, err := params.LoadNodeConfig(string(configBytes))
|
|
require.NoError(t, err)
|
|
tc.Validate(t, tmpDir, loadedConfig)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigWriteRead(t *testing.T) {
|
|
tmpDir, err := ioutil.TempDir(os.TempDir(), "geth-config-tests")
|
|
require.Nil(t, err)
|
|
defer os.RemoveAll(tmpDir) // nolint: errcheck
|
|
|
|
nodeConfig, err := params.NewNodeConfig(tmpDir, "", params.FleetBeta, params.RopstenNetworkID)
|
|
require.Nil(t, err, "cannot create new config object")
|
|
|
|
err = nodeConfig.Save()
|
|
require.Nil(t, err, "cannot persist configuration")
|
|
|
|
loadedConfigData, err := ioutil.ReadFile(filepath.Join(nodeConfig.DataDir, "config.json"))
|
|
require.Nil(t, err, "cannot read configuration from disk")
|
|
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"NetworkId": %d`, params.RopstenNetworkID))
|
|
require.Contains(t, string(loadedConfigData), fmt.Sprintf(`"DataDir": "%s"`, tmpDir))
|
|
}
|
|
|
|
// TestNodeConfigValidate checks validation of individual fields.
|
|
func TestNodeConfigValidate(t *testing.T) {
|
|
testCases := []struct {
|
|
Name string
|
|
Config string
|
|
Error string
|
|
FieldErrors map[string]string // map[Field]Tag
|
|
}{
|
|
{
|
|
Name: "Valid JSON config",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/tmp/data"
|
|
}`,
|
|
Error: "",
|
|
FieldErrors: nil,
|
|
},
|
|
{
|
|
Name: "Invalid JSON config",
|
|
Config: `{"NetworkId": }`,
|
|
Error: "invalid character '}'",
|
|
FieldErrors: nil,
|
|
},
|
|
{
|
|
Name: "Invalid field type",
|
|
Config: `{"NetworkId": "abc"}`,
|
|
Error: "json: cannot unmarshal string into Go struct field",
|
|
FieldErrors: nil,
|
|
},
|
|
{
|
|
Name: "Validate all required fields",
|
|
Config: `{}`,
|
|
Error: "",
|
|
FieldErrors: map[string]string{
|
|
"NetworkID": "required",
|
|
"DataDir": "required",
|
|
},
|
|
},
|
|
{
|
|
Name: "Validate Name does not contain slash",
|
|
Config: `{
|
|
"NetworkId": 1,
|
|
"DataDir": "/some/dir",
|
|
"Name": "invalid/name"
|
|
}`,
|
|
Error: "",
|
|
FieldErrors: map[string]string{
|
|
"Name": "excludes",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Logf("Test Case %s", tc.Name)
|
|
|
|
_, err := params.LoadNodeConfig(tc.Config)
|
|
|
|
switch err := err.(type) {
|
|
case validator.ValidationErrors:
|
|
for _, ve := range err {
|
|
require.Contains(t, tc.FieldErrors, ve.Field())
|
|
require.Equal(t, tc.FieldErrors[ve.Field()], ve.Tag())
|
|
}
|
|
case error:
|
|
require.Contains(t, err.Error(), tc.Error)
|
|
case nil:
|
|
require.Empty(t, tc.Error)
|
|
require.Nil(t, tc.FieldErrors)
|
|
}
|
|
}
|
|
}
|