github.com/cosmos/cosmos-sdk@v0.50.10/server/util_test.go (about)

     1  package server_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  
    13  	cmtcfg "github.com/cometbft/cometbft/config"
    14  	db "github.com/cosmos/cosmos-db"
    15  	"github.com/spf13/cobra"
    16  	"github.com/spf13/viper"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/cosmos/cosmos-sdk/client"
    20  	"github.com/cosmos/cosmos-sdk/client/flags"
    21  	"github.com/cosmos/cosmos-sdk/server"
    22  	"github.com/cosmos/cosmos-sdk/server/config"
    23  	servertypes "github.com/cosmos/cosmos-sdk/server/types"
    24  	sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
    25  	"github.com/cosmos/cosmos-sdk/types/module"
    26  	"github.com/cosmos/cosmos-sdk/types/module/testutil"
    27  	genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
    28  )
    29  
    30  var errCanceledInPreRun = errors.New("canceled in prerun")
    31  
    32  // Used in each test to run the function under test via Cobra
    33  // but to always halt the command
    34  func preRunETestImpl(cmd *cobra.Command, args []string) error {
    35  	if err := server.InterceptConfigsPreRunHandler(cmd, "", nil, cmtcfg.DefaultConfig()); err != nil {
    36  		return err
    37  	}
    38  
    39  	return errCanceledInPreRun
    40  }
    41  
    42  func TestGetAppDBBackend(t *testing.T) {
    43  	v := viper.New()
    44  	require.Equal(t, server.GetAppDBBackend(v), db.GoLevelDBBackend)
    45  	v.Set("db_backend", "dbtype1") // value from CometBFT config
    46  	require.Equal(t, server.GetAppDBBackend(v), db.BackendType("dbtype1"))
    47  	v.Set("app-db-backend", "dbtype2") // value from app.toml
    48  	require.Equal(t, server.GetAppDBBackend(v), db.BackendType("dbtype2"))
    49  }
    50  
    51  func TestInterceptConfigsPreRunHandlerCreatesConfigFilesWhenMissing(t *testing.T) {
    52  	tempDir := t.TempDir()
    53  	cmd := server.StartCmd(nil, "/foobar")
    54  	if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
    55  		t.Fatalf("Could not set home flag [%T] %v", err, err)
    56  	}
    57  
    58  	cmd.PreRunE = preRunETestImpl
    59  
    60  	serverCtx := &server.Context{}
    61  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
    62  	if err := cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
    63  		t.Fatalf("function failed with [%T] %v", err, err)
    64  	}
    65  
    66  	// Test that config.toml is created
    67  	configTomlPath := path.Join(tempDir, "config", "config.toml")
    68  	s, err := os.Stat(configTomlPath)
    69  	if err != nil {
    70  		t.Fatalf("Could not stat config.toml after run %v", err)
    71  	}
    72  
    73  	if !s.Mode().IsRegular() {
    74  		t.Fatal("config.toml not created as regular file")
    75  	}
    76  
    77  	if s.Size() == 0 {
    78  		t.Fatal("config.toml created as empty file")
    79  	}
    80  
    81  	// Test that CometBFT config is initialized
    82  	if serverCtx.Config == nil {
    83  		t.Fatal("CometBFT config not created")
    84  	}
    85  
    86  	// Test that app.toml is created
    87  	appTomlPath := path.Join(tempDir, "config", "app.toml")
    88  	s, err = os.Stat(appTomlPath)
    89  	if err != nil {
    90  		t.Fatalf("Could not stat app.toml after run %v", err)
    91  	}
    92  
    93  	if !s.Mode().IsRegular() {
    94  		t.Fatal("appp.toml not created as regular file")
    95  	}
    96  
    97  	if s.Size() == 0 {
    98  		t.Fatal("config.toml created as empty file")
    99  	}
   100  
   101  	// Test that the config for use in server/start.go is created
   102  	if serverCtx.Viper == nil {
   103  		t.Error("app config Viper instance not created")
   104  	}
   105  }
   106  
   107  func TestInterceptConfigsPreRunHandlerReadsConfigToml(t *testing.T) {
   108  	const testDbBackend = "awesome_test_db"
   109  	tempDir := t.TempDir()
   110  	err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm)
   111  	if err != nil {
   112  		t.Fatalf("creating config dir failed: %v", err)
   113  	}
   114  	configTomlPath := path.Join(tempDir, "config", "config.toml")
   115  	writer, err := os.Create(configTomlPath)
   116  	if err != nil {
   117  		t.Fatalf("creating config.toml file failed: %v", err)
   118  	}
   119  
   120  	_, err = fmt.Fprintf(writer, "db_backend = '%s'\n", testDbBackend)
   121  	if err != nil {
   122  		t.Fatalf("Failed writing string to config.toml: %v", err)
   123  	}
   124  
   125  	if err := writer.Close(); err != nil {
   126  		t.Fatalf("Failed closing config.toml: %v", err)
   127  	}
   128  
   129  	cmd := server.StartCmd(nil, "/foobar")
   130  	if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
   131  		t.Fatalf("Could not set home flag [%T] %v", err, err)
   132  	}
   133  
   134  	cmd.PreRunE = preRunETestImpl
   135  
   136  	serverCtx := &server.Context{}
   137  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   138  
   139  	if err := cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   140  		t.Fatalf("function failed with [%T] %v", err, err)
   141  	}
   142  
   143  	if testDbBackend != serverCtx.Config.DBBackend {
   144  		t.Error("backend was not set from config.toml")
   145  	}
   146  }
   147  
   148  func TestInterceptConfigsPreRunHandlerReadsAppToml(t *testing.T) {
   149  	const testHaltTime = 1337
   150  	tempDir := t.TempDir()
   151  	err := os.Mkdir(path.Join(tempDir, "config"), os.ModePerm)
   152  	if err != nil {
   153  		t.Fatalf("creating config dir failed: %v", err)
   154  	}
   155  	appTomlPath := path.Join(tempDir, "config", "app.toml")
   156  	writer, err := os.Create(appTomlPath)
   157  	if err != nil {
   158  		t.Fatalf("creating app.toml file failed: %v", err)
   159  	}
   160  
   161  	_, err = fmt.Fprintf(writer, "halt-time = %d\n", testHaltTime)
   162  	if err != nil {
   163  		t.Fatalf("Failed writing string to app.toml: %v", err)
   164  	}
   165  
   166  	if err := writer.Close(); err != nil {
   167  		t.Fatalf("Failed closing app.toml: %v", err)
   168  	}
   169  	cmd := server.StartCmd(nil, tempDir)
   170  
   171  	cmd.PreRunE = preRunETestImpl
   172  
   173  	serverCtx := &server.Context{}
   174  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   175  
   176  	if err := cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   177  		t.Fatalf("function failed with [%T] %v", err, err)
   178  	}
   179  
   180  	if testHaltTime != serverCtx.Viper.GetInt("halt-time") {
   181  		t.Error("Halt time was not set from app.toml")
   182  	}
   183  }
   184  
   185  func TestInterceptConfigsPreRunHandlerReadsFlags(t *testing.T) {
   186  	const testAddr = "tcp://127.1.2.3:12345"
   187  	tempDir := t.TempDir()
   188  	cmd := server.StartCmd(nil, "/foobar")
   189  
   190  	if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
   191  		t.Fatalf("Could not set home flag [%T] %v", err, err)
   192  	}
   193  
   194  	// This flag is added by tendermint
   195  	if err := cmd.Flags().Set("rpc.laddr", testAddr); err != nil {
   196  		t.Fatalf("Could not set address flag [%T] %v", err, err)
   197  	}
   198  
   199  	cmd.PreRunE = preRunETestImpl
   200  
   201  	serverCtx := &server.Context{}
   202  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   203  
   204  	if err := cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   205  		t.Fatalf("function failed with [%T] %v", err, err)
   206  	}
   207  
   208  	if testAddr != serverCtx.Config.RPC.ListenAddress {
   209  		t.Error("RPCListenAddress was not set from command flags")
   210  	}
   211  }
   212  
   213  func TestInterceptConfigsPreRunHandlerReadsEnvVars(t *testing.T) {
   214  	const testAddr = "tcp://127.1.2.3:12345"
   215  	tempDir := t.TempDir()
   216  	cmd := server.StartCmd(nil, "/foobar")
   217  	if err := cmd.Flags().Set(flags.FlagHome, tempDir); err != nil {
   218  		t.Fatalf("Could not set home flag [%T] %v", err, err)
   219  	}
   220  
   221  	executableName, err := os.Executable()
   222  	if err != nil {
   223  		t.Fatalf("Could not get executable name: %v", err)
   224  	}
   225  	basename := path.Base(executableName)
   226  	basename = strings.ReplaceAll(basename, ".", "_")
   227  	// This is added by tendermint
   228  	envVarName := fmt.Sprintf("%s_RPC_LADDR", strings.ToUpper(basename))
   229  	require.NoError(t, os.Setenv(envVarName, testAddr))
   230  	t.Cleanup(func() {
   231  		require.NoError(t, os.Unsetenv(envVarName))
   232  	})
   233  
   234  	cmd.PreRunE = preRunETestImpl
   235  
   236  	serverCtx := &server.Context{}
   237  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   238  
   239  	if err := cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   240  		t.Fatalf("function failed with [%T] %v", err, err)
   241  	}
   242  
   243  	if testAddr != serverCtx.Config.RPC.ListenAddress {
   244  		t.Errorf("RPCListenAddress was not set from env. var. %q", envVarName)
   245  	}
   246  }
   247  
   248  /*
   249   The following tests are here to check the precedence of each
   250   of the configuration sources. A common setup functionality is used
   251   to avoid duplication of code between tests.
   252  */
   253  
   254  var (
   255  	TestAddrExpected    = "tcp://127.126.125.124:12345" // expected to be used in test
   256  	TestAddrNotExpected = "tcp://127.127.127.127:11111" // not expected to be used in test
   257  )
   258  
   259  type precedenceCommon struct {
   260  	envVarName     string
   261  	flagName       string
   262  	configTomlPath string
   263  
   264  	cmd *cobra.Command
   265  }
   266  
   267  func newPrecedenceCommon(t *testing.T) precedenceCommon {
   268  	retval := precedenceCommon{}
   269  
   270  	// Determine the env. var. name based off the executable name
   271  	executableName, err := os.Executable()
   272  	if err != nil {
   273  		t.Fatalf("Could not get executable name: %v", err)
   274  	}
   275  	basename := path.Base(executableName)
   276  	basename = strings.ReplaceAll(basename, ".", "_")
   277  	basename = strings.ReplaceAll(basename, "-", "_")
   278  	// Store the name of the env. var.
   279  	retval.envVarName = fmt.Sprintf("%s_RPC_LADDR", strings.ToUpper(basename))
   280  
   281  	// Store the flag name. This flag is added by tendermint
   282  	retval.flagName = "rpc.laddr"
   283  
   284  	// Create a tempdir and create './config' under that
   285  	tempDir := t.TempDir()
   286  	err = os.Mkdir(path.Join(tempDir, "config"), os.ModePerm)
   287  	if err != nil {
   288  		t.Fatalf("creating config dir failed: %v", err)
   289  	}
   290  	// Store the path for config.toml
   291  	retval.configTomlPath = path.Join(tempDir, "config", "config.toml")
   292  
   293  	// always remove the env. var. after each test execution
   294  	t.Cleanup(func() {
   295  		// This should not fail but if it does just panic
   296  		if err := os.Unsetenv(retval.envVarName); err != nil {
   297  			panic("Could not clear configuration env. var. used in test")
   298  		}
   299  	})
   300  
   301  	// Set up the command object that is used in this test
   302  	retval.cmd = server.StartCmd(nil, tempDir)
   303  	retval.cmd.PreRunE = preRunETestImpl
   304  
   305  	return retval
   306  }
   307  
   308  func (v precedenceCommon) setAll(t *testing.T, setFlag, setEnvVar, setConfigFile *string) {
   309  	if setFlag != nil {
   310  		if err := v.cmd.Flags().Set(v.flagName, *setFlag); err != nil {
   311  			t.Fatalf("Failed setting flag %q", v.flagName)
   312  		}
   313  	}
   314  
   315  	if setEnvVar != nil {
   316  		require.NoError(t, os.Setenv(v.envVarName, *setEnvVar))
   317  	}
   318  
   319  	if setConfigFile != nil {
   320  		writer, err := os.Create(v.configTomlPath)
   321  		if err != nil {
   322  			t.Fatalf("creating config.toml file failed: %v", err)
   323  		}
   324  
   325  		_, err = fmt.Fprintf(writer, "[rpc]\nladdr = \"%s\"\n", *setConfigFile)
   326  		if err != nil {
   327  			t.Fatalf("Failed writing string to config.toml: %v", err)
   328  		}
   329  
   330  		if err := writer.Close(); err != nil {
   331  			t.Fatalf("Failed closing config.toml: %v", err)
   332  		}
   333  	}
   334  }
   335  
   336  func TestInterceptConfigsPreRunHandlerPrecedenceFlag(t *testing.T) {
   337  	testCommon := newPrecedenceCommon(t)
   338  	testCommon.setAll(t, &TestAddrExpected, &TestAddrNotExpected, &TestAddrNotExpected)
   339  
   340  	serverCtx := &server.Context{}
   341  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   342  
   343  	if err := testCommon.cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   344  		t.Fatalf("function failed with [%T] %v", err, err)
   345  	}
   346  
   347  	if TestAddrExpected != serverCtx.Config.RPC.ListenAddress {
   348  		t.Fatalf("RPCListenAddress was not set from flag %q", testCommon.flagName)
   349  	}
   350  }
   351  
   352  func TestInterceptConfigsPreRunHandlerPrecedenceEnvVar(t *testing.T) {
   353  	testCommon := newPrecedenceCommon(t)
   354  	testCommon.setAll(t, nil, &TestAddrExpected, &TestAddrNotExpected)
   355  
   356  	serverCtx := &server.Context{}
   357  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   358  
   359  	if err := testCommon.cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   360  		t.Fatalf("function failed with [%T] %v", err, err)
   361  	}
   362  
   363  	if TestAddrExpected != serverCtx.Config.RPC.ListenAddress {
   364  		t.Errorf("RPCListenAddress was not set from env. var. %q", testCommon.envVarName)
   365  	}
   366  }
   367  
   368  func TestInterceptConfigsPreRunHandlerPrecedenceConfigFile(t *testing.T) {
   369  	testCommon := newPrecedenceCommon(t)
   370  	testCommon.setAll(t, nil, nil, &TestAddrExpected)
   371  
   372  	serverCtx := &server.Context{}
   373  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   374  
   375  	if err := testCommon.cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   376  		t.Fatalf("function failed with [%T] %v", err, err)
   377  	}
   378  
   379  	if TestAddrExpected != serverCtx.Config.RPC.ListenAddress {
   380  		t.Errorf("RPCListenAddress was not read from file %q", testCommon.configTomlPath)
   381  	}
   382  }
   383  
   384  func TestInterceptConfigsPreRunHandlerPrecedenceConfigDefault(t *testing.T) {
   385  	testCommon := newPrecedenceCommon(t)
   386  	// Do not set anything
   387  
   388  	serverCtx := &server.Context{}
   389  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   390  
   391  	if err := testCommon.cmd.ExecuteContext(ctx); err != errCanceledInPreRun {
   392  		t.Fatalf("function failed with [%T] %v", err, err)
   393  	}
   394  
   395  	if serverCtx.Config.RPC.ListenAddress != "tcp://127.0.0.1:26657" {
   396  		t.Error("RPCListenAddress is not using default")
   397  	}
   398  }
   399  
   400  // Ensure that if interceptConfigs encounters any error other than non-existen errors
   401  // that we correctly return the offending error, for example a permission error.
   402  // See https://github.com/cosmos/cosmos-sdk/issues/7578
   403  func TestInterceptConfigsWithBadPermissions(t *testing.T) {
   404  	tempDir := t.TempDir()
   405  	subDir := filepath.Join(tempDir, "nonPerms")
   406  	if err := os.Mkdir(subDir, 0o600); err != nil {
   407  		t.Fatalf("Failed to create sub directory: %v", err)
   408  	}
   409  	cmd := server.StartCmd(nil, "/foobar")
   410  	if err := cmd.Flags().Set(flags.FlagHome, subDir); err != nil {
   411  		t.Fatalf("Could not set home flag [%T] %v", err, err)
   412  	}
   413  
   414  	cmd.PreRunE = preRunETestImpl
   415  
   416  	serverCtx := &server.Context{}
   417  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   418  	if err := cmd.ExecuteContext(ctx); !os.IsPermission(err) {
   419  		t.Fatalf("Failed to catch permissions error, got: [%T] %v", err, err)
   420  	}
   421  }
   422  
   423  func TestEmptyMinGasPrices(t *testing.T) {
   424  	tempDir := t.TempDir()
   425  	err := os.Mkdir(filepath.Join(tempDir, "config"), os.ModePerm)
   426  	require.NoError(t, err)
   427  	encCfg := testutil.MakeTestEncodingConfig()
   428  
   429  	// Run InitCmd to create necessary config files.
   430  	clientCtx := client.Context{}.WithHomeDir(tempDir).WithCodec(encCfg.Codec)
   431  	serverCtx := server.NewDefaultContext()
   432  	ctx := context.WithValue(context.Background(), server.ServerContextKey, serverCtx)
   433  	ctx = context.WithValue(ctx, client.ClientContextKey, &clientCtx)
   434  	cmd := genutilcli.InitCmd(module.NewBasicManager(), tempDir)
   435  	cmd.SetArgs([]string{"appnode-test"})
   436  	err = cmd.ExecuteContext(ctx)
   437  	require.NoError(t, err)
   438  
   439  	// Modify app.toml.
   440  	appCfgTempFilePath := filepath.Join(tempDir, "config", "app.toml")
   441  	appConf := config.DefaultConfig()
   442  	appConf.BaseConfig.MinGasPrices = ""
   443  	config.WriteConfigFile(appCfgTempFilePath, appConf)
   444  
   445  	// Run StartCmd.
   446  	cmd = server.StartCmd(nil, tempDir)
   447  	cmd.PreRunE = func(cmd *cobra.Command, _ []string) error {
   448  		ctx, err := server.InterceptConfigsAndCreateContext(cmd, "", nil, cmtcfg.DefaultConfig())
   449  		if err != nil {
   450  			return err
   451  		}
   452  
   453  		return server.SetCmdServerContext(cmd, ctx)
   454  	}
   455  	err = cmd.ExecuteContext(ctx)
   456  	require.Errorf(t, err, sdkerrors.ErrAppConfig.Error())
   457  }
   458  
   459  type mapGetter map[string]interface{}
   460  
   461  func (m mapGetter) Get(key string) interface{} {
   462  	return m[key]
   463  }
   464  
   465  var _ servertypes.AppOptions = mapGetter{}