mukan-sdk/x/epochs/keeper/abci_test.go
Mukan Erkin Törük 20afb5db80
Some checks failed
Build SimApp / build (amd64) (push) Waiting to run
Build SimApp / build (arm64) (push) Waiting to run
CodeQL / Analyze (push) Waiting to run
Build & Push / build (push) Waiting to run
Run Gosec / Gosec (push) Waiting to run
Lint / golangci-lint (push) Waiting to run
Checks dependencies and mocks generation / Check go mod tidy (push) Waiting to run
Checks dependencies and mocks generation / Check up to date mocks (push) Waiting to run
System Tests / setup (push) Waiting to run
System Tests / test-system (push) Blocked by required conditions
System Tests / test-system-legacy (push) Blocked by required conditions
Tests / Code Coverage / split-test-files (push) Waiting to run
Tests / Code Coverage / tests (00) (push) Blocked by required conditions
Tests / Code Coverage / tests (01) (push) Blocked by required conditions
Tests / Code Coverage / tests (02) (push) Blocked by required conditions
Tests / Code Coverage / tests (03) (push) Blocked by required conditions
Tests / Code Coverage / test-integration (push) Waiting to run
Tests / Code Coverage / test-e2e (push) Waiting to run
Tests / Code Coverage / repo-analysis (push) Blocked by required conditions
Tests / Code Coverage / test-sim-nondeterminism (push) Waiting to run
Tests / Code Coverage / test-clientv2 (push) Waiting to run
Tests / Code Coverage / test-core (push) Waiting to run
Tests / Code Coverage / test-depinject (push) Waiting to run
Tests / Code Coverage / test-errors (push) Waiting to run
Tests / Code Coverage / test-math (push) Waiting to run
Tests / Code Coverage / test-schema (push) Waiting to run
Tests / Code Coverage / test-collections (push) Waiting to run
Tests / Code Coverage / test-cosmovisor (push) Waiting to run
Tests / Code Coverage / test-confix (push) Waiting to run
Tests / Code Coverage / test-store (push) Waiting to run
Tests / Code Coverage / test-log (push) Waiting to run
Tests / Code Coverage / test-x-tx (push) Waiting to run
Tests / Code Coverage / test-x-nft (push) Waiting to run
Tests / Code Coverage / test-x-circuit (push) Waiting to run
Tests / Code Coverage / test-x-feegrant (push) Waiting to run
Tests / Code Coverage / test-x-evidence (push) Waiting to run
Tests / Code Coverage / test-x-upgrade (push) Waiting to run
Tests / Code Coverage / test-tools-benchmark (push) Waiting to run
Build & Push SDK Proto Builder / build (push) Has been cancelled
initial: sovereign Mukan Network fork
2026-05-11 03:18:24 +03:00

192 lines
9.4 KiB
Go

