191 lines
6.3 KiB
Go
191 lines
6.3 KiB
Go
|
package upgrade
|
||
|
|
||
|
import (
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/cosmos/cosmos-sdk/codec"
|
||
|
"github.com/cosmos/cosmos-sdk/store"
|
||
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||
|
"github.com/cosmos/cosmos-sdk/types/module"
|
||
|
"github.com/cosmos/cosmos-sdk/x/gov"
|
||
|
"github.com/stretchr/testify/suite"
|
||
|
abci "github.com/tendermint/tendermint/abci/types"
|
||
|
"github.com/tendermint/tendermint/libs/log"
|
||
|
dbm "github.com/tendermint/tm-db"
|
||
|
)
|
||
|
|
||
|
type TestSuite struct {
|
||
|
suite.Suite
|
||
|
keeper Keeper
|
||
|
querier sdk.Querier
|
||
|
handler gov.Handler
|
||
|
module module.AppModule
|
||
|
ctx sdk.Context
|
||
|
cms store.CommitMultiStore
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) SetupTest() {
|
||
|
db := dbm.NewMemDB()
|
||
|
s.cms = store.NewCommitMultiStore(db)
|
||
|
key := sdk.NewKVStoreKey("upgrade")
|
||
|
cdc := codec.New()
|
||
|
RegisterCodec(cdc)
|
||
|
s.keeper = NewKeeper(key, cdc)
|
||
|
s.handler = NewSoftwareUpgradeProposalHandler(s.keeper)
|
||
|
s.querier = NewQuerier(s.keeper)
|
||
|
s.module = NewAppModule(s.keeper)
|
||
|
s.cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, db)
|
||
|
_ = s.cms.LoadLatestVersion()
|
||
|
s.ctx = sdk.NewContext(s.cms, abci.Header{Height: 10, Time: time.Now()}, false, log.NewNopLogger())
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestRequireName() {
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{}})
|
||
|
s.Require().NotNil(err)
|
||
|
s.Require().Equal(sdk.CodeUnknownRequest, err.Code())
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestRequireFutureTime() {
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Time: s.ctx.BlockHeader().Time}})
|
||
|
s.Require().NotNil(err)
|
||
|
s.Require().Equal(sdk.CodeUnknownRequest, err.Code())
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestRequireFutureBlock() {
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Height: s.ctx.BlockHeight()}})
|
||
|
s.Require().NotNil(err)
|
||
|
s.Require().Equal(sdk.CodeUnknownRequest, err.Code())
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestCantSetBothTimeAndHeight() {
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Time: time.Now(), Height: s.ctx.BlockHeight() + 1}})
|
||
|
s.Require().NotNil(err)
|
||
|
s.Require().Equal(sdk.CodeUnknownRequest, err.Code())
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestDoTimeUpgrade() {
|
||
|
s.T().Log("Verify can schedule an upgrade")
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Time: time.Now()}})
|
||
|
s.Require().Nil(err)
|
||
|
|
||
|
s.VerifyDoUpgrade()
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestDoHeightUpgrade() {
|
||
|
s.T().Log("Verify can schedule an upgrade")
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}})
|
||
|
s.Require().Nil(err)
|
||
|
|
||
|
s.VerifyDoUpgrade()
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestCanOverwriteScheduleUpgrade() {
|
||
|
s.T().Log("Can overwrite plan")
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "bad_test", Height: s.ctx.BlockHeight() + 10}})
|
||
|
s.Require().Nil(err)
|
||
|
err = s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Height: s.ctx.BlockHeight() + 1}})
|
||
|
s.Require().Nil(err)
|
||
|
|
||
|
s.VerifyDoUpgrade()
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) VerifyDoUpgrade() {
|
||
|
s.T().Log("Verify that a panic happens at the upgrade time/height")
|
||
|
newCtx := sdk.NewContext(s.cms, abci.Header{Height: s.ctx.BlockHeight() + 1, Time: time.Now()}, false, log.NewNopLogger())
|
||
|
req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()}
|
||
|
s.Require().Panics(func() {
|
||
|
s.module.BeginBlock(newCtx, req)
|
||
|
})
|
||
|
|
||
|
s.T().Log("Verify that the upgrade can be successfully applied with a handler")
|
||
|
s.keeper.SetUpgradeHandler("test", func(ctx sdk.Context, plan Plan) {})
|
||
|
s.Require().NotPanics(func() {
|
||
|
s.module.BeginBlock(newCtx, req)
|
||
|
})
|
||
|
|
||
|
s.VerifyCleared(newCtx)
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestHaltIfTooNew() {
|
||
|
s.T().Log("Verify that we don't panic with registered plan not in database at all")
|
||
|
var called int
|
||
|
s.keeper.SetUpgradeHandler("future", func(ctx sdk.Context, plan Plan) { called++ })
|
||
|
|
||
|
newCtx := sdk.NewContext(s.cms, abci.Header{Height: s.ctx.BlockHeight() + 1, Time: time.Now()}, false, log.NewNopLogger())
|
||
|
req := abci.RequestBeginBlock{Header: newCtx.BlockHeader()}
|
||
|
s.Require().NotPanics(func() {
|
||
|
s.module.BeginBlock(newCtx, req)
|
||
|
})
|
||
|
s.Require().Equal(0, called)
|
||
|
|
||
|
s.T().Log("Verify we panic if we have a registered handler ahead of time")
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "future", Height: s.ctx.BlockHeight() + 3}})
|
||
|
s.Require().NoError(err)
|
||
|
s.Require().Panics(func() {
|
||
|
s.module.BeginBlock(newCtx, req)
|
||
|
})
|
||
|
s.Require().Equal(0, called)
|
||
|
|
||
|
s.T().Log("Verify we no longer panic if the plan is on time")
|
||
|
|
||
|
futCtx := sdk.NewContext(s.cms, abci.Header{Height: s.ctx.BlockHeight() + 3, Time: time.Now()}, false, log.NewNopLogger())
|
||
|
req = abci.RequestBeginBlock{Header: futCtx.BlockHeader()}
|
||
|
s.Require().NotPanics(func() {
|
||
|
s.module.BeginBlock(futCtx, req)
|
||
|
})
|
||
|
s.Require().Equal(1, called)
|
||
|
|
||
|
s.VerifyCleared(futCtx)
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) VerifyCleared(newCtx sdk.Context) {
|
||
|
s.T().Log("Verify that the upgrade plan has been cleared")
|
||
|
bz, err := s.querier(newCtx, []string{QueryCurrent}, abci.RequestQuery{})
|
||
|
s.Require().NoError(err)
|
||
|
s.Require().Nil(bz)
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestCanClear() {
|
||
|
s.T().Log("Verify upgrade is scheduled")
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Time: time.Now()}})
|
||
|
s.Require().Nil(err)
|
||
|
|
||
|
s.handler(s.ctx, CancelSoftwareUpgradeProposal{Title: "cancel"})
|
||
|
|
||
|
s.VerifyCleared(s.ctx)
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestCantApplySameUpgradeTwice() {
|
||
|
s.TestDoTimeUpgrade()
|
||
|
s.T().Log("Verify an upgrade named \"test\" can't be scheduled twice")
|
||
|
err := s.handler(s.ctx, SoftwareUpgradeProposal{Title: "prop", Plan: Plan{Name: "test", Time: time.Now()}})
|
||
|
s.Require().NotNil(err)
|
||
|
s.Require().Equal(sdk.CodeUnknownRequest, err.Code())
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestNoSpuriousUpgrades() {
|
||
|
s.T().Log("Verify that no upgrade panic is triggered in the BeginBlocker when we haven't scheduled an upgrade")
|
||
|
req := abci.RequestBeginBlock{Header: s.ctx.BlockHeader()}
|
||
|
s.Require().NotPanics(func() {
|
||
|
s.module.BeginBlock(s.ctx, req)
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func (s *TestSuite) TestPlanStringer() {
|
||
|
t, err := time.Parse(time.RFC3339, "2020-01-01T00:00:00Z")
|
||
|
s.Require().Nil(err)
|
||
|
s.Require().Equal(`Upgrade Plan
|
||
|
Name: test
|
||
|
Time: 2020-01-01T00:00:00Z
|
||
|
Info: `, Plan{Name: "test", Time: t}.String())
|
||
|
s.Require().Equal(`Upgrade Plan
|
||
|
Name: test
|
||
|
Height: 100
|
||
|
Info: `, Plan{Name: "test", Height: 100}.String())
|
||
|
}
|
||
|
|
||
|
func TestTestSuite(t *testing.T) {
|
||
|
suite.Run(t, new(TestSuite))
|
||
|
}
|