code.vegaprotocol.io/vega@v0.79.0/core/integration/steps/the_referral_set_stats_should_be.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 steps
    17  
    18  import (
    19  	"errors"
    20  	"fmt"
    21  	"strings"
    22  
    23  	"code.vegaprotocol.io/vega/core/integration/stubs"
    24  	"code.vegaprotocol.io/vega/core/types"
    25  	"code.vegaprotocol.io/vega/libs/num"
    26  
    27  	"github.com/cucumber/godog"
    28  	"golang.org/x/exp/maps"
    29  	"golang.org/x/exp/slices"
    30  )
    31  
    32  func TheReferralSetStatsShouldBe(broker *stubs.BrokerStub, code, epochStr, volumeStr string, table *godog.Table) error {
    33  	epoch, err := U64(epochStr)
    34  	if err != nil {
    35  		return fmt.Errorf("could not parse epoch: %w", err)
    36  	}
    37  
    38  	expectedVolume, overflown := num.UintFromString(volumeStr, 10)
    39  	if overflown {
    40  		return fmt.Errorf("could not parse the expected volume %q", volumeStr)
    41  	}
    42  	setID := types.ReferralSetID(code)
    43  
    44  	expectedRefereesStats, err := parseReferralStatsShouldBeTable(table)
    45  	if err != nil {
    46  		return fmt.Errorf("table is invalid: %w", err)
    47  	}
    48  
    49  	stats := broker.ReferralSetStats()
    50  	for _, stat := range stats {
    51  		if stat.AtEpoch == epoch && stat.SetID == setID {
    52  			if !stat.ReferralSetRunningVolume.EQ(expectedVolume) {
    53  				return fmt.Errorf("refferal set stats for set ID %q at epoch %q expect a running volume of %v, but got %v", code, epochStr, volumeStr, stat.ReferralSetRunningVolume)
    54  			}
    55  
    56  			return compareRefereesStats(expectedRefereesStats, stat.RefereesStats, stat.RewardFactors)
    57  		}
    58  	}
    59  
    60  	return fmt.Errorf("no stats found for set ID %q at epoch %q", code, epochStr)
    61  }
    62  
    63  type refereeStats struct {
    64  	DiscountFactor types.Factors
    65  	RewardFactor   types.Factors
    66  }
    67  
    68  func parseReferralStatsShouldBeTable(table *godog.Table) (map[types.PartyID]*refereeStats, error) {
    69  	rows := StrictParseTable(table, []string{
    70  		"party",
    71  		"discount infra factor",
    72  		"discount maker factor",
    73  		"discount liquidity factor",
    74  		"reward infra factor",
    75  		"reward maker factor",
    76  		"reward liquidity factor",
    77  	}, []string{})
    78  
    79  	stats := map[types.PartyID]*refereeStats{}
    80  	for _, row := range rows {
    81  		specificRow := newReferralSetStatsShouldBeRow(row)
    82  		partyID := specificRow.Party()
    83  		_, alreadyRegistered := stats[partyID]
    84  		if alreadyRegistered {
    85  			return nil, fmt.Errorf("cannot have more than one expectation for party %q", partyID)
    86  		}
    87  		stats[partyID] = &refereeStats{
    88  			DiscountFactor: types.Factors{
    89  				Infra:     specificRow.DiscountInfraFactor(),
    90  				Maker:     specificRow.DiscountMakerFactor(),
    91  				Liquidity: specificRow.DiscountLiqFactor(),
    92  			},
    93  			RewardFactor: types.Factors{
    94  				Infra:     specificRow.RewardInfraFactor(),
    95  				Maker:     specificRow.RewardMakerFactor(),
    96  				Liquidity: specificRow.RewardLiqFactor(),
    97  			},
    98  		}
    99  	}
   100  
   101  	return stats, nil
   102  }
   103  
   104  func compareRefereesStats(
   105  	expectedRefereesStats map[types.PartyID]*refereeStats,
   106  	foundRefereesStats map[types.PartyID]*types.RefereeStats,
   107  	foundRewardFactor types.Factors,
   108  ) error {
   109  	foundRefereesIDs := maps.Keys(foundRefereesStats)
   110  	expectedRefereesIDs := maps.Keys(expectedRefereesStats)
   111  
   112  	slices.Sort(foundRefereesIDs)
   113  	slices.Sort(expectedRefereesIDs)
   114  
   115  	unexpectedParties := []string{}
   116  	partiesNotFound := []string{}
   117  
   118  	for _, expectedID := range expectedRefereesIDs {
   119  		if _, ok := foundRefereesStats[expectedID]; !ok {
   120  			partiesNotFound = append(partiesNotFound, string(expectedID))
   121  		}
   122  	}
   123  
   124  	for _, foundID := range foundRefereesIDs {
   125  		if _, ok := expectedRefereesStats[foundID]; !ok {
   126  			unexpectedParties = append(unexpectedParties, string(foundID))
   127  		}
   128  	}
   129  
   130  	var errStr string
   131  	if len(partiesNotFound) > 0 {
   132  		errStr = "parties not found: " + strings.Join(partiesNotFound, ", ")
   133  	}
   134  	if len(unexpectedParties) > 0 {
   135  		if errStr != "" {
   136  			errStr += ", and "
   137  		}
   138  		errStr += "unexpected parties: " + strings.Join(unexpectedParties, ", ")
   139  	}
   140  	if errStr != "" {
   141  		return errors.New(errStr)
   142  	}
   143  
   144  	for _, refereeID := range expectedRefereesIDs {
   145  		refereeIDStr := string(refereeID)
   146  		foundRefereeStats := foundRefereesStats[refereeID]
   147  		expectedRefereeStats := expectedRefereesStats[refereeID]
   148  		if !expectedRefereeStats.RewardFactor.Equal(foundRewardFactor) {
   149  			return fmt.Errorf("expecting reward factor of %v but got %v for party %q", expectedRefereeStats.RewardFactor.String(), foundRewardFactor.String(), refereeIDStr)
   150  		}
   151  		if !foundRefereeStats.DiscountFactors.Equal(expectedRefereeStats.DiscountFactor) {
   152  			return fmt.Errorf("expecting discount factor of %v but got %v for party %q", expectedRefereeStats.DiscountFactor.String(), foundRefereeStats.DiscountFactors.String(), refereeIDStr)
   153  		}
   154  	}
   155  
   156  	return nil
   157  }
   158  
   159  type referralSetStatsShouldBeRow struct {
   160  	row RowWrapper
   161  }
   162  
   163  func newReferralSetStatsShouldBeRow(r RowWrapper) referralSetStatsShouldBeRow {
   164  	row := referralSetStatsShouldBeRow{
   165  		row: r,
   166  	}
   167  	return row
   168  }
   169  
   170  func (r referralSetStatsShouldBeRow) Party() types.PartyID {
   171  	return types.PartyID(r.row.MustStr("party"))
   172  }
   173  
   174  func (r referralSetStatsShouldBeRow) DiscountInfraFactor() num.Decimal {
   175  	return r.row.MustDecimal("discount infra factor")
   176  }
   177  
   178  func (r referralSetStatsShouldBeRow) DiscountMakerFactor() num.Decimal {
   179  	return r.row.MustDecimal("discount maker factor")
   180  }
   181  
   182  func (r referralSetStatsShouldBeRow) DiscountLiqFactor() num.Decimal {
   183  	return r.row.MustDecimal("discount liquidity factor")
   184  }
   185  
   186  func (r referralSetStatsShouldBeRow) RewardInfraFactor() num.Decimal {
   187  	return r.row.MustDecimal("reward infra factor")
   188  }
   189  
   190  func (r referralSetStatsShouldBeRow) RewardMakerFactor() num.Decimal {
   191  	return r.row.MustDecimal("reward maker factor")
   192  }
   193  
   194  func (r referralSetStatsShouldBeRow) RewardLiqFactor() num.Decimal {
   195  	return r.row.MustDecimal("reward liquidity factor")
   196  }