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{}