code.vegaprotocol.io/vega@v0.79.0/core/activitystreak/activitiystreak_snapshot_test.go (about)

     1  // Copyright (C) 2023 Gobalsky Labs Limited
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    15  
    16  package activitystreak_test
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  	"testing"
    22  
    23  	"code.vegaprotocol.io/vega/core/activitystreak"
    24  	"code.vegaprotocol.io/vega/core/activitystreak/mocks"
    25  	"code.vegaprotocol.io/vega/core/events"
    26  	"code.vegaprotocol.io/vega/core/types"
    27  	"code.vegaprotocol.io/vega/libs/num"
    28  	"code.vegaprotocol.io/vega/libs/proto"
    29  	"code.vegaprotocol.io/vega/logging"
    30  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    31  	snapshotpb "code.vegaprotocol.io/vega/protos/vega/snapshot/v1"
    32  
    33  	"github.com/golang/mock/gomock"
    34  	"github.com/stretchr/testify/assert"
    35  )
    36  
    37  type testSnapshotEngine struct {
    38  	*activitystreak.SnapshotEngine
    39  
    40  	ctrl         *gomock.Controller
    41  	broker       *mocks.MockBroker
    42  	marketsStats *mocks.MockMarketsStatsAggregator
    43  }
    44  
    45  func getTestSnapshotEngine(t *testing.T) *testSnapshotEngine {
    46  	t.Helper()
    47  	ctrl := gomock.NewController(t)
    48  	marketsStats := mocks.NewMockMarketsStatsAggregator(ctrl)
    49  	broker := mocks.NewMockBroker(ctrl)
    50  
    51  	e := &testSnapshotEngine{
    52  		SnapshotEngine: activitystreak.NewSnapshotEngine(
    53  			logging.NewTestLogger(), marketsStats, broker,
    54  		),
    55  		ctrl:         ctrl,
    56  		broker:       broker,
    57  		marketsStats: marketsStats,
    58  	}
    59  
    60  	e.OnMinQuantumOpenNationalVolumeUpdate(context.Background(), num.NewUint(100))
    61  	e.OnMinQuantumTradeVolumeUpdate(context.Background(), num.NewUint(200))
    62  	e.OnRewardsActivityStreakInactivityLimit(context.Background(), num.NewUint(10))
    63  	assert.NoError(t, e.OnBenefitTiersUpdate(context.Background(), &vegapb.ActivityStreakBenefitTiers{
    64  		Tiers: []*vegapb.ActivityStreakBenefitTier{
    65  			{
    66  				MinimumActivityStreak: 1,
    67  				RewardMultiplier:      "2",
    68  				VestingMultiplier:     "1.5",
    69  			},
    70  			{
    71  				MinimumActivityStreak: 7,
    72  				RewardMultiplier:      "3",
    73  				VestingMultiplier:     "2.5",
    74  			},
    75  			{
    76  				MinimumActivityStreak: 14,
    77  				RewardMultiplier:      "4",
    78  				VestingMultiplier:     "3.5",
    79  			},
    80  		},
    81  	}))
    82  
    83  	return e
    84  }
    85  
    86  func TestSnapshot(t *testing.T) {
    87  	e1 := getTestSnapshotEngine(t)
    88  
    89  	t.Run("setting up engine 1", func(t *testing.T) {
    90  		e1.marketsStats.EXPECT().GetMarketStats().Times(2).Return(
    91  			map[string]*types.MarketStats{
    92  				"market1": {
    93  					PartiesOpenNotionalVolume: map[string]*num.Uint{
    94  						"party1": num.NewUint(100),
    95  						"party2": num.NewUint(100),
    96  						"party3": num.NewUint(0),
    97  					},
    98  					PartiesTotalTradeVolume: map[string]*num.Uint{
    99  						"party1": num.NewUint(20),
   100  						"party2": num.NewUint(50),
   101  						"party3": num.NewUint(150),
   102  					},
   103  				},
   104  				"market2": {
   105  					PartiesOpenNotionalVolume: map[string]*num.Uint{
   106  						"party1": num.NewUint(20),
   107  						"party2": num.NewUint(20),
   108  						"party3": num.NewUint(20),
   109  					},
   110  					PartiesTotalTradeVolume: map[string]*num.Uint{
   111  						"party1": num.NewUint(20),
   112  						"party2": num.NewUint(50),
   113  						"party3": num.NewUint(100),
   114  					},
   115  				},
   116  			},
   117  		)
   118  
   119  		e1.broker.EXPECT().SendBatch(gomock.Any()).Times(1)
   120  		e1.broker.EXPECT().SendBatch(gomock.Any()).Times(1).Do(
   121  			func(evts []events.Event) {
   122  				assert.Len(t, evts, 3)
   123  
   124  				pas := evts[0].(*events.PartyActivityStreak)
   125  				assert.Equal(t, pas.Proto().Party, "party1")
   126  				assert.True(t, pas.Proto().IsActive)
   127  				assert.Equal(t, int(pas.Proto().ActiveFor), 2)
   128  				assert.Equal(t, int(pas.Proto().InactiveFor), 0)
   129  				assert.Equal(t, int(pas.Proto().Epoch), 2)
   130  				assert.Equal(t, pas.Proto().RewardDistributionActivityMultiplier, "2")
   131  				assert.Equal(t, pas.Proto().RewardVestingActivityMultiplier, "1.5")
   132  				pas = evts[1].(*events.PartyActivityStreak)
   133  				assert.Equal(t, pas.Proto().Party, "party2")
   134  				assert.True(t, pas.Proto().IsActive)
   135  				assert.Equal(t, int(pas.Proto().ActiveFor), 2)
   136  				assert.Equal(t, int(pas.Proto().InactiveFor), 0)
   137  				assert.Equal(t, int(pas.Proto().Epoch), 2)
   138  				assert.Equal(t, pas.Proto().RewardDistributionActivityMultiplier, "2")
   139  				assert.Equal(t, pas.Proto().RewardVestingActivityMultiplier, "1.5")
   140  				pas = evts[2].(*events.PartyActivityStreak)
   141  				assert.Equal(t, pas.Proto().Party, "party3")
   142  				assert.True(t, pas.Proto().IsActive)
   143  				assert.Equal(t, int(pas.Proto().ActiveFor), 2)
   144  				assert.Equal(t, int(pas.Proto().InactiveFor), 0)
   145  				assert.Equal(t, int(pas.Proto().Epoch), 2)
   146  				assert.Equal(t, pas.Proto().RewardDistributionActivityMultiplier, "2")
   147  				assert.Equal(t, pas.Proto().RewardVestingActivityMultiplier, "1.5")
   148  			},
   149  		)
   150  
   151  		e1.OnEpochEvent(context.Background(), types.Epoch{
   152  			Seq:    1,
   153  			Action: vegapb.EpochAction_EPOCH_ACTION_END,
   154  		})
   155  		e1.OnEpochEvent(context.Background(), types.Epoch{
   156  			Seq:    2,
   157  			Action: vegapb.EpochAction_EPOCH_ACTION_END,
   158  		})
   159  	})
   160  
   161  	state1, _, err := e1.GetState(activitystreak.ActivityStreakKey)
   162  	assert.NoError(t, err)
   163  	assert.NotNil(t, state1)
   164  
   165  	ppayload := &snapshotpb.Payload{}
   166  	err = proto.Unmarshal(state1, ppayload)
   167  	assert.NoError(t, err)
   168  
   169  	e2 := getTestSnapshotEngine(t)
   170  	_, err = e2.LoadState(context.Background(), types.PayloadFromProto(ppayload))
   171  	assert.NoError(t, err)
   172  
   173  	// now assert the v2 produce the same state
   174  	state2, _, err := e2.GetState(activitystreak.ActivityStreakKey)
   175  	assert.NoError(t, err)
   176  	assert.NotNil(t, state2)
   177  
   178  	assert.Equal(t, state1, state2)
   179  
   180  	epochForward(t, e1, "engine 1")
   181  	epochForward(t, e2, "engine 2")
   182  
   183  	t.Run("finally comparing final state from both engines", func(t *testing.T) {
   184  		state1, _, err := e1.GetState(activitystreak.ActivityStreakKey)
   185  		assert.NoError(t, err)
   186  		assert.NotNil(t, state1)
   187  
   188  		ppayload := &snapshotpb.Payload{}
   189  		err = proto.Unmarshal(state1, ppayload)
   190  		assert.NoError(t, err)
   191  
   192  		_, err = e2.LoadState(context.Background(), types.PayloadFromProto(ppayload))
   193  		assert.NoError(t, err)
   194  
   195  		// now assert the v2 produce the same state
   196  		state2, _, err := e2.GetState(activitystreak.ActivityStreakKey)
   197  		assert.NoError(t, err)
   198  		assert.NotNil(t, state2)
   199  
   200  		assert.Equal(t, state1, state2)
   201  	})
   202  }
   203  
   204  func epochForward(t *testing.T, e *testSnapshotEngine, name string) {
   205  	t.Helper()
   206  	t.Run(fmt.Sprintf("moving time for %v", name), func(t *testing.T) {
   207  		e.marketsStats.EXPECT().GetMarketStats().Times(1).Return(
   208  			map[string]*types.MarketStats{},
   209  		)
   210  
   211  		e.broker.EXPECT().SendBatch(gomock.Any()).Times(1).Do(
   212  			func(evts []events.Event) {
   213  				assert.Len(t, evts, 3)
   214  
   215  				pas := evts[0].(*events.PartyActivityStreak)
   216  				assert.Equal(t, pas.Proto().Party, "party1")
   217  				assert.False(t, pas.Proto().IsActive)
   218  				assert.Equal(t, int(pas.Proto().ActiveFor), 2)
   219  				assert.Equal(t, int(pas.Proto().InactiveFor), 1)
   220  				assert.Equal(t, int(pas.Proto().Epoch), 3)
   221  				assert.Equal(t, pas.Proto().RewardDistributionActivityMultiplier, "2")
   222  				assert.Equal(t, pas.Proto().RewardVestingActivityMultiplier, "1.5")
   223  				pas = evts[1].(*events.PartyActivityStreak)
   224  				assert.Equal(t, pas.Proto().Party, "party2")
   225  				assert.False(t, pas.Proto().IsActive)
   226  				assert.Equal(t, int(pas.Proto().ActiveFor), 2)
   227  				assert.Equal(t, int(pas.Proto().InactiveFor), 1)
   228  				assert.Equal(t, int(pas.Proto().Epoch), 3)
   229  				assert.Equal(t, pas.Proto().RewardDistributionActivityMultiplier, "2")
   230  				assert.Equal(t, pas.Proto().RewardVestingActivityMultiplier, "1.5")
   231  				pas = evts[2].(*events.PartyActivityStreak)
   232  				assert.Equal(t, pas.Proto().Party, "party3")
   233  				assert.False(t, pas.Proto().IsActive)
   234  				assert.Equal(t, int(pas.Proto().ActiveFor), 2)
   235  				assert.Equal(t, int(pas.Proto().InactiveFor), 1)
   236  				assert.Equal(t, int(pas.Proto().Epoch), 3)
   237  				assert.Equal(t, pas.Proto().RewardDistributionActivityMultiplier, "2")
   238  				assert.Equal(t, pas.Proto().RewardVestingActivityMultiplier, "1.5")
   239  			},
   240  		)
   241  
   242  		e.OnEpochEvent(context.Background(), types.Epoch{
   243  			Seq:    3,
   244  			Action: vegapb.EpochAction_EPOCH_ACTION_END,
   245  		})
   246  	})
   247  }