code.vegaprotocol.io/vega@v0.79.0/datanode/sqlstore/volume_rebate_programs_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 sqlstore_test
    17  
    18  import (
    19  	"testing"
    20  	"time"
    21  
    22  	"code.vegaprotocol.io/vega/datanode/entities"
    23  	"code.vegaprotocol.io/vega/datanode/sqlstore"
    24  	"code.vegaprotocol.io/vega/libs/ptr"
    25  	"code.vegaprotocol.io/vega/protos/vega"
    26  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    27  
    28  	"github.com/georgysavva/scany/pgxscan"
    29  	"github.com/stretchr/testify/assert"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  func setupVolumeRebateProgramTest(t *testing.T) (*sqlstore.Blocks, *sqlstore.VolumeRebatePrograms) {
    34  	t.Helper()
    35  
    36  	bs := sqlstore.NewBlocks(connectionSource)
    37  	rs := sqlstore.NewVolumeRebatePrograms(connectionSource)
    38  
    39  	return bs, rs
    40  }
    41  
    42  func TestVolumeRebatePrograms_AddVolumeRebateProgram(t *testing.T) {
    43  	bs, rs := setupVolumeRebateProgramTest(t)
    44  	ctx := tempTransaction(t)
    45  
    46  	block := addTestBlock(t, ctx, bs)
    47  	block2 := addTestBlock(t, ctx, bs)
    48  	one64 := uint64(1)
    49  
    50  	t.Run("AddVolumeRebateProgram should create a new volume rebate program record", func(t *testing.T) {
    51  		endTime := block.VegaTime.Add(time.Hour)
    52  		endTime2 := block2.VegaTime.Add(time.Hour)
    53  
    54  		programs := []*eventspb.VolumeRebateProgramStarted{
    55  			{
    56  				Program: &vega.VolumeRebateProgram{
    57  					Version: 1,
    58  					Id:      GenerateID(),
    59  					BenefitTiers: []*vega.VolumeRebateBenefitTier{
    60  						{
    61  							MinimumPartyMakerVolumeFraction: "1",
    62  							AdditionalMakerRebate:           "0.01",
    63  							TierNumber:                      ptr.From(one64),
    64  						},
    65  						{
    66  							MinimumPartyMakerVolumeFraction: "2",
    67  							AdditionalMakerRebate:           "0.1",
    68  							TierNumber:                      ptr.From(one64 * 2),
    69  						},
    70  					},
    71  					EndOfProgramTimestamp: endTime.Unix(),
    72  					WindowLength:          100,
    73  				},
    74  			},
    75  			{
    76  				Program: &vega.VolumeRebateProgram{
    77  					Version: 1,
    78  					Id:      GenerateID(),
    79  					BenefitTiers: []*vega.VolumeRebateBenefitTier{
    80  						{
    81  							MinimumPartyMakerVolumeFraction: "3",
    82  							AdditionalMakerRebate:           "0.02",
    83  							TierNumber:                      ptr.From(one64),
    84  						},
    85  						{
    86  							MinimumPartyMakerVolumeFraction: "4",
    87  							AdditionalMakerRebate:           "0.2",
    88  							TierNumber:                      ptr.From(one64 * 2),
    89  						},
    90  					},
    91  					EndOfProgramTimestamp: endTime2.Unix(),
    92  					WindowLength:          200,
    93  				},
    94  			},
    95  		}
    96  
    97  		want := entities.VolumeRebateProgramFromProto(programs[0].Program, block.VegaTime, 0)
    98  		err := rs.AddVolumeRebateProgram(ctx, want)
    99  		require.NoError(t, err)
   100  
   101  		var got []entities.VolumeRebateProgram
   102  		require.NoError(t, pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM volume_rebate_programs"))
   103  		require.Len(t, got, 1)
   104  		assert.Equal(t, *want, got[0])
   105  
   106  		want2 := entities.VolumeRebateProgramFromProto(programs[1].Program, block2.VegaTime, 0)
   107  		err = rs.AddVolumeRebateProgram(ctx, want2)
   108  		require.NoError(t, err)
   109  
   110  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM volume_rebate_programs")
   111  		require.NoError(t, err)
   112  		require.Len(t, got, 2)
   113  		wantAll := []entities.VolumeRebateProgram{*want, *want2}
   114  		assert.Equal(t, wantAll, got)
   115  	})
   116  }
   117  
   118  func getVolumeRebateEvents(t *testing.T, endTime time.Time) (*eventspb.VolumeRebateProgramStarted,
   119  	*eventspb.VolumeRebateProgramUpdated, *eventspb.VolumeRebateProgramEnded,
   120  ) {
   121  	t.Helper()
   122  
   123  	one64 := uint64(1)
   124  	started := eventspb.VolumeRebateProgramStarted{
   125  		Program: &vega.VolumeRebateProgram{
   126  			Version: 1,
   127  			Id:      GenerateID(),
   128  			BenefitTiers: []*vega.VolumeRebateBenefitTier{
   129  				{
   130  					MinimumPartyMakerVolumeFraction: "1000",
   131  					AdditionalMakerRebate:           "0.01",
   132  					TierNumber:                      ptr.From(one64),
   133  				},
   134  				{
   135  					MinimumPartyMakerVolumeFraction: "10000",
   136  					AdditionalMakerRebate:           "0.1",
   137  					TierNumber:                      ptr.From(one64 * 2),
   138  				},
   139  			},
   140  			EndOfProgramTimestamp: endTime.Unix(),
   141  			WindowLength:          100,
   142  		},
   143  		StartedAt: endTime.Add(-1 * time.Hour).UnixNano(),
   144  		AtEpoch:   1,
   145  	}
   146  
   147  	updated := eventspb.VolumeRebateProgramUpdated{
   148  		Program: &vega.VolumeRebateProgram{
   149  			Version: 2,
   150  			Id:      GenerateID(),
   151  			BenefitTiers: []*vega.VolumeRebateBenefitTier{
   152  				{
   153  					MinimumPartyMakerVolumeFraction: "2000",
   154  					AdditionalMakerRebate:           "0.02",
   155  					TierNumber:                      ptr.From(one64),
   156  				},
   157  				{
   158  					MinimumPartyMakerVolumeFraction: "20000",
   159  					AdditionalMakerRebate:           "0.2",
   160  					TierNumber:                      ptr.From(one64 * 2),
   161  				},
   162  			},
   163  			EndOfProgramTimestamp: endTime.Unix(),
   164  			WindowLength:          200,
   165  		},
   166  		UpdatedAt: endTime.Add(-30 * time.Minute).UnixNano(),
   167  		AtEpoch:   2,
   168  	}
   169  
   170  	ended := eventspb.VolumeRebateProgramEnded{
   171  		Version: 2,
   172  		Id:      updated.Program.Id,
   173  		EndedAt: endTime.UnixNano(),
   174  		AtEpoch: 3,
   175  	}
   176  
   177  	return &started, &updated, &ended
   178  }
   179  
   180  func TestVolumeRebatePrograms_UpdateVolumeRebateProgram(t *testing.T) {
   181  	bs, rs := setupVolumeRebateProgramTest(t)
   182  	ctx := tempTransaction(t)
   183  
   184  	block := addTestBlock(t, ctx, bs)
   185  	endTime := block.VegaTime.Add(time.Hour)
   186  	started, updated, _ := getVolumeRebateEvents(t, endTime)
   187  
   188  	var want, wantUpdated *entities.VolumeRebateProgram
   189  	t.Run("UpdateVolumeRebateProgram should create a new referral program record with the updated data", func(t *testing.T) {
   190  		want = entities.VolumeRebateProgramFromProto(started.Program, block.VegaTime, 0)
   191  		err := rs.AddVolumeRebateProgram(ctx, want)
   192  		require.NoError(t, err)
   193  
   194  		var got []entities.VolumeRebateProgram
   195  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM volume_rebate_programs")
   196  		require.NoError(t, err)
   197  
   198  		require.Len(t, got, 1)
   199  		assert.Equal(t, *want, got[0])
   200  
   201  		block = addTestBlock(t, ctx, bs)
   202  		wantUpdated = entities.VolumeRebateProgramFromProto(updated.Program, block.VegaTime, 0)
   203  		err = rs.UpdateVolumeRebateProgram(ctx, wantUpdated)
   204  		require.NoError(t, err)
   205  
   206  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM volume_rebate_programs")
   207  		require.NoError(t, err)
   208  
   209  		require.Len(t, got, 2)
   210  
   211  		wantAll := []entities.VolumeRebateProgram{*want, *wantUpdated}
   212  		assert.Equal(t, wantAll, got)
   213  	})
   214  
   215  	t.Run("The current_rebate view should list the updated rebate program record", func(t *testing.T) {
   216  		var got []entities.VolumeRebateProgram
   217  		err := pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM current_volume_rebate_program")
   218  		require.NoError(t, err)
   219  		require.Len(t, got, 1)
   220  		assert.Equal(t, *wantUpdated, got[0])
   221  	})
   222  }
   223  
   224  func TestVolumeRebatePrograms_EndVolumeRebateProgram(t *testing.T) {
   225  	bs, rs := setupVolumeRebateProgramTest(t)
   226  	ctx := tempTransaction(t)
   227  
   228  	t.Run("EndVolumeRebateProgram should create a new rebate program record with the data from the current rebate program and set the ended_at timestamp", func(t *testing.T) {
   229  		block := addTestBlock(t, ctx, bs)
   230  		endTime := block.VegaTime.Add(time.Hour)
   231  		startedEvent, updatedEvent, endedEvent := getVolumeRebateEvents(t, endTime)
   232  
   233  		started := entities.VolumeRebateProgramFromProto(startedEvent.Program, block.VegaTime, 1)
   234  		err := rs.AddVolumeRebateProgram(ctx, started)
   235  		require.NoError(t, err)
   236  
   237  		block = addTestBlock(t, ctx, bs)
   238  		updated := entities.VolumeRebateProgramFromProto(updatedEvent.Program, block.VegaTime, 2)
   239  		err = rs.UpdateVolumeRebateProgram(ctx, updated)
   240  		require.NoError(t, err)
   241  
   242  		block = addTestBlock(t, ctx, bs)
   243  		err = rs.EndVolumeRebateProgram(ctx, endedEvent.Version, endTime, block.VegaTime, 3)
   244  		require.NoError(t, err)
   245  
   246  		ended := entities.VolumeRebateProgramFromProto(updatedEvent.Program, block.VegaTime, 3)
   247  		ended.Version = endedEvent.Version
   248  		ended.EndedAt = &endTime
   249  
   250  		var got []entities.VolumeRebateProgram
   251  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM volume_rebate_programs order by vega_time")
   252  		require.NoError(t, err)
   253  		require.Len(t, got, 3)
   254  		wantAll := []entities.VolumeRebateProgram{*started, *updated, *ended}
   255  		assert.Equal(t, wantAll, got)
   256  
   257  		err = pgxscan.Select(ctx, connectionSource, &got, "SELECT * FROM current_volume_rebate_program")
   258  		require.NoError(t, err)
   259  		require.Len(t, got, 1)
   260  		assert.Equal(t, *ended, got[0])
   261  	})
   262  }
   263  
   264  func TestVolumeRebatePrograms_GetCurrentVolumeRebateProgram(t *testing.T) {
   265  	bs, rs := setupVolumeRebateProgramTest(t)
   266  	ctx := tempTransaction(t)
   267  
   268  	t.Run("GetCurrentVolumeRebateProgram should return the current rebate program information", func(t *testing.T) {
   269  		block := addTestBlock(t, ctx, bs)
   270  		endTime := block.VegaTime.Add(time.Hour)
   271  		startedEvent, updatedEvent, endedEvent := getVolumeRebateEvents(t, endTime)
   272  
   273  		started := entities.VolumeRebateProgramFromProto(startedEvent.Program, block.VegaTime, 1)
   274  		err := rs.AddVolumeRebateProgram(ctx, started)
   275  		require.NoError(t, err)
   276  
   277  		got, err := rs.GetCurrentVolumeRebateProgram(ctx)
   278  		require.NoError(t, err)
   279  		assert.Equal(t, *started, got)
   280  
   281  		block = addTestBlock(t, ctx, bs)
   282  		updated := entities.VolumeRebateProgramFromProto(updatedEvent.Program, block.VegaTime, 2)
   283  		err = rs.UpdateVolumeRebateProgram(ctx, updated)
   284  		require.NoError(t, err)
   285  
   286  		got, err = rs.GetCurrentVolumeRebateProgram(ctx)
   287  		require.NoError(t, err)
   288  		assert.Equal(t, *updated, got)
   289  
   290  		block = addTestBlock(t, ctx, bs)
   291  		err = rs.EndVolumeRebateProgram(ctx, endedEvent.Version, endTime, block.VegaTime, 3)
   292  		require.NoError(t, err)
   293  
   294  		ended := entities.VolumeRebateProgramFromProto(updatedEvent.Program, block.VegaTime, 3)
   295  		ended.Version = endedEvent.Version
   296  		ended.EndedAt = &endTime
   297  
   298  		got, err = rs.GetCurrentVolumeRebateProgram(ctx)
   299  		require.NoError(t, err)
   300  		assert.Equal(t, *ended, got)
   301  	})
   302  }