diff --git a/common/types/constants.go b/common/types/constants.go index f52bd78f7a..a4426c93f4 100644 --- a/common/types/constants.go +++ b/common/types/constants.go @@ -1,6 +1,6 @@ package types const ( - STALE_ENTRY_TIME int64 = 1440 // 1440 blocks (equivalent to 24 hours when block_time = 1min) + STALE_ENTRY_TIME = 1440 // 1440 blocks (equivalent to 24 hours when block_time = 1min) MODULE_NAME string = "common" ) diff --git a/common/types/utils.go b/common/types/utils.go index 19bce27358..84810e0509 100644 --- a/common/types/utils.go +++ b/common/types/utils.go @@ -4,64 +4,53 @@ import ( "golang.org/x/exp/constraints" ) -func FindMin[T constraints.Ordered](s []T) T { - if len(s) == 0 { - var zero T - return zero - } - m := s[0] - for _, v := range s { - if m > v { - m = v +func FindMin[T constraints.Ordered](s []T) (m T) { + if len(s) > 0 { + m = s[0] + for _, v := range s[1:] { + if m > v { + m = v + } } } return m } -func FindMax[T constraints.Ordered](s []T) T { - if len(s) == 0 { - var zero T - return zero - } - m := s[0] - for _, v := range s { - if m < v { - m = v +func FindMax[T constraints.Ordered](s []T) (m T) { + if len(s) > 0 { + m = s[0] + for _, v := range s[1:] { + if m < v { + m = v + } } } return m } func Intersection[T comparable](arrays ...[]T) []T { - // Create a map to store the elements and their occurrence count elements := make(map[T]int) - // Iterate through each array for _, arr := range arrays { - // Create a map to store the elements of the current array arrElements := make(map[T]bool) - // Populate the map with elements from the current array for _, elem := range arr { - arrElements[elem] = true - } - - // Increment the occurrence count for each element in the map - for elem := range arrElements { - elements[elem]++ + if _, ok := arrElements[elem]; !ok { + arrElements[elem] = true + elements[elem]++ + } } } - var intersection []T + res := make([]T, 0) - // Check the occurrence count of each element for elem, count := range elements { if count == len(arrays) { - intersection = append(intersection, elem) + res = append(res, elem) } } - return intersection + return res } func Union[T comparable](arrays ...[]T) []T { diff --git a/common/types/utils_test.go b/common/types/utils_test.go new file mode 100644 index 0000000000..dd6e4a4ce9 --- /dev/null +++ b/common/types/utils_test.go @@ -0,0 +1,70 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestFindMin(t *testing.T) { + for _, tt := range []struct { + name string + slice []int + min int + }{ + {"empty", []int{}, 0}, + {"one element", []int{1}, 1}, + {"min is first", []int{1, 2, 3}, 1}, + {"min is middle", []int{2, 1, 3}, 1}, + {"min is last", []int{3, 2, 1}, 1}, + {"min is zero", []int{3, 0, 1}, 0}, + {"min < zero", []int{3, -2, 1}, -2}, + {"min twice", []int{3, 1, 1}, 1}, + } { + t.Run(tt.name, func(t *testing.T) { + min := FindMin(tt.slice) + require.Equal(t, tt.min, min) + }) + } +} + +func TestFindMax(t *testing.T) { + for _, tt := range []struct { + name string + slice []int + max int + }{ + {"empty", []int{}, 0}, + {"one element", []int{1}, 1}, + {"max is first", []int{3, 2, 1}, 3}, + {"max is middle", []int{2, 1, 3}, 3}, + {"max is last", []int{1, 2, 3}, 3}, + {"max is zero", []int{-3, 0, -1}, 0}, + {"max < zero", []int{-3, -2, -5}, -2}, + {"max twice", []int{1, 3, 3}, 3}, + } { + t.Run(tt.name, func(t *testing.T) { + max := FindMax(tt.slice) + require.Equal(t, tt.max, max) + }) + } +} + +func TestIntersection(t *testing.T) { + for _, tt := range []struct { + name string + slices [][]int + result []int + }{ + {"zero slices", [][]int{}, []int{}}, + {"one slice", [][]int{{1, 2}}, []int{1, 2}}, + {"two slices, one empty", [][]int{{1, 2}, {}}, []int{}}, + {"two slices, non empty", [][]int{{1, 2, 3}, {1, 3}}, []int{1, 3}}, + } { + t.Run(tt.name, func(t *testing.T) { + res := Intersection(tt.slices...) + require.Subset(t, tt.result, res) + require.Subset(t, res, tt.result) + }) + } +} diff --git a/testutil/common/common.go b/testutil/common/common.go index c2c6b924f0..daf95791c5 100644 --- a/testutil/common/common.go +++ b/testutil/common/common.go @@ -12,7 +12,6 @@ import ( conflictconstruct "github.com/lavanet/lava/x/conflict/types/construct" epochstoragetypes "github.com/lavanet/lava/x/epochstorage/types" "github.com/lavanet/lava/x/pairing/types" - plantypes "github.com/lavanet/lava/x/plans/types" spectypes "github.com/lavanet/lava/x/spec/types" subscriptiontypes "github.com/lavanet/lava/x/subscription/types" "github.com/stretchr/testify/require" @@ -23,47 +22,10 @@ type Account struct { Addr sdk.AccAddress } -func CreateMockSpec() spectypes.Spec { - specName := "mockSpec" - spec := spectypes.Spec{} - spec.Name = specName - spec.Index = specName - spec.Enabled = true - spec.ReliabilityThreshold = 4294967295 - spec.BlockDistanceForFinalizedData = 0 - spec.DataReliabilityEnabled = true - spec.MinStakeClient = sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(100)) - spec.MinStakeProvider = sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(1000)) - spec.ApiCollections = []*spectypes.ApiCollection{{Enabled: true, CollectionData: spectypes.CollectionData{ApiInterface: "stub", Type: "GET"}, Apis: []*spectypes.Api{{Name: specName + "API", ComputeUnits: 100, Enabled: true}}}} - spec.BlockDistanceForFinalizedData = 0 - return spec -} - -func CreateMockPlan() plantypes.Plan { - policy := plantypes.Policy{ - TotalCuLimit: 100000, - EpochCuLimit: 10000, - MaxProvidersToPair: 3, - GeolocationProfile: 1, - } - plan := plantypes.Plan{ - Index: "mockPlan", - Description: "plan for testing", - Type: "rpc", - Block: 100, - Price: sdk.NewCoin("ulava", sdk.NewInt(100)), - AllowOveruse: true, - OveruseRate: 10, - AnnualDiscountPercentage: 20, - PlanPolicy: policy, - } - - return plan -} - func CreateNewAccount(ctx context.Context, keepers testkeeper.Keepers, balance int64) (acc Account) { acc.SK, acc.Addr = sigs.GenerateFloatingKey() - keepers.BankKeeper.SetBalance(sdk.UnwrapSDKContext(ctx), acc.Addr, sdk.NewCoins(sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(balance)))) + coins := sdk.NewCoins(sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(balance))) + keepers.BankKeeper.SetBalance(sdk.UnwrapSDKContext(ctx), acc.Addr, coins) return } diff --git a/testutil/common/mock.go b/testutil/common/mock.go new file mode 100644 index 0000000000..cc0d9d79dc --- /dev/null +++ b/testutil/common/mock.go @@ -0,0 +1,51 @@ +package common + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + epochstoragetypes "github.com/lavanet/lava/x/epochstorage/types" + plantypes "github.com/lavanet/lava/x/plans/types" + spectypes "github.com/lavanet/lava/x/spec/types" +) + +func CreateMockSpec() spectypes.Spec { + specName := "mock_spec" + spec := spectypes.Spec{} + spec.Name = specName + spec.Index = specName + spec.Enabled = true + spec.ReliabilityThreshold = 4294967295 + spec.BlockDistanceForFinalizedData = 0 + spec.DataReliabilityEnabled = true + spec.MinStakeClient = sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(100)) + spec.MinStakeProvider = sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(1000)) + spec.ApiCollections = []*spectypes.ApiCollection{{Enabled: true, CollectionData: spectypes.CollectionData{ApiInterface: "stub", Type: "GET"}, Apis: []*spectypes.Api{{Name: specName + "API", ComputeUnits: 100, Enabled: true}}}} + spec.BlockDistanceForFinalizedData = 0 + return spec +} + +func CreateMockPlan() plantypes.Plan { + plan := plantypes.Plan{ + Index: "mock_plan", + Description: "plan for testing", + Type: "rpc", + Block: 100, + Price: sdk.NewCoin("ulava", sdk.NewInt(100)), + AllowOveruse: true, + OveruseRate: 10, + AnnualDiscountPercentage: 20, + PlanPolicy: CreateMockPolicy(), + } + + return plan +} + +func CreateMockPolicy() plantypes.Policy { + policy := plantypes.Policy{ + TotalCuLimit: 100000, + EpochCuLimit: 10000, + MaxProvidersToPair: 3, + GeolocationProfile: 1, + } + + return policy +} diff --git a/testutil/common/tester.go b/testutil/common/tester.go new file mode 100644 index 0000000000..65f9ae9ccd --- /dev/null +++ b/testutil/common/tester.go @@ -0,0 +1,310 @@ +package common + +import ( + "context" + "strconv" + "testing" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/lavanet/lava/common/types" + testkeeper "github.com/lavanet/lava/testutil/keeper" + planstypes "github.com/lavanet/lava/x/plans/types" + projectstypes "github.com/lavanet/lava/x/projects/types" + spectypes "github.com/lavanet/lava/x/spec/types" + subscriptionkeeper "github.com/lavanet/lava/x/subscription/keeper" + subscriptiontypes "github.com/lavanet/lava/x/subscription/types" +) + +type Tester struct { + T *testing.T + + GoCtx context.Context + Ctx sdk.Context + Servers *testkeeper.Servers + Keepers *testkeeper.Keepers + + accounts map[string]Account + plans map[string]planstypes.Plan + policies map[string]planstypes.Policy + projects map[string]projectstypes.ProjectData + specs map[string]spectypes.Spec +} + +func NewTester(t *testing.T) *Tester { + servers, keepers, GoCtx := testkeeper.InitAllKeepers(t) + + ts := &Tester{ + T: t, + GoCtx: GoCtx, + Ctx: sdk.UnwrapSDKContext(GoCtx), + Servers: servers, + Keepers: keepers, + + accounts: make(map[string]Account), + plans: make(map[string]planstypes.Plan), + policies: make(map[string]planstypes.Policy), + projects: make(map[string]projectstypes.ProjectData), + specs: make(map[string]spectypes.Spec), + } + + // AdvanceBlock() and AdvanceEpoch() always use the current time for the + // first block (and ignores the time delta arg if given); So call it here + // to generate a first timestamp and avoid any subsequent call with detla + // argument call having the delta ignored. + ts.AdvanceEpoch() + + return ts +} + +func (ts *Tester) AddAccount(name string, balance int64) *Tester { + ts.accounts[name] = CreateNewAccount(ts.GoCtx, *ts.Keepers, balance) + return ts +} + +func (ts *Tester) SetupAccounts(numSub, numAdm, numDev int) *Tester { + for i := 0; i < numSub; i++ { + name := "sub" + strconv.Itoa(i+1) + ts.AddAccount(name, 20000) + } + for i := 0; i < numAdm; i++ { + name := "adm" + strconv.Itoa(i+1) + ts.AddAccount(name, 10000) + } + for i := 0; i < numDev; i++ { + name := "dev" + strconv.Itoa(i+1) + ts.AddAccount(name, 10000) + } + + return ts +} + +func (ts *Tester) Account(name string) (Account, string) { + account, ok := ts.accounts[name] + if !ok { + panic("tester: unknown account: '" + name + "'") + } + return account, account.Addr.String() +} + +func (ts *Tester) AddPlan(name string, plan planstypes.Plan) *Tester { + err := ts.Keepers.Plans.AddPlan(ts.Ctx, plan) + if err != nil { + panic("tester: falied to add plan: '" + plan.Index + "'") + } + ts.plans[name] = plan + return ts +} + +func (ts *Tester) Plan(name string) planstypes.Plan { + plan, ok := ts.plans[name] + if !ok { + panic("tester: unknown plan: '" + name + "'") + } + return plan +} + +func (ts *Tester) AddPolicy(name string, policy planstypes.Policy) *Tester { + ts.policies[name] = policy + return ts +} + +func (ts *Tester) Policy(name string) planstypes.Policy { + policy, ok := ts.policies[name] + if !ok { + panic("tester: unknown policy: '" + name + "'") + } + return policy +} + +func (ts *Tester) AddProjectData(name string, pd projectstypes.ProjectData) *Tester { + ts.projects[name] = pd + return ts +} + +func (ts *Tester) ProjectData(name string) projectstypes.ProjectData { + project, ok := ts.projects[name] + if !ok { + panic("tester: unknown project: '" + name + "'") + } + return project +} + +func (ts *Tester) AddSpec(name string, spec spectypes.Spec) *Tester { + ts.Keepers.Spec.SetSpec(ts.Ctx, spec) + ts.specs[name] = spec + return ts +} + +func (ts *Tester) Spec(name string) spectypes.Spec { + spec, ok := ts.specs[name] + if !ok { + panic("tester: unknown spec: '" + name + "'") + } + return spec +} + +// keeper helpers + +func (ts *Tester) FindPlan(index string, block uint64) (planstypes.Plan, bool) { + return ts.Keepers.Plans.FindPlan(ts.Ctx, index, block) +} + +func (ts *Tester) GetProjectForBlock(projectID string, block uint64) (projectstypes.Project, error) { + return ts.Keepers.Projects.GetProjectForBlock(ts.Ctx, projectID, block) +} + +func (ts *Tester) GetProjectForDeveloper(devkey string, optional ...uint64) (projectstypes.Project, error) { + block := ts.BlockHeight() + if len(optional) > 1 { + panic("GetProjectForDeveloper: more than one optional arg") + } + if len(optional) > 0 { + block = optional[0] + } + return ts.Keepers.Projects.GetProjectForDeveloper(ts.Ctx, devkey, block) +} + +func (ts *Tester) GetProjectDeveloperData(devkey string, block uint64) (projectstypes.ProtoDeveloperData, error) { + return ts.Keepers.Projects.GetProjectDeveloperData(ts.Ctx, devkey, block) +} + +// proposals, transactions, queries + +func (ts *Tester) TxProposalAddPlans(plans ...planstypes.Plan) error { + return testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans) +} + +func (ts *Tester) TxProposalDelPlans(indices ...string) error { + return testkeeper.SimulatePlansDelProposal(ts.Ctx, ts.Keepers.Plans, indices) +} + +func (ts *Tester) TxProposalAddSpecs(specs ...spectypes.Spec) error { + return testkeeper.SimulateSpecAddProposal(ts.Ctx, ts.Keepers.Spec, specs) +} + +// TxSubscriptionBuy: implement 'tx subscription buy' +func (ts *Tester) TxSubscriptionBuy(creator, consumer string, plan string, months int) (*subscriptiontypes.MsgBuyResponse, error) { + msg := &subscriptiontypes.MsgBuy{ + Creator: creator, + Consumer: consumer, + Index: plan, + Duration: uint64(months), + } + return ts.Servers.SubscriptionServer.Buy(ts.GoCtx, msg) +} + +// TxSubscriptionAddProject: implement 'tx subscription add-project' +func (ts *Tester) TxSubscriptionAddProject(creator string, pd projectstypes.ProjectData) error { + msg := &subscriptiontypes.MsgAddProject{ + Creator: creator, + ProjectData: pd, + } + _, err := ts.Servers.SubscriptionServer.AddProject(ts.GoCtx, msg) + return err +} + +// TxSubscriptionAddProject: implement 'tx subscription del-project' +func (ts *Tester) TxSubscriptionDelProject(creator string, projectID string) error { + msg := &subscriptiontypes.MsgDelProject{ + Creator: creator, + Name: projectID, + } + _, err := ts.Servers.SubscriptionServer.DelProject(ts.GoCtx, msg) + return err +} + +// QuerySubscriptionListProjects: implement 'q subscription list-projects' +func (ts *Tester) QuerySubscriptionListProjects(subkey string) (*subscriptiontypes.QueryListProjectsResponse, error) { + msg := &subscriptiontypes.QueryListProjectsRequest{ + Subscription: subkey, + } + return ts.Keepers.Subscription.ListProjects(ts.GoCtx, msg) +} + +// QueryProjectInfo implements 'q project info' +func (ts *Tester) QueryProjectInfo(projectID string) (*projectstypes.QueryInfoResponse, error) { + msg := &projectstypes.QueryInfoRequest{Project: projectID} + return ts.Keepers.Projects.Info(ts.GoCtx, msg) +} + +// QueryProjectDeveloper implements 'q project developer' +func (ts *Tester) QueryProjectDeveloper(devkey string) (*projectstypes.QueryDeveloperResponse, error) { + msg := &projectstypes.QueryDeveloperRequest{Developer: devkey} + return ts.Keepers.Projects.Developer(ts.GoCtx, msg) +} + +func (ts *Tester) BlockHeight() uint64 { + return uint64(ts.Ctx.BlockHeight()) +} + +func (ts *Tester) BlockTime() time.Time { + return ts.Ctx.BlockTime() +} + +// blocks and epochs + +func (ts *Tester) BlocksToSave() uint64 { + blocksToSave, err := ts.Keepers.Epochstorage.BlocksToSave(ts.Ctx, ts.BlockHeight()) + if err != nil { + panic("BlocksToSave: failed to fetch: " + err.Error()) + } + return blocksToSave +} + +func (ts *Tester) AdvanceBlocks(count uint64, delta ...time.Duration) *Tester { + for i := 0; i < int(count); i++ { + ts.GoCtx = testkeeper.AdvanceBlock(ts.GoCtx, ts.Keepers, delta...) + } + ts.Ctx = sdk.UnwrapSDKContext(ts.GoCtx) + return ts +} + +func (ts *Tester) AdvanceBlock(delta ...time.Duration) *Tester { + return ts.AdvanceBlocks(1, delta...) +} + +func (ts *Tester) AdvanceEpochs(count uint64, delta ...time.Duration) *Tester { + for i := 0; i < int(count); i++ { + ts.GoCtx = testkeeper.AdvanceEpoch(ts.GoCtx, ts.Keepers) + } + ts.Ctx = sdk.UnwrapSDKContext(ts.GoCtx) + return ts +} + +func (ts *Tester) AdvanceEpoch(delta ...time.Duration) *Tester { + return ts.AdvanceEpochs(1, delta...) +} + +func (ts *Tester) AdvanceBlockUntilStale(delta ...time.Duration) *Tester { + return ts.AdvanceBlocks(types.STALE_ENTRY_TIME) +} + +func (ts *Tester) AdvanceEpochUntilStale(delta ...time.Duration) *Tester { + block := ts.BlockHeight() + types.STALE_ENTRY_TIME + for block > ts.BlockHeight() { + ts.AdvanceEpoch() + } + return ts +} + +// AdvanceMonthFrom advanced blocks by given months, i.e. until block times +// exceeds at least that many months since the from argument (minus 5 seconds, +// so caller can control when to cross the desired time). +func (ts *Tester) AdvanceMonthsFrom(from time.Time, months int) *Tester { + for next := from; months > 0; months -= 1 { + next = subscriptionkeeper.NextMonth(next) + delta := next.Sub(ts.BlockTime()) + if months == 1 { + delta -= 5 * time.Second + } + ts.AdvanceBlock(delta) + } + return ts +} + +// AdvanceMonth advanced blocks by given months, like AdvanceMonthsFrom, +// starting from the current block's timestamp +func (ts *Tester) AdvanceMonths(months int) *Tester { + return ts.AdvanceMonthsFrom(ts.BlockTime(), months) +} diff --git a/testutil/keeper/keepers_init.go b/testutil/keeper/keepers_init.go index 0121f48ad1..f920648214 100644 --- a/testutil/keeper/keepers_init.go +++ b/testutil/keeper/keepers_init.go @@ -106,6 +106,17 @@ func SimulatePlansDelProposal(ctx sdk.Context, plansKeeper planskeeper.Keeper, p return err } +func SimulateSpecAddProposal(ctx sdk.Context, specKeeper speckeeper.Keeper, specsToPropose []spectypes.Spec) error { + proposal := spectypes.NewSpecAddProposal("mockProposal", "mockProposal specs add for testing", specsToPropose) + err := proposal.ValidateBasic() + if err != nil { + return err + } + proposalHandler := spec.NewSpecProposalsHandler(specKeeper) + err = proposalHandler(ctx, proposal) + return err +} + func InitAllKeepers(t testing.TB) (*Servers, *Keepers, context.Context) { db := tmdb.NewMemDB() stateStore := store.NewCommitMultiStore(db) diff --git a/x/plans/keeper/plan_test.go b/x/plans/keeper/plan_test.go index c0eb875631..ad24e1bccf 100644 --- a/x/plans/keeper/plan_test.go +++ b/x/plans/keeper/plan_test.go @@ -1,70 +1,59 @@ package keeper_test import ( - "context" "strconv" "strings" "testing" sdk "github.com/cosmos/cosmos-sdk/types" - commontypes "github.com/lavanet/lava/common/types" + "github.com/lavanet/lava/testutil/common" testkeeper "github.com/lavanet/lava/testutil/keeper" "github.com/lavanet/lava/testutil/nullify" epochstoragetypes "github.com/lavanet/lava/x/epochstorage/types" - "github.com/lavanet/lava/x/plans/keeper" "github.com/lavanet/lava/x/plans/types" "github.com/stretchr/testify/require" ) -type testStruct struct { - ctx context.Context - keepers *testkeeper.Keepers +type tester struct { + common.Tester } -func (ts *testStruct) advanceEpochUntilStale() { - block := sdk.UnwrapSDKContext(ts.ctx).BlockHeight() + commontypes.STALE_ENTRY_TIME - for block > sdk.UnwrapSDKContext(ts.ctx).BlockHeight() { - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - } +func newTester(t *testing.T) *tester { + return &tester{Tester: *common.NewTester(t)} } -func createNPlanEntry(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Plan { - items := make([]types.Plan, n) - for i := range items { - items[i].Index = strconv.Itoa(i) +func TestPlanEntryGet(t *testing.T) { + ts := newTester(t) - keeper.AddPlan(ctx, items[i]) - } - return items -} + plan := common.CreateMockPlan() + plan.Block = ts.BlockHeight() -func TestPlanEntryGet(t *testing.T) { - keeper, ctx := testkeeper.PlanKeeper(t) - items := createNPlanEntry(keeper, ctx, 10) - for _, item := range items { - tempPlan, found := keeper.FindPlan(ctx, item.GetIndex(), uint64(ctx.BlockHeight())) - require.True(t, found) - require.Equal(t, - nullify.Fill(&item), - nullify.Fill(&tempPlan), - ) - } + err := ts.TxProposalAddPlans(plan) + require.Nil(t, err) + + res, found := ts.FindPlan(plan.Index, ts.BlockHeight()) + require.True(t, found) + require.Equal(t, + nullify.Fill(&plan), + nullify.Fill(&res), + ) } // createTestPlans returns a slice of plans for testing -func createTestPlans(planAmount int, withSameIndex bool, startIndex int) []types.Plan { - testPlans := []types.Plan{} +func (ts *tester) createTestPlans(count int, withSameIndex bool, startIndex int) []types.Plan { + var plans []types.Plan + policy := types.Policy{ TotalCuLimit: 1000, EpochCuLimit: 100, MaxProvidersToPair: 3, } - for i := startIndex; i < startIndex+planAmount; i++ { + for i := startIndex; i < startIndex+count; i++ { planIndex := "mockplan" + strconv.Itoa(i) overuseRate := uint64(10) - dummyPlan := types.Plan{ + plan := types.Plan{ Index: planIndex, Description: "plan to test", Type: "rpc", @@ -76,80 +65,58 @@ func createTestPlans(planAmount int, withSameIndex bool, startIndex int) []types AnnualDiscountPercentage: 20, } - testPlans = append(testPlans, dummyPlan) + plans = append(plans, plan) - // if we need to create a plan with the same index, create an additional one - // with a different overuseRate and append it to testPlans (we increase the - // counter so we won't get more plans than planAmount) + // if withSameIndex is true, create an additional plan with a + // different overuseRate and append it to plans if withSameIndex { - overuseRate2 := uint64(15) - dummyPlan2 := dummyPlan - dummyPlan2.OveruseRate = overuseRate2 - testPlans = append(testPlans, dummyPlan2) + plan.OveruseRate = uint64(15) + plans = append(plans, plan) } } - return testPlans + return plans } -// Test that the process of: plan is added, an update is added, stale version -// is removed works correctly. Make sure that a stale plan with subs is not removed -func TestPlanAdditionDifferentEpoch(t *testing.T) { - // setup the testStruct - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) +// TestAddAndUpdateOtherEpoch tests the lifecycle of a plan: add, update, +// old version becomes stale and removed (unless it has subscriptions) +func TestAddAndUpdateOtherEpoch(t *testing.T) { + ts := newTester(t) + plans := ts.createTestPlans(1, true, 0) - // advance an epoch and create a plan - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - testPlans := createTestPlans(1, true, 0) - - // simulate a plan proposal of the first plans - err := testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, []types.Plan{testPlans[0]}) + // proposal with a plan + ts.AdvanceEpoch() + err := ts.TxProposalAddPlans(plans[0]) require.Nil(t, err) - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - - // simulate a plan proposal of the second plans - err = testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, []types.Plan{testPlans[1]}) + // proposal with plan update + ts.AdvanceEpoch() + err = ts.TxProposalAddPlans(plans[1]) require.Nil(t, err) - // get the plan storage and verify that there are two plans in the plan storage - plansIndices := ts.keepers.Plans.GetAllPlanIndices(sdk.UnwrapSDKContext(ts.ctx)) - require.Equal(t, 1, len(plansIndices)) + indices := ts.Keepers.Plans.GetAllPlanIndices(ts.Ctx) + require.Equal(t, 1, len(indices)) - // verify that testPlans[1] is the latest plan version (its index should be first in storageIndexList) - planLatestVersion, found := ts.keepers.Plans.FindPlan( - sdk.UnwrapSDKContext(ts.ctx), - plansIndices[0], - uint64(sdk.UnwrapSDKContext(ts.ctx).BlockHeight()), - ) + // updated plan should be the latest + plan, found := ts.FindPlan(indices[0], ts.BlockHeight()) require.True(t, found) - require.Equal(t, testPlans[1].OveruseRate, planLatestVersion.GetOveruseRate()) + require.Equal(t, plans[1].OveruseRate, plan.GetOveruseRate()) } -// Test that if two plans with the same index are added in the same epoch then we keep only the latest one +// TestAddAndUpdateSameBlock addding the same plan twice in the same block; +// Only the latter should prevail. func TestUpdatePlanInSameEpoch(t *testing.T) { - // setup the testStruct - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) - - // advance an epoch and create a plan - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - testPlans := createTestPlans(1, true, 0) + ts := newTester(t) + plans := ts.createTestPlans(1, true, 0) - // simulate a proposal of the plans - err := testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, testPlans) + // proposal with a plan + ts.AdvanceEpoch() + err := testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans) require.Nil(t, err) - // verify the latest one is kept (testPlans[1] that is the last element in the testPlans array) - planLatestVersion, found := ts.keepers.Plans.FindPlan( - sdk.UnwrapSDKContext(ts.ctx), - testPlans[0].GetIndex(), - uint64(sdk.UnwrapSDKContext(ts.ctx).BlockHeight()), - ) + plan, found := ts.FindPlan(plans[0].Index, ts.BlockHeight()) require.True(t, found) - require.Equal(t, testPlans[1].GetOveruseRate(), planLatestVersion.GetOveruseRate()) + require.Equal(t, plans[1].GetOveruseRate(), plan.GetOveruseRate()) } const ( @@ -162,16 +129,11 @@ const ( TYPE_FIELD ) -// Test that the plan verification before adding it to the plan storage is working correctly -func TestInvalidPlanAddition(t *testing.T) { - // setup the testStruct - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) - - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) +// TestAddInvalid Plan tests plan verification before addition +func TestAddInvalidPlan(t *testing.T) { + ts := newTester(t) + ts.AdvanceEpoch() - // test invalid plan addition tests := []struct { name string fieldIndex int @@ -187,244 +149,186 @@ func TestInvalidPlanAddition(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - planToTest := createTestPlans(1, false, 0) + plans := ts.createTestPlans(1, false, 0) - // each test, change one field to an invalid value switch tt.fieldIndex { case PRICE_FIELD: - planToTest[0].Price = sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.ZeroInt()) + plans[0].Price = sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.ZeroInt()) case OVERUSE_FIELDS: - planToTest[0].AllowOveruse = true - planToTest[0].OveruseRate = 0 + plans[0].AllowOveruse = true + plans[0].OveruseRate = 0 case CU_FIELD_TOTAL: - planToTest[0].PlanPolicy.TotalCuLimit = 0 + plans[0].PlanPolicy.TotalCuLimit = 0 case CU_FIELD_EPOCH: - planToTest[0].PlanPolicy.EpochCuLimit = 0 + plans[0].PlanPolicy.EpochCuLimit = 0 case SERVICERS_FIELD: - planToTest[0].PlanPolicy.MaxProvidersToPair = 1 + plans[0].PlanPolicy.MaxProvidersToPair = 1 case DESCRIPTION_FIELD: - planToTest[0].Description = strings.Repeat("a", types.MAX_LEN_PLAN_DESCRIPTION+1) + plans[0].Description = strings.Repeat("a", types.MAX_LEN_PLAN_DESCRIPTION+1) case TYPE_FIELD: - planToTest[0].Type = strings.Repeat("a", types.MAX_LEN_PLAN_TYPE+1) + plans[0].Type = strings.Repeat("a", types.MAX_LEN_PLAN_TYPE+1) } - // simulate a plan proposal - should fail - err := testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, planToTest) + err := testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans) require.NotNil(t, err) }) } } const ( - TEST_PACKAGES_WITH_SAME_ID_AMOUNT = 3 - TEST_PACKAGES_WITH_DIFFERENT_ID_AMOUNT = 5 + TEST_PLANS_DIFF_ID = 3 + TEST_PLANS_SAME_ID = 5 ) -// Test multiple plan addition and removals -func TestMultiplePlansAdditions(t *testing.T) { - // setup the testStruct - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) +// TestAddRemove tests multiple plan addition (some in same block) +func TestAddRemove(t *testing.T) { + ts := newTester(t) + ts.AdvanceEpoch() - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) + plans1 := ts.createTestPlans(TEST_PLANS_DIFF_ID, false, 0) + plans2 := ts.createTestPlans(TEST_PLANS_SAME_ID, true, len(plans1)) - // create plans (both plans which have the same ID and different ID) - testPlansWithDifferentIDs := createTestPlans(TEST_PACKAGES_WITH_DIFFERENT_ID_AMOUNT, false, 0) - testPlansWithSameIDs := createTestPlans(TEST_PACKAGES_WITH_SAME_ID_AMOUNT, true, TEST_PACKAGES_WITH_DIFFERENT_ID_AMOUNT) - - // simulate a plan proposal of testPlansWithDifferentIDs - err := testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, testPlansWithDifferentIDs) + err := testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans1) require.Nil(t, err) - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) + ts.AdvanceEpoch() - // simulate a plan proposal of testPlansWithSameIDs - err = testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, testPlansWithSameIDs) + err = testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans2) require.Nil(t, err) - // check there are enough plans in the storage (should not be - // TEST_PACKAGES_WITH_DIFFERENT_ID_AMOUNT+2*TEST_PACKAGES_WITH_SAME_ID_AMOUNT) since we - // propose the duplicate plans in a single block so only the latest are kept - plansIndices := ts.keepers.Plans.GetAllPlanIndices(sdk.UnwrapSDKContext(ts.ctx)) - require.Equal(t, TEST_PACKAGES_WITH_DIFFERENT_ID_AMOUNT+TEST_PACKAGES_WITH_SAME_ID_AMOUNT, len(plansIndices)) + // plans with same ID added in same block: only 1 (of each pair) prevailed + indices := ts.Keepers.Plans.GetAllPlanIndices(ts.Ctx) + require.Equal(t, TEST_PLANS_SAME_ID+TEST_PLANS_DIFF_ID, len(indices)) } -// Test that proposes two valid plans and an invalid one and checks that none have passed -func TestProposeBadAndGoodPlans(t *testing.T) { - // setup the testStruct - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) - - // advance an epoch and create a plan - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - testPlans := createTestPlans(3, false, 0) +// TestAddBadAndGoodPlans tests a mix of good and bad plans - none passes. +func TestAddBadAndGoodPlans(t *testing.T) { + ts := newTester(t) + ts.AdvanceEpoch() + // create plans and spoil one of them + plans := ts.createTestPlans(3, false, 0) // make one of the plans invalid - testPlans[2].PlanPolicy.TotalCuLimit = 0 + plans[2].PlanPolicy.TotalCuLimit = 0 - // simulate a plan proposal of testPlans (note, inside SimulatePlansAddProposal - // it fails the test when a plan is invalid. So we avoid checking the error to - // make sure later there are no plans in the storage) - _ = testkeeper.SimulatePlansAddProposal(sdk.UnwrapSDKContext(ts.ctx), ts.keepers.Plans, testPlans) + err := testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans) + require.NotNil(t, err) - // check there are no plans in the storage - plansIndices := ts.keepers.Plans.GetAllPlanIndices(sdk.UnwrapSDKContext(ts.ctx)) - require.Equal(t, 0, len(plansIndices)) + indices := ts.Keepers.Plans.GetAllPlanIndices(ts.Ctx) + require.Equal(t, 0, len(indices)) } -// Test that creates 3 versions of a plan and checks the removal of the first two +// TestPlansStaleRemoval tests that removal of stale versions func TestPlansStaleRemoval(t *testing.T) { - // setup the testStruct - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) - - // advance an epoch and create a plan - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - testPlans := createTestPlans(1, true, 0) - - // save the first plan in the KVstore - err := ts.keepers.Plans.AddPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[0]) - firstPlanBlockHeight := uint64(sdk.UnwrapSDKContext(ts.ctx).BlockHeight()) - testPlans[0].Block = firstPlanBlockHeight - require.Nil(t, err) + ts := newTester(t) + ts.AdvanceEpoch() + + plans := ts.createTestPlans(1, true, 0) - // increase the first plans' refCount - firstPlanFromStore, found := ts.keepers.Plans.GetPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[0].GetIndex()) + // add 1st plan and keep a reference + err := ts.TxProposalAddPlans(plans[0]) + require.Nil(t, err) + plans[0].Block = ts.BlockHeight() + res, found := ts.Keepers.Plans.GetPlan(ts.Ctx, plans[0].Index) require.True(t, found) - require.Equal(t, testPlans[0], firstPlanFromStore) + require.Equal(t, plans[0], res) - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) + ts.AdvanceEpoch() - // save the second plan in the KVstore - err = ts.keepers.Plans.AddPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[1]) - secondPlanBlockHeight := uint64(sdk.UnwrapSDKContext(ts.ctx).BlockHeight()) - testPlans[1].Block = secondPlanBlockHeight + // add 2nd plan and keep a reference + err = ts.TxProposalAddPlans(plans[1]) require.Nil(t, err) - - // increase the second plans' refCount - secondPlanFromStore, found := ts.keepers.Plans.GetPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[1].GetIndex()) + plans[1].Block = ts.BlockHeight() + res, found = ts.Keepers.Plans.GetPlan(ts.Ctx, plans[1].Index) require.True(t, found) - require.Equal(t, testPlans[1], secondPlanFromStore) + require.Equal(t, plans[1], res) - // advance enough epochs so the first two packages will be stale - ts.advanceEpochUntilStale() + ts.AdvanceEpoch() - // create an additional plan and add it to the store to trigger plan deletion code - newPlan := testPlans[1] - newPlan.OveruseRate += 20 - newPlanBlockHeight := uint64(sdk.UnwrapSDKContext(ts.ctx).BlockHeight()) - newPlan.Block = newPlanBlockHeight - err = ts.keepers.Plans.AddPlan(sdk.UnwrapSDKContext(ts.ctx), newPlan) + // add 3rd plan (so the prevous two would become stale) + plan := plans[1] + plan.OveruseRate += 20 + err = ts.TxProposalAddPlans(plan) require.Nil(t, err) + plan.Block = ts.BlockHeight() - // check that the plans were not(!) deleted since their refCount is > 0 - firstPlanFromStore, found = ts.keepers.Plans.FindPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[0].GetIndex(), firstPlanBlockHeight) + // advance enough epochs to make removed entries become stale + ts.AdvanceEpochUntilStale() + + // the first two plans should not be deleted (refcount > 0) + res, found = ts.FindPlan(plans[0].Index, plans[0].Block) require.True(t, found) - require.Equal(t, testPlans[0], firstPlanFromStore) - secondPlanFromStore, found = ts.keepers.Plans.FindPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[1].GetIndex(), secondPlanBlockHeight) + require.Equal(t, plans[0], res) + res, found = ts.FindPlan(plans[1].Index, plans[1].Block) require.True(t, found) - require.Equal(t, testPlans[1], secondPlanFromStore) + require.Equal(t, plans[1], res) - // decrease the old plans' refCount - ts.keepers.Plans.PutPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[0].GetIndex(), firstPlanBlockHeight) - ts.keepers.Plans.PutPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[1].GetIndex(), secondPlanBlockHeight) + // decremenet the old plans' refCount + ts.Keepers.Plans.PutPlan(ts.Ctx, plans[0].Index, plans[0].Block) + ts.Keepers.Plans.PutPlan(ts.Ctx, plans[1].Index, plans[1].Block) - // advance an epoch and create an newer plan to add (and trigger the plan deletion) - ts.advanceEpochUntilStale() - - newerPlanBlockHeight := uint64(sdk.UnwrapSDKContext(ts.ctx).BlockHeight()) - newerPlan := newPlan - newerPlan.OveruseRate += 20 - newerPlan.Block = newerPlanBlockHeight - err = ts.keepers.Plans.AddPlan(sdk.UnwrapSDKContext(ts.ctx), newerPlan) - require.Nil(t, err) + ts.AdvanceEpochUntilStale() // check that the old plans were removed - _, found = ts.keepers.Plans.FindPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[0].GetIndex(), firstPlanBlockHeight) + _, found = ts.FindPlan(plans[0].Index, plans[0].Block) require.False(t, found) - _, found = ts.keepers.Plans.FindPlan(sdk.UnwrapSDKContext(ts.ctx), testPlans[1].GetIndex(), secondPlanBlockHeight) + _, found = ts.FindPlan(plans[1].Index, plans[1].Block) require.False(t, found) - // get the new and newer plans from the store - newPlanFromStore, found := ts.keepers.Plans.FindPlan(sdk.UnwrapSDKContext(ts.ctx), newPlan.GetIndex(), newPlanBlockHeight) - require.True(t, found) - require.Equal(t, newPlan, newPlanFromStore) - newerPlanFromStore, found := ts.keepers.Plans.FindPlan(sdk.UnwrapSDKContext(ts.ctx), newerPlan.GetIndex(), newerPlanBlockHeight) + // verify that the newest is still there + res, found = ts.FindPlan(plan.Index, plan.Block) require.True(t, found) - require.Equal(t, newerPlan, newerPlanFromStore) + require.Equal(t, plan, res) } -// Test that creates a plan and then deletes it -func TestPlansDelete(t *testing.T) { - ts := &testStruct{} - _, ts.keepers, ts.ctx = testkeeper.InitAllKeepers(t) +// TestAddAndDelete tests creation and deleteion of plans +func TestAddAndDelete(t *testing.T) { + ts := newTester(t) + ts.AdvanceEpoch() - // advance an epoch and create a plan - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - ctx := sdk.UnwrapSDKContext(ts.ctx) + plans := ts.createTestPlans(1, true, 0) + index := plans[0].Index - testPlans := createTestPlans(1, true, 0) - index := testPlans[0].Index - - err := testkeeper.SimulatePlansAddProposal(ctx, ts.keepers.Plans, []types.Plan{testPlans[0]}) + err := testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, plans[0:1]) require.Nil(t, err) + indices := ts.Keepers.Plans.GetAllPlanIndices(ts.Ctx) + require.Equal(t, 1, len(indices)) - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - ctx = sdk.UnwrapSDKContext(ts.ctx) - block1 := uint64(ctx.BlockHeight()) - - // verify there is one plan visible - plansIndices := ts.keepers.Plans.GetAllPlanIndices(ctx) - require.Equal(t, 1, len(plansIndices)) + ts.AdvanceEpoch() + block1 := ts.BlockHeight() - // advance an epoch - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - ctx = sdk.UnwrapSDKContext(ts.ctx) - - // now delete the plan - err = testkeeper.SimulatePlansDelProposal(ctx, ts.keepers.Plans, []string{index}) + err = testkeeper.SimulatePlansDelProposal(ts.Ctx, ts.Keepers.Plans, []string{index}) require.Nil(t, err) - block2 := uint64(ctx.BlockHeight()) - // delete only takes effect next epoch, so for now still visible - _, found := ts.keepers.Plans.FindPlan(ctx, index, block2+10) + block2 := ts.BlockHeight() + _, found := ts.FindPlan(index, block2) require.True(t, found) // advance an epoch (so the delete will take effect) - ts.ctx = testkeeper.AdvanceEpoch(ts.ctx, ts.keepers) - ctx = sdk.UnwrapSDKContext(ts.ctx) + ts.AdvanceEpoch() // verify there are zero plans visible - plansIndices = ts.keepers.Plans.GetAllPlanIndices(ctx) - require.Equal(t, 0, len(plansIndices)) - _, found = ts.keepers.Plans.GetPlan(ctx, index) + indices = ts.Keepers.Plans.GetAllPlanIndices(ts.Ctx) + require.Equal(t, 0, len(indices)) + _, found = ts.Keepers.Plans.GetPlan(ts.Ctx, index) require.False(t, found) // but the plan is not stale yet, so can be found (until block2 + ~epoch) - _, found = ts.keepers.Plans.FindPlan(ctx, index, block1) + _, found = ts.FindPlan(index, block1) require.True(t, found) - - _, found = ts.keepers.Plans.FindPlan(ctx, index, block2+30) + _, found = ts.FindPlan(index, block2+30) require.False(t, found) // advance epoch until the plan becomes stale - ts.advanceEpochUntilStale() - ctx = sdk.UnwrapSDKContext(ts.ctx) - - // because testutils.AdvanceBlock() and testutils.AdvanceEpoch() are sloppy - ts.keepers.Plans.BeginBlock(ctx) + ts.AdvanceEpochUntilStale() - _, found = ts.keepers.Plans.FindPlan(ctx, index, block1) + _, found = ts.FindPlan(index, block1) require.False(t, found) - _, found = ts.keepers.Plans.FindPlan(ctx, index, block2) + _, found = ts.FindPlan(index, block2) require.False(t, found) // fail attempt to delete the plan again - err = testkeeper.SimulatePlansDelProposal(ctx, ts.keepers.Plans, []string{index}) + err = testkeeper.SimulatePlansDelProposal(ts.Ctx, ts.Keepers.Plans, []string{index}) require.NotNil(t, err) } diff --git a/x/projects/keeper/project_test.go b/x/projects/keeper/project_test.go index 990b1573ee..9022488dc3 100644 --- a/x/projects/keeper/project_test.go +++ b/x/projects/keeper/project_test.go @@ -1,93 +1,58 @@ package keeper_test import ( - "context" - "math" - "strconv" "strings" "testing" - sdk "github.com/cosmos/cosmos-sdk/types" commontypes "github.com/lavanet/lava/common/types" "github.com/lavanet/lava/testutil/common" testkeeper "github.com/lavanet/lava/testutil/keeper" planstypes "github.com/lavanet/lava/x/plans/types" "github.com/lavanet/lava/x/projects/types" - subscriptiontypes "github.com/lavanet/lava/x/subscription/types" "github.com/stretchr/testify/require" ) const projectName = "mockname" -type testStruct struct { - t *testing.T - servers *testkeeper.Servers - keepers *testkeeper.Keepers - _ctx context.Context - ctx sdk.Context - accounts map[string]string - projects map[string]types.ProjectData +type tester struct { + common.Tester } -func newTestStruct(t *testing.T) *testStruct { - servers, keepers, _ctx := testkeeper.InitAllKeepers(t) - ts := &testStruct{ - t: t, - servers: servers, - keepers: keepers, - _ctx: _ctx, - ctx: sdk.UnwrapSDKContext(_ctx), - } - ts.AdvanceEpoch(1) +func newTester(t *testing.T) *tester { + ts := &tester{Tester: *common.NewTester(t)} + ts.AddPlan("mock", common.CreateMockPlan()) + ts.AddPolicy("mock", common.CreateMockPolicy()) return ts } -func (ts *testStruct) prepareData(numSub, numAdmin, numDevel int) { - ts.accounts = make(map[string]string) - for i := 0; i < numSub; i++ { - k := "sub" + strconv.Itoa(i+1) - v := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - ts.accounts[k] = v - } - for i := 0; i < numAdmin; i++ { - k := "adm" + strconv.Itoa(i+1) - v := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - ts.accounts[k] = v - } - for i := 0; i < numDevel; i++ { - k := "dev" + strconv.Itoa(i+1) - v := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - ts.accounts[k] = v - } - - ts.projects = make(map[string]types.ProjectData) +func (ts *tester) setupProjectData() *tester { + ts.AddAccount("pd1_adm", 10000) + ts.AddAccount("pd2_both", 10000) + ts.AddAccount("pd3_adm", 10000) + ts.AddAccount("pd3_dev", 10000) - ts.accounts["pd1_adm"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - ts.accounts["pd2_both"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - ts.accounts["pd3_adm"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - ts.accounts["pd3_dev"] = common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + _, pd1adm := ts.Account("pd1_adm") + _, pd2both := ts.Account("pd2_both") + _, pd3adm := ts.Account("pd3_adm") + _, pd3dev := ts.Account("pd3_dev") // admin key keys_1_admin := []types.ProjectKey{ - types.ProjectAdminKey(ts.accounts["pd1_adm"]), + types.ProjectAdminKey(pd1adm), } - // developer key + // "both" key keys_1_admin_dev := []types.ProjectKey{ - types.NewProjectKey(ts.accounts["pd2_both"]). + types.NewProjectKey(pd2both). AddType(types.ProjectKey_ADMIN). AddType(types.ProjectKey_DEVELOPER), } - // both (admin+developer) key keys_2_admin_and_dev := []types.ProjectKey{ - types.ProjectAdminKey(ts.accounts["pd3_adm"]), - types.ProjectDeveloperKey(ts.accounts["pd3_dev"]), + types.ProjectAdminKey(pd3adm), + types.ProjectDeveloperKey(pd3dev), } - policy1 := &planstypes.Policy{ - GeolocationProfile: math.MaxUint64, - MaxProvidersToPair: 2, - } + policy := ts.Policy("mock") templates := []struct { code string @@ -97,310 +62,301 @@ func (ts *testStruct) prepareData(numSub, numAdmin, numDevel int) { policy *planstypes.Policy }{ // project with admin key, enabled, has policy - {"pd1", "mock_project_1", true, keys_1_admin, policy1}, + {"pd1", "mock_project_1", true, keys_1_admin, &policy}, // project with "both" key, disabled, with policy - {"pd2", "mock_project_2", false, keys_1_admin_dev, policy1}, + {"pd2", "mock_project_2", false, keys_1_admin_dev, &policy}, // project with 2 keys (one admin, one developer) disabled, no policy {"pd3", "mock_project_3", false, keys_2_admin_and_dev, nil}, } for _, tt := range templates { - ts.projects[tt.code] = types.ProjectData{ + pd := types.ProjectData{ Name: tt.name, Enabled: tt.enabled, ProjectKeys: tt.keys, Policy: tt.policy, } + ts.AddProjectData(tt.code, pd) } -} - -func (ts *testStruct) BlockHeight() uint64 { - return uint64(ts.ctx.BlockHeight()) -} - -func (ts *testStruct) AdvanceBlock(count int) { - for i := 0; i < count; i += 1 { - ts._ctx = testkeeper.AdvanceBlock(ts._ctx, ts.keepers) - } - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) -} -func (ts *testStruct) AdvanceEpoch(count int) { - for i := 0; i < count; i += 1 { - ts._ctx = testkeeper.AdvanceEpoch(ts._ctx, ts.keepers) - } - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) + return ts } -func (ts *testStruct) isKeyInProject(index, key string, kind types.ProjectKey_Type) bool { - resp, err := ts.keepers.Projects.Info(ts._ctx, &types.QueryInfoRequest{Project: index}) - require.Nil(ts.t, err, "project: "+index+", key: "+key) +func (ts *tester) isKeyInProject(index, key string, kind types.ProjectKey_Type) bool { + resp, err := ts.Keepers.Projects.Info(ts.GoCtx, &types.QueryInfoRequest{Project: index}) + require.Nil(ts.T, err, "project: "+index+", key: "+key) pk := resp.Project.GetKey(key) return pk.IsType(kind) } -func (ts *testStruct) addProjectKeys(index, creator string, projectKeys ...types.ProjectKey) error { +func (ts *tester) addProjectKeys(index, creator string, projectKeys ...types.ProjectKey) error { msg := types.MsgAddKeys{ Creator: creator, Project: index, ProjectKeys: projectKeys, } - _, err := ts.servers.ProjectServer.AddKeys(ts._ctx, &msg) + _, err := ts.Servers.ProjectServer.AddKeys(ts.GoCtx, &msg) return err } -func (ts *testStruct) delProjectKeys(index, creator string, projectKeys ...types.ProjectKey) error { +func (ts *tester) delProjectKeys(index, creator string, projectKeys ...types.ProjectKey) error { msg := types.MsgDelKeys{ Creator: creator, Project: index, ProjectKeys: projectKeys, } - _, err := ts.servers.ProjectServer.DelKeys(ts._ctx, &msg) + _, err := ts.Servers.ProjectServer.DelKeys(ts.GoCtx, &msg) return err } +func (ts *tester) txSetProjectPolicy(projectID string, subkey string, policy planstypes.Policy) (*types.MsgSetPolicyResponse, error) { + msg := types.MsgSetPolicy{ + Creator: subkey, + Policy: policy, + Project: projectID, + } + return ts.Servers.ProjectServer.SetPolicy(ts.GoCtx, &msg) +} + +func (ts *tester) txSetSubscriptionPolicy(projectID string, subkey string, policy planstypes.Policy) (*types.MsgSetSubscriptionPolicyResponse, error) { + msg := types.MsgSetSubscriptionPolicy{ + Creator: subkey, + Policy: policy, + Projects: []string{projectID}, + } + return ts.Servers.ProjectServer.SetSubscriptionPolicy(ts.GoCtx, &msg) +} + func TestCreateDefaultProject(t *testing.T) { - ts := newTestStruct(t) + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - subAddr := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - plan := common.CreateMockPlan() + plan := ts.Plan("mock") + _, sub1Addr := ts.Account("sub1") - err := ts.keepers.Projects.CreateAdminProject(ts.ctx, subAddr, plan) + err := ts.Keepers.Projects.CreateAdminProject(ts.Ctx, sub1Addr, plan) require.Nil(t, err) + ts.AdvanceBlock() + // subscription key is a developer in the default project - response1, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: subAddr}) + res1, err := ts.QueryProjectDeveloper(sub1Addr) require.Nil(t, err) - ts.AdvanceEpoch(1) - - response2, err := ts.keepers.Projects.Info(ts._ctx, &types.QueryInfoRequest{Project: response1.Project.Index}) + res2, err := ts.QueryProjectInfo(res1.Project.Index) require.Nil(t, err) - require.Equal(t, response2.Project, response1.Project) + require.Equal(t, res2.Project, res1.Project) } func TestCreateProject(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(1, 0, 0) // 1 sub, 0 adm, 0 dev + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev + ts.setupProjectData() - projectData := ts.projects["pd2"] - plan := common.CreateMockPlan() + plan := ts.Plan("mock") + projectData := ts.ProjectData("pd2") - subAddr := ts.accounts["sub1"] - admAddr := ts.accounts["pd2_both"] + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("pd2_both") - err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() - // test invalid project name - defaultProjectName := types.ADMIN_PROJECT_NAME - longProjectName := strings.Repeat(defaultProjectName, types.MAX_PROJECT_NAME_LEN+1) - invalidProjectName := "projectName," + // test bad project names - testProjectData := projectData - testProjectData.ProjectKeys = []types.ProjectKey{} + projectNameDefault := types.ADMIN_PROJECT_NAME + projectNameLong := strings.Repeat(projectNameDefault, types.MAX_PROJECT_NAME_LEN+1) + projectNameInvalid := "projectName," nameTests := []struct { name string projectName string }{ {"bad projectName (duplicate)", projectData.Name}, - {"bad projectName (too long)", longProjectName}, - {"bad projectName (contains comma)", invalidProjectName}, + {"bad projectName (too long)", projectNameLong}, + {"bad projectName (contains comma)", projectNameInvalid}, {"bad projectName (empty)", ""}, } + pd1 := projectData + pd1.ProjectKeys = []types.ProjectKey{} + for _, tt := range nameTests { t.Run(tt.name, func(t *testing.T) { - testProjectData.Name = tt.projectName - err = ts.keepers.Projects.CreateProject(ts.ctx, subAddr, testProjectData, plan) - require.NotNil(t, err) + pd1.Name = tt.projectName + err = ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, pd1, plan) + require.NotNil(t, err, tt.name) }) } - // continue testing traits that are not related to the project's name - // try creating a project with invalid project keys - invalidKeysProjectData := projectData - invalidKeysProjectData.Name = "nonDuplicateProjectName" - invalidKeysProjectData.ProjectKeys = []types.ProjectKey{ - types.ProjectDeveloperKey(subAddr), - types.ProjectAdminKey(subAddr).AddType(0x4), + // test bad project keys + + pd2 := projectData + pd2.Name = "nonDuplicateProjectName" + pd2.ProjectKeys = []types.ProjectKey{ + types.ProjectDeveloperKey(sub1Addr), + types.ProjectAdminKey(sub1Addr).AddType(0x4), } - // should fail because there's an invalid key - _, err = ts.servers.SubscriptionServer.AddProject(ts._ctx, &subscriptiontypes.MsgAddProject{ - Creator: subAddr, - ProjectData: invalidKeysProjectData, - }) + err = ts.TxSubscriptionAddProject(sub1Addr, pd2) require.NotNil(t, err) - // subscription key is not a developer, so get project by developer should fail (if it succeeds, - // then the valid project key from invalidKeysProjectData was registered, which is undesired!) - _, err = ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: subAddr}) + // subscription key is not a developer + _, err = ts.QueryProjectDeveloper(sub1Addr) require.NotNil(t, err) - response1, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: admAddr}) + res1, err := ts.QueryProjectDeveloper(adm1Addr) require.Nil(t, err) - - response2, err := ts.keepers.Projects.Info(ts._ctx, &types.QueryInfoRequest{Project: response1.Project.Index}) + res2, err := ts.QueryProjectInfo(res1.Project.Index) require.Nil(t, err) - require.Equal(t, response2.Project, response1.Project) + require.Equal(t, res1.Project, res2.Project) - _, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, response1.Project.Index, ts.BlockHeight()) + _, err = ts.GetProjectForBlock(res1.Project.Index, ts.BlockHeight()) require.Nil(t, err) // there should be one project key - require.Equal(t, 1, len(response2.Project.ProjectKeys)) + require.Equal(t, 1, len(res2.Project.ProjectKeys)) // the project key is the admin key - require.Equal(t, response2.Project.ProjectKeys[0].Key, admAddr) + require.Equal(t, res2.Project.ProjectKeys[0].Key, adm1Addr) // the admin is both an admin and a developer - require.True(t, response2.Project.ProjectKeys[0].IsType(types.ProjectKey_ADMIN)) - require.True(t, response2.Project.ProjectKeys[0].IsType(types.ProjectKey_DEVELOPER)) + require.True(t, res2.Project.ProjectKeys[0].IsType(types.ProjectKey_ADMIN)) + require.True(t, res2.Project.ProjectKeys[0].IsType(types.ProjectKey_DEVELOPER)) } func TestProjectsServerAPI(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(2, 1, 5) // 2 sub, 1 adm, 5 dev - - sub1Acc := common.CreateNewAccount(ts._ctx, *ts.keepers, 20000) - sub1Addr := sub1Acc.Addr.String() + ts := newTester(t) + ts.SetupAccounts(1, 0, 2) // 1 sub, 0 adm, 2 dev - dev1Addr := ts.accounts["dev1"] - dev2Addr := ts.accounts["dev2"] + _, sub1Addr := ts.Account("sub1") + _, dev1Addr := ts.Account("dev1") + _, dev2Addr := ts.Account("dev2") - plan := common.CreateMockPlan() - ts.keepers.Plans.AddPlan(ts.ctx, plan) + plan := ts.Plan("mock") + err := ts.TxProposalAddPlans(plan) + require.Nil(t, err) - common.BuySubscription(t, ts._ctx, *ts.keepers, *ts.servers, sub1Acc, plan.Index) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) + require.Nil(t, err) projectData := types.ProjectData{ - Name: "mockname2", + Name: "mockname1", Enabled: true, ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}, Policy: &plan.PlanPolicy, } - projectID := types.ProjectIndex(sub1Addr, projectData.Name) - - msgAddProject := &subscriptiontypes.MsgAddProject{ - Creator: sub1Addr, - ProjectData: projectData, - } - _, err := ts.servers.SubscriptionServer.AddProject(ts._ctx, msgAddProject) + err = ts.TxSubscriptionAddProject(sub1Addr, projectData) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() - msgAddKeys := types.MsgAddKeys{ - Creator: sub1Addr, - Project: projectID, - ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(dev2Addr)}, - } - _, err = ts.servers.ProjectServer.AddKeys(ts._ctx, &msgAddKeys) + projectID := types.ProjectIndex(sub1Addr, projectData.Name) + err = ts.addProjectKeys(projectID, sub1Addr, types.ProjectDeveloperKey(dev2Addr)) require.Nil(t, err) - ts.AdvanceBlock(1) + ts.AdvanceBlock() require.True(t, ts.isKeyInProject(projectID, dev1Addr, types.ProjectKey_DEVELOPER)) require.True(t, ts.isKeyInProject(projectID, dev2Addr, types.ProjectKey_DEVELOPER)) - msgQueryDev := &types.QueryDeveloperRequest{Developer: sub1Addr} - res, err := ts.keepers.Projects.Developer(ts._ctx, msgQueryDev) + res, err := ts.QueryProjectDeveloper(sub1Addr) require.Nil(t, err) require.Equal(t, 1, len(res.Project.ProjectKeys)) } func TestDeleteProject(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(1, 0, 0) // 1 sub, 0 adm, 0 dev + ts := newTester(t) + ts.SetupAccounts(1, 0, 2) // 1 sub, 0 adm, 2 dev + ts.setupProjectData() - projectData := ts.projects["pd2"] - plan := common.CreateMockPlan() + plan := ts.Plan("mock") + projectData := ts.ProjectData("pd2") - subAddr := ts.accounts["sub1"] - projectID := types.ProjectIndex(subAddr, projectData.Name) + _, sub1Addr := ts.Account("sub1") + projectID := types.ProjectIndex(sub1Addr, projectData.Name) - err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() - _, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectID, ts.BlockHeight()) + _, err = ts.GetProjectForBlock(projectID, ts.BlockHeight()) require.Nil(t, err) - err = ts.keepers.Projects.DeleteProject(ts.ctx, subAddr, "nonsense") + err = ts.Keepers.Projects.DeleteProject(ts.Ctx, sub1Addr, "nonsense") require.NotNil(t, err) - err = ts.keepers.Projects.DeleteProject(ts.ctx, subAddr, projectID) + err = ts.Keepers.Projects.DeleteProject(ts.Ctx, sub1Addr, projectID) require.Nil(t, err) - _, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectID, ts.BlockHeight()) + _, err = ts.GetProjectForBlock(projectID, ts.BlockHeight()) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() - _, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectID, ts.BlockHeight()) + _, err = ts.GetProjectForBlock(projectID, ts.BlockHeight()) require.NotNil(t, err) } func TestAddDelKeys(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(1, 0, 2) // 1 sub, 0 adm, 2 dev + ts := newTester(t) + ts.SetupAccounts(1, 0, 2) // 1 sub, 0 adm, 2 dev + ts.setupProjectData() - projectData := ts.projects["pd3"] - plan := common.CreateMockPlan() + projectData := ts.ProjectData("pd3") + plan := ts.Plan("mock") - subAddr := ts.accounts["sub1"] - admAddr := ts.accounts["pd3_adm"] - dev1Addr := ts.accounts["pd3_dev"] - dev2Addr := ts.accounts["dev1"] - dev3Addr := ts.accounts["dev2"] + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("pd3_adm") + _, dev1Addr := ts.Account("pd3_dev") + _, dev2Addr := ts.Account("dev1") + _, dev3Addr := ts.Account("dev2") - err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() - projectRes, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: dev1Addr}) + res, err := ts.QueryProjectDeveloper(dev1Addr) require.Nil(t, err) - project := projectRes.Project - pk := types.ProjectAdminKey(dev1Addr) + project := res.Project - // try adding myself as admin, should fail + // add myself as admin (should fail) + pk := types.ProjectAdminKey(dev1Addr) err = ts.addProjectKeys(project.Index, dev1Addr, pk) require.NotNil(t, err) - // admin key adding an invalid key + // admin key adds an invalid key (should fail) pk = types.NewProjectKey(dev2Addr).AddType(0x4) - err = ts.addProjectKeys(project.Index, admAddr, pk) + err = ts.addProjectKeys(project.Index, adm1Addr, pk) require.NotNil(t, err) - // admin key adding a developer + // admin key adds a developer pk = types.ProjectDeveloperKey(dev2Addr) - err = ts.addProjectKeys(project.Index, admAddr, pk) + err = ts.addProjectKeys(project.Index, adm1Addr, pk) require.Nil(t, err) - // developer tries to add the second developer as admin + // developer tries to add an admin (should fail) pk = types.ProjectAdminKey(dev2Addr) err = ts.addProjectKeys(project.Index, dev1Addr, pk) require.NotNil(t, err) // admin adding admin pk = types.ProjectAdminKey(dev1Addr) - err = ts.addProjectKeys(project.Index, admAddr, pk) + err = ts.addProjectKeys(project.Index, adm1Addr, pk) require.Nil(t, err) require.True(t, ts.isKeyInProject(project.Index, dev1Addr, types.ProjectKey_ADMIN)) require.True(t, ts.isKeyInProject(project.Index, dev2Addr, types.ProjectKey_DEVELOPER)) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() // new admin adding another developer pk = types.ProjectDeveloperKey(dev3Addr) @@ -410,73 +366,73 @@ func TestAddDelKeys(t *testing.T) { require.True(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) // fetch project with new developer - _, err = ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: dev3Addr}) + _, err = ts.QueryProjectDeveloper(dev3Addr) require.Nil(t, err) - // developer delete admin - pk = types.ProjectAdminKey(admAddr) + // developer delete admin (should fail) + pk = types.ProjectAdminKey(adm1Addr) err = ts.delProjectKeys(project.Index, dev2Addr, pk) require.NotNil(t, err) - // developer delete developer + // developer delete developer (should fail) pk = types.ProjectDeveloperKey(dev3Addr) err = ts.delProjectKeys(project.Index, dev2Addr, pk) require.NotNil(t, err) - // new admin delete subscription owner (admin) - pk = types.ProjectAdminKey(subAddr) - err = ts.delProjectKeys(project.Index, admAddr, pk) + // new admin delete subscription owner admin (should fail) + pk = types.ProjectAdminKey(sub1Addr) + err = ts.delProjectKeys(project.Index, adm1Addr, pk) require.NotNil(t, err) // subscription owner (admin) delete other admin - pk = types.ProjectAdminKey(admAddr) - err = ts.delProjectKeys(project.Index, subAddr, pk) + pk = types.ProjectAdminKey(adm1Addr) + err = ts.delProjectKeys(project.Index, sub1Addr, pk) require.Nil(t, err) - // admin delete developer (admin removed already!) + // admin delete developer after admin removed (should fail) pk = types.ProjectDeveloperKey(dev3Addr) - err = ts.delProjectKeys(project.Index, admAddr, pk) + err = ts.delProjectKeys(project.Index, adm1Addr, pk) require.NotNil(t, err) // subscription owner (admin) delete developer pk = types.ProjectDeveloperKey(dev3Addr) - err = ts.delProjectKeys(project.Index, subAddr, pk) + err = ts.delProjectKeys(project.Index, sub1Addr, pk) require.Nil(t, err) // deletion take effect in next epoch - require.True(t, ts.isKeyInProject(project.Index, admAddr, types.ProjectKey_ADMIN)) + require.True(t, ts.isKeyInProject(project.Index, adm1Addr, types.ProjectKey_ADMIN)) require.True(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) - ts.AdvanceEpoch(1) - require.False(t, ts.isKeyInProject(project.Index, admAddr, types.ProjectKey_ADMIN)) + ts.AdvanceEpoch() + require.False(t, ts.isKeyInProject(project.Index, adm1Addr, types.ProjectKey_ADMIN)) require.False(t, ts.isKeyInProject(project.Index, dev3Addr, types.ProjectKey_DEVELOPER)) } func TestAddAdminInTwoProjects(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(1, 0, 0) // 1 sub, 0 admin, 0 devel + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 admin, 0 devel + ts.setupProjectData() - projectData := ts.projects["pd1"] - plan := common.CreateMockPlan() + projectData := ts.ProjectData("pd1") + plan := ts.Plan("mock") - subAddr := ts.accounts["sub1"] - admAddr := ts.accounts["pd1_adm"] + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("pd1_adm") - err := ts.keepers.Projects.CreateAdminProject(ts.ctx, subAddr, plan) + err := ts.Keepers.Projects.CreateAdminProject(ts.Ctx, sub1Addr, plan) require.Nil(t, err) - // this is not supposed to fail because you can use the same admin key for two different projects - // creating a regular project (not admin project) so subAccount won't be a developer there - err = ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) + err = ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() - _, err = ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: admAddr}) + _, err = ts.QueryProjectDeveloper(adm1Addr) require.NotNil(t, err) - - response, err := ts.keepers.Projects.Developer(ts._ctx, &types.QueryDeveloperRequest{Developer: subAddr}) + res, err := ts.QueryProjectDeveloper(sub1Addr) require.Nil(t, err) - require.Equal(t, response.Project.Index, types.ProjectIndex(subAddr, types.ADMIN_PROJECT_NAME)) + + projectID := types.ProjectIndex(sub1Addr, types.ADMIN_PROJECT_NAME) + require.Equal(t, res.Project.Index, projectID) } func TestSetPolicy(t *testing.T) { @@ -488,29 +444,30 @@ func TestSetSubscriptionPolicy(t *testing.T) { } func setPolicyTest(t *testing.T, testAdminPolicy bool) { - ts := newTestStruct(t) - ts.prepareData(1, 0, 1) // 1 sub, 0 admin, 1 data + ts := newTester(t) + ts.SetupAccounts(1, 0, 1) // 1 sub, 0 admin, 1 data + ts.setupProjectData() - projectData := ts.projects["pd1"] - plan := common.CreateMockPlan() + projectData := ts.ProjectData("pd1") + plan := ts.Plan("mock") - subAddr := ts.accounts["sub1"] - admAddr := ts.accounts["pd1_adm"] - devAddr := ts.accounts["dev1"] + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("pd1_adm") + _, dev1Addr := ts.Account("dev1") - projectID := types.ProjectIndex(subAddr, projectData.Name) + projectID := types.ProjectIndex(sub1Addr, projectData.Name) - err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() - pk := types.ProjectDeveloperKey(devAddr) - err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID, admAddr, []types.ProjectKey{pk}) + pk := types.ProjectDeveloperKey(dev1Addr) + err = ts.addProjectKeys(projectID, adm1Addr, pk) require.Nil(t, err) spec := common.CreateMockSpec() - ts.keepers.Spec.SetSpec(ts.ctx, spec) + ts.Keepers.Spec.SetSpec(ts.Ctx, spec) templates := []struct { name string @@ -525,62 +482,86 @@ func setPolicyTest(t *testing.T, testAdminPolicy bool) { setSubscriptionPolicySuccess bool }{ { - "valid policy (admin account)", admAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "valid policy (admin account)", adm1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 3, true, false, }, - { - "valid policy (subscription account)", subAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "valid policy (subscription account)", sub1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 3, true, true, }, - { - "bad creator (developer account -- not admin)", devAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "bad creator (developer account -- not admin)", dev1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 3, false, false, }, - { - "bad projectID (doesn't exist)", devAddr, "fakeProjectId", uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "bad projectID (doesn't exist)", dev1Addr, "fakeProjectId", uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 3, false, false, }, - { - "invalid geolocation (0)", devAddr, projectID, uint64(0), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "invalid geolocation (0)", dev1Addr, projectID, uint64(0), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 3, false, false, }, - { // note: currently, we don't verify the chain policies - "bad chainID (doesn't exist)", subAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: "LOL", Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "bad chainID (doesn't exist)", sub1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: "LOL", + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 3, true, true, }, - { // note: currently, we don't verify the chain policies - "bad API (doesn't exist)", subAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{"lol"}}}, + "bad API (doesn't exist)", sub1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{"lol"}, + }}, 100, 10, 3, true, true, }, { // note: currently, we don't verify the chain policies - "chainID and API not supported (exist in Lava's specs)", subAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: "ETH1", Apis: []string{"eth_accounts"}}}, + "chainID and API not supported (exist in Lava's specs)", sub1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: "ETH1", + Apis: []string{"eth_accounts"}, + }}, 100, 10, 3, true, true, }, { - "epoch CU larger than total CU", subAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "epoch CU larger than total CU", sub1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 10, 100, 3, false, false, }, { - "bad maxProvidersToPair", subAddr, projectID, uint64(1), - []planstypes.ChainPolicy{{ChainId: spec.Index, Apis: []string{spec.ApiCollections[0].Apis[0].Name}}}, + "bad maxProvidersToPair", sub1Addr, projectID, uint64(1), + []planstypes.ChainPolicy{{ + ChainId: spec.Index, + Apis: []string{spec.ApiCollections[0].Apis[0].Name}, + }}, 100, 10, 1, false, false, }, } @@ -596,43 +577,22 @@ func setPolicyTest(t *testing.T, testAdminPolicy bool) { } if testAdminPolicy { - SetPolicyMessage := types.MsgSetPolicy{ - Creator: tt.creator, - Policy: newPolicy, - Project: tt.projectID, - } - - err = SetPolicyMessage.ValidateBasic() - require.Nil(t, err) - - _, err := ts.servers.ProjectServer.SetPolicy(ts._ctx, &SetPolicyMessage) + _, err := ts.txSetProjectPolicy(tt.projectID, tt.creator, newPolicy) if tt.setAdminPolicySuccess { require.Nil(t, err) - ts.AdvanceEpoch(1) - - proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, tt.projectID, ts.BlockHeight()) + ts.AdvanceEpoch() + proj, err := ts.GetProjectForBlock(tt.projectID, ts.BlockHeight()) require.Nil(t, err) - require.Equal(t, newPolicy, *proj.AdminPolicy) } else { require.NotNil(t, err) } } else { - setSubscriptionPolicyMessage := types.MsgSetSubscriptionPolicy{ - Creator: tt.creator, - Policy: newPolicy, - Projects: []string{tt.projectID}, - } - - err = setSubscriptionPolicyMessage.ValidateBasic() - require.Nil(t, err) - - _, err := ts.servers.ProjectServer.SetSubscriptionPolicy(ts._ctx, &setSubscriptionPolicyMessage) + _, err := ts.txSetSubscriptionPolicy(tt.projectID, tt.creator, newPolicy) if tt.setSubscriptionPolicySuccess { require.Nil(t, err) - ts.AdvanceEpoch(1) - - proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, tt.projectID, ts.BlockHeight()) + ts.AdvanceEpoch() + proj, err := ts.GetProjectForBlock(tt.projectID, ts.BlockHeight()) require.Nil(t, err) require.Equal(t, newPolicy, *proj.SubscriptionPolicy) } else { @@ -644,87 +604,89 @@ func setPolicyTest(t *testing.T, testAdminPolicy bool) { } func TestChargeComputeUnits(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(0, 0, 1) // 0 sub, 0 adm, 1 dev + ts := newTester(t) + ts.SetupAccounts(0, 0, 1) // 0 sub, 0 adm, 1 dev + ts.setupProjectData() - projectData := ts.projects["pd1"] - plan := common.CreateMockPlan() + projectData := ts.ProjectData("pd1") + plan := ts.Plan("mock") - subAddr := ts.accounts["pd1_adm"] - devAddr := ts.accounts["dev1"] + _, sub1Addr := ts.Account("pd1_adm") + _, dev1Addr := ts.Account("dev1") - err := ts.keepers.Projects.CreateProject(ts.ctx, subAddr, projectData, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() block1 := ts.BlockHeight() - projectID := types.ProjectIndex(subAddr, projectData.Name) - project, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectID, block1) + projectID := types.ProjectIndex(sub1Addr, projectData.Name) + project, err := ts.GetProjectForBlock(projectID, block1) require.Nil(t, err) // first epoch to add some delay before adding the developer key. - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() block2 := ts.BlockHeight() // add developer key (created fixation) - err = ts.addProjectKeys(project.Index, subAddr, types.ProjectDeveloperKey(devAddr)) + err = ts.addProjectKeys(project.Index, sub1Addr, types.ProjectDeveloperKey(dev1Addr)) require.Nil(t, err) // second epoch to move further, otherwise snapshot will affect the current new // dev key instead of creating a separate fixation version. - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() block3 := ts.BlockHeight() - ts.keepers.Projects.SnapshotSubscriptionProjects(ts.ctx, subAddr) + ts.Keepers.Projects.SnapshotSubscriptionProjects(ts.Ctx, sub1Addr) // try to charge CUs: should update oldest and second-oldest entries, but not the latest // (because the latter is in a new snapshot) - err = ts.keepers.Projects.ChargeComputeUnitsToProject(ts.ctx, project, block1, 1000) + err = ts.Keepers.Projects.ChargeComputeUnitsToProject(ts.Ctx, project, block1, 1000) require.Nil(t, err) - proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block1) + proj, err := ts.GetProjectForBlock(project.Index, block1) require.Nil(t, err) require.Equal(t, uint64(1000), proj.UsedCu) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block2) + proj, err = ts.GetProjectForBlock(project.Index, block2) require.Nil(t, err) require.Equal(t, uint64(1000), proj.UsedCu) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block3) + proj, err = ts.GetProjectForBlock(project.Index, block3) require.Nil(t, err) require.Equal(t, uint64(0), proj.UsedCu) - ts.keepers.Projects.ChargeComputeUnitsToProject(ts.ctx, project, block2, 1000) + err = ts.Keepers.Projects.ChargeComputeUnitsToProject(ts.Ctx, project, block2, 1000) + require.Nil(t, err) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block1) + proj, err = ts.GetProjectForBlock(project.Index, block1) require.Nil(t, err) require.Equal(t, uint64(1000), proj.UsedCu) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block2) + proj, err = ts.GetProjectForBlock(project.Index, block2) require.Nil(t, err) require.Equal(t, uint64(2000), proj.UsedCu) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, project.Index, block3) + proj, err = ts.GetProjectForBlock(project.Index, block3) require.Nil(t, err) require.Equal(t, uint64(0), proj.UsedCu) } func TestAddDelKeysSameEpoch(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(2, 2, 6) // 2 sub, 1 adm, 5 dev - - sub1Addr := ts.accounts["sub1"] - sub2Addr := ts.accounts["sub2"] - adm1Addr := ts.accounts["adm1"] - adm2Addr := ts.accounts["adm2"] - dev1Addr := ts.accounts["dev1"] - dev2Addr := ts.accounts["dev2"] - dev3Addr := ts.accounts["dev3"] - dev4Addr := ts.accounts["dev4"] - dev5Addr := ts.accounts["dev5"] - dev6Addr := ts.accounts["dev6"] - - plan := common.CreateMockPlan() + ts := newTester(t) + ts.SetupAccounts(2, 2, 6) // 2 sub, 2 adm, 6 dev + + _, sub1Addr := ts.Account("sub1") + _, sub2Addr := ts.Account("sub2") + _, adm1Addr := ts.Account("adm1") + _, adm2Addr := ts.Account("adm2") + _, dev1Addr := ts.Account("dev1") + _, dev2Addr := ts.Account("dev2") + _, dev3Addr := ts.Account("dev3") + _, dev4Addr := ts.Account("dev4") + _, dev5Addr := ts.Account("dev5") + _, dev6Addr := ts.Account("dev6") + + plan := ts.Plan("mock") projectData1 := types.ProjectData{ Name: "mockname1", @@ -732,7 +694,7 @@ func TestAddDelKeysSameEpoch(t *testing.T) { ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub1Addr)}, Policy: &plan.PlanPolicy, } - err := ts.keepers.Projects.CreateProject(ts.ctx, sub1Addr, projectData1, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData1, plan) require.Nil(t, err) projectData2 := types.ProjectData{ @@ -741,29 +703,24 @@ func TestAddDelKeysSameEpoch(t *testing.T) { ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub2Addr)}, Policy: &plan.PlanPolicy, } - err = ts.keepers.Projects.CreateProject(ts.ctx, sub2Addr, projectData2, plan) + err = ts.Keepers.Projects.CreateProject(ts.Ctx, sub2Addr, projectData2, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceBlock() projectID1 := types.ProjectIndex(sub1Addr, projectData1.Name) projectID2 := types.ProjectIndex(sub2Addr, projectData2.Name) - err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID1, sub1Addr, - []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}) + err = ts.addProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev1Addr)) require.Nil(t, err) require.True(t, ts.isKeyInProject(projectID1, dev1Addr, types.ProjectKey_DEVELOPER)) - ts.AdvanceBlock(1) - - err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID1, sub1Addr, - []types.ProjectKey{types.ProjectDeveloperKey(dev2Addr)}) + err = ts.addProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev2Addr)) require.Nil(t, err) - require.True(t, ts.isKeyInProject(projectID1, dev1Addr, types.ProjectKey_DEVELOPER)) require.True(t, ts.isKeyInProject(projectID1, dev2Addr, types.ProjectKey_DEVELOPER)) - proj, err := ts.keepers.Projects.GetProjectForDeveloper(ts.ctx, sub1Addr, ts.BlockHeight()) + proj, err := ts.GetProjectForDeveloper(sub1Addr) require.Nil(t, err) require.Equal(t, 3, len(proj.ProjectKeys)) @@ -773,7 +730,6 @@ func TestAddDelKeysSameEpoch(t *testing.T) { err = ts.addProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev3Addr)) require.Nil(t, err) - ts.AdvanceEpoch(1) require.True(t, ts.isKeyInProject(projectID1, adm1Addr, types.ProjectKey_ADMIN)) require.True(t, ts.isKeyInProject(projectID1, dev3Addr, types.ProjectKey_DEVELOPER)) @@ -787,11 +743,13 @@ func TestAddDelKeysSameEpoch(t *testing.T) { err = ts.delProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev3Addr)) require.NotNil(t, err) - ts.AdvanceEpoch(1) + // del takes effect in next epoch + ts.AdvanceEpoch() + require.False(t, ts.isKeyInProject(projectID1, adm1Addr, types.ProjectKey_ADMIN)) require.False(t, ts.isKeyInProject(projectID1, dev3Addr, types.ProjectKey_DEVELOPER)) - // add, del (and add again) in same epoch + // add, del, and add again in same epoch err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectAdminKey(adm1Addr)) require.Nil(t, err) err = ts.delProjectKeys(projectID2, sub2Addr, types.ProjectAdminKey(adm1Addr)) @@ -806,7 +764,9 @@ func TestAddDelKeysSameEpoch(t *testing.T) { err = ts.addProjectKeys(projectID2, adm1Addr, types.ProjectDeveloperKey(dev4Addr)) require.NotNil(t, err) - // add, del: admin, should be invalid immediately + ts.AdvanceEpoch() + + // add, del admin (admin should invalid immediately) err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectAdminKey(adm2Addr)) require.Nil(t, err) err = ts.addProjectKeys(projectID2, adm2Addr, types.ProjectDeveloperKey(dev6Addr)) @@ -816,13 +776,15 @@ func TestAddDelKeysSameEpoch(t *testing.T) { err = ts.delProjectKeys(projectID2, adm2Addr, types.ProjectDeveloperKey(dev6Addr)) require.NotNil(t, err) - ts.AdvanceEpoch(1) + // del takes effect in next epoch + ts.AdvanceEpoch() + require.True(t, ts.isKeyInProject(projectID2, adm1Addr, types.ProjectKey_ADMIN)) require.False(t, ts.isKeyInProject(projectID2, dev4Addr, types.ProjectKey_DEVELOPER)) require.False(t, ts.isKeyInProject(projectID2, adm2Addr, types.ProjectKey_ADMIN)) require.True(t, ts.isKeyInProject(projectID2, dev6Addr, types.ProjectKey_DEVELOPER)) - // add dev to two projects in same epoch - latter fails + // add dev to two projects in same epoch (latter fails) err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectDeveloperKey(dev5Addr)) require.Nil(t, err) err = ts.addProjectKeys(projectID2, sub1Addr, types.ProjectDeveloperKey(dev5Addr)) @@ -832,15 +794,15 @@ func TestAddDelKeysSameEpoch(t *testing.T) { func TestDelKeysDelProjectSameEpoch(t *testing.T) { var err error - ts := newTestStruct(t) - ts.prepareData(1, 1, 2) // 1 sub, 1 adm, 1 dev + ts := newTester(t) + ts.SetupAccounts(1, 1, 2) // 1 sub, 1 adm, 2 dev - sub1Addr := ts.accounts["sub1"] - adm1Addr := ts.accounts["adm1"] - dev1Addr := ts.accounts["dev1"] - dev2Addr := ts.accounts["dev2"] + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("adm1") + _, dev1Addr := ts.Account("dev1") + _, dev2Addr := ts.Account("dev2") - plan := common.CreateMockPlan() + plan := ts.Plan("mock") projectsData := []types.ProjectData{ { @@ -873,11 +835,11 @@ func TestDelKeysDelProjectSameEpoch(t *testing.T) { for i := range projectsData { projectsID[i] = types.ProjectIndex(sub1Addr, projectsData[i].Name) - err = ts.keepers.Projects.CreateProject(ts.ctx, sub1Addr, projectsData[i], plan) + err = ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectsData[i], plan) require.Nil(t, err) } - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() // part (1): delete keys then project @@ -888,37 +850,37 @@ func TestDelKeysDelProjectSameEpoch(t *testing.T) { require.Nil(t, err) // now delete the projects (double delete) in same epoch - err = ts.keepers.Projects.DeleteProject(ts.ctx, sub1Addr, projectsID[0]) + err = ts.Keepers.Projects.DeleteProject(ts.Ctx, sub1Addr, projectsID[0]) require.Nil(t, err) - err = ts.keepers.Projects.DeleteProject(ts.ctx, sub1Addr, projectsID[1]) + err = ts.Keepers.Projects.DeleteProject(ts.Ctx, sub1Addr, projectsID[1]) require.Nil(t, err) - proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[0], ts.BlockHeight()) + proj, err := ts.GetProjectForBlock(projectsID[0], ts.BlockHeight()) require.Nil(t, err) require.Equal(t, 1, len(proj.ProjectKeys)) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[1], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[1], ts.BlockHeight()) require.Nil(t, err) require.Equal(t, 1, len(proj.ProjectKeys)) // wait for next epoch for delete(s) to take effect - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[0], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[0], ts.BlockHeight()) require.NotNil(t, err) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[1], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[1], ts.BlockHeight()) require.NotNil(t, err) // should not panic - ts.AdvanceBlock(2 * int(commontypes.STALE_ENTRY_TIME)) + ts.AdvanceBlocks(2 * commontypes.STALE_ENTRY_TIME) // part (2): delete project then keys // delete the projects - err = ts.keepers.Projects.DeleteProject(ts.ctx, sub1Addr, projectsID[2]) + err = ts.Keepers.Projects.DeleteProject(ts.Ctx, sub1Addr, projectsID[2]) require.Nil(t, err) - err = ts.keepers.Projects.DeleteProject(ts.ctx, sub1Addr, projectsID[3]) + err = ts.Keepers.Projects.DeleteProject(ts.Ctx, sub1Addr, projectsID[3]) require.Nil(t, err) // delete key from each project: should being (project being deleted) @@ -927,36 +889,36 @@ func TestDelKeysDelProjectSameEpoch(t *testing.T) { err = ts.delProjectKeys(projectsID[3], sub1Addr, types.ProjectDeveloperKey(dev1Addr)) require.NotNil(t, err) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[2], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[2], ts.BlockHeight()) require.Nil(t, err) require.Equal(t, 1, len(proj.ProjectKeys)) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[3], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[3], ts.BlockHeight()) require.Nil(t, err) require.Equal(t, 1, len(proj.ProjectKeys)) // wait for next epoch for delete(s) to take effect - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[2], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[2], ts.BlockHeight()) require.NotNil(t, err) - proj, err = ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectsID[3], ts.BlockHeight()) + proj, err = ts.GetProjectForBlock(projectsID[3], ts.BlockHeight()) require.NotNil(t, err) // should not panic - ts.AdvanceBlock(2 * int(commontypes.STALE_ENTRY_TIME)) + ts.AdvanceBlocks(2 * commontypes.STALE_ENTRY_TIME) } func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { - ts := newTestStruct(t) - ts.prepareData(2, 0, 1) // 2 sub, 0 adm, 1 dev + ts := newTester(t) + ts.SetupAccounts(2, 0, 1) // 2 sub, 0 adm, 1 dev - sub1Addr := ts.accounts["sub1"] - sub2Addr := ts.accounts["sub2"] - dev1Addr := ts.accounts["dev1"] + _, sub1Addr := ts.Account("sub1") + _, sub2Addr := ts.Account("sub2") + _, dev1Addr := ts.Account("dev1") - plan := common.CreateMockPlan() + plan := ts.Plan("mock") projectName1 := "mockname1" projectName2 := "mockname2" @@ -970,7 +932,7 @@ func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub1Addr)}, Policy: &plan.PlanPolicy, } - err := ts.keepers.Projects.CreateProject(ts.ctx, sub1Addr, projectData1, plan) + err := ts.Keepers.Projects.CreateProject(ts.Ctx, sub1Addr, projectData1, plan) require.Nil(t, err) projectData2 := types.ProjectData{ @@ -979,25 +941,22 @@ func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { ProjectKeys: []types.ProjectKey{types.ProjectDeveloperKey(sub2Addr)}, Policy: &plan.PlanPolicy, } - err = ts.keepers.Projects.CreateProject(ts.ctx, sub2Addr, projectData2, plan) + err = ts.Keepers.Projects.CreateProject(ts.Ctx, sub2Addr, projectData2, plan) require.Nil(t, err) - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() - err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID1, sub1Addr, - []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}) + err = ts.addProjectKeys(projectID1, sub1Addr, types.ProjectDeveloperKey(dev1Addr)) require.Nil(t, err) - err = ts.keepers.Projects.AddKeysToProject(ts.ctx, projectID2, sub2Addr, - []types.ProjectKey{types.ProjectDeveloperKey(dev1Addr)}) + err = ts.addProjectKeys(projectID2, sub2Addr, types.ProjectDeveloperKey(dev1Addr)) require.NotNil(t, err) // developer was already added to the first project - ts.AdvanceEpoch(1) + ts.AdvanceEpoch() - proj1, err := ts.keepers.Projects.GetProjectForDeveloper(ts.ctx, sub1Addr, ts.BlockHeight()) + proj1, err := ts.GetProjectForDeveloper(sub1Addr) require.Nil(t, err) - - proj2, err := ts.keepers.Projects.GetProjectForDeveloper(ts.ctx, sub2Addr, ts.BlockHeight()) + proj2, err := ts.GetProjectForDeveloper(sub2Addr) require.Nil(t, err) require.Equal(t, 2, len(proj1.ProjectKeys)) @@ -1005,18 +964,11 @@ func TestAddDevKeyToDifferentProjectsInSameBlock(t *testing.T) { } func TestSetPolicySelectedProviders(t *testing.T) { - servers, keepers, _ctx := testkeeper.InitAllKeepers(t) - ctx := sdk.UnwrapSDKContext(_ctx) + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - adm1Addr := common.CreateNewAccount(_ctx, *keepers, 10000).Addr.String() - projectData := types.ProjectData{ - Name: "name", - Enabled: true, - ProjectKeys: []types.ProjectKey{{Key: adm1Addr, Kinds: uint32(types.ProjectKey_ADMIN)}}, - Policy: &planstypes.Policy{MaxProvidersToPair: 2, GeolocationProfile: math.MaxUint64}, - } - subAddr := projectData.ProjectKeys[0].Key - projPolicy := projectData.Policy + _, sub1Addr := ts.Account("sub1") + policy := ts.Policy("mock") allowed := planstypes.SELECTED_PROVIDERS_MODE_ALLOWED mixed := planstypes.SELECTED_PROVIDERS_MODE_MIXED @@ -1029,11 +981,11 @@ func TestSetPolicySelectedProviders(t *testing.T) { projProviders []string }{ {[]string{}, []string{}, []string{}}, - {[]string{subAddr}, []string{subAddr}, []string{subAddr}}, - {[]string{subAddr}, []string{subAddr}, []string{}}, + {[]string{sub1Addr}, []string{sub1Addr}, []string{sub1Addr}}, + {[]string{sub1Addr}, []string{sub1Addr}, []string{}}, {[]string{"lalala"}, []string{"lalala"}, []string{"lalala"}}, - {[]string{subAddr, subAddr}, []string{subAddr, subAddr}, []string{subAddr, subAddr}}, - {[]string{subAddr}, []string{}, []string{}}, + {[]string{sub1Addr, sub1Addr}, []string{sub1Addr, sub1Addr}, []string{sub1Addr, sub1Addr}}, + {[]string{sub1Addr}, []string{}, []string{}}, } templates := []struct { @@ -1065,57 +1017,42 @@ func TestSetPolicySelectedProviders(t *testing.T) { for _, tt := range templates { t.Run(tt.name, func(t *testing.T) { providersSet := providersSets[tt.providerSet] - plan := common.CreateMockPlan() + plan := ts.Plan("mock") plan.PlanPolicy.SelectedProvidersMode = tt.planMode plan.PlanPolicy.SelectedProviders = providersSet.planProviders - err := testkeeper.SimulatePlansAddProposal(ctx, keepers.Plans, []planstypes.Plan{plan}) + err := testkeeper.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, []planstypes.Plan{plan}) if tt.planPolicyValid { require.Nil(t, err) } else { require.NotNil(t, err) } - _, err = servers.SubscriptionServer.Buy(_ctx, &subscriptiontypes.MsgBuy{ - Creator: subAddr, - Consumer: subAddr, - Index: plan.Index, - Duration: 1, - }) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) - subProjects, err := keepers.Subscription.ListProjects(_ctx, &subscriptiontypes.QueryListProjectsRequest{ - Subscription: subAddr, - }) + res, err := ts.QuerySubscriptionListProjects(sub1Addr) require.Nil(t, err) - require.Equal(t, 1, len(subProjects.Projects)) + require.Equal(t, 1, len(res.Projects)) - adminProject, err := keepers.Projects.GetProjectForBlock(ctx, subProjects.Projects[0], uint64(ctx.BlockHeight())) + admProject, err := ts.GetProjectForBlock(res.Projects[0], ts.BlockHeight()) require.Nil(t, err) - projPolicy.SelectedProvidersMode = tt.projMode - projPolicy.SelectedProviders = providersSet.projProviders + policy.SelectedProvidersMode = tt.projMode + policy.SelectedProviders = providersSet.projProviders - _, err = servers.ProjectServer.SetPolicy(_ctx, &types.MsgSetPolicy{ - Creator: subAddr, - Project: adminProject.Index, - Policy: *projPolicy, - }) + _, err = ts.txSetProjectPolicy(admProject.Index, sub1Addr, policy) if tt.projPolicyValid { require.Nil(t, err) } else { require.NotNil(t, err) } - projPolicy.SelectedProvidersMode = tt.subMode - projPolicy.SelectedProviders = providersSet.subProviders + policy.SelectedProvidersMode = tt.subMode + policy.SelectedProviders = providersSet.subProviders - _, err = servers.ProjectServer.SetSubscriptionPolicy(_ctx, &types.MsgSetSubscriptionPolicy{ - Creator: subAddr, - Projects: []string{adminProject.Index}, - Policy: *projPolicy, - }) + _, err = ts.txSetSubscriptionPolicy(admProject.Index, sub1Addr, policy) if tt.subPolicyValid { require.Nil(t, err) } else { diff --git a/x/spec/keeper/spec_test.go b/x/spec/keeper/spec_test.go index 5da9c14c38..6d88a08ee7 100644 --- a/x/spec/keeper/spec_test.go +++ b/x/spec/keeper/spec_test.go @@ -1,7 +1,6 @@ package keeper_test import ( - "context" "os" "strconv" "strings" @@ -9,52 +8,37 @@ import ( "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" + common "github.com/lavanet/lava/testutil/common" keepertest "github.com/lavanet/lava/testutil/keeper" "github.com/lavanet/lava/testutil/nullify" - "github.com/lavanet/lava/x/spec" "github.com/lavanet/lava/x/spec/client/utils" "github.com/lavanet/lava/x/spec/keeper" "github.com/lavanet/lava/x/spec/types" "github.com/stretchr/testify/require" ) -type testStruct struct { - t *testing.T - servers *keepertest.Servers - keepers *keepertest.Keepers - _ctx context.Context - ctx sdk.Context +type tester struct { + common.Tester } -func newTestStruct(t *testing.T) *testStruct { - servers, keepers, _ctx := keepertest.InitAllKeepers(t) - ts := &testStruct{ - t: t, - servers: servers, - keepers: keepers, - _ctx: _ctx, - ctx: sdk.UnwrapSDKContext(_ctx), - } - ts.AdvanceEpoch(1) - return ts +func newTester(t *testing.T) *tester { + return &tester{Tester: *common.NewTester(t)} } -func (ts *testStruct) BlockHeight() uint64 { - return uint64(ts.ctx.BlockHeight()) +func (ts *tester) getSpec(index string) (types.Spec, bool) { + return ts.Keepers.Spec.GetSpec(ts.Ctx, index) } -func (ts *testStruct) AdvanceBlock(count int) { - for i := 0; i < count; i += 1 { - ts._ctx = keepertest.AdvanceBlock(ts._ctx, ts.keepers) - } - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) +func (ts *tester) setSpec(spec types.Spec) { + ts.Keepers.Spec.SetSpec(ts.Ctx, spec) } -func (ts *testStruct) AdvanceEpoch(count int) { - for i := 0; i < count; i += 1 { - ts._ctx = keepertest.AdvanceEpoch(ts._ctx, ts.keepers) - } - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) +func (ts *tester) removeSpec(index string) { + ts.Keepers.Spec.RemoveSpec(ts.Ctx, index) +} + +func (ts *tester) expandSpec(spec types.Spec) (types.Spec, error) { + return ts.Keepers.Spec.ExpandSpec(ts.Ctx, spec) } // prepareMockApis returns a slice of mock ServiceApi for use in Spec @@ -104,7 +88,15 @@ func prepareMockParsing(count int) []*types.ParseDirective { return mockParsing } -func createApiCollection(apiCount int, apiIds []int, parsingCount int, apiInterface string, connectionType string, addon string, imports []*types.CollectionData) *types.ApiCollection { +func createApiCollection( + apiCount int, + apiIds []int, + parsingCount int, + apiInterface string, + connectionType string, + addon string, + imports []*types.CollectionData, +) *types.ApiCollection { return &types.ApiCollection{ Enabled: true, CollectionData: types.CollectionData{ @@ -132,8 +124,24 @@ func generateHeaders(count int) (retHeaders []*types.Header) { return } -func createApiCollectionWithHeaders(apiCount int, apiIds []int, parsingCount int, headersCount int, apiInterface string, connectionType string, addon string, imports []*types.CollectionData) *types.ApiCollection { - apiCollection := createApiCollection(apiCount, apiIds, parsingCount, apiInterface, connectionType, addon, imports) +func createApiCollectionWithHeaders( + apiCount int, + apiIds []int, + parsingCount int, + headersCount int, + apiInterface string, + connectionType string, + addon string, + imports []*types.CollectionData, +) *types.ApiCollection { + apiCollection := createApiCollection( + apiCount, + apiIds, + parsingCount, + apiInterface, + connectionType, + addon, + imports) apiCollection.Headers = generateHeaders(headersCount) return apiCollection } @@ -159,13 +167,15 @@ func createNSpec(keeper *keeper.Keeper, ctx sdk.Context, n int) []types.Spec { return items } +func (ts *tester) createNSpec(n int) []types.Spec { + return createNSpec(&ts.Keepers.Spec, ts.Ctx, n) +} + func TestSpecGet(t *testing.T) { - keeper, ctx := keepertest.SpecKeeper(t) - items := createNSpec(keeper, ctx, 10) + ts := newTester(t) + items := ts.createNSpec(10) for _, item := range items { - rst, found := keeper.GetSpec(ctx, - item.Index, - ) + rst, found := ts.getSpec(item.Index) require.True(t, found) require.Equal(t, nullify.Fill(&item), @@ -175,33 +185,28 @@ func TestSpecGet(t *testing.T) { } func TestSpecRemove(t *testing.T) { - keeper, ctx := keepertest.SpecKeeper(t) - items := createNSpec(keeper, ctx, 10) + ts := newTester(t) + items := ts.createNSpec(10) for _, item := range items { - keeper.RemoveSpec(ctx, - item.Index, - ) - _, found := keeper.GetSpec(ctx, - item.Index, - ) + ts.removeSpec(item.Index) + _, found := ts.getSpec(item.Index) require.False(t, found) } } func TestSpecGetAll(t *testing.T) { - keeper, ctx := keepertest.SpecKeeper(t) - items := createNSpec(keeper, ctx, 10) + ts := newTester(t) + items := ts.createNSpec(10) + rst := ts.Keepers.Spec.GetAllSpec(ts.Ctx) require.ElementsMatch(t, nullify.Fill(items), - nullify.Fill(keeper.GetAllSpec(ctx)), + nullify.Fill(rst), ) } -// prepareMockCurrentSpecs returns a slice of Spec according to the template -// therein, to simulate the collection of existing Spec(s) on the chain. -func prepareMockCurrentSpecs(keeper *keeper.Keeper, ctx sdk.Context, apis []*types.Api) map[string]types.Spec { - currentSpecs := make(map[string]types.Spec) - +// setupSpecsForSpecInheritance returns a slice of Spec according to the +// template therein, to simulate collection of existing Spec(s) on the chain. +func (ts *tester) setupSpecsForSpecInheritance(apis []*types.Api) { template := []struct { name string enabled bool @@ -216,21 +221,23 @@ func prepareMockCurrentSpecs(keeper *keeper.Keeper, ctx sdk.Context, apis []*typ } for _, tt := range template { + apiCollection := &types.ApiCollection{ + Enabled: true, + CollectionData: types.CollectionData{ApiInterface: "stub"}, + Apis: selectMockApis(apis, tt.apis), + } spec := types.Spec{ Name: tt.name, Index: tt.name, Enabled: tt.enabled, - ApiCollections: []*types.ApiCollection{{Enabled: true, CollectionData: types.CollectionData{ApiInterface: "stub"}, Apis: selectMockApis(apis, tt.apis)}}, + ApiCollections: []*types.ApiCollection{apiCollection}, } - currentSpecs[tt.name] = spec - keeper.SetSpec(ctx, spec) + ts.AddSpec(tt.name, spec) // also calls SetSpec() } - - return currentSpecs } -// Note: the API identifiers below refer to the APIs from the -// function prepareMockCurrentApis() above +// Note: the API identifiers below refer to the APIs from the function +// setupSpecsForSpecInheritance() above var specTemplates = []struct { desc string name string @@ -329,19 +336,12 @@ var specTemplates = []struct { }, } -func simulateSpecProposal(ts *testStruct, name, title string, specs []types.Spec) error { - proposal := types.NewSpecAddProposal(name, title, specs) - proposalHandler := spec.NewSpecProposalsHandler(ts.keepers.Spec) - return proposalHandler(ts.ctx, proposal) -} - // Test Spec with "import" directives (using to the templates above). func TestSpecWithImport(t *testing.T) { - ts := newTestStruct(t) - keeper := ts.keepers.Spec + ts := newTester(t) apis := prepareMockApis(8) - _ = prepareMockCurrentSpecs(&keeper, ts.ctx, apis) + ts.setupSpecsForSpecInheritance(apis) for _, tt := range specTemplates { sp := types.Spec{ @@ -361,7 +361,7 @@ func TestSpecWithImport(t *testing.T) { } t.Run(tt.desc, func(t *testing.T) { - fullspec, err := keeper.ExpandSpec(ts.ctx, sp) + fullspec, err := ts.expandSpec(sp) if tt.ok == true { require.Nil(t, err, err) require.Len(t, fullspec.ApiCollections[0].Apis, len(tt.result)) @@ -379,7 +379,7 @@ func TestSpecWithImport(t *testing.T) { require.True(t, found) } - keeper.SetSpec(ts.ctx, sp) + ts.setSpec(sp) } else { require.NotNil(t, err) } @@ -387,9 +387,7 @@ func TestSpecWithImport(t *testing.T) { } } -func prepareMockCurrentSpecsForApiCollectionInheritance(keeper *keeper.Keeper, ctx sdk.Context) map[string]types.Spec { - currentSpecs := make(map[string]types.Spec) - +func (ts *tester) setupSpecsForApiInheritance() { template := []struct { name string enabled bool @@ -435,16 +433,12 @@ func prepareMockCurrentSpecsForApiCollectionInheritance(keeper *keeper.Keeper, c Enabled: tt.enabled, ApiCollections: tt.apiCollections, } - currentSpecs[tt.name] = spec - keeper.SetSpec(ctx, spec) + ts.AddSpec(tt.name, spec) // also calls SetSpec() } - - return currentSpecs } func TestSpecUpdateInherit(t *testing.T) { - ts := newTestStruct(t) - keeper := ts.keepers.Spec + ts := newTester(t) api := types.Api{ Enabled: true, @@ -489,47 +483,47 @@ func TestSpecUpdateInherit(t *testing.T) { } // add a parent spec and a child spec - err := simulateSpecProposal(ts, "parent", "propose parent spec", []types.Spec{parentSpec}) + err := keepertest.SimulateSpecAddProposal(ts.Ctx, ts.Keepers.Spec, []types.Spec{parentSpec}) require.Nil(t, err) - err = simulateSpecProposal(ts, "child", "propose child spec", []types.Spec{childSpec}) + err = keepertest.SimulateSpecAddProposal(ts.Ctx, ts.Keepers.Spec, []types.Spec{childSpec}) require.Nil(t, err) block1 := ts.BlockHeight() - sp, found := keeper.GetSpec(ts.ctx, "child") + sp, found := ts.getSpec("child") require.True(t, found) - sp, err = keeper.ExpandSpec(ts.ctx, sp) + sp, err = ts.expandSpec(sp) require.Nil(t, err) require.Equal(t, uint64(10), sp.ApiCollections[0].Apis[0].ComputeUnits) require.Equal(t, block1, sp.BlockLastUpdated) - ts.AdvanceBlock(1) + ts.AdvanceBlock() // modify the parent spec and verify that the child is refreshed parentSpec.ApiCollections[0].Apis[0].ComputeUnits = 20 - err = simulateSpecProposal(ts, "parent", "propose parent spec", []types.Spec{parentSpec}) + err = keepertest.SimulateSpecAddProposal(ts.Ctx, ts.Keepers.Spec, []types.Spec{parentSpec}) require.Nil(t, err) - sp, found = keeper.GetSpec(ts.ctx, "parent") + sp, found = ts.getSpec("parent") require.True(t, found) require.Equal(t, uint64(20), sp.ApiCollections[0].Apis[0].ComputeUnits) require.Equal(t, block1+1, sp.BlockLastUpdated) - sp, found = keeper.GetSpec(ts.ctx, "child") + sp, found = ts.getSpec("child") require.True(t, found) - sp, err = keeper.ExpandSpec(ts.ctx, sp) + sp, err = ts.expandSpec(sp) require.Nil(t, err) require.Equal(t, uint64(20), sp.ApiCollections[0].Apis[0].ComputeUnits) require.Equal(t, block1+1, sp.BlockLastUpdated) } func TestApiCollectionsExpandAndInheritance(t *testing.T) { - ts := newTestStruct(t) - keeper := ts.keepers.Spec + ts := newTester(t) - debugme := prepareMockCurrentSpecsForApiCollectionInheritance(&keeper, ts.ctx) + apis := prepareMockApis(20) + ts.setupSpecsForApiInheritance() specTemplates := []struct { desc string @@ -705,8 +699,6 @@ func TestApiCollectionsExpandAndInheritance(t *testing.T) { }, } - apis := prepareMockApis(20) - for _, tt := range specTemplates { sp := types.Spec{ Name: tt.name, @@ -717,8 +709,7 @@ func TestApiCollectionsExpandAndInheritance(t *testing.T) { } t.Run(tt.desc, func(t *testing.T) { - fullspec, err := keeper.ExpandSpec(ts.ctx, sp) - _ = debugme + fullspec, err := ts.expandSpec(sp) if tt.ok == true { // check Result against the baseline spec "test1", "", "", // count apis to totalApis @@ -768,7 +759,7 @@ func TestApiCollectionsExpandAndInheritance(t *testing.T) { } require.True(t, found) } - keeper.SetSpec(ts.ctx, sp) + ts.setSpec(sp) } else { require.Error(t, err, "spec with no error although expected %s", sp.Index) } @@ -777,8 +768,7 @@ func TestApiCollectionsExpandAndInheritance(t *testing.T) { } func TestCookbookSpecs(t *testing.T) { - ts := newTestStruct(t) - keeper := ts.keepers.Spec + ts := newTester(t) getToTopMostPath := "../../../" proposalFile := "./cookbook/specs/spec_add_ibc.json,./cookbook/specs/spec_add_cosmoswasm.json,./cookbook/specs/spec_add_cosmossdk.json,./cookbook/specs/spec_add_cosmossdk_full.json,./cookbook/specs/spec_add_ethereum.json,./cookbook/specs/spec_add_cosmoshub.json,./cookbook/specs/spec_add_lava.json,./cookbook/specs/spec_add_osmosis.json,./cookbook/specs/spec_add_fantom.json,./cookbook/specs/spec_add_celo.json,./cookbook/specs/spec_add_optimism.json,./cookbook/specs/spec_add_arbitrum.json,./cookbook/specs/spec_add_starknet.json,./cookbook/specs/spec_add_aptos.json,./cookbook/specs/spec_add_juno.json,./cookbook/specs/spec_add_polygon.json,./cookbook/specs/spec_add_evmos.json,./cookbook/specs/spec_add_base.json,./cookbook/specs/spec_add_canto.json,./cookbook/specs/spec_add_sui.json,./cookbook/specs/spec_add_solana.json,./cookbook/specs/spec_add_bsc.json,./cookbook/specs/spec_add_axelar.json,./cookbook/specs/spec_add_avalanche.json,./cookbook/specs/spec_add_fvm.json" @@ -793,8 +783,8 @@ func TestCookbookSpecs(t *testing.T) { require.Nil(t, err) for _, sp := range proposal.Proposal.Specs { - keeper.SetSpec(ts.ctx, sp) - fullspec, err := keeper.ExpandSpec(ts.ctx, sp) + ts.setSpec(sp) + fullspec, err := ts.expandSpec(sp) require.NoError(t, err) require.NotNil(t, fullspec) } diff --git a/x/subscription/keeper/grpc_query_current.go b/x/subscription/keeper/grpc_query_current.go index 2b72daca28..be8d908a28 100644 --- a/x/subscription/keeper/grpc_query_current.go +++ b/x/subscription/keeper/grpc_query_current.go @@ -17,10 +17,8 @@ func (k Keeper) Current(goCtx context.Context, req *types.QueryCurrentRequest) ( ctx := sdk.UnwrapSDKContext(goCtx) res := types.QueryCurrentResponse{} - block := uint64(ctx.BlockHeight()) - - var sub types.Subscription - if found := k.subsFS.FindEntry(ctx, req.Consumer, block, &sub); found { + sub, found := k.GetSubscription(ctx, req.Consumer) + if found { res.Sub = &sub } diff --git a/x/subscription/keeper/subscription_test.go b/x/subscription/keeper/subscription_test.go index a780ee464f..2bba67d87f 100644 --- a/x/subscription/keeper/subscription_test.go +++ b/x/subscription/keeper/subscription_test.go @@ -1,9 +1,7 @@ package keeper_test import ( - "context" "strconv" - "strings" "testing" "time" @@ -11,142 +9,67 @@ import ( commontypes "github.com/lavanet/lava/common/types" "github.com/lavanet/lava/testutil/common" keepertest "github.com/lavanet/lava/testutil/keeper" - "github.com/lavanet/lava/utils/sigs" epochstoragetypes "github.com/lavanet/lava/x/epochstorage/types" planstypes "github.com/lavanet/lava/x/plans/types" projectstypes "github.com/lavanet/lava/x/projects/types" - subscriptionkeeper "github.com/lavanet/lava/x/subscription/keeper" "github.com/lavanet/lava/x/subscription/types" "github.com/stretchr/testify/require" ) -func createNPlans(ts *testStruct, n int) []planstypes.Plan { - keeper := &ts.keepers.Plans - - items := make([]planstypes.Plan, n) - plan := common.CreateMockPlan() - - for i := range items { - items[i] = plan - items[i].Index += strconv.Itoa(i + 1) - keeper.AddPlan(ts.ctx, items[i]) - } - - return items -} - -type testStruct struct { - _ctx context.Context - ctx sdk.Context - keepers *keepertest.Keepers - servers *keepertest.Servers - plans []planstypes.Plan -} - -func setupTestStruct(t *testing.T, numPlans int) *testStruct { - servers, keepers, _ctx := keepertest.InitAllKeepers(t) - - _ctx = keepertest.AdvanceEpoch(_ctx, keepers) - ctx := sdk.UnwrapSDKContext(_ctx) - - ts := testStruct{ - _ctx: _ctx, - ctx: ctx, - keepers: keepers, - servers: servers, - } - - ts.plans = createNPlans(&ts, numPlans) - - return &ts -} - -func (ts *testStruct) blockHeight() uint64 { - return uint64(ts.ctx.BlockHeight()) -} - -func (ts *testStruct) advanceBlock(count int, delta ...time.Duration) { - for i := 0; i < count; i += 1 { - ts._ctx = keepertest.AdvanceBlock(ts._ctx, ts.keepers, delta...) - } - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) -} - -func (ts *testStruct) advanceEpoch(delta ...time.Duration) { - ts._ctx = keepertest.AdvanceEpoch(ts._ctx, ts.keepers, delta...) - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) +type tester struct { + common.Tester } -func (ts *testStruct) advanceUntilStale(delta ...time.Duration) { - for i := 0; i < int(commontypes.STALE_ENTRY_TIME); i += 1 { - ts.advanceBlock(1) - } +func newTester(t *testing.T) *tester { + ts := &tester{Tester: *common.NewTester(t)} + ts.AddPlan("mock", common.CreateMockPlan()) + return ts } -// advance block time by given months (minus 1 second) from a given time -// and then trigger begin-epoch for each such month -func (ts *testStruct) advanceMonths(from time.Time, months int) { - for next := from; months > 0; months -= 1 { - next = subscriptionkeeper.NextMonth(next) - delta := next.Sub(ts.ctx.BlockTime()) - time.Second - ts.advanceBlock(1, delta) - ts.advanceEpoch() - } +func (ts *tester) getSubscription(consumer string) (types.Subscription, bool) { + return ts.Keepers.Subscription.GetSubscription(ts.Ctx, consumer) } func TestCreateSubscription(t *testing.T) { - ts := setupTestStruct(t, 3) - keeper := ts.keepers.Subscription - - creators := []struct { - address string - amount int64 - }{ - { - address: "FILL", - amount: 100000, - }, - { - address: "FILL", - amount: 1, - }, - { - address: "invalid creator", - amount: 0, - }, + ts := newTester(t) + ts.SetupAccounts(2, 0, 4) // 2 sub, 0 adm, 4 dev + + _, sub1Addr := ts.Account("sub1") + _, sub2Addr := ts.Account("sub2") + _, dev1Addr := ts.Account("dev1") + _, dev2Addr := ts.Account("dev2") + _, dev3Addr := ts.Account("dev3") + + consumers := []string{dev1Addr, dev2Addr, dev3Addr, "invalid"} + creators := []string{sub1Addr, sub2Addr, "invalid"} + + var plans []planstypes.Plan + for i := 0; i < 3; i++ { + plan := ts.Plan("mock") + plan.Index += strconv.Itoa(i + 1) + plan.Block = ts.BlockHeight() + err := ts.TxProposalAddPlans(plan) + require.Nil(t, err) + plans = append(plans, plan) } - for i := range creators { - if creators[i].address == "FILL" { - account := common.CreateNewAccount(ts._ctx, *ts.keepers, creators[i].amount) - creators[i].address = account.Addr.String() - } - } - - consumers := make([]string, 4) - for i := range consumers { - account := common.CreateNewAccount(ts._ctx, *ts.keepers, 1) - consumers[i] = account.Addr.String() - } - consumers[3] = "invalid consumer" - // delete one plan, and advance to next epoch to take effect - err := ts.keepers.Plans.DelPlan(ts.ctx, ts.plans[2].Index) + err := ts.TxProposalDelPlans(plans[2].Index) require.Nil(t, err) - ts._ctx = keepertest.AdvanceEpoch(ts._ctx, ts.keepers) - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) + + ts.AdvanceEpoch() template := []struct { name string index string creator int consumers []int - duration uint64 + duration int success bool }{ { name: "create subscriptions", - index: ts.plans[0].Index, + index: plans[0].Index, creator: 0, consumers: []int{0, 1}, duration: 1, @@ -154,7 +77,7 @@ func TestCreateSubscription(t *testing.T) { }, { name: "invalid creator", - index: ts.plans[0].Index, + index: plans[0].Index, creator: 2, consumers: []int{2}, duration: 1, @@ -162,7 +85,7 @@ func TestCreateSubscription(t *testing.T) { }, { name: "invalid consumer", - index: ts.plans[0].Index, + index: plans[0].Index, creator: 0, consumers: []int{3}, duration: 1, @@ -170,7 +93,7 @@ func TestCreateSubscription(t *testing.T) { }, { name: "duration too long", - index: ts.plans[0].Index, + index: plans[0].Index, creator: 0, consumers: []int{2}, duration: 13, @@ -178,7 +101,7 @@ func TestCreateSubscription(t *testing.T) { }, { name: "insufficient funds", - index: ts.plans[0].Index, + index: plans[0].Index, creator: 1, consumers: []int{2}, duration: 1, @@ -202,7 +125,7 @@ func TestCreateSubscription(t *testing.T) { }, { name: "deleted plan", - index: ts.plans[2].Index, + index: plans[2].Index, creator: 0, consumers: []int{2}, duration: 1, @@ -210,7 +133,7 @@ func TestCreateSubscription(t *testing.T) { }, { name: "double subscription", - index: ts.plans[1].Index, + index: plans[1].Index, creator: 0, consumers: []int{0}, duration: 1, @@ -222,16 +145,15 @@ func TestCreateSubscription(t *testing.T) { for _, consumer := range tt.consumers { t.Run(tt.name, func(t *testing.T) { sub := types.Subscription{ - Creator: creators[tt.creator].address, + Creator: creators[tt.creator], Consumer: consumers[consumer], PlanIndex: tt.index, } - err := keeper.CreateSubscription( - ts.ctx, sub.Creator, sub.Consumer, sub.PlanIndex, tt.duration) + _, err := ts.TxSubscriptionBuy(sub.Creator, sub.Consumer, sub.PlanIndex, tt.duration) if tt.success { require.Nil(t, err, tt.name) - _, found := keeper.GetSubscription(ts.ctx, sub.Consumer) + _, found := ts.getSubscription(sub.Consumer) require.True(t, found, tt.name) } else { require.NotNil(t, err, tt.name) @@ -242,234 +164,197 @@ func TestCreateSubscription(t *testing.T) { } func TestSubscriptionExpiration(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription - - account := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - creator := account.Addr.String() - - blocksToSave, err := ts.keepers.Epochstorage.BlocksToSave(ts.ctx, uint64(ts.ctx.BlockHeight())) - require.Nil(t, err) + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 2 sub, 0 adm, 0 dev - // fill memory - for uint64(ts.ctx.BlockHeight()) < blocksToSave { - ts.advanceBlock(1) - } + _, sub1Addr := ts.Account("sub1") + plan := ts.Plan("mock") - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 1) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) - - _, found := keeper.GetSubscription(ts.ctx, creator) + _, found := ts.getSubscription(sub1Addr) require.True(t, found) - timestamp := ts.ctx.BlockTime() + // advance 1 month + epoch, subscription should expire + ts.AdvanceMonths(1) + ts.AdvanceEpoch() - ts.advanceMonths(timestamp, 1) - _, found = keeper.GetSubscription(ts.ctx, creator) + _, found = ts.getSubscription(sub1Addr) require.False(t, found) } func TestRenewSubscription(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription - - account := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - creator := account.Addr.String() - - blocksToSave, err := ts.keepers.Epochstorage.BlocksToSave(ts.ctx, uint64(ts.ctx.BlockHeight())) - require.Nil(t, err) + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - // fill memory - for uint64(ts.ctx.BlockHeight()) < blocksToSave { - ts.advanceBlock(1) - } + _, sub1Addr := ts.Account("sub1") + plan := ts.Plan("mock") - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 6) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 6) require.Nil(t, err) - - timestamp := ts.ctx.BlockTime() - - _, found := keeper.GetSubscription(ts.ctx, creator) + _, found := ts.getSubscription(sub1Addr) require.True(t, found) // fast-forward three months - ts.advanceMonths(timestamp, 3) - sub, found := keeper.GetSubscription(ts.ctx, creator) + ts.AdvanceMonths(3).AdvanceEpoch() + sub, found := ts.getSubscription(sub1Addr) require.True(t, found) require.Equal(t, uint64(3), sub.DurationLeft) // with 3 months duration left, asking for 12 more should fail - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 12) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 12) require.NotNil(t, err) - // but asking for additional 9 months (10 would also be fine (the extra month extension below)) - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 9) + // but 9 additional month (even 10, the extra month extension below) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 9) require.Nil(t, err) - - sub, found = keeper.GetSubscription(ts.ctx, creator) + sub, found = ts.getSubscription(sub1Addr) require.True(t, found) require.Equal(t, uint64(12), sub.DurationLeft) require.Equal(t, uint64(9), sub.DurationTotal) // edit the subscription's plan (allow more CU) - subPlan, found := ts.keepers.Plans.FindPlan(ts.ctx, sub.PlanIndex, sub.PlanBlock) - require.True(t, found) - oldPlanCuPerEpoch := subPlan.PlanPolicy.EpochCuLimit - subPlan.PlanPolicy.EpochCuLimit += 100 - err = keepertest.SimulatePlansAddProposal(ts.ctx, ts.keepers.Plans, []planstypes.Plan{subPlan}) + cuPerEpoch := plan.PlanPolicy.EpochCuLimit + plan.PlanPolicy.EpochCuLimit += 100 + + err = keepertest.SimulatePlansAddProposal(ts.Ctx, ts.Keepers.Plans, []planstypes.Plan{plan}) require.Nil(t, err) - // try extending the subscription (normally we could extend with 1 more month, but since the - // subscription's plan changed, the extension should fail) - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 1) + // try extending the subscription (we could extend with 1 more month, + // but since the subscription's plan changed, the extension should fail) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.NotNil(t, err) require.Equal(t, uint64(12), sub.DurationLeft) require.Equal(t, uint64(9), sub.DurationTotal) // get the subscription's plan and make sure it uses the old plan - subPlan, found = ts.keepers.Plans.FindPlan(ts.ctx, sub.PlanIndex, sub.PlanBlock) + plan, found = ts.FindPlan(sub.PlanIndex, sub.PlanBlock) require.True(t, found) - require.Equal(t, oldPlanCuPerEpoch, subPlan.PlanPolicy.EpochCuLimit) + require.Equal(t, cuPerEpoch, plan.PlanPolicy.EpochCuLimit) // delete the plan, and try to renew the subscription again - err = ts.keepers.Plans.DelPlan(ts.ctx, ts.plans[0].Index) + err = ts.TxProposalDelPlans(plan.Index) require.Nil(t, err) - ts._ctx = keepertest.AdvanceEpoch(ts._ctx, ts.keepers) - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) + + ts.AdvanceEpoch() // fast-forward another month, renewal should fail - ts.advanceMonths(timestamp, 1) - _, found = keeper.GetSubscription(ts.ctx, creator) + ts.AdvanceMonths(1).AdvanceEpoch() + _, found = ts.getSubscription(sub1Addr) require.True(t, found) - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 10) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 10) require.NotNil(t, err) } func TestSubscriptionAdminProject(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - account := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - creator := account.Addr.String() + _, sub1Addr := ts.Account("sub1") + plan := ts.Plan("mock") - err := keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 1) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) - block := uint64(ts.ctx.BlockHeight()) - // a newly created subscription is expected to have one default project, // with the subscription address as its developer key - _, err = ts.keepers.Projects.GetProjectDeveloperData(ts.ctx, creator, block) + _, err = ts.GetProjectDeveloperData(sub1Addr, ts.BlockHeight()) require.Nil(t, err) } func TestMonthlyRechargeCU(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription - projectKeeper := ts.keepers.Projects + ts := newTester(t) + ts.SetupAccounts(1, 1, 1) // 1 sub, 1 adm, 1 dev - account := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - anotherAccount := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - creator := account.Addr.String() + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("adm1") + _, dev1Addr := ts.Account("dev1") + plan := ts.Plan("mock") - blocksToSave, err := ts.keepers.Epochstorage.BlocksToSave(ts.ctx, uint64(ts.ctx.BlockHeight())) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 3) require.Nil(t, err) - // fill memory - for uint64(ts.ctx.BlockHeight()) < blocksToSave { - ts.advanceBlock(1) - } - - err = keeper.CreateSubscription(ts.ctx, creator, creator, ts.plans[0].Index, 3) - require.Nil(t, err) - - timestamp := ts.ctx.BlockTime() - // add another project under the subcscription projectData := projectstypes.ProjectData{ Name: "another_project", Enabled: true, ProjectKeys: []projectstypes.ProjectKey{ - projectstypes.ProjectDeveloperKey(anotherAccount.Addr.String()), + projectstypes.ProjectDeveloperKey(dev1Addr), }, Policy: &planstypes.Policy{ - GeolocationProfile: uint64(1), + GeolocationProfile: 1, TotalCuLimit: 1000, EpochCuLimit: 100, MaxProvidersToPair: 3, }, } - err = keeper.AddProjectToSubscription(ts.ctx, creator, projectData) + err = ts.TxSubscriptionAddProject(sub1Addr, projectData) require.Nil(t, err) - // we'll test both the default project and the second project, which differ in their developers template := []struct { name string subscription string developer string - usedCuPerProject uint64 // total CU in sub is 1000 -- let each project use 500 + usedCuPerProject uint64 // total sub CU is 1000; each project uses 500 }{ - {"default project", creator, creator, 500}, - {"second project (non-default)", creator, anotherAccount.Addr.String(), 500}, + {"default project", sub1Addr, sub1Addr, 500}, + {"second project (non-default)", sub1Addr, dev1Addr, 500}, } for ti, tt := range template { t.Run(tt.name, func(t *testing.T) { - block1 := uint64(ts.ctx.BlockHeight()) - - ts._ctx = keepertest.AdvanceEpoch(ts._ctx, ts.keepers) - ts.ctx = sdk.UnwrapSDKContext(ts._ctx) + block1 := ts.BlockHeight() + ts.AdvanceEpoch() // charge the subscription - err = keeper.ChargeComputeUnitsToSubscription(ts.ctx, tt.subscription, block1, tt.usedCuPerProject) + err = ts.Keepers.Subscription.ChargeComputeUnitsToSubscription( + ts.Ctx, tt.subscription, block1, tt.usedCuPerProject) require.Nil(t, err) - sub, found := keeper.GetSubscription(ts.ctx, tt.subscription) - require.True(t, found) - // verify the CU charge of the subscription is updated correctly - // it depends on the iteration index since the same subscription is charged for all projects + sub, found := ts.getSubscription(tt.subscription) + require.True(t, found) require.Equal(t, sub.MonthCuLeft, sub.MonthCuTotal-tt.usedCuPerProject) - proj, err := projectKeeper.GetProjectForDeveloper(ts.ctx, tt.developer, block1) - require.Nil(t, err) // charge the project - err = projectKeeper.ChargeComputeUnitsToProject(ts.ctx, proj, block1, tt.usedCuPerProject) + proj, err := ts.GetProjectForDeveloper(tt.developer, block1) + require.Nil(t, err) + err = ts.Keepers.Projects.ChargeComputeUnitsToProject( + ts.Ctx, proj, block1, tt.usedCuPerProject) require.Nil(t, err) // verify that project used the CU - proj, err = projectKeeper.GetProjectForDeveloper(ts.ctx, tt.developer, block1) + proj, err = ts.GetProjectForDeveloper(tt.developer, block1) require.Nil(t, err) require.Equal(t, tt.usedCuPerProject, proj.UsedCu) - block2 := uint64(ts.ctx.BlockHeight()) + block2 := ts.BlockHeight() // force fixation entry (by adding project key) - admAddr := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - projKey := []projectstypes.ProjectKey{projectstypes.ProjectAdminKey(admAddr)} - projectKeeper.AddKeysToProject(ts.ctx, projectstypes.ADMIN_PROJECT_NAME, tt.developer, projKey) + projKey := []projectstypes.ProjectKey{projectstypes.ProjectAdminKey(adm1Addr)} + ts.Keepers.Projects.AddKeysToProject(ts.Ctx, projectstypes.ADMIN_PROJECT_NAME, tt.developer, projKey) - // fast-forward one month (since we expire the subscription in every iteration, it depends on the iteration number) - ts.advanceMonths(timestamp, ti+1) - sub, found = keeper.GetSubscription(ts.ctx, creator) + // fast-forward one month + ts.AdvanceMonths(1).AdvanceEpoch() + sub, found = ts.getSubscription(sub1Addr) require.True(t, found) require.Equal(t, sub.DurationTotal-uint64(ti+1), sub.DurationLeft) - block3 := uint64(ts.ctx.BlockHeight()) + block3 := ts.BlockHeight() - // check that subscription and project have renewed CUs, and that the - // project created a snapshot for last month - sub, found = keeper.GetSubscription(ts.ctx, tt.subscription) + // check that subscription and project have renewed CUs, and that + // the project created a snapshot for last month + sub, found = ts.getSubscription(tt.subscription) require.True(t, found) require.Equal(t, sub.MonthCuLeft, sub.MonthCuTotal) - proj, err = projectKeeper.GetProjectForDeveloper(ts.ctx, tt.developer, block1) + proj, err = ts.GetProjectForDeveloper(tt.developer, block1) require.Nil(t, err) require.Equal(t, tt.usedCuPerProject, proj.UsedCu) - proj, err = projectKeeper.GetProjectForDeveloper(ts.ctx, tt.developer, block2) + proj, err = ts.GetProjectForDeveloper(tt.developer, block2) require.Nil(t, err) require.Equal(t, tt.usedCuPerProject, proj.UsedCu) - proj, err = projectKeeper.GetProjectForDeveloper(ts.ctx, tt.developer, block3) + proj, err = ts.GetProjectForDeveloper(tt.developer, block3) require.Nil(t, err) require.Equal(t, uint64(0), proj.UsedCu) }) @@ -477,18 +362,12 @@ func TestMonthlyRechargeCU(t *testing.T) { } func TestExpiryTime(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription - - // AdvanceBlock() always uses the current time for the first block (and - // (ignores the time delta arg if given); So call it here first to avoid - // the call below being the first and having the delta are ignored. - ts.advanceBlock(1) + ts := newTester(t) template := []struct { now [3]int // year, month, day res [3]int // year, month, day - months uint64 + months int }{ // monthly {[3]int{2000, 3, 1}, [3]int{2000, 4, 1}, 1}, @@ -513,87 +392,79 @@ func TestExpiryTime(t *testing.T) { {[3]int{2000, 3, 1}, [3]int{2000, 4, 1}, 12}, } - plan := ts.plans[0] + plan := ts.Plan("mock") for _, tt := range template { now := time.Date(tt.now[0], time.Month(tt.now[1]), tt.now[2], 12, 0, 0, 0, time.UTC) t.Run(now.Format("2006-01-02"), func(t *testing.T) { - // TODO: need new creator because Projects doesn't really delete projects - creator := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + // new account per attempt + _, sub1Addr := ts.AddAccount("tmp", 10000).Account("tmp") - delta := now.Sub(ts.ctx.BlockTime()) - ts.advanceBlock(1, delta) + delta := now.Sub(ts.BlockTime()) + ts.AdvanceBlock(delta) - err := keeper.CreateSubscription(ts.ctx, creator, creator, plan.Index, tt.months) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, tt.months) require.Nil(t, err) - sub, found := keeper.GetSubscription(ts.ctx, creator) + sub, found := ts.getSubscription(sub1Addr) require.True(t, found) - require.Equal(t, tt.months, sub.DurationTotal) + require.Equal(t, uint64(tt.months), sub.DurationTotal) // will expire and remove - timestamp := ts.ctx.BlockTime() - ts.advanceMonths(timestamp, int(tt.months)) - ts.advanceUntilStale() + ts.AdvanceMonths(tt.months).AdvanceEpoch() + ts.AdvanceBlockUntilStale() }) } } func TestSubscriptionExpire(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - _, account := sigs.GenerateFloatingKey() - coins := sdk.NewCoins(sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(10000))) - ts.keepers.BankKeeper.SetBalance(ts.ctx, account, coins) + sub1Acct, sub1Addr := ts.Account("sub1") + plan := ts.Plan("mock") - consumer := account.String() - - blocksToSave, err := ts.keepers.Epochstorage.BlocksToSave(ts.ctx, uint64(ts.ctx.BlockHeight())) - require.Nil(t, err) + coins := sdk.NewCoins(sdk.NewCoin(epochstoragetypes.TokenDenom, sdk.NewInt(10000))) + ts.Keepers.BankKeeper.SetBalance(ts.Ctx, sub1Acct.Addr, coins) - err = keeper.CreateSubscription(ts.ctx, consumer, consumer, "mockPlan1", 1) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) - block := uint64(ts.ctx.BlockHeight()) - timestamp := ts.ctx.BlockTime() - - // fill memory - for uint64(ts.ctx.BlockHeight()) < blocksToSave { - ts.advanceBlock(1) - } + block := ts.BlockHeight() - _, found := keeper.GetSubscription(ts.ctx, consumer) + _, found := ts.getSubscription(sub1Addr) require.True(t, found) - err = keeper.ChargeComputeUnitsToSubscription(ts.ctx, consumer, block, 10) + err = ts.Keepers.Subscription.ChargeComputeUnitsToSubscription( + ts.Ctx, sub1Addr, block, 10) require.Nil(t, err) // fast-forward one month - ts.advanceMonths(timestamp, 1) + ts.AdvanceMonths(1).AdvanceEpoch() // subscription no longer searchable, but can still charge for previous usage - _, found = keeper.GetSubscription(ts.ctx, consumer) + _, found = ts.getSubscription(sub1Addr) require.False(t, found) - err = keeper.ChargeComputeUnitsToSubscription(ts.ctx, consumer, block, 10) + err = ts.Keepers.Subscription.ChargeComputeUnitsToSubscription( + ts.Ctx, sub1Addr, block, 10) require.Nil(t, err) - ts.advanceUntilStale() + ts.AdvanceBlockUntilStale() // subscription no longer charge-able for previous usage - err = keeper.ChargeComputeUnitsToSubscription(ts.ctx, consumer, block, 10) + err = ts.Keepers.Subscription.ChargeComputeUnitsToSubscription( + ts.Ctx, sub1Addr, block, 10) require.NotNil(t, err) } func TestPrice(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription + ts := newTester(t) template := []struct { name string - duration uint64 + duration int discount uint64 price int64 cost int64 @@ -607,51 +478,42 @@ func TestPrice(t *testing.T) { for _, tt := range template { t.Run(tt.name, func(t *testing.T) { - // NOTE: need new creator because Projects doesn't really delete projects - address := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr - creator := address.String() + // new account per attempt + sub1Acct, sub1Addr := ts.AddAccount("tmp", 10000).Account("tmp") - plan := ts.plans[0] + plan := ts.Plan("mock") plan.AnnualDiscountPercentage = tt.discount plan.Price = sdk.NewCoin("ulava", sdk.NewInt(tt.price)) - ts.keepers.Plans.AddPlan(ts.ctx, plan) + err := ts.TxProposalAddPlans(plan) + require.Nil(t, err) - err := keeper.CreateSubscription(ts.ctx, creator, creator, plan.Index, tt.duration) + _, err = ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, tt.duration) require.Nil(t, err) - _, found := keeper.GetSubscription(ts.ctx, creator) + _, found := ts.getSubscription(sub1Addr) require.True(t, found) - balance := ts.keepers.BankKeeper.GetBalance(ts.ctx, address, epochstoragetypes.TokenDenom) + balance := ts.Keepers.BankKeeper.GetBalance(ts.Ctx, sub1Acct.Addr, epochstoragetypes.TokenDenom) require.Equal(t, balance.Amount.Int64(), 10000-tt.cost) // will expire and remove - timestamp := ts.ctx.BlockTime() - ts.advanceMonths(timestamp, int(tt.duration)) + ts.AdvanceMonths(tt.duration) }) } } func TestAddProjectToSubscription(t *testing.T) { - ts := setupTestStruct(t, 1) - keeper := ts.keepers.Subscription - plan := ts.plans[0] - - subPayer := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - consumer := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) - regularAccount := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000) + ts := newTester(t) + ts.SetupAccounts(1, 1, 1) // 1 sub, 0 adm, 2 dev - subPayerAddr := subPayer.Addr.String() - consumerAddr := consumer.Addr.String() - regularAccountAddr := regularAccount.Addr.String() + _, sub1Addr := ts.Account("sub1") + _, adm1Addr := ts.Account("adm1") + _, dev1Addr := ts.Account("dev1") + plan := ts.Plan("mock") - err := keeper.CreateSubscription(ts.ctx, subPayerAddr, consumerAddr, plan.Index, 1) + _, err := ts.TxSubscriptionBuy(sub1Addr, dev1Addr, plan.Index, 1) require.Nil(t, err) - defaultProjectName := projectstypes.ADMIN_PROJECT_NAME - longProjectName := strings.Repeat(defaultProjectName, projectstypes.MAX_PROJECT_NAME_LEN) - invalidProjectName := "project_name," - template := []struct { name string subscription string @@ -659,13 +521,11 @@ func TestAddProjectToSubscription(t *testing.T) { projectName string success bool }{ - {"project admin = regular account", consumerAddr, regularAccountAddr, "test1", true}, - {"project admin = subscription payer account", consumerAddr, subPayerAddr, "test2", true}, - {"bad subscription account (regular account)", regularAccountAddr, consumerAddr, "test4", false}, - {"bad subscription account (subscription payer account)", subPayerAddr, consumerAddr, "test5", false}, - {"bad projectName (duplicate)", consumerAddr, regularAccountAddr, defaultProjectName, false}, - {"bad projectName (too long)", consumerAddr, regularAccountAddr, longProjectName, false}, - {"bad projectName (contains comma)", consumerAddr, regularAccountAddr, invalidProjectName, false}, + {"project admin = regular account", dev1Addr, adm1Addr, "test1", true}, + {"project admin = subscription payer account", dev1Addr, sub1Addr, "test2", true}, + {"bad subscription account (regular account)", adm1Addr, dev1Addr, "test3", false}, + {"bad subscription account (subscription payer account)", sub1Addr, dev1Addr, "test4", false}, + {"bad projectName (duplicate)", dev1Addr, adm1Addr, "invalid:name", false}, } for _, tt := range template { @@ -677,11 +537,11 @@ func TestAddProjectToSubscription(t *testing.T) { projectstypes.ProjectAdminKey(tt.anotherAdmin), }, } - err = keeper.AddProjectToSubscription(ts.ctx, tt.subscription, projectData) + projectID := projectstypes.ProjectIndex(tt.subscription, tt.projectName) + err = ts.TxSubscriptionAddProject(tt.subscription, projectData) if tt.success { require.Nil(t, err) - ts.advanceEpoch() - proj, err := ts.keepers.Projects.GetProjectForBlock(ts.ctx, projectstypes.ProjectIndex(tt.subscription, tt.projectName), uint64(ts.ctx.BlockHeight())) + proj, err := ts.GetProjectForBlock(projectID, ts.BlockHeight()) require.Nil(t, err) require.Equal(t, tt.subscription, proj.Subscription) } else { @@ -692,27 +552,17 @@ func TestAddProjectToSubscription(t *testing.T) { } func TestGetProjectsForSubscription(t *testing.T) { - ts := setupTestStruct(t, 1) - plan := ts.plans[0] + ts := newTester(t) + ts.SetupAccounts(2, 0, 0) // 2 sub, 0 adm, 0 dev - subAcc1 := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() - subAcc2 := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + _, sub1Addr := ts.Account("sub1") + _, sub2Addr := ts.Account("sub2") + plan := ts.Plan("mock") // buy two subscriptions - _, err := ts.servers.SubscriptionServer.Buy(ts._ctx, &types.MsgBuy{ - Creator: subAcc1, - Consumer: subAcc1, - Index: plan.Index, - Duration: 1, - }) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) - - _, err = ts.servers.SubscriptionServer.Buy(ts._ctx, &types.MsgBuy{ - Creator: subAcc2, - Consumer: subAcc2, - Index: plan.Index, - Duration: 1, - }) + _, err = ts.TxSubscriptionBuy(sub2Addr, sub2Addr, plan.Index, 1) require.Nil(t, err) // add two projects to the first subscription @@ -721,10 +571,7 @@ func TestGetProjectsForSubscription(t *testing.T) { Enabled: true, Policy: &plan.PlanPolicy, } - _, err = ts.servers.SubscriptionServer.AddProject(ts._ctx, &types.MsgAddProject{ - Creator: subAcc1, - ProjectData: projData1, - }) + err = ts.TxSubscriptionAddProject(sub1Addr, projData1) require.Nil(t, err) projData2 := projectstypes.ProjectData{ @@ -732,101 +579,72 @@ func TestGetProjectsForSubscription(t *testing.T) { Enabled: false, Policy: &plan.PlanPolicy, } - _, err = ts.servers.SubscriptionServer.AddProject(ts._ctx, &types.MsgAddProject{ - Creator: subAcc1, - ProjectData: projData2, - }) + err = ts.TxSubscriptionAddProject(sub1Addr, projData2) require.Nil(t, err) - res1, err := ts.keepers.Subscription.ListProjects(ts._ctx, &types.QueryListProjectsRequest{ - Subscription: subAcc1, - }) + res1, err := ts.QuerySubscriptionListProjects(sub1Addr) require.Nil(t, err) - res2, err := ts.keepers.Subscription.ListProjects(ts._ctx, &types.QueryListProjectsRequest{ - Subscription: subAcc2, - }) + res2, err := ts.QuerySubscriptionListProjects(sub2Addr) require.Nil(t, err) - // number of projects expected (+1 because there's an auto-generated admin project) + // number of projects +1 to account for auto-generated admin project require.Equal(t, 3, len(res1.Projects)) require.Equal(t, 1, len(res2.Projects)) - _, err = ts.servers.SubscriptionServer.DelProject(ts._ctx, &types.MsgDelProject{ - Creator: subAcc1, - Name: projData2.Name, - }) + err = ts.TxSubscriptionDelProject(sub1Addr, projData2.Name) require.Nil(t, err) } func TestAddDelProjectForSubscription(t *testing.T) { - ts := setupTestStruct(t, 1) - plan := ts.plans[0] + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - subAcc := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + _, sub1Addr := ts.Account("sub1") + plan := ts.Plan("mock") - // buy subscription - _, err := ts.servers.SubscriptionServer.Buy(ts._ctx, &types.MsgBuy{ - Creator: subAcc, - Consumer: subAcc, - Index: plan.Index, - Duration: 1, - }) + // buy subscription and add project + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) - // add project to the subscription projData := projectstypes.ProjectData{ Name: "proj", Enabled: true, Policy: &plan.PlanPolicy, } - _, err = ts.servers.SubscriptionServer.AddProject(ts._ctx, &types.MsgAddProject{ - Creator: subAcc, - ProjectData: projData, - }) + err = ts.TxSubscriptionAddProject(sub1Addr, projData) require.Nil(t, err) - ts.advanceEpoch() + ts.AdvanceEpoch() - res, err := ts.keepers.Subscription.ListProjects(ts._ctx, &types.QueryListProjectsRequest{ - Subscription: subAcc, - }) + res, err := ts.QuerySubscriptionListProjects(sub1Addr) require.Nil(t, err) require.Equal(t, 2, len(res.Projects)) // del project to the subscription - _, err = ts.servers.SubscriptionServer.DelProject(ts._ctx, &types.MsgDelProject{ - Creator: subAcc, - Name: projData.Name, - }) + err = ts.TxSubscriptionDelProject(sub1Addr, projData.Name) require.Nil(t, err) - ts.advanceEpoch() + ts.AdvanceEpoch() - res, err = ts.keepers.Subscription.ListProjects(ts._ctx, &types.QueryListProjectsRequest{ - Subscription: subAcc, - }) + res, err = ts.QuerySubscriptionListProjects(sub1Addr) require.Nil(t, err) require.Equal(t, 1, len(res.Projects)) } func TestDelProjectEndSubscription(t *testing.T) { - ts := setupTestStruct(t, 1) - plan := ts.plans[0] + ts := newTester(t) + ts.SetupAccounts(1, 0, 0) // 1 sub, 0 adm, 0 dev - subAcc := common.CreateNewAccount(ts._ctx, *ts.keepers, 10000).Addr.String() + _, sub1Addr := ts.Account("sub1") + plan := ts.Plan("mock") // buy subscription - _, err := ts.servers.SubscriptionServer.Buy(ts._ctx, &types.MsgBuy{ - Creator: subAcc, - Consumer: subAcc, - Index: plan.Index, - Duration: 1, - }) + _, err := ts.TxSubscriptionBuy(sub1Addr, sub1Addr, plan.Index, 1) require.Nil(t, err) // time of buy subscription - start := ts.ctx.BlockTime() + start := ts.BlockTime() // add project to the subscription projData := projectstypes.ProjectData{ @@ -834,41 +652,29 @@ func TestDelProjectEndSubscription(t *testing.T) { Enabled: true, Policy: &plan.PlanPolicy, } - _, err = ts.servers.SubscriptionServer.AddProject(ts._ctx, &types.MsgAddProject{ - Creator: subAcc, - ProjectData: projData, - }) + err = ts.TxSubscriptionAddProject(sub1Addr, projData) require.Nil(t, err) - ts.advanceEpoch() + ts.AdvanceEpoch() - res, err := ts.keepers.Subscription.ListProjects(ts._ctx, &types.QueryListProjectsRequest{ - Subscription: subAcc, - }) + res, err := ts.QuerySubscriptionListProjects(sub1Addr) require.Nil(t, err) require.Equal(t, 2, len(res.Projects)) - // advance time to just before subscription expiry, so project deletion and the - // subsequent expiry will occur in the same epoch - next := subscriptionkeeper.NextMonth(start) - delta := next.Sub(ts.ctx.BlockTime()) - time.Second - ts.advanceBlock(1, delta) + // advance time to just before subscription expiry, so project deletion + // and the subsequent expiry will occur in the same epoch + ts.AdvanceMonthsFrom(start, 1) // del project to the subscription - _, err = ts.servers.SubscriptionServer.DelProject(ts._ctx, &types.MsgDelProject{ - Creator: subAcc, - Name: projData.Name, - }) + err = ts.TxSubscriptionDelProject(sub1Addr, projData.Name) require.Nil(t, err) // expire subscription (by advancing an epoch, we are close enough to expiry) - ts.advanceEpoch() + ts.AdvanceEpoch() - _, err = ts.keepers.Subscription.ListProjects(ts._ctx, &types.QueryListProjectsRequest{ - Subscription: subAcc, - }) + _, err = ts.QuerySubscriptionListProjects(sub1Addr) require.NotNil(t, err) // should not panic - ts.advanceBlock(2 * int(commontypes.STALE_ENTRY_TIME)) + ts.AdvanceBlock(2 * commontypes.STALE_ENTRY_TIME) }