package keeper_test
import (
"maps"
"slices"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/x/epochs/types"
)
// This test is responsible for testing how epochs increment based off
// of their initial conditions, and subsequent block height / times.
func (suite *KeeperTestSuite) TestEpochInfoBeginBlockChanges() {
block1Time := time.Unix(1656907200, 0).UTC()
const (
defaultIdentifier = "hourly"
defaultDuration = time.Hour
// eps is short for epsilon - in this case a negligible amount of time.
eps = time.Nanosecond
)
tests := map[string]struct {
// if identifier, duration is not set, we make it defaultIdentifier and defaultDuration.
// EpochCountingStarted, if unspecified, is inferred by CurrentEpoch == 0
// StartTime is inferred to be block1Time if left blank.
initialEpochInfo types.EpochInfo
blockHeightTimePairs map[int]time.Time
expEpochInfo types.EpochInfo
}{
"First block running at exactly start time sets epoch tick": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time, CurrentEpochStartHeight: 1},
},
"First block run sets start time, subsequent blocks within timer interval do not cause timer tick": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(time.Second), 3: block1Time.Add(time.Minute), 4: block1Time.Add(30 * time.Minute)},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time, CurrentEpochStartHeight: 1},
},
"Second block at exactly timer interval later does not tick": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(defaultDuration)},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time, CurrentEpochStartHeight: 1},
},
"Second block at timer interval + epsilon later does tick": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(defaultDuration).Add(eps)},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Hour), CurrentEpochStartHeight: 2},
},
"Downtime recovery (many intervals), first block causes 1 tick and sets current start time 1 interval ahead": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(24 * time.Hour)},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Hour), CurrentEpochStartHeight: 2},
},
"Downtime recovery (many intervals), second block is at tick 2, w/ start time 2 intervals ahead": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(24 * time.Hour), 3: block1Time.Add(24 * time.Hour).Add(eps)},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 3, CurrentEpochStartTime: block1Time.Add(2 * time.Hour), CurrentEpochStartHeight: 3},
},
"Many blocks between first and second tick": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 1, CurrentEpochStartTime: block1Time},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(time.Second), 3: block1Time.Add(2 * time.Second), 4: block1Time.Add(time.Hour).Add(eps)},
expEpochInfo: types.EpochInfo{StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Hour), CurrentEpochStartHeight: 4},
},
"Distinct identifier and duration still works": {
initialEpochInfo: types.EpochInfo{Identifier: "hello", Duration: time.Minute, StartTime: block1Time, CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
blockHeightTimePairs: map[int]time.Time{2: block1Time.Add(time.Second), 3: block1Time.Add(time.Minute).Add(eps)},
expEpochInfo: types.EpochInfo{Identifier: "hello", Duration: time.Minute, StartTime: block1Time, CurrentEpoch: 2, CurrentEpochStartTime: block1Time.Add(time.Minute), CurrentEpochStartHeight: 3},
},
"StartTime in future won't get ticked on first block": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time.Add(time.Second), CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
// currentEpochStartHeight is 0 since it hasn't started or been triggered
expEpochInfo: types.EpochInfo{StartTime: block1Time.Add(time.Second), CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}, CurrentEpochStartHeight: 0},
},
"StartTime in past will get ticked on first block": {
initialEpochInfo: types.EpochInfo{StartTime: block1Time.Add(-time.Second), CurrentEpoch: 0, CurrentEpochStartTime: time.Time{}},
expEpochInfo: types.EpochInfo{StartTime: block1Time.Add(-time.Second), CurrentEpoch: 1, CurrentEpochStartTime: block1Time.Add(-time.Second), CurrentEpochStartHeight: 1},
},
}
for name, test := range tests {
suite.Run(name, func() {
suite.SetupTest()
suite.Ctx = suite.Ctx.WithBlockHeight(1).WithBlockTime(block1Time)
initialEpoch := initializeBlankEpochInfoFields(test.initialEpochInfo, defaultIdentifier, defaultDuration)
err := suite.EpochsKeeper.AddEpochInfo(suite.Ctx, initialEpoch)
suite.Require().NoError(err)
err = suite.EpochsKeeper.BeginBlocker(suite.Ctx)
suite.Require().NoError(err)
// get sorted heights
heights := slices.SortedFunc(maps.Keys(test.blockHeightTimePairs), func(i, j int) int {
if test.blockHeightTimePairs[i].Before(test.blockHeightTimePairs[j]) {
return -1
} else if test.blockHeightTimePairs[i].After(test.blockHeightTimePairs[j]) {
return 1
}
return 0
})
for _, h := range heights {
// for each height in order, run begin block
suite.Ctx = suite.Ctx.WithBlockHeight(int64(h)).WithBlockTime(test.blockHeightTimePairs[h])
err := suite.EpochsKeeper.BeginBlocker(suite.Ctx)
suite.Require().NoError(err)
}
expEpoch := initializeBlankEpochInfoFields(test.expEpochInfo, initialEpoch.Identifier, initialEpoch.Duration)
actEpoch, err := suite.EpochsKeeper.EpochInfo.Get(suite.Ctx, initialEpoch.Identifier)
suite.Require().NoError(err)
suite.Require().Equal(expEpoch, actEpoch)
})
}
}
// initializeBlankEpochInfoFields set identifier, duration and epochCountingStarted if blank in epoch
func initializeBlankEpochInfoFields(epoch types.EpochInfo, identifier string, duration time.Duration) types.EpochInfo {
if epoch.Identifier == "" {
epoch.Identifier = identifier
}
if epoch.Duration == time.Duration(0) {
epoch.Duration = duration
}
epoch.EpochCountingStarted = (epoch.CurrentEpoch != 0)
return epoch
}
func TestEpochStartingOneMonthAfterInitGenesis(t *testing.T) {
ctx, epochsKeeper := Setup(t)
// On init genesis, default epochs information is set
// To check init genesis again, should make it fresh status
epochInfos, err := epochsKeeper.AllEpochInfos(ctx)
require.NoError(t, err)
for _, epochInfo := range epochInfos {
err := epochsKeeper.EpochInfo.Remove(ctx, epochInfo.Identifier)
require.NoError(t, err)
}
now := time.Now()
week := time.Hour * 24 * 7
month := time.Hour * 24 * 30
initialBlockHeight := int64(1)
ctx = ctx.WithBlockHeight(initialBlockHeight).WithBlockTime(now)
err = epochsKeeper.InitGenesis(ctx, types.GenesisState{
Epochs: []types.EpochInfo{
{
Identifier: "monthly",
StartTime: now.Add(month),
Duration: time.Hour * 24 * 30,
CurrentEpoch: 0,
CurrentEpochStartHeight: ctx.BlockHeight(),
CurrentEpochStartTime: time.Time{},
EpochCountingStarted: false,
},
},
})
require.NoError(t, err)
// epoch not started yet
epochInfo, err := epochsKeeper.EpochInfo.Get(ctx, "monthly")
require.NoError(t, err)
require.Equal(t, epochInfo.CurrentEpoch, int64(0))
require.Equal(t, epochInfo.CurrentEpochStartHeight, initialBlockHeight)
require.Equal(t, epochInfo.CurrentEpochStartTime, time.Time{})
require.Equal(t, epochInfo.EpochCountingStarted, false)
// after 1 week
ctx = ctx.WithBlockHeight(2).WithBlockTime(now.Add(week))
err = epochsKeeper.BeginBlocker(ctx)
require.NoError(t, err)
// epoch not started yet
epochInfo, err = epochsKeeper.EpochInfo.Get(ctx, "monthly")
require.NoError(t, err)
require.Equal(t, epochInfo.CurrentEpoch, int64(0))
require.Equal(t, epochInfo.CurrentEpochStartHeight, initialBlockHeight)
require.Equal(t, epochInfo.CurrentEpochStartTime, time.Time{})
require.Equal(t, epochInfo.EpochCountingStarted, false)
// after 1 month
ctx = ctx.WithBlockHeight(3).WithBlockTime(now.Add(month))
err = epochsKeeper.BeginBlocker(ctx)
require.NoError(t, err)
// epoch started
epochInfo, err = epochsKeeper.EpochInfo.Get(ctx, "monthly")
require.NoError(t, err)
require.Equal(t, epochInfo.CurrentEpoch, int64(1))
require.Equal(t, epochInfo.CurrentEpochStartHeight, ctx.BlockHeight())
require.Equal(t, epochInfo.CurrentEpochStartTime.UTC().String(), now.Add(month).UTC().String())
require.Equal(t, epochInfo.EpochCountingStarted, true)
}