From 46e6d810f00181c8e9a5d1af0ad0d08a08433520 Mon Sep 17 00:00:00 2001 From: zsystm Date: Thu, 5 Sep 2024 21:59:10 +0900 Subject: [PATCH 1/4] test(evmengine/keeper): add tcs for upgrades --- .../keeper/upgrades_internal_test.go | 272 ++++++++++++++++++ 1 file changed, 272 insertions(+) create mode 100644 client/x/evmengine/keeper/upgrades_internal_test.go diff --git a/client/x/evmengine/keeper/upgrades_internal_test.go b/client/x/evmengine/keeper/upgrades_internal_test.go new file mode 100644 index 00000000..63b2eb0e --- /dev/null +++ b/client/x/evmengine/keeper/upgrades_internal_test.go @@ -0,0 +1,272 @@ +package keeper + +import ( + "testing" + + cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + + moduletestutil "github.com/piplabs/story/client/x/evmengine/testutil" + "github.com/piplabs/story/client/x/evmengine/types" + "github.com/piplabs/story/contracts/bindings" + "github.com/piplabs/story/lib/errors" + "github.com/piplabs/story/lib/ethclient/mock" + "github.com/piplabs/story/lib/k1util" + "github.com/piplabs/story/lib/tutil" +) + +const ( + dummyAddressHex = "0x1398C32A45Bc409b6C652E25bb0a3e702492A4ab" +) + +var ( + dummyContractAddress = common.HexToAddress(dummyAddressHex) + dummyHash = common.HexToHash("0x1398C32A45Bc409b6C652E25bb0a3e702492A4ab") +) + +func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { + t.Parallel() + keeper, ctx, ctrl, _, uk := setupTestEnvironment(t) + defer ctrl.Finish() + + tcs := []struct { + name string + ev func() *bindings.UpgradeEntrypointSoftwareUpgrade + setupMock func() + expectedErr string + }{ + { + name: "pass: valid software upgrade event", + ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { + return &bindings.UpgradeEntrypointSoftwareUpgrade{ + Name: "test-upgrade", + Height: 1, + Info: "test-info", + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(nil) + }, + }, + // Fail cases: The following test cases simulate basic error scenarios. + // Since a mocked upgrade keeper is used, not all error cases can be tested here. + // Comprehensive error testing would require the real upgrade keeper, which is beyond the scope of this unit test. + { + name: "fail: invalid upgrade event - height is 0", + ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { + return &bindings.UpgradeEntrypointSoftwareUpgrade{ + Name: "test upgrade", + Height: 0, + Info: "test-info", + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(errors.New("height must be greater than 0")) + }, + expectedErr: "height must be greater than 0", + }, + { + name: "fail: invalid upgrade event - name is empty", + ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { + return &bindings.UpgradeEntrypointSoftwareUpgrade{ + Name: "", + Height: 1, + Info: "test-info", + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(errors.New("name cannot be empty")) + }, + expectedErr: "name cannot be empty", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + tc.setupMock() + err := keeper.ProcessSoftwareUpgrade(ctx, tc.ev()) + if tc.expectedErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestKeeper_ProcessUpgradeEvents(t *testing.T) { + t.Parallel() + keeper, ctx, ctrl, _, uk := setupTestEnvironment(t) + defer ctrl.Finish() + + upgradeAbi, err := bindings.UpgradeEntrypointMetaData.GetAbi() + require.NoError(t, err, "failed to load ABI") + + tcs := []struct { + name string + evmEvents func() []*types.EVMEvent + setupMock func() + expectedErr string + }{ + { + name: "pass: nil events - nothing to process", + evmEvents: func() []*types.EVMEvent { return nil }, + }, + { + name: "pass: empty events - nothing to process", + evmEvents: func() []*types.EVMEvent { return []*types.EVMEvent{} }, + }, + { + name: "pass: one valid upgrade event", + evmEvents: func() []*types.EVMEvent { + data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade", int64(1), "test-info") + require.NoError(t, err) + return []*types.EVMEvent{ + { + Address: dummyContractAddress.Bytes(), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, + Data: data, + }, + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(nil) + }, + }, + { + name: "pass: multiple valid upgrade events", + evmEvents: func() []*types.EVMEvent { + data1, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade1", int64(2), "test-info") + require.NoError(t, err) + data2, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade2", int64(3), "test-info") + require.NoError(t, err) + return []*types.EVMEvent{ + { + Address: dummyContractAddress.Bytes(), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, + Data: data1, + }, + { + Address: dummyContractAddress.Bytes(), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, + Data: data2, + }, + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(nil).Times(2) + }, + }, + // Failed but pass cases: The following test cases simulate basic error scenarios. + // Since a mocked upgrade keeper is used, not all error cases can be tested here. + // Comprehensive error testing would require the real upgrade keeper, which is beyond the scope of this unit test. + { + name: "pass(failed but continue): invalid upgrade event - height is 0", + evmEvents: func() []*types.EVMEvent { + data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade", int64(0), "test-info") + require.NoError(t, err) + return []*types.EVMEvent{ + { + Address: dummyContractAddress.Bytes(), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, + Data: data, + }, + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(errors.New("height must be greater than 0")) + }, + }, + { + name: "pass(failed but continue): invalid upgrade event - name is empty", + evmEvents: func() []*types.EVMEvent { + data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("", int64(5), "test-info") + require.NoError(t, err) + return []*types.EVMEvent{ + { + Address: dummyContractAddress.Bytes(), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, + Data: data, + }, + } + }, + setupMock: func() { + uk.EXPECT().ScheduleUpgrade(gomock.Any(), gomock.Any()).Return(errors.New("name cannot be empty")) + }, + }, + { + name: "pass(failed but continue): invalid upgrade event - not an upgrade event, it don't reach ProcessSoftwareUpgrade", + evmEvents: func() []*types.EVMEvent { + return []*types.EVMEvent{ + { + Address: dummyContractAddress.Bytes(), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes(), dummyHash.Bytes()}, + }, + } + }, + }, + + // Fail case: When given EVMEvent is not valid + { + name: "fail: invalid EVMEvent", + evmEvents: func() []*types.EVMEvent { + return []*types.EVMEvent{ + { + Address: []byte("invalid address"), + Topics: [][]byte{types.SoftwareUpgradeEvent.ID.Bytes()}, + }, + } + }, + expectedErr: "invalid address length", + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + if tc.setupMock != nil { + tc.setupMock() + } + cachedCtx, _ := ctx.CacheContext() + err := keeper.ProcessUpgradeEvents(cachedCtx, 1, tc.evmEvents()) + if tc.expectedErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErr) + return + } else { + require.NoError(t, err) + } + }) + } +} + +func setupTestEnvironment(t *testing.T) (*Keeper, sdk.Context, *gomock.Controller, *mock.MockClient, *moduletestutil.MockUpgradeKeeper) { + cdc := getCodec(t) + txConfig := authtx.NewTxConfig(cdc, nil) + mockEngine, err := newMockEngineAPI(0) + require.NoError(t, err) + + cmtAPI := newMockCometAPI(t, nil) + header := cmtproto.Header{Height: 1, AppHash: tutil.RandomHash().Bytes(), ProposerAddress: cmtAPI.validatorSet.Validators[0].Address} + ctrl := gomock.NewController(t) + mockClient := mock.NewMockClient(ctrl) + ak := moduletestutil.NewMockAccountKeeper(ctrl) + esk := moduletestutil.NewMockEvmStakingKeeper(ctrl) + uk := moduletestutil.NewMockUpgradeKeeper(ctrl) + + ctx, storeService := setupCtxStore(t, &header) + + keeper, err := NewKeeper(cdc, storeService, &mockEngine, mockClient, txConfig, ak, esk, uk) + require.NoError(t, err) + keeper.SetCometAPI(cmtAPI) + nxtAddr, err := k1util.PubKeyToAddress(cmtAPI.validatorSet.Validators[1].PubKey) + require.NoError(t, err) + keeper.SetValidatorAddress(nxtAddr) + populateGenesisHead(ctx, t, keeper) + + return keeper, ctx, ctrl, mockClient, uk +} From 4e1d795359f7e66f66faa8d5362e7ef64563632f Mon Sep 17 00:00:00 2001 From: zsystm Date: Thu, 5 Sep 2024 22:06:27 +0900 Subject: [PATCH 2/4] fix ci --- client/x/evmengine/keeper/upgrades_internal_test.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/client/x/evmengine/keeper/upgrades_internal_test.go b/client/x/evmengine/keeper/upgrades_internal_test.go index 63b2eb0e..c407c023 100644 --- a/client/x/evmengine/keeper/upgrades_internal_test.go +++ b/client/x/evmengine/keeper/upgrades_internal_test.go @@ -8,7 +8,6 @@ import ( authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" - "go.uber.org/mock/gomock" moduletestutil "github.com/piplabs/story/client/x/evmengine/testutil" "github.com/piplabs/story/client/x/evmengine/types" @@ -17,6 +16,8 @@ import ( "github.com/piplabs/story/lib/ethclient/mock" "github.com/piplabs/story/lib/k1util" "github.com/piplabs/story/lib/tutil" + + "go.uber.org/mock/gomock" ) const ( @@ -42,6 +43,7 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { { name: "pass: valid software upgrade event", ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { + return &bindings.UpgradeEntrypointSoftwareUpgrade{ Name: "test-upgrade", Height: 1, @@ -58,6 +60,7 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { { name: "fail: invalid upgrade event - height is 0", ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { + return &bindings.UpgradeEntrypointSoftwareUpgrade{ Name: "test upgrade", Height: 0, @@ -72,6 +75,7 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { { name: "fail: invalid upgrade event - name is empty", ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { + return &bindings.UpgradeEntrypointSoftwareUpgrade{ Name: "", Height: 1, @@ -87,6 +91,7 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { + t.Parallel() tc.setupMock() err := keeper.ProcessSoftwareUpgrade(ctx, tc.ev()) if tc.expectedErr != "" { @@ -228,6 +233,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { for _, tc := range tcs { t.Run(tc.name, func(t *testing.T) { + t.Parallel() if tc.setupMock != nil { tc.setupMock() } From 50b2d432bbd5a43835fce3fec01643fb4d00b16b Mon Sep 17 00:00:00 2001 From: zsystm Date: Thu, 5 Sep 2024 22:18:26 +0900 Subject: [PATCH 3/4] fix ci --- .../keeper/upgrades_internal_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/client/x/evmengine/keeper/upgrades_internal_test.go b/client/x/evmengine/keeper/upgrades_internal_test.go index c407c023..a65b88bc 100644 --- a/client/x/evmengine/keeper/upgrades_internal_test.go +++ b/client/x/evmengine/keeper/upgrades_internal_test.go @@ -31,8 +31,8 @@ var ( func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { t.Parallel() - keeper, ctx, ctrl, _, uk := setupTestEnvironment(t) - defer ctrl.Finish() + keeper, ctx, ctrl, uk := setupTestEnvironment(t) + t.Cleanup(ctrl.Finish) tcs := []struct { name string @@ -43,7 +43,6 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { { name: "pass: valid software upgrade event", ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { - return &bindings.UpgradeEntrypointSoftwareUpgrade{ Name: "test-upgrade", Height: 1, @@ -60,7 +59,6 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { { name: "fail: invalid upgrade event - height is 0", ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { - return &bindings.UpgradeEntrypointSoftwareUpgrade{ Name: "test upgrade", Height: 0, @@ -75,7 +73,6 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { { name: "fail: invalid upgrade event - name is empty", ev: func() *bindings.UpgradeEntrypointSoftwareUpgrade { - return &bindings.UpgradeEntrypointSoftwareUpgrade{ Name: "", Height: 1, @@ -106,8 +103,8 @@ func TestKeeper_ProcessSoftwareUpgrade(t *testing.T) { func TestKeeper_ProcessUpgradeEvents(t *testing.T) { t.Parallel() - keeper, ctx, ctrl, _, uk := setupTestEnvironment(t) - defer ctrl.Finish() + keeper, ctx, ctrl, uk := setupTestEnvironment(t) + t.Cleanup(ctrl.Finish) upgradeAbi, err := bindings.UpgradeEntrypointMetaData.GetAbi() require.NoError(t, err, "failed to load ABI") @@ -131,6 +128,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { evmEvents: func() []*types.EVMEvent { data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade", int64(1), "test-info") require.NoError(t, err) + return []*types.EVMEvent{ { Address: dummyContractAddress.Bytes(), @@ -150,6 +148,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { require.NoError(t, err) data2, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade2", int64(3), "test-info") require.NoError(t, err) + return []*types.EVMEvent{ { Address: dummyContractAddress.Bytes(), @@ -175,6 +174,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { evmEvents: func() []*types.EVMEvent { data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("test-upgrade", int64(0), "test-info") require.NoError(t, err) + return []*types.EVMEvent{ { Address: dummyContractAddress.Bytes(), @@ -192,6 +192,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { evmEvents: func() []*types.EVMEvent { data, err := upgradeAbi.Events["SoftwareUpgrade"].Inputs.NonIndexed().Pack("", int64(5), "test-info") require.NoError(t, err) + return []*types.EVMEvent{ { Address: dummyContractAddress.Bytes(), @@ -250,7 +251,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { } } -func setupTestEnvironment(t *testing.T) (*Keeper, sdk.Context, *gomock.Controller, *mock.MockClient, *moduletestutil.MockUpgradeKeeper) { +func setupTestEnvironment(t *testing.T) (*Keeper, sdk.Context, *gomock.Controller, *moduletestutil.MockUpgradeKeeper) { cdc := getCodec(t) txConfig := authtx.NewTxConfig(cdc, nil) mockEngine, err := newMockEngineAPI(0) @@ -274,5 +275,5 @@ func setupTestEnvironment(t *testing.T) (*Keeper, sdk.Context, *gomock.Controlle keeper.SetValidatorAddress(nxtAddr) populateGenesisHead(ctx, t, keeper) - return keeper, ctx, ctrl, mockClient, uk + return keeper, ctx, ctrl, uk } From a3bdb1361bff7582dfc97aeaa0ccb345ac9650ec Mon Sep 17 00:00:00 2001 From: zsystm Date: Thu, 5 Sep 2024 22:21:39 +0900 Subject: [PATCH 4/4] chore: fix ci --- client/x/evmengine/keeper/upgrades_internal_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/x/evmengine/keeper/upgrades_internal_test.go b/client/x/evmengine/keeper/upgrades_internal_test.go index a65b88bc..9f7a7b6a 100644 --- a/client/x/evmengine/keeper/upgrades_internal_test.go +++ b/client/x/evmengine/keeper/upgrades_internal_test.go @@ -243,7 +243,6 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { if tc.expectedErr != "" { require.Error(t, err) require.Contains(t, err.Error(), tc.expectedErr) - return } else { require.NoError(t, err) } @@ -252,6 +251,7 @@ func TestKeeper_ProcessUpgradeEvents(t *testing.T) { } func setupTestEnvironment(t *testing.T) (*Keeper, sdk.Context, *gomock.Controller, *moduletestutil.MockUpgradeKeeper) { + t.Helper() cdc := getCodec(t) txConfig := authtx.NewTxConfig(cdc, nil) mockEngine, err := newMockEngineAPI(0)