package keeper_test import ( "context" "testing" "time" "github.com/stretchr/testify/suite" "cosmossdk.io/log" "cosmossdk.io/math" storetypes "cosmossdk.io/store/types" cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" sdkaddress "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/runtime" "github.com/cosmos/cosmos-sdk/testutil/integration" simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims" sdk "github.com/cosmos/cosmos-sdk/types" moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" icatypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/types" clienttypes "github.com/cosmos/ibc-go/v8/modules/core/02-client/types" connectiontypes "github.com/cosmos/ibc-go/v8/modules/core/03-connection/types" channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" portkeeper "github.com/cosmos/ibc-go/v8/modules/core/05-port/keeper" ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" "github.com/sonr-io/sonr/app" "github.com/sonr-io/sonr/x/dex/keeper" "github.com/sonr-io/sonr/x/dex/types" didtypes "github.com/sonr-io/sonr/x/did/types" ) var maccPerms = map[string][]string{ authtypes.FeeCollectorName: nil, stakingtypes.BondedPoolName: {authtypes.Burner, authtypes.Staking}, stakingtypes.NotBondedPoolName: {authtypes.Burner, authtypes.Staking}, minttypes.ModuleName: {authtypes.Minter}, govtypes.ModuleName: {authtypes.Burner}, } type testFixture struct { suite.Suite ctx sdk.Context k keeper.Keeper msgServer types.MsgServer queryServer types.QueryServer accountkeeper authkeeper.AccountKeeper bankkeeper bankkeeper.BaseKeeper stakingKeeper *stakingkeeper.Keeper mintkeeper mintkeeper.Keeper addrs []sdk.AccAddress govModAddr string } // SetupTest creates a new test fixture func SetupTest(t *testing.T) *testFixture { t.Helper() f := new(testFixture) cfg := sdk.GetConfig() cfg.SetBech32PrefixForAccount(app.Bech32PrefixAccAddr, app.Bech32PrefixAccPub) cfg.SetBech32PrefixForValidator(app.Bech32PrefixValAddr, app.Bech32PrefixValPub) cfg.SetBech32PrefixForConsensusNode(app.Bech32PrefixConsAddr, app.Bech32PrefixConsPub) cfg.SetCoinType(app.CoinType) validatorAddressCodec := sdkaddress.NewBech32Codec(app.Bech32PrefixValAddr) consensusAddressCodec := sdkaddress.NewBech32Codec(app.Bech32PrefixConsAddr) // Base setup logger := log.NewTestLogger(t) encCfg := moduletestutil.MakeTestEncodingConfig() // Register auth types interfaces authtypes.RegisterInterfaces(encCfg.InterfaceRegistry) banktypes.RegisterInterfaces(encCfg.InterfaceRegistry) stakingtypes.RegisterInterfaces(encCfg.InterfaceRegistry) minttypes.RegisterInterfaces(encCfg.InterfaceRegistry) f.govModAddr = authtypes.NewModuleAddress(govtypes.ModuleName).String() // Initialize test addresses f.addrs = simtestutil.CreateIncrementalAccounts(3) // Setup store keys keys := storetypes.NewKVStoreKeys( types.StoreKey, authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, minttypes.StoreKey, capabilitytypes.StoreKey, ) memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) cdc := encCfg.Codec // Initialize keepers authority := authtypes.NewModuleAddress(govtypes.ModuleName) maccPerms[types.ModuleName] = nil f.accountkeeper = authkeeper.NewAccountKeeper( cdc, runtime.NewKVStoreService(keys[authtypes.StoreKey]), authtypes.ProtoBaseAccount, maccPerms, sdkaddress.NewBech32Codec(app.Bech32PrefixAccAddr), app.Bech32PrefixAccAddr, authority.String(), ) f.bankkeeper = bankkeeper.NewBaseKeeper( cdc, runtime.NewKVStoreService(keys[banktypes.StoreKey]), f.accountkeeper, nil, authority.String(), logger, ) f.stakingKeeper = stakingkeeper.NewKeeper( cdc, runtime.NewKVStoreService(keys[stakingtypes.StoreKey]), f.accountkeeper, f.bankkeeper, authority.String(), validatorAddressCodec, consensusAddressCodec, ) f.mintkeeper = mintkeeper.NewKeeper( cdc, runtime.NewKVStoreService(keys[minttypes.StoreKey]), f.stakingKeeper, f.accountkeeper, f.bankkeeper, authtypes.FeeCollectorName, authority.String(), ) // Create capability keeper for IBC capabilityKeeper := capabilitykeeper.NewKeeper( cdc, keys[capabilitytypes.StoreKey], memKeys[capabilitytypes.MemStoreKey], ) // Create scoped keeper for the DEX module scopedKeeper := capabilityKeeper.ScopeToModule(types.ModuleName) // Create port keeper portKeeper := portkeeper.NewKeeper(scopedKeeper) // Create mock expected keepers mockICS4Wrapper := &mockICS4Wrapper{} mockAccountKeeper := &mockAccountKeeper{} mockBankKeeper := &mockBankKeeper{} mockICAControllerKeeper := &mockICAControllerKeeper{} mockConnectionKeeper := &mockConnectionKeeper{} mockChannelKeeper := &mockChannelKeeper{} mockDIDKeeper := &mockDIDKeeper{} mockDWNKeeper := &mockDWNKeeper{} // Initialize DEX keeper f.k = keeper.NewKeeper( cdc, runtime.NewKVStoreService(keys[types.StoreKey]), mockICS4Wrapper, &portKeeper, scopedKeeper, mockAccountKeeper, mockBankKeeper, mockICAControllerKeeper, mockConnectionKeeper, mockChannelKeeper, mockDIDKeeper, mockDWNKeeper, authority.String(), ) f.msgServer = keeper.NewMsgServerImpl(f.k) f.queryServer = keeper.NewQueryServerImpl(f.k) // Initialize context with proper multistore cms := integration.CreateMultiStore(keys, logger) for _, key := range memKeys { cms.MountStoreWithDB(key, storetypes.StoreTypeMemory, nil) } f.ctx = sdk.NewContext(cms, cmtproto.Header{ Height: 1, Time: time.Now(), }, false, logger) // Fund test accounts initCoins := sdk.NewCoins(sdk.NewCoin("usnr", math.NewInt(1000000000))) for _, addr := range f.addrs { err := f.bankkeeper.MintCoins(f.ctx, minttypes.ModuleName, initCoins) if err != nil { panic(err) } err = f.bankkeeper.SendCoinsFromModuleToAccount( f.ctx, minttypes.ModuleName, addr, initCoins, ) if err != nil { panic(err) } } return f } // KeeperTestSuite runs all keeper tests type KeeperTestSuite struct { suite.Suite f *testFixture } func TestKeeperSuite(t *testing.T) { suite.Run(t, new(KeeperTestSuite)) } func (suite *KeeperTestSuite) SetupTest() { suite.f = SetupTest(suite.T()) } // Test basic keeper operations func (suite *KeeperTestSuite) TestRegisterDEXAccount() { did := "did:sonr:test123" connectionID := "connection-0" // Register a new DEX account through keeper method account, err := suite.f.k.RegisterDEXAccount( suite.f.ctx, did, connectionID, []string{"swap", "liquidity"}, ) suite.Require().NoError(err) suite.Require().NotNil(account) // Retrieve the account retrieved, err := suite.f.k.GetDEXAccount(suite.f.ctx, did, connectionID) suite.Require().NoError(err) suite.Require().NotNil(retrieved) suite.Require().Equal(did, retrieved.Did) suite.Require().Equal(connectionID, retrieved.ConnectionId) suite.Require().Equal(types.ACCOUNT_STATUS_PENDING, retrieved.Status) } func (suite *KeeperTestSuite) TestGetDEXAccountsByDID() { did := "did:sonr:test456" // Register multiple accounts for the same DID connections := []string{"connection-0", "connection-1"} for _, connID := range connections { _, err := suite.f.k.RegisterDEXAccount( suite.f.ctx, did, connID, []string{"swap"}, ) suite.Require().NoError(err) } // Retrieve all accounts for the DID accounts, err := suite.f.k.GetDEXAccountsByDID(suite.f.ctx, did) suite.Require().NoError(err) suite.Require().Len(accounts, 2) } func (suite *KeeperTestSuite) TestParamsOperations() { // Set params params := types.Params{ Enabled: true, MaxAccountsPerDid: 5, DefaultTimeoutSeconds: 600, AllowedConnections: []string{"connection-0", "connection-1"}, MinSwapAmount: "100", MaxDailyVolume: "1000000", RateLimits: types.RateLimitParams{ MaxOpsPerBlock: 10, MaxOpsPerDidPerDay: 100, CooldownBlocks: 5, }, Fees: types.FeeParams{ SwapFeeBps: 30, // 0.3% LiquidityFeeBps: 10, // 0.1% OrderFeeBps: 20, // 0.2% FeeCollector: "sonr1feecolllector", }, } err := suite.f.k.Params.Set(suite.f.ctx, params) suite.Require().NoError(err) // Get params retrieved, err := suite.f.k.Params.Get(suite.f.ctx) suite.Require().NoError(err) suite.Require().Equal(params.Enabled, retrieved.Enabled) suite.Require().Equal(params.MaxAccountsPerDid, retrieved.MaxAccountsPerDid) suite.Require().Equal(params.AllowedConnections, retrieved.AllowedConnections) } // Mock implementations for expected keepers type mockICS4Wrapper struct{} func (m *mockICS4Wrapper) SendPacket( ctx sdk.Context, channelCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte, ) (uint64, error) { return 1, nil } func (m *mockICS4Wrapper) WriteAcknowledgement( ctx sdk.Context, chanCap *capabilitytypes.Capability, packet ibcexported.PacketI, acknowledgement ibcexported.Acknowledgement, ) error { return nil } func (m *mockICS4Wrapper) GetAppVersion(ctx sdk.Context, portID, channelID string) (string, bool) { return "ics27-1", true } type mockAccountKeeper struct{} func (m *mockAccountKeeper) GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI { return nil } func (m *mockAccountKeeper) SetAccount(ctx context.Context, acc sdk.AccountI) {} func (m *mockAccountKeeper) NewAccountWithAddress( ctx sdk.Context, addr sdk.AccAddress, ) sdk.AccountI { return nil } func (m *mockAccountKeeper) GetModuleAccount( ctx context.Context, moduleName string, ) sdk.ModuleAccountI { return nil } func (m *mockAccountKeeper) GetModuleAddress(name string) sdk.AccAddress { return sdk.AccAddress{} } type mockBankKeeper struct{} func (m *mockBankKeeper) SendCoins( ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins, ) error { return nil } func (m *mockBankKeeper) SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins { return sdk.NewCoins() } type mockICAControllerKeeper struct{} func (m *mockICAControllerKeeper) RegisterInterchainAccount( ctx sdk.Context, connectionID, owner, version string, ) error { return nil } func (m *mockICAControllerKeeper) GetInterchainAccountAddress( ctx sdk.Context, connectionID, portID string, ) (string, bool) { return "cosmos1test", true } func (m *mockICAControllerKeeper) SendTx( ctx sdk.Context, chanCap *capabilitytypes.Capability, connectionID, portID string, icaPacketData icatypes.InterchainAccountPacketData, timeoutTimestamp uint64, ) (uint64, error) { return 1, nil } func (m *mockICAControllerKeeper) GetActiveChannelID( ctx sdk.Context, connectionID, portID string, ) (string, bool) { return "channel-0", true } type mockConnectionKeeper struct{} func (m *mockConnectionKeeper) GetConnection( ctx sdk.Context, connectionID string, ) (connectiontypes.ConnectionEnd, bool) { return connectiontypes.ConnectionEnd{ ClientId: "07-tendermint-0", Versions: []*connectiontypes.Version{{ Identifier: "1", Features: []string{"ORDER_ORDERED", "ORDER_UNORDERED"}, }}, State: connectiontypes.OPEN, Counterparty: connectiontypes.Counterparty{ ClientId: "07-tendermint-0", ConnectionId: "connection-0", }, }, true } type mockChannelKeeper struct{} func (m *mockChannelKeeper) GetChannel( ctx sdk.Context, portID, channelID string, ) (channeltypes.Channel, bool) { return channeltypes.Channel{ State: channeltypes.OPEN, Ordering: channeltypes.ORDERED, Counterparty: channeltypes.Counterparty{ PortId: "icahost", ChannelId: "channel-0", }, ConnectionHops: []string{"connection-0"}, Version: "ics27-1", }, true } func (m *mockChannelKeeper) GetNextSequenceSend( ctx sdk.Context, portID, channelID string, ) (uint64, bool) { return 1, true } func (m *mockChannelKeeper) SendPacket( ctx sdk.Context, chanCap *capabilitytypes.Capability, sourcePort string, sourceChannel string, timeoutHeight clienttypes.Height, timeoutTimestamp uint64, data []byte, ) (uint64, error) { return 1, nil } type mockDIDKeeper struct{} func (m *mockDIDKeeper) GetDIDDocument( ctx context.Context, did string, ) (*didtypes.DIDDocument, error) { return &didtypes.DIDDocument{ Id: did, }, nil } type mockDWNKeeper struct{}