0g-chain/x/swap/keeper/invariants_test.go
Nick DeLuca b86cfc9f14
Add Invariants for Swap Module (#979)
* add swap module invariants

* typo

* update alias file for invariants

* typo in test name

* fix typo - method iterates share record, not pools
2021-08-05 20:43:55 -05:00

236 lines
8.1 KiB
Go

package keeper_test
import (
"testing"
"github.com/kava-labs/kava/x/swap/keeper"
"github.com/kava-labs/kava/x/swap/testutil"
"github.com/kava-labs/kava/x/swap/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/suite"
)
type invariantTestSuite struct {
testutil.Suite
invariants map[string]map[string]sdk.Invariant
}
func (suite *invariantTestSuite) SetupTest() {
suite.Suite.SetupTest()
suite.invariants = make(map[string]map[string]sdk.Invariant)
keeper.RegisterInvariants(suite, suite.Keeper)
}
func (suite *invariantTestSuite) SetupValidState() {
suite.Keeper.SetPool(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
),
sdk.NewInt(3e6),
))
suite.AddCoinsToModule(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
),
)
suite.Keeper.SetDepositorShares(suite.Ctx, types.NewShareRecord(
sdk.AccAddress("depositor 1"),
types.PoolID("ukava", "usdx"),
sdk.NewInt(2e6),
))
suite.Keeper.SetDepositorShares(suite.Ctx, types.NewShareRecord(
sdk.AccAddress("depositor 2"),
types.PoolID("ukava", "usdx"),
sdk.NewInt(1e6),
))
suite.Keeper.SetPool(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("hard", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(2e6)),
),
sdk.NewInt(1e6),
))
suite.AddCoinsToModule(
sdk.NewCoins(
sdk.NewCoin("hard", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(2e6)),
),
)
suite.Keeper.SetDepositorShares(suite.Ctx, types.NewShareRecord(
sdk.AccAddress("depositor 1"),
types.PoolID("hard", "usdx"),
sdk.NewInt(1e6),
))
}
func (suite *invariantTestSuite) RegisterRoute(moduleName string, route string, invariant sdk.Invariant) {
_, exists := suite.invariants[moduleName]
if !exists {
suite.invariants[moduleName] = make(map[string]sdk.Invariant)
}
suite.invariants[moduleName][route] = invariant
}
func (suite *invariantTestSuite) runInvariant(route string, invariant func(k keeper.Keeper) sdk.Invariant) (string, bool) {
ctx := suite.Ctx
registeredInvariant := suite.invariants[types.ModuleName][route]
suite.Require().NotNil(registeredInvariant)
// direct call
dMessage, dBroken := invariant(suite.Keeper)(ctx)
// registered call
rMessage, rBroken := registeredInvariant(ctx)
// all call
aMessage, aBroken := keeper.AllInvariants(suite.Keeper)(ctx)
// require matching values for direct call and registered call
suite.Require().Equal(dMessage, rMessage, "expected registered invariant message to match")
suite.Require().Equal(dBroken, rBroken, "expected registered invariant broken to match")
// require matching values for direct call and all invariants call if broken
suite.Require().Equal(dBroken, aBroken, "expected all invariant broken to match")
if dBroken {
suite.Require().Equal(dMessage, aMessage, "expected all invariant message to match")
}
// return message, broken
return dMessage, dBroken
}
func (suite *invariantTestSuite) TestPoolRecordsInvariant() {
// default state is valid
message, broken := suite.runInvariant("pool-records", keeper.PoolRecordsInvariant)
suite.Equal("swap: validate pool records broken invariant\npool record invalid\n", message)
suite.Equal(false, broken)
suite.SetupValidState()
message, broken = suite.runInvariant("pool-records", keeper.PoolRecordsInvariant)
suite.Equal("swap: validate pool records broken invariant\npool record invalid\n", message)
suite.Equal(false, broken)
// broken with invalid pool record
suite.Keeper.SetPool_Raw(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
),
sdk.NewInt(-1e6),
))
message, broken = suite.runInvariant("pool-records", keeper.PoolRecordsInvariant)
suite.Equal("swap: validate pool records broken invariant\npool record invalid\n", message)
suite.Equal(true, broken)
}
func (suite *invariantTestSuite) TestShareRecordsInvariant() {
message, broken := suite.runInvariant("share-records", keeper.ShareRecordsInvariant)
suite.Equal("swap: validate share records broken invariant\nshare record invalid\n", message)
suite.Equal(false, broken)
suite.SetupValidState()
message, broken = suite.runInvariant("share-records", keeper.ShareRecordsInvariant)
suite.Equal("swap: validate share records broken invariant\nshare record invalid\n", message)
suite.Equal(false, broken)
// broken with invalid share record
suite.Keeper.SetDepositorShares_Raw(suite.Ctx, types.NewShareRecord(
sdk.AccAddress("depositor 1"),
types.PoolID("ukava", "usdx"),
sdk.NewInt(-1e6),
))
message, broken = suite.runInvariant("share-records", keeper.ShareRecordsInvariant)
suite.Equal("swap: validate share records broken invariant\nshare record invalid\n", message)
suite.Equal(true, broken)
}
func (suite *invariantTestSuite) TestPoolReservesInvariant() {
message, broken := suite.runInvariant("pool-reserves", keeper.PoolReservesInvariant)
suite.Equal("swap: pool reserves broken invariant\npool reserves do not match module account\n", message)
suite.Equal(false, broken)
suite.SetupValidState()
message, broken = suite.runInvariant("pool-reserves", keeper.PoolReservesInvariant)
suite.Equal("swap: pool reserves broken invariant\npool reserves do not match module account\n", message)
suite.Equal(false, broken)
// broken when reserves are greater than module balance
suite.Keeper.SetPool(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(2e6)),
sdk.NewCoin("usdx", sdk.NewInt(10e6)),
),
sdk.NewInt(5e6),
))
message, broken = suite.runInvariant("pool-reserves", keeper.PoolReservesInvariant)
suite.Equal("swap: pool reserves broken invariant\npool reserves do not match module account\n", message)
suite.Equal(true, broken)
// broken when reserves are less than the module balance
suite.Keeper.SetPool(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e5)),
sdk.NewCoin("usdx", sdk.NewInt(5e5)),
),
sdk.NewInt(3e5),
))
message, broken = suite.runInvariant("pool-reserves", keeper.PoolReservesInvariant)
suite.Equal("swap: pool reserves broken invariant\npool reserves do not match module account\n", message)
suite.Equal(true, broken)
}
func (suite *invariantTestSuite) TestPoolSharesInvariant() {
message, broken := suite.runInvariant("pool-shares", keeper.PoolSharesInvariant)
suite.Equal("swap: pool shares broken invariant\npool shares do not match depositor shares\n", message)
suite.Equal(false, broken)
suite.SetupValidState()
message, broken = suite.runInvariant("pool-shares", keeper.PoolSharesInvariant)
suite.Equal("swap: pool shares broken invariant\npool shares do not match depositor shares\n", message)
suite.Equal(false, broken)
// broken when total shares are greater than depositor shares
suite.Keeper.SetPool(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
),
sdk.NewInt(5e6),
))
message, broken = suite.runInvariant("pool-shares", keeper.PoolSharesInvariant)
suite.Equal("swap: pool shares broken invariant\npool shares do not match depositor shares\n", message)
suite.Equal(true, broken)
// broken when total shares are less than the depositor shares
suite.Keeper.SetPool(suite.Ctx, types.NewPoolRecord(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
),
sdk.NewInt(1e5),
))
message, broken = suite.runInvariant("pool-shares", keeper.PoolSharesInvariant)
suite.Equal("swap: pool shares broken invariant\npool shares do not match depositor shares\n", message)
suite.Equal(true, broken)
// broken when pool record is missing
suite.Keeper.DeletePool(suite.Ctx, types.PoolID("ukava", "usdx"))
suite.RemoveCoinsFromModule(
sdk.NewCoins(
sdk.NewCoin("ukava", sdk.NewInt(1e6)),
sdk.NewCoin("usdx", sdk.NewInt(5e6)),
),
)
message, broken = suite.runInvariant("pool-shares", keeper.PoolSharesInvariant)
suite.Equal("swap: pool shares broken invariant\npool shares do not match depositor shares\n", message)
suite.Equal(true, broken)
}
func TestInvariantTestSuite(t *testing.T) {
suite.Run(t, new(invariantTestSuite))
}