package precisebank_test import ( "testing" sdkmath "cosmossdk.io/math" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/0glabs/0g-chain/x/precisebank" "github.com/0glabs/0g-chain/x/precisebank/keeper" "github.com/0glabs/0g-chain/x/precisebank/testutil" "github.com/0glabs/0g-chain/x/precisebank/types" "github.com/stretchr/testify/suite" ) type GenesisTestSuite struct { testutil.Suite } func TestGenesisTestSuite(t *testing.T) { suite.Run(t, new(GenesisTestSuite)) } func (suite *GenesisTestSuite) TestInitGenesis() { tests := []struct { name string setupFn func() genesisState *types.GenesisState panicMsg string }{ { "valid - default genesisState", func() {}, types.DefaultGenesisState(), "", }, { "valid - empty genesisState", func() {}, &types.GenesisState{}, "failed to validate precisebank genesis state: nil remainder amount", }, { "valid - module balance matches non-zero amount", func() { // Set module account balance to expected amount err := suite.BankKeeper.MintCoins( suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdkmath.NewInt(2))), ) suite.Require().NoError(err) }, types.NewGenesisState( types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().SubRaw(1)), types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().SubRaw(1)), }, // 2 leftover from 0.999... + 0.999... sdkmath.NewInt(2), ), "", }, { // Other GenesisState.Validate() tests are in types/genesis_test.go "invalid genesisState - GenesisState.Validate() is called", func() {}, types.NewGenesisState( types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), types.NewFractionalBalance(sdk.AccAddress{1}.String(), sdkmath.NewInt(1)), }, sdkmath.ZeroInt(), ), "failed to validate precisebank genesis state: invalid balances: duplicate address 0g1qy20a4yf", }, { "invalid - module balance insufficient", func() {}, types.NewGenesisState( types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().SubRaw(1)), types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().SubRaw(1)), }, // 2 leftover from 0.999... + 0.999... sdkmath.NewInt(2), ), "module account balance does not match sum of fractional balances and remainder, balance is 0ua0gi but expected 2000000000000neuron (2ua0gi)", }, { "invalid - module balance excessive", func() { // Set module account balance to greater than expected amount err := suite.BankKeeper.MintCoins( suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdkmath.NewInt(100))), ) suite.Require().NoError(err) }, types.NewGenesisState( types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().SubRaw(1)), types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().SubRaw(1)), }, sdkmath.NewInt(2), ), "module account balance does not match sum of fractional balances and remainder, balance is 100ua0gi but expected 2000000000000neuron (2ua0gi)", }, { "sets module account", func() { // Delete the module account first to ensure it's created here moduleAcc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) suite.AccountKeeper.RemoveAccount(suite.Ctx, moduleAcc) // Ensure module account is deleted in state. // GetModuleAccount() will always return non-nil and does not // necessarily equate to the account being stored in the account store. suite.Require().Nil(suite.AccountKeeper.GetAccount(suite.Ctx, moduleAcc.GetAddress())) }, types.DefaultGenesisState(), "", }, } for _, tc := range tests { suite.Run(tc.name, func() { suite.SetupTest() tc.setupFn() if tc.panicMsg != "" { suite.Require().PanicsWithValue( tc.panicMsg, func() { precisebank.InitGenesis( suite.Ctx, suite.Keeper, suite.AccountKeeper, suite.BankKeeper, tc.genesisState, ) }, ) return } suite.Require().NotPanics(func() { precisebank.InitGenesis( suite.Ctx, suite.Keeper, suite.AccountKeeper, suite.BankKeeper, tc.genesisState, ) }) // Ensure module account is created moduleAcc := suite.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) suite.NotNil(moduleAcc) suite.NotNil( suite.AccountKeeper.GetAccount(suite.Ctx, moduleAcc.GetAddress()), "module account should be created & stored in account store", ) // Verify balances are set in state, get full list of balances in // state to ensure they are set AND no extra balances are set var bals []types.FractionalBalance suite.Keeper.IterateFractionalBalances(suite.Ctx, func(addr sdk.AccAddress, bal sdkmath.Int) bool { bals = append(bals, types.NewFractionalBalance(addr.String(), bal)) return false }) suite.Require().ElementsMatch(tc.genesisState.Balances, bals, "balances should be set in state") remainder := suite.Keeper.GetRemainderAmount(suite.Ctx) suite.Require().Equal(tc.genesisState.Remainder, remainder, "remainder should be set in state") // Additional verification of state via invariants invariantFn := keeper.AllInvariants(suite.Keeper) msg, broken := invariantFn(suite.Ctx) suite.Require().False(broken, "invariants should not be broken after InitGenesis") suite.Require().Empty(msg, "invariants should not return a message after InitGenesis") }) } } func (suite *GenesisTestSuite) TestExportGenesis() { // ExportGenesis(InitGenesis(genesisState)) == genesisState // Must also be valid. tests := []struct { name string initGenesisState func() *types.GenesisState }{ { "InitGenesis(DefaultGenesisState)", func() *types.GenesisState { return types.DefaultGenesisState() }, }, { "balances, no remainder", func() *types.GenesisState { err := suite.BankKeeper.MintCoins( suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdkmath.NewInt(1))), ) suite.Require().NoError(err) return types.NewGenesisState( types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().QuoRaw(2)), types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().QuoRaw(2)), }, sdkmath.ZeroInt(), ) }, }, { "balances, remainder", func() *types.GenesisState { err := suite.BankKeeper.MintCoins( suite.Ctx, types.ModuleName, sdk.NewCoins(sdk.NewCoin(types.IntegerCoinDenom, sdkmath.NewInt(1))), ) suite.Require().NoError(err) return types.NewGenesisState( types.FractionalBalances{ types.NewFractionalBalance(sdk.AccAddress{1}.String(), types.ConversionFactor().QuoRaw(2)), types.NewFractionalBalance(sdk.AccAddress{2}.String(), types.ConversionFactor().QuoRaw(2).SubRaw(1)), }, sdkmath.OneInt(), ) }, }, } for _, tc := range tests { suite.Run(tc.name, func() { // Reset state suite.SetupTest() initGs := tc.initGenesisState() suite.Require().NotPanics(func() { precisebank.InitGenesis( suite.Ctx, suite.Keeper, suite.AccountKeeper, suite.BankKeeper, initGs, ) }) genesisState := precisebank.ExportGenesis(suite.Ctx, suite.Keeper) suite.Require().NoError(genesisState.Validate(), "exported genesis state should be valid") suite.Require().Equal( initGs, genesisState, "exported genesis state should equal initial genesis state", ) }) } }