fix all bits broken by viper API changes (#5982)

github.com/spf13/viper's recent releases introduced a semantic
change in some public API such as viper.IsSet(), which have
broken some of our flags checks. Instead of checking whether
users have changed a flag's default value we should rely on such
defaults and adjust runtime behaviour accordingly. In order to do
so, it's important that we pick sane defaults for all our flags.

The --pruning flag and configuration option now allow for a
fake custom strategy. When users elect custom, then the
pruning-{keep,snapshot}-every options are interpreted and
parsed; else they're ignored.
Zero is pruning-{keep,snapshot}-every default value. When
users choose to set a custom pruning strategy they are
signalling that they want more fine-grainted control, therefore
it's legitimate to expect them to know what they are doing and
enter valid values for both options.

Ref #5964
This commit is contained in:
Alessio Treglia 2020-04-14 16:24:27 +01:00 committed by GitHub
parent a6588a4d2d
commit e8cedf243f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 147 additions and 161 deletions

View File

@ -52,6 +52,7 @@ that parse log messages.
older clients.
* (x/auth) [\#5844](https://github.com/cosmos/cosmos-sdk/pull/5844) `tx sign` command now returns an error when signing is attempted with offline/multisig keys.
* (client/keys) [\#5889](https://github.com/cosmos/cosmos-sdk/pull/5889) Remove `keys update` command.
* (server) [\#5982](https://github.com/cosmos/cosmos-sdk/pull/5982) `--pruning` now must be set to `custom` if you want to customise the granular options.
### API Breaking Changes
@ -114,6 +115,7 @@ invalid or incomplete requests.
* (x/genutil) [\#5938](https://github.com/cosmos/cosmos-sdk/pull/5938) Fix `InitializeNodeValidatorFiles` error handling.
* (x/staking) [\#5949](https://github.com/cosmos/cosmos-sdk/pull/5949) Skip staking `HistoricalInfoKey` in simulations as headers are not exported.
* (x/auth) [\#5950](https://github.com/cosmos/cosmos-sdk/pull/5950) Fix `IncrementSequenceDecorator` to use is `IsReCheckTx` instead of `IsCheckTx` to allow account sequence incrementing.
* (client) [\#5964](https://github.com/cosmos/cosmos-sdk/issues/5964) `--trust-node` is now false by default - for real. Users must ensure it is set to true if they don't want to enable the verifier.
### State Machine Breaking

View File

@ -88,6 +88,7 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
}
}
trustNode := viper.GetBool(flags.FlagTrustNode)
ctx := CLIContext{
Client: rpc,
ChainID: viper.GetString(flags.FlagChainID),
@ -99,7 +100,7 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
OutputFormat: viper.GetString(cli.OutputFlag),
Height: viper.GetInt64(flags.FlagHeight),
HomeDir: homedir,
TrustNode: viper.GetBool(flags.FlagTrustNode),
TrustNode: trustNode,
UseLedger: viper.GetBool(flags.FlagUseLedger),
BroadcastMode: viper.GetString(flags.FlagBroadcastMode),
Simulate: viper.GetBool(flags.FlagDryRun),
@ -111,9 +112,13 @@ func NewCLIContextWithInputAndFrom(input io.Reader, from string) CLIContext {
SkipConfirm: viper.GetBool(flags.FlagSkipConfirmation),
}
if offline {
return ctx
}
// create a verifier for the specific chain ID and RPC client
verifier, err := CreateVerifier(ctx, DefaultVerifierCacheSize)
if err != nil && viper.IsSet(flags.FlagTrustNode) {
if err != nil && !trustNode {
fmt.Printf("failed to create verifier: %s\n", err)
os.Exit(1)
}

View File

@ -21,15 +21,6 @@ func TestCLIContext_WithOffline(t *testing.T) {
ctx := context.NewCLIContext()
require.True(t, ctx.Offline)
require.Nil(t, ctx.Client)
viper.Reset()
viper.Set(flags.FlagOffline, false)
viper.Set(flags.FlagNode, "tcp://localhost:26657")
ctx = context.NewCLIContext()
require.False(t, ctx.Offline)
require.NotNil(t, ctx.Client)
}
func TestCLIContext_WithGenOnly(t *testing.T) {

View File

@ -190,23 +190,16 @@ func RunAddCmd(cmd *cobra.Command, args []string, kb keyring.Keyring, inBuf *buf
coinType := uint32(viper.GetInt(flagCoinType))
account := uint32(viper.GetInt(flagAccount))
index := uint32(viper.GetInt(flagIndex))
hdPath := viper.GetString(flagHDPath)
useBIP44 := !viper.IsSet(flagHDPath)
var hdPath string
if useBIP44 {
if len(hdPath) == 0 {
hdPath = hd.CreateHDPath(coinType, account, index).String()
} else {
hdPath = viper.GetString(flagHDPath)
} else if viper.GetBool(flags.FlagUseLedger) {
return errors.New("cannot set custom bip32 path with ledger")
}
// If we're using ledger, only thing we need is the path and the bech32 prefix.
if viper.GetBool(flags.FlagUseLedger) {
if !useBIP44 {
return errors.New("cannot set custom bip32 path with ledger")
}
bech32PrefixAccAddr := sdk.GetConfig().GetBech32AccountAddrPrefix()
info, err := kb.SaveLedgerKey(name, hd.Secp256k1, bech32PrefixAccAddr, coinType, account, index)
if err != nil {

View File

@ -35,7 +35,9 @@ type BaseConfig struct {
// InterBlockCache enables inter-block caching.
InterBlockCache bool `mapstructure:"inter-block-cache"`
Pruning string `mapstructure:"pruning"`
Pruning string `mapstructure:"pruning"`
PruningKeepEvery string `mapstructure:"pruning-keep-every"`
PruningSnapshotEvery string `mapstructure:"pruning-snapshot-every"`
}
// Config defines the server's top level configuration
@ -74,9 +76,11 @@ func (c *Config) GetMinGasPrices() sdk.DecCoins {
func DefaultConfig() *Config {
return &Config{
BaseConfig{
MinGasPrices: defaultMinGasPrices,
InterBlockCache: true,
Pruning: store.PruningStrategySyncable,
MinGasPrices: defaultMinGasPrices,
InterBlockCache: true,
Pruning: store.PruningStrategySyncable,
PruningKeepEvery: "0",
PruningSnapshotEvery: "0",
},
}
}

View File

@ -34,11 +34,16 @@ halt-time = {{ .BaseConfig.HaltTime }}
# InterBlockCache enables inter-block caching.
inter-block-cache = {{ .BaseConfig.InterBlockCache }}
# Pruning sets the pruning strategy: syncable, nothing, everything
# Pruning sets the pruning strategy: syncable, nothing, everything, custom
# syncable: only those states not needed for state syncing will be deleted (keeps last 100 + every 10000th)
# nothing: all historic states will be saved, nothing will be deleted (i.e. archiving node)
# everything: all saved states will be deleted, storing only the current state
# custom: allows fine-grained control through the pruning-keep-every and pruning-snapshot-every options.
pruning = "{{ .BaseConfig.Pruning }}"
# These are applied if and only if the pruning strategy is custom.
pruning-keep-every = "{{ .BaseConfig.PruningKeepEvery }}"
pruning-snapshot-every = "{{ .BaseConfig.PruningSnapshotEvery }}"
`
var configTemplate *template.Template

View File

@ -1,6 +1,8 @@
package server
import (
"fmt"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/store"
@ -9,17 +11,23 @@ import (
// GetPruningOptionsFromFlags parses start command flags and returns the correct PruningOptions.
// flagPruning prevails over flagPruningKeepEvery and flagPruningSnapshotEvery.
// Default option is PruneSyncable.
func GetPruningOptionsFromFlags() store.PruningOptions {
if viper.IsSet(flagPruning) {
return store.NewPruningOptionsFromString(viper.GetString(flagPruning))
}
func GetPruningOptionsFromFlags() (store.PruningOptions, error) {
strategy := viper.GetString(flagPruning)
switch strategy {
case "syncable", "nothing", "everything":
return store.NewPruningOptionsFromString(viper.GetString(flagPruning)), nil
if viper.IsSet(flagPruningKeepEvery) && viper.IsSet(flagPruningSnapshotEvery) {
return store.PruningOptions{
case "custom":
opts := store.PruningOptions{
KeepEvery: viper.GetInt64(flagPruningKeepEvery),
SnapshotEvery: viper.GetInt64(flagPruningSnapshotEvery),
}
}
if !opts.IsValid() {
return opts, fmt.Errorf("invalid granular options")
}
return opts, nil
return store.PruneSyncable
default:
return store.PruningOptions{}, fmt.Errorf("unknown pruning strategy %s", strategy)
}
}

View File

@ -14,6 +14,7 @@ func TestGetPruningOptionsFromFlags(t *testing.T) {
name string
initParams func()
expectedOptions store.PruningOptions
wantErr bool
}{
{
name: "pruning",
@ -25,6 +26,7 @@ func TestGetPruningOptionsFromFlags(t *testing.T) {
{
name: "granular pruning",
initParams: func() {
viper.Set(flagPruning, "custom")
viper.Set(flagPruningSnapshotEvery, 1234)
viper.Set(flagPruningKeepEvery, 4321)
},
@ -44,8 +46,14 @@ func TestGetPruningOptionsFromFlags(t *testing.T) {
tt := tt
t.Run(tt.name, func(j *testing.T) {
viper.Reset()
viper.SetDefault(flagPruning, "syncable")
tt.initParams()
require.Equal(t, tt.expectedOptions, GetPruningOptionsFromFlags())
opts, err := GetPruningOptionsFromFlags()
if tt.wantErr {
require.Error(t, err)
return
}
require.Equal(t, tt.expectedOptions, opts)
})
}
}

View File

@ -72,7 +72,8 @@ For profiling and benchmarking purposes, CPU profiling can be enabled via the '-
which accepts a path for the resulting pprof file.
`,
PreRunE: func(cmd *cobra.Command, args []string) error {
return checkPruningParams()
_, err := GetPruningOptionsFromFlags()
return err
},
RunE: func(cmd *cobra.Command, args []string) error {
if !viper.GetBool(flagWithTendermint) {
@ -91,9 +92,9 @@ which accepts a path for the resulting pprof file.
cmd.Flags().Bool(flagWithTendermint, true, "Run abci app embedded in-process with tendermint")
cmd.Flags().String(flagAddress, "tcp://0.0.0.0:26658", "Listen address")
cmd.Flags().String(flagTraceStore, "", "Enable KVStore tracing to an output file")
cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything")
cmd.Flags().Int64(flagPruningKeepEvery, 0, "Define the state number that will be kept")
cmd.Flags().Int64(flagPruningSnapshotEvery, 0, "Defines the state that will be snapshot for pruning")
cmd.Flags().String(flagPruning, "syncable", "Pruning strategy: syncable, nothing, everything, custom")
cmd.Flags().Int64(flagPruningKeepEvery, 0, "Define the state number that will be kept. Ignored if pruning is not custom.")
cmd.Flags().Int64(flagPruningSnapshotEvery, 0, "Defines the state that will be snapshot for pruning. Ignored if pruning is not custom.")
cmd.Flags().String(
FlagMinGasPrices, "",
"Minimum gas prices to accept for transactions; Any fee in a tx must meet this minimum (e.g. 0.01photino;0.0001stake)",
@ -104,32 +105,15 @@ which accepts a path for the resulting pprof file.
cmd.Flags().Bool(FlagInterBlockCache, true, "Enable inter-block caching")
cmd.Flags().String(flagCPUProfile, "", "Enable CPU profiling and write to the provided file")
viper.BindPFlag(flagPruning, cmd.Flags().Lookup(flagPruning))
viper.BindPFlag(flagPruningKeepEvery, cmd.Flags().Lookup(flagPruningKeepEvery))
viper.BindPFlag(flagPruningSnapshotEvery, cmd.Flags().Lookup(flagPruningSnapshotEvery))
// add support for all Tendermint-specific command line options
tcmd.AddNodeFlags(cmd)
return cmd
}
// checkPruningParams checks that the provided pruning params are correct
func checkPruningParams() error {
if !viper.IsSet(flagPruning) && !viper.IsSet(flagPruningKeepEvery) && !viper.IsSet(flagPruningSnapshotEvery) {
return nil
}
if viper.IsSet(flagPruning) {
if viper.IsSet(flagPruningKeepEvery) || viper.IsSet(flagPruningSnapshotEvery) {
return errPruningWithGranularOptions
}
return nil
}
if !(viper.IsSet(flagPruningKeepEvery) && viper.IsSet(flagPruningSnapshotEvery)) {
return errPruningGranularOptions
}
return nil
}
func startStandAlone(ctx *Context, appCreator AppCreator) error {
addr := viper.GetString(flagAddress)
home := viper.GetString("home")

View File

@ -1,6 +1,7 @@
package server
import (
"fmt"
"testing"
"github.com/spf13/viper"
@ -8,8 +9,6 @@ import (
)
func TestPruningOptions(t *testing.T) {
startCommand := StartCmd(nil, nil)
tests := []struct {
name string
paramInit func()
@ -17,68 +16,49 @@ func TestPruningOptions(t *testing.T) {
expectedErr error
}{
{
name: "none set, returns nil and will use default from flags",
name: "default",
paramInit: func() {},
returnsErr: false,
expectedErr: nil,
},
{
name: "unknown strategy",
paramInit: func() { viper.Set(flagPruning, "unknown") },
returnsErr: true,
expectedErr: fmt.Errorf("unknown pruning strategy unknown"),
},
{
name: "only keep-every provided",
paramInit: func() {
viper.Set(flagPruning, "custom")
viper.Set(flagPruningKeepEvery, 12345)
},
returnsErr: true,
expectedErr: errPruningGranularOptions,
},
{
name: "only snapshot-every provided",
paramInit: func() {
viper.Set(flagPruningSnapshotEvery, 12345)
},
returnsErr: true,
expectedErr: errPruningGranularOptions,
},
{
name: "pruning flag with other granular options 1",
paramInit: func() {
viper.Set(flagPruning, "set")
viper.Set(flagPruningSnapshotEvery, 1234)
},
returnsErr: true,
expectedErr: errPruningWithGranularOptions,
},
{
name: "pruning flag with other granular options 2",
paramInit: func() {
viper.Set(flagPruning, "set")
viper.Set(flagPruningKeepEvery, 1234)
},
returnsErr: true,
expectedErr: errPruningWithGranularOptions,
},
{
name: "pruning flag with other granular options 3",
paramInit: func() {
viper.Set(flagPruning, "set")
viper.Set(flagPruningKeepEvery, 1234)
viper.Set(flagPruningSnapshotEvery, 1234)
},
returnsErr: true,
expectedErr: errPruningWithGranularOptions,
},
{
name: "only prunning set",
paramInit: func() {
viper.Set(flagPruning, "set")
},
returnsErr: false,
expectedErr: nil,
},
{
name: "only granular set",
name: "only snapshot-every provided",
paramInit: func() {
viper.Set(flagPruning, "custom")
viper.Set(flagPruningSnapshotEvery, 12345)
viper.Set(flagPruningKeepEvery, 12345)
},
returnsErr: true,
expectedErr: fmt.Errorf("invalid granular options"),
},
{
name: "pruning flag with other granular options 3",
paramInit: func() {
viper.Set(flagPruning, "custom")
viper.Set(flagPruningKeepEvery, 1234)
viper.Set(flagPruningSnapshotEvery, 1234)
},
returnsErr: false,
expectedErr: nil,
},
{
name: "nothing strategy",
paramInit: func() {
viper.Set(flagPruning, "nothing")
},
returnsErr: false,
expectedErr: nil,
@ -90,9 +70,10 @@ func TestPruningOptions(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
viper.Reset()
viper.SetDefault(flagPruning, "syncable")
startCommand := StartCmd(nil, nil)
tt.paramInit()
err := startCommand.PreRunE(nil, nil)
err := startCommand.PreRunE(startCommand, nil)
if tt.returnsErr {
require.EqualError(t, err, tt.expectedErr.Error())

View File

@ -1,6 +1,4 @@
// Package rest provides HTTP types and primitives for REST
// requests validation and responses handling.
package rest
package rest_test
import (
"errors"
@ -12,24 +10,27 @@ import (
"strings"
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
)
func TestBaseReq_Sanitize(t *testing.T) {
t.Parallel()
sanitized := BaseReq{ChainID: " test",
sanitized := rest.BaseReq{ChainID: " test",
Memo: "memo ",
From: " cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0 ",
Gas: " ",
GasAdjustment: " 0.3",
}.Sanitize()
require.Equal(t, BaseReq{ChainID: "test",
require.Equal(t, rest.BaseReq{ChainID: "test",
Memo: "memo",
From: "cosmos1cq0sxam6x4l0sv9yz3a2vlqhdhvt2k6jtgcse0",
Gas: "",
@ -44,25 +45,25 @@ func TestBaseReq_ValidateBasic(t *testing.T) {
onestake, err := types.ParseDecCoins("1.0stake")
require.NoError(t, err)
req1 := NewBaseReq(
req1 := rest.NewBaseReq(
fromAddr, "", "nonempty", "", "", 0, 0, tenstakes, nil, false,
)
req2 := NewBaseReq(
req2 := rest.NewBaseReq(
"", "", "nonempty", "", "", 0, 0, tenstakes, nil, false,
)
req3 := NewBaseReq(
req3 := rest.NewBaseReq(
fromAddr, "", "", "", "", 0, 0, tenstakes, nil, false,
)
req4 := NewBaseReq(
req4 := rest.NewBaseReq(
fromAddr, "", "nonempty", "", "", 0, 0, tenstakes, onestake, false,
)
req5 := NewBaseReq(
req5 := rest.NewBaseReq(
fromAddr, "", "nonempty", "", "", 0, 0, types.Coins{}, types.DecCoins{}, false,
)
tests := []struct {
name string
req BaseReq
req rest.BaseReq
w http.ResponseWriter
want bool
}{
@ -102,21 +103,21 @@ func TestParseHTTPArgs(t *testing.T) {
limit int
err bool
}{
{"no params", req0, httptest.NewRecorder(), []string{}, DefaultPage, DefaultLimit, false},
{"Limit", req1, httptest.NewRecorder(), []string{}, DefaultPage, 5, false},
{"Page", req2, httptest.NewRecorder(), []string{}, 5, DefaultLimit, false},
{"no params", req0, httptest.NewRecorder(), []string{}, rest.DefaultPage, rest.DefaultLimit, false},
{"Limit", req1, httptest.NewRecorder(), []string{}, rest.DefaultPage, 5, false},
{"Page", req2, httptest.NewRecorder(), []string{}, 5, rest.DefaultLimit, false},
{"Page and limit", req3, httptest.NewRecorder(), []string{}, 5, 5, false},
{"error page 0", reqE1, httptest.NewRecorder(), []string{}, DefaultPage, DefaultLimit, true},
{"error limit 0", reqE2, httptest.NewRecorder(), []string{}, DefaultPage, DefaultLimit, true},
{"error page 0", reqE1, httptest.NewRecorder(), []string{}, rest.DefaultPage, rest.DefaultLimit, true},
{"error limit 0", reqE2, httptest.NewRecorder(), []string{}, rest.DefaultPage, rest.DefaultLimit, true},
{"tags", req4, httptest.NewRecorder(), []string{"foo='faa'"}, DefaultPage, DefaultLimit, false},
{"tags", reqTxH, httptest.NewRecorder(), []string{"tx.height<=14", "tx.height>=12"}, DefaultPage, DefaultLimit, false},
{"tags", req4, httptest.NewRecorder(), []string{"foo='faa'"}, rest.DefaultPage, rest.DefaultLimit, false},
{"tags", reqTxH, httptest.NewRecorder(), []string{"tx.height<=14", "tx.height>=12"}, rest.DefaultPage, rest.DefaultLimit, false},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
tags, page, limit, err := ParseHTTPArgs(tt.req)
tags, page, limit, err := rest.ParseHTTPArgs(tt.req)
sort.Strings(tags)
@ -158,7 +159,7 @@ func TestParseQueryHeight(t *testing.T) {
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
cliCtx, ok := ParseQueryHeightOrReturnBadRequest(tt.w, tt.cliCtx, tt.req)
cliCtx, ok := rest.ParseQueryHeightOrReturnBadRequest(tt.w, tt.cliCtx, tt.req)
if tt.expectedOk {
require.True(t, ok)
require.Equal(t, tt.expectedHeight, cliCtx.Height)
@ -185,6 +186,7 @@ func TestProcessPostResponse(t *testing.T) {
}
// setup
viper.Set(flags.FlagOffline, true)
ctx := context.NewCLIContext()
height := int64(194423)
@ -205,7 +207,7 @@ func TestProcessPostResponse(t *testing.T) {
jsonNoIndent, err := ctx.Codec.MarshalJSON(acc)
require.Nil(t, err)
respNoIndent := NewResponseWithHeight(height, jsonNoIndent)
respNoIndent := rest.NewResponseWithHeight(height, jsonNoIndent)
expectedNoIndent, err := ctx.Codec.MarshalJSON(respNoIndent)
require.Nil(t, err)
@ -215,7 +217,7 @@ func TestProcessPostResponse(t *testing.T) {
// check that negative height writes an error
w := httptest.NewRecorder()
ctx = ctx.WithHeight(-1)
PostProcessResponse(w, ctx, acc)
rest.PostProcessResponse(w, ctx, acc)
require.Equal(t, http.StatusInternalServerError, w.Code)
// check that height returns expected response
@ -231,21 +233,21 @@ func TestReadRESTReq(t *testing.T) {
reqBody := ioutil.NopCloser(strings.NewReader(`{"chain_id":"alessio","memo":"text"}`))
req := &http.Request{Body: reqBody}
w := httptest.NewRecorder()
var br BaseReq
var br rest.BaseReq
// test OK
ReadRESTReq(w, req, codec.New(), &br)
rest.ReadRESTReq(w, req, codec.New(), &br)
res := w.Result()
t.Cleanup(func() { res.Body.Close() })
require.Equal(t, BaseReq{ChainID: "alessio", Memo: "text"}, br)
require.Equal(t, rest.BaseReq{ChainID: "alessio", Memo: "text"}, br)
require.Equal(t, http.StatusOK, res.StatusCode)
// test non valid JSON
reqBody = ioutil.NopCloser(strings.NewReader(`MALFORMED`))
req = &http.Request{Body: reqBody}
br = BaseReq{}
br = rest.BaseReq{}
w = httptest.NewRecorder()
ReadRESTReq(w, req, codec.New(), &br)
rest.ReadRESTReq(w, req, codec.New(), &br)
require.Equal(t, br, br)
res = w.Result()
t.Cleanup(func() { res.Body.Close() })
@ -255,7 +257,7 @@ func TestReadRESTReq(t *testing.T) {
func TestWriteSimulationResponse(t *testing.T) {
t.Parallel()
w := httptest.NewRecorder()
WriteSimulationResponse(w, codec.New(), 10)
rest.WriteSimulationResponse(w, codec.New(), 10)
res := w.Result()
t.Cleanup(func() { res.Body.Close() })
require.Equal(t, http.StatusOK, res.StatusCode)
@ -268,12 +270,12 @@ func TestWriteSimulationResponse(t *testing.T) {
func TestParseUint64OrReturnBadRequest(t *testing.T) {
t.Parallel()
w := httptest.NewRecorder()
_, ok := ParseUint64OrReturnBadRequest(w, "100")
_, ok := rest.ParseUint64OrReturnBadRequest(w, "100")
require.True(t, ok)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
w = httptest.NewRecorder()
_, ok = ParseUint64OrReturnBadRequest(w, "-100")
_, ok = rest.ParseUint64OrReturnBadRequest(w, "-100")
require.False(t, ok)
require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
}
@ -281,17 +283,17 @@ func TestParseUint64OrReturnBadRequest(t *testing.T) {
func TestParseFloat64OrReturnBadRequest(t *testing.T) {
t.Parallel()
w := httptest.NewRecorder()
_, ok := ParseFloat64OrReturnBadRequest(w, "100", 0)
_, ok := rest.ParseFloat64OrReturnBadRequest(w, "100", 0)
require.True(t, ok)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
w = httptest.NewRecorder()
_, ok = ParseFloat64OrReturnBadRequest(w, "bad request", 0)
_, ok = rest.ParseFloat64OrReturnBadRequest(w, "bad request", 0)
require.False(t, ok)
require.Equal(t, http.StatusBadRequest, w.Result().StatusCode)
w = httptest.NewRecorder()
ret, ok := ParseFloat64OrReturnBadRequest(w, "", 9.0)
ret, ok := rest.ParseFloat64OrReturnBadRequest(w, "", 9.0)
require.Equal(t, float64(9), ret)
require.True(t, ok)
require.Equal(t, http.StatusOK, w.Result().StatusCode)
@ -299,11 +301,11 @@ func TestParseFloat64OrReturnBadRequest(t *testing.T) {
func TestParseQueryParamBool(t *testing.T) {
req := httptest.NewRequest("GET", "/target?boolean=true", nil)
require.True(t, ParseQueryParamBool(req, "boolean"))
require.False(t, ParseQueryParamBool(req, "nokey"))
require.True(t, rest.ParseQueryParamBool(req, "boolean"))
require.False(t, rest.ParseQueryParamBool(req, "nokey"))
req = httptest.NewRequest("GET", "/target?boolean=false", nil)
require.False(t, ParseQueryParamBool(req, "boolean"))
require.False(t, ParseQueryParamBool(req, ""))
require.False(t, rest.ParseQueryParamBool(req, "boolean"))
require.False(t, rest.ParseQueryParamBool(req, ""))
}
func TestPostProcessResponseBare(t *testing.T) {
@ -314,7 +316,7 @@ func TestPostProcessResponseBare(t *testing.T) {
w := httptest.NewRecorder()
bs := []byte("text string")
PostProcessResponseBare(w, ctx, bs)
rest.PostProcessResponseBare(w, ctx, bs)
res := w.Result()
require.Equal(t, http.StatusOK, res.StatusCode)
@ -333,7 +335,7 @@ func TestPostProcessResponseBare(t *testing.T) {
S string `json:"s"`
}{X: 10, S: "test"}
PostProcessResponseBare(w, ctx, data)
rest.PostProcessResponseBare(w, ctx, data)
res = w.Result()
require.Equal(t, http.StatusOK, res.StatusCode)
@ -355,7 +357,7 @@ func TestPostProcessResponseBare(t *testing.T) {
S string `json:"s"`
}{X: 10, S: "test"}
PostProcessResponseBare(w, ctx, data)
rest.PostProcessResponseBare(w, ctx, data)
res = w.Result()
require.Equal(t, http.StatusOK, res.StatusCode)
@ -371,7 +373,7 @@ func TestPostProcessResponseBare(t *testing.T) {
w = httptest.NewRecorder()
data2 := badJSONMarshaller{}
PostProcessResponseBare(w, ctx, data2)
rest.PostProcessResponseBare(w, ctx, data2)
res = w.Result()
require.Equal(t, http.StatusInternalServerError, res.StatusCode)
@ -401,7 +403,7 @@ func runPostProcessResponse(t *testing.T, ctx context.CLIContext, obj interface{
// test using regular struct
w := httptest.NewRecorder()
PostProcessResponse(w, ctx, obj)
rest.PostProcessResponse(w, ctx, obj)
require.Equal(t, http.StatusOK, w.Code, w.Body)
resp := w.Result()
@ -421,7 +423,7 @@ func runPostProcessResponse(t *testing.T, ctx context.CLIContext, obj interface{
// test using marshalled struct
w = httptest.NewRecorder()
PostProcessResponse(w, ctx, marshalled)
rest.PostProcessResponse(w, ctx, marshalled)
require.Equal(t, http.StatusOK, w.Code, w.Body)
resp = w.Result()
@ -452,12 +454,12 @@ func TestCheckErrors(t *testing.T) {
wantString string
wantStatus int
}{
{"500", CheckInternalServerError, err, true, `{"error":"ERROR"}`, http.StatusInternalServerError},
{"500 (no error)", CheckInternalServerError, nil, false, ``, http.StatusInternalServerError},
{"400", CheckBadRequestError, err, true, `{"error":"ERROR"}`, http.StatusBadRequest},
{"400 (no error)", CheckBadRequestError, nil, false, ``, http.StatusBadRequest},
{"404", CheckNotFoundError, err, true, `{"error":"ERROR"}`, http.StatusNotFound},
{"404 (no error)", CheckNotFoundError, nil, false, ``, http.StatusNotFound},
{"500", rest.CheckInternalServerError, err, true, `{"error":"ERROR"}`, http.StatusInternalServerError},
{"500 (no error)", rest.CheckInternalServerError, nil, false, ``, http.StatusInternalServerError},
{"400", rest.CheckBadRequestError, err, true, `{"error":"ERROR"}`, http.StatusBadRequest},
{"400 (no error)", rest.CheckBadRequestError, nil, false, ``, http.StatusBadRequest},
{"404", rest.CheckNotFoundError, err, true, `{"error":"ERROR"}`, http.StatusNotFound},
{"404 (no error)", rest.CheckNotFoundError, nil, false, ``, http.StatusNotFound},
}
for _, tt := range tests {

View File

@ -3,14 +3,17 @@ package common
import (
"testing"
"github.com/spf13/viper"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
)
func TestQueryDelegationRewardsAddrValidation(t *testing.T) {
cdc := codec.New()
viper.Set(flags.FlagOffline, true)
ctx := context.NewCLIContext().WithCodec(cdc)
type args struct {
delAddr string