From 16d21c4dedf616257bacaf75de9ce107f90104d3 Mon Sep 17 00:00:00 2001 From: Tanmay Date: Tue, 10 Sep 2024 18:14:27 -0400 Subject: [PATCH] add unit tests --- testutil/keeper/mocks/emissions/observer.go | 6 +- x/observer/keeper/ballot_test.go | 190 ++++++++++++++++++++ x/observer/keeper/events_test.go | 75 ++++++++ x/observer/keeper/grpc_query_ballot_test.go | 2 +- x/observer/types/ballot.go | 1 + x/observer/types/ballot_test.go | 105 +++++++++++ 6 files changed, 375 insertions(+), 4 deletions(-) create mode 100644 x/observer/keeper/events_test.go diff --git a/testutil/keeper/mocks/emissions/observer.go b/testutil/keeper/mocks/emissions/observer.go index 2a10eea3dd..fd50cf5fbe 100644 --- a/testutil/keeper/mocks/emissions/observer.go +++ b/testutil/keeper/mocks/emissions/observer.go @@ -15,9 +15,9 @@ type EmissionObserverKeeper struct { mock.Mock } -// ClearMaturedBallots provides a mock function with given fields: ctx, maturityBlocks -func (_m *EmissionObserverKeeper) ClearMaturedBallots(ctx types.Context, maturityBlocks int64) { - _m.Called(ctx, maturityBlocks) +// ClearMaturedBallots provides a mock function with given fields: ctx, ballots, maturityBlocks +func (_m *EmissionObserverKeeper) ClearMaturedBallots(ctx types.Context, ballots []observertypes.Ballot, maturityBlocks int64) { + _m.Called(ctx, ballots, maturityBlocks) } // GetBallot provides a mock function with given fields: ctx, index diff --git a/x/observer/keeper/ballot_test.go b/x/observer/keeper/ballot_test.go index 2f88571558..a5d2d0eafe 100644 --- a/x/observer/keeper/ballot_test.go +++ b/x/observer/keeper/ballot_test.go @@ -118,3 +118,193 @@ func TestKeeper_GetAllBallots(t *testing.T) { require.Equal(t, 1, len(ballots)) require.Equal(t, b, ballots[0]) } + +func TestKeeper_DeleteBallot(t *testing.T) { + t.Run("delete existing ballot", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + b := &types.Ballot{ + BallotIdentifier: identifier, + } + k.SetBallot(ctx, b) + _, found := k.GetBallot(ctx, identifier) + require.True(t, found) + + //Act + k.DeleteBallot(ctx, identifier) + + //Assert + _, found = k.GetBallot(ctx, identifier) + require.False(t, found) + }) + + t.Run("delete non-existing ballot,nothing happens", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + identifier := sample.ZetaIndex(t) + numberOfBallots := 10 + for i := 0; i < numberOfBallots; i++ { + k.SetBallot(ctx, &types.Ballot{ + BallotIdentifier: sample.ZetaIndex(t), + }) + } + + require.Len(t, k.GetAllBallots(ctx), numberOfBallots) + + //Act + k.DeleteBallot(ctx, identifier) + + //Assert + _, found := k.GetBallot(ctx, identifier) + require.False(t, found) + require.Len(t, k.GetAllBallots(ctx), numberOfBallots) + }) +} + +func TestKeeper_DeleteBallotList(t *testing.T) { + t.Run("delete existing ballot list", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + numberOfBallotLists := 10 + for i := 0; i < numberOfBallotLists; i++ { + k.AddBallotToList(ctx, types.Ballot{ + Index: sample.ZetaIndex(t), + BallotCreationHeight: 1, + }) + } + + _, found := k.GetBallotList(ctx, 1) + require.True(t, found) + + //Act + k.DeleteBallotList(ctx, 1) + + //Assert + _, found = k.GetBallotList(ctx, 1) + require.False(t, found) + }) + + t.Run("delete non-existing ballot list, nothing happens", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + numberOfBallotLists := 10 + for i := 0; i < numberOfBallotLists; i++ { + k.AddBallotToList(ctx, types.Ballot{ + Index: sample.ZetaIndex(t), + BallotCreationHeight: 1, + }) + } + + _, found := k.GetBallotList(ctx, 1) + require.True(t, found) + + //Act + k.DeleteBallotList(ctx, 2) + + //Assert + _, found = k.GetBallotList(ctx, 1) + require.True(t, found) + }) +} + +func TestKeeper_ClearMaturedBallots(t *testing.T) { + t.Run("clear matured ballots successfully", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + numberOfBallots := 10 + ballots := make([]types.Ballot, numberOfBallots) + for i := 0; i < numberOfBallots; i++ { + b := types.Ballot{ + BallotIdentifier: sample.ZetaIndex(t), + BallotCreationHeight: 1, + } + k.AddBallotToList(ctx, b) + k.SetBallot(ctx, &b) + ballots[i] = b + } + _, found := k.GetBallotList(ctx, 1) + require.True(t, found) + require.Equal(t, numberOfBallots, len(k.GetAllBallots(ctx))) + + //Act + k.ClearMaturedBallots(ctx, ballots, 0) + + //Assert + for _, b := range ballots { + _, found = k.GetBallot(ctx, b.BallotIdentifier) + require.False(t, found) + } + _, found = k.GetBallotList(ctx, 0) + require.False(t, found) + eventCount := 0 + for _, event := range ctx.EventManager().Events() { + if event.Type == "zetachain.zetacore.observer.EventBallotDeleted" { + eventCount++ + } + } + require.Equal(t, numberOfBallots, eventCount) + }) + + t.Run("clear only ballotList if no ballots are found", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + numberOfBallots := 10 + ballots := make([]types.Ballot, numberOfBallots) + for i := 0; i < numberOfBallots; i++ { + b := types.Ballot{ + BallotIdentifier: sample.ZetaIndex(t), + BallotCreationHeight: 1, + } + k.AddBallotToList(ctx, b) + ballots[i] = b + } + _, found := k.GetBallotList(ctx, 1) + require.True(t, found) + require.Equal(t, 0, len(k.GetAllBallots(ctx))) + + //Act + k.ClearMaturedBallots(ctx, []types.Ballot{}, 0) + + //Assert + _, found = k.GetBallotList(ctx, 1) + require.False(t, found) + require.Equal(t, 0, len(k.GetAllBallots(ctx))) + }) + + t.Run("clear only ballots successfully if ballotList is not found", func(t *testing.T) { + //Arrange + k, ctx, _, _ := keepertest.ObserverKeeper(t) + numberOfBallots := 10 + ballots := make([]types.Ballot, numberOfBallots) + for i := 0; i < numberOfBallots; i++ { + b := types.Ballot{ + BallotIdentifier: sample.ZetaIndex(t), + BallotCreationHeight: 1, + } + k.SetBallot(ctx, &b) + ballots[i] = b + } + require.Equal(t, numberOfBallots, len(k.GetAllBallots(ctx))) + _, found := k.GetBallotList(ctx, 1) + require.False(t, found) + + //Act + k.ClearMaturedBallots(ctx, ballots, 0) + + //Assert + for _, b := range ballots { + _, found := k.GetBallot(ctx, b.BallotIdentifier) + require.False(t, found) + } + _, found = k.GetBallotList(ctx, 1) + require.False(t, found) + eventCount := 0 + for _, event := range ctx.EventManager().Events() { + if event.Type == "zetachain.zetacore.observer.EventBallotDeleted" { + eventCount++ + } + } + require.Equal(t, numberOfBallots, eventCount) + }) +} diff --git a/x/observer/keeper/events_test.go b/x/observer/keeper/events_test.go new file mode 100644 index 0000000000..6f286e141e --- /dev/null +++ b/x/observer/keeper/events_test.go @@ -0,0 +1,75 @@ +package keeper_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/require" + keepertest "github.com/zeta-chain/node/testutil/keeper" + "github.com/zeta-chain/node/testutil/sample" + "github.com/zeta-chain/node/x/observer/keeper" + "github.com/zeta-chain/node/x/observer/types" +) + +func TestEmitEventBallotDeleted(t *testing.T) { + tt := []struct { + name string + ballotIdentifier string + ballotType types.ObservationType + voters []string + voteType []types.VoteType + }{ + { + name: "successfull votes only", + ballotIdentifier: sample.ZetaIndex(t), + ballotType: types.ObservationType_InboundTx, + voters: []string{"voter1", "voter2"}, + voteType: []types.VoteType{types.VoteType_SuccessObservation, types.VoteType_SuccessObservation}, + }, + + { + name: "failed votes only", + ballotIdentifier: sample.ZetaIndex(t), + ballotType: types.ObservationType_InboundTx, + voters: []string{"voter1", "voter2"}, + voteType: []types.VoteType{types.VoteType_FailureObservation, types.VoteType_FailureObservation}, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + _, ctx, _, _ := keepertest.ObserverKeeper(t) + ballot := types.Ballot{ + BallotIdentifier: tc.ballotIdentifier, + ObservationType: tc.ballotType, + VoterList: tc.voters, + Votes: tc.voteType, + } + keeper.EmitEventBallotDeleted(ctx, ballot) + for _, event := range ctx.EventManager().Events() { + for _, attr := range event.Attributes { + if attr.Key == "ballot_identifier" { + require.Equal(t, tc.ballotIdentifier, RemoveQuotes(attr.Value)) + } + if attr.Key == "ballot_type" { + require.Equal(t, tc.ballotType.String(), RemoveQuotes(attr.Value)) + } + if attr.Key == "voters" { + expectedString := "" + for _, voter := range ballot.GenerateVoterList() { + st := fmt.Sprintf("{\"voter_address\":\"%s\",\"vote_type\":\"%s\"}", voter.VoterAddress, voter.VoteType) + expectedString += st + expectedString += "," + } + expectedString = expectedString[:len(expectedString)-1] + require.Equal(t, expectedString, RemoveQuotes(attr.Value)) + } + } + } + + }) + } +} + +func RemoveQuotes(s string) string { + return s[1 : len(s)-1] +} diff --git a/x/observer/keeper/grpc_query_ballot_test.go b/x/observer/keeper/grpc_query_ballot_test.go index fe34971a64..48b76b40e7 100644 --- a/x/observer/keeper/grpc_query_ballot_test.go +++ b/x/observer/keeper/grpc_query_ballot_test.go @@ -124,7 +124,7 @@ func TestKeeper_BallotByIdentifier(t *testing.T) { require.NoError(t, err) require.Equal(t, &types.QueryBallotByIdentifierResponse{ BallotIdentifier: ballot.BallotIdentifier, - Voters: []*types.VoterList{ + Voters: []types.VoterList{ { VoterAddress: voter, VoteType: types.VoteType_SuccessObservation, diff --git a/x/observer/types/ballot.go b/x/observer/types/ballot.go index 8464f31032..967a5d5749 100644 --- a/x/observer/types/ballot.go +++ b/x/observer/types/ballot.go @@ -125,5 +125,6 @@ func (m Ballot) GenerateVoterList() []VoterList { } votersList[i] = voter } + return votersList } diff --git a/x/observer/types/ballot_test.go b/x/observer/types/ballot_test.go index 3f428e99d4..5cba1713ab 100644 --- a/x/observer/types/ballot_test.go +++ b/x/observer/types/ballot_test.go @@ -477,5 +477,110 @@ func Test_BuildRewardsDistribution(t *testing.T) { require.Equal(t, test.expectedMap, rewardsMap) }) } +} + +func TestBallot_GenerateVoterList(t *testing.T) { + tt := []struct { + name string + voters []string + votes []VoteType + expectedVoterList []VoterList + }{ + { + name: "Success observation", + voters: []string{"Observer1", "Observer2", "Observer3", "Observer4"}, + votes: []VoteType{ + VoteType_SuccessObservation, + VoteType_SuccessObservation, + VoteType_SuccessObservation, + VoteType_SuccessObservation, + }, + expectedVoterList: []VoterList{ + { + VoterAddress: "Observer1", + VoteType: VoteType_SuccessObservation, + }, + { + VoterAddress: "Observer2", + VoteType: VoteType_SuccessObservation, + }, + { + VoterAddress: "Observer3", + VoteType: VoteType_SuccessObservation, + }, + { + VoterAddress: "Observer4", + VoteType: VoteType_SuccessObservation, + }, + }, + }, + { + name: "Failure observation", + voters: []string{"Observer1", "Observer2", "Observer3", "Observer4"}, + votes: []VoteType{ + VoteType_FailureObservation, + VoteType_FailureObservation, + VoteType_FailureObservation, + VoteType_FailureObservation, + }, + expectedVoterList: []VoterList{ + { + VoterAddress: "Observer1", + VoteType: VoteType_FailureObservation, + }, + { + VoterAddress: "Observer2", + VoteType: VoteType_FailureObservation, + }, + { + VoterAddress: "Observer3", + VoteType: VoteType_FailureObservation, + }, + { + VoterAddress: "Observer4", + VoteType: VoteType_FailureObservation, + }, + }, + }, + + { + name: "mixed observation", + voters: []string{"Observer1", "Observer2", "Observer3", "Observer4"}, + votes: []VoteType{ + VoteType_FailureObservation, + VoteType_FailureObservation, + VoteType_SuccessObservation, + VoteType_SuccessObservation, + }, + expectedVoterList: []VoterList{ + { + VoterAddress: "Observer1", + VoteType: VoteType_FailureObservation, + }, + { + VoterAddress: "Observer2", + VoteType: VoteType_FailureObservation, + }, + { + VoterAddress: "Observer3", + VoteType: VoteType_SuccessObservation, + }, + { + VoterAddress: "Observer4", + VoteType: VoteType_SuccessObservation, + }, + }, + }, + } + for _, test := range tt { + t.Run(test.name, func(t *testing.T) { + ballot := Ballot{ + VoterList: test.voters, + Votes: test.votes, + } + voterList := ballot.GenerateVoterList() + require.Equal(t, test.expectedVoterList, voterList) + }) + } }