github.com/lino-network/lino@v0.6.11/x/reputation/repv2/reputation_test.go (about)

     1  package repv2
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  
     7  	// "math/rand"
     8  	"os"
     9  	"path/filepath"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/lino-network/lino/x/reputation/repv2/internal"
    14  	"github.com/stretchr/testify/suite"
    15  )
    16  
    17  type ReputationTestSuite struct {
    18  	suite.Suite
    19  	store                ReputationStore
    20  	rep                  ReputationImpl
    21  	roundDurationSeconds int64
    22  	bestN                int
    23  	userMaxN             int
    24  	time                 time.Time
    25  }
    26  
    27  func TestReputationTestSuite(t *testing.T) {
    28  	suite.Run(t, &ReputationTestSuite{})
    29  }
    30  
    31  func (suite *ReputationTestSuite) SetupTest() {
    32  	suite.roundDurationSeconds = 25 * 3600
    33  	suite.bestN = 30
    34  	suite.userMaxN = 10
    35  	suite.store = NewReputationStore(internal.NewMockStore(), DefaultInitialReputation)
    36  	suite.rep = NewReputation(
    37  		suite.store, suite.bestN, suite.userMaxN,
    38  		DefaultRoundDurationSeconds, DefaultSampleWindowSize, DefaultDecayFactor,
    39  	).(ReputationImpl)
    40  	suite.time = time.Date(1995, time.February, 5, 11, 11, 0, 0, time.UTC)
    41  }
    42  
    43  func (suite *ReputationTestSuite) MoveToNewRound() {
    44  	suite.time = suite.time.Add(time.Duration(suite.roundDurationSeconds) * time.Second)
    45  	suite.rep.Update(Time(suite.time.Unix()))
    46  }
    47  
    48  // for Int(*big.Int), need this on comparing with zero value.
    49  func (suite *ReputationTestSuite) EqualZero(a Int, args ...interface{}) {
    50  	suite.Equal(0, a.Cmp(NewInt(0)), "%d is not Int zero", a.Int64())
    51  }
    52  
    53  func (suite *ReputationTestSuite) TestUpdateTime() {
    54  	suite.MoveToNewRound()
    55  	t := suite.time
    56  	rep := suite.rep
    57  	r, rt := rep.GetCurrentRound()
    58  	suite.Equal(RoundId(2), r)
    59  	suite.Equal(Time(t.Unix()), rt)
    60  
    61  	t2 := t.Add(time.Duration(10*3600) * time.Second)
    62  	rep.Update(Time(t2.Unix()))
    63  	r, rt = rep.GetCurrentRound()
    64  	suite.Equal(RoundId(2), r)
    65  	suite.Equal(Time(t.Unix()), rt)
    66  
    67  	t3 := t2.Add(time.Duration(16*3600) * time.Second)
    68  	rep.Update(Time(t3.Unix()))
    69  	r, rt = rep.GetCurrentRound()
    70  	suite.Equal(RoundId(3), r)
    71  	suite.Equal(Time(t3.Unix()), rt)
    72  
    73  	t4 := t3.Add(time.Duration(10*3600) * time.Second)
    74  	rep.Update(Time(t4.Unix()))
    75  	r, rt = rep.GetCurrentRound()
    76  	suite.Equal(RoundId(3), r)
    77  	suite.Equal(Time(t3.Unix()), rt)
    78  }
    79  
    80  func (suite *ReputationTestSuite) TestExportImportFile() {
    81  	rep := suite.rep
    82  	var user1 Uid = "user1"
    83  	var user2 Uid = "user2"
    84  	var post1 Pid = "post1"
    85  	var post2 Pid = "post2"
    86  	rep.IncFreeScore(user1, NewInt(10000))
    87  	rep.IncFreeScore(user2, NewInt(10000))
    88  
    89  	rep.DonateAt(user1, post1, NewInt(100000))
    90  	rep.DonateAt(user1, post1, NewInt(1000))
    91  	rep.DonateAt(user2, post1, NewInt(100000))
    92  	rep.DonateAt(user2, post2, NewInt(1000))
    93  	suite.MoveToNewRound()
    94  
    95  	rep.DonateAt(user1, post1, NewInt(3333))
    96  	rep.DonateAt(user1, post1, NewInt(4444))
    97  	rep.DonateAt(user2, post1, NewInt(5555))
    98  	rep.DonateAt(user2, post2, NewInt(1324))
    99  	suite.MoveToNewRound()
   100  	suite.MoveToNewRound()
   101  
   102  	suite.Require().Equal(NewInt(10920), suite.rep.GetReputation("user1"))
   103  	suite.Require().Equal(NewInt(10910), suite.rep.GetReputation("user2"))
   104  
   105  	dir, err := ioutil.TempDir("", "example")
   106  	suite.Require().Nil(err)
   107  	defer os.RemoveAll(dir) // clean up
   108  
   109  	tmpfn := filepath.Join(dir, "tmpfile")
   110  	suite.MoveToNewRound()
   111  	err = rep.ExportToFile(tmpfn)
   112  	suite.Nil(err)
   113  
   114  	imported := NewReputation(
   115  		NewReputationStore(internal.NewMockStore(), DefaultInitialReputation),
   116  		suite.bestN, suite.userMaxN,
   117  		DefaultRoundDurationSeconds, DefaultSampleWindowSize, DefaultDecayFactor,
   118  	).(ReputationImpl)
   119  
   120  	err = imported.ImportFromFile(tmpfn)
   121  	suite.Nil(err)
   122  	suite.Equal(suite.rep.GetReputation("user1"), imported.GetReputation("user1"))
   123  	suite.Equal(suite.rep.GetReputation("user2"), imported.GetReputation("user2"))
   124  }
   125  
   126  func (suite *ReputationTestSuite) TestExtractConsumptionInfo() {
   127  	newset := func(ids []Pid) map[Pid]bool {
   128  		rst := make(map[Pid]bool)
   129  		for _, id := range ids {
   130  			rst[id] = true
   131  		}
   132  		return rst
   133  	}
   134  	cases := []struct {
   135  		user     *userMeta
   136  		seedSet  map[Pid]bool
   137  		expected consumptionInfo
   138  	}{
   139  		{
   140  			&userMeta{
   141  				Unsettled: []Donation{
   142  					{
   143  						Pid:    "a",
   144  						Amount: NewInt(333),
   145  						Impact: NewInt(333),
   146  					},
   147  					{
   148  						Pid:    "qf93f",
   149  						Amount: NewInt(999),
   150  						Impact: NewInt(222),
   151  					},
   152  					{
   153  						Pid:    "b",
   154  						Amount: NewInt(55555),
   155  						Impact: NewInt(1),
   156  					},
   157  				},
   158  			},
   159  			newset([]Pid{"a", "b", "c", "ddd"}),
   160  			consumptionInfo{
   161  				seed:    NewInt(55888),
   162  				other:   NewInt(999),
   163  				seedIF:  NewInt(334),
   164  				otherIF: NewInt(222),
   165  			},
   166  		},
   167  		{
   168  			&userMeta{
   169  				Unsettled: []Donation{
   170  					{
   171  						Pid:    "not",
   172  						Amount: NewInt(1),
   173  						Impact: NewInt(1),
   174  					},
   175  				},
   176  			},
   177  			newset([]Pid{"a", "b", "c", "ddd"}),
   178  			consumptionInfo{
   179  				seed:    NewInt(0),
   180  				other:   NewInt(1),
   181  				seedIF:  NewInt(0),
   182  				otherIF: NewInt(1),
   183  			},
   184  		},
   185  	}
   186  
   187  	for i, v := range cases {
   188  		suite.Equal(v.expected, suite.rep.extractConsumptionInfo(v.user, v.seedSet), "case: %d", i)
   189  	}
   190  }
   191  
   192  func (suite *ReputationTestSuite) TestGetSeedSet() {
   193  	rep := suite.rep
   194  	var user1 Uid = "user1"
   195  	var user2 Uid = "user2"
   196  	var post1 Pid = "post1"
   197  	var post2 Pid = "post2"
   198  	rep.IncFreeScore(user1, NewInt(10000))
   199  	rep.IncFreeScore(user2, NewInt(10000))
   200  
   201  	rep.DonateAt(user1, post1, NewInt(100000))
   202  	rep.DonateAt(user1, post1, NewInt(1000))
   203  	rep.DonateAt(user2, post1, NewInt(100000))
   204  	rep.DonateAt(user2, post2, NewInt(1000))
   205  	suite.MoveToNewRound()
   206  
   207  	set := rep.getSeedSet(1)
   208  	suite.Equal(1, len(set))
   209  	suite.True(set[post1])
   210  	suite.False(set[post2])
   211  
   212  	rep.DonateAt(user1, post1, NewInt(10000))
   213  	rep.DonateAt(user2, post2, NewInt(10000))
   214  	suite.MoveToNewRound()
   215  	set = rep.getSeedSet(2)
   216  	suite.Equal(2, len(set))
   217  	suite.True(set[post1])
   218  	suite.True(set[post2])
   219  	suite.False(set["other"])
   220  }
   221  
   222  func (suite *ReputationTestSuite) TestComputeReputation() {
   223  	rep := suite.rep
   224  	suite.EqualZero(rep.computeReputation(NewInt(0), NewInt(1000)))
   225  	suite.Equal(NewInt(969), rep.computeReputation(NewInt(999), NewInt(3)))
   226  }
   227  
   228  func (suite *ReputationTestSuite) TestComputeNewRepData() {
   229  	cases := []struct {
   230  		repData     reputationData
   231  		consumption consumptionInfo
   232  		repeat      int
   233  		expected    reputationData
   234  	}{
   235  		{
   236  			// growth curve
   237  			reputationData{
   238  				consumption: NewInt(1),
   239  				hold:        NewInt(0),
   240  				reputation:  NewInt(1),
   241  			},
   242  			consumptionInfo{
   243  				seed:    NewInt(10000 * 100000),
   244  				other:   NewInt(333 * 100000),
   245  				seedIF:  NewInt(1),
   246  				otherIF: NewInt(0),
   247  			},
   248  			5,
   249  			reputationData{
   250  				consumption: NewInt(409510000),
   251  				hold:        NewInt(32804999),
   252  				reputation:  NewInt(81460010),
   253  			},
   254  		},
   255  		{
   256  			// decrease curve
   257  			reputationData{
   258  				consumption: NewInt(1000 * 100000),
   259  				hold:        NewInt(10 * 100000),
   260  				reputation:  NewInt(900 * 100000),
   261  			},
   262  			consumptionInfo{
   263  				seed:    NewInt(10 * 100000),
   264  				other:   NewInt(600 * 100000),
   265  				seedIF:  NewInt(10 * 100000),
   266  				otherIF: NewInt(600 * 100000),
   267  			},
   268  			13,
   269  			reputationData{
   270  				consumption: NewInt(37860000),
   271  				hold:        NewInt(10 * 100000),
   272  				reputation:  NewInt(27860000),
   273  			},
   274  		},
   275  	}
   276  	rep := suite.rep
   277  	for i, c := range cases {
   278  		data := c.repData
   279  		for j := 0; j < c.repeat; j++ {
   280  			data = rep.computeNewRepData(data, c.consumption)
   281  		}
   282  		suite.Equal(c.expected, data, "case: %d", i)
   283  	}
   284  }
   285  
   286  func (suite *ReputationTestSuite) TestComputeNewRepDataDecreaseToZero() {
   287  	repData := reputationData{
   288  		consumption: NewInt(1),
   289  		hold:        NewInt(0),
   290  		reputation:  NewInt(1),
   291  	}
   292  	consumptions := consumptionInfo{
   293  		seed:    NewInt(0),
   294  		other:   NewInt(1),
   295  		seedIF:  NewInt(0),
   296  		otherIF: NewInt(1),
   297  	}
   298  
   299  	rep := suite.rep
   300  	newrep := rep.computeNewRepData(repData, consumptions)
   301  	suite.EqualZero(newrep.reputation)
   302  }
   303  
   304  // reputation values are greater than zero even if data does not make sense.
   305  func (suite *ReputationTestSuite) TestComputeNewRepDataGTEZero() {
   306  	repData := reputationData{
   307  		consumption: NewInt(0),
   308  		hold:        NewInt(0),
   309  		reputation:  NewInt(0),
   310  	}
   311  	consumptions := consumptionInfo{
   312  		seed:    NewInt(1000),
   313  		other:   NewInt(10000),
   314  		seedIF:  NewInt(0),
   315  		otherIF: NewInt(3333),
   316  	}
   317  
   318  	rep := suite.rep
   319  	newrep := rep.computeNewRepData(repData, consumptions)
   320  	suite.True(newrep.reputation.Cmp(NewInt(0)) >= 0)
   321  	suite.True(newrep.hold.Cmp(NewInt(0)) >= 0)
   322  	suite.True(newrep.hold.Cmp(NewInt(0)) >= 0)
   323  }
   324  
   325  func (suite *ReputationTestSuite) TestDonateAtGrow1() {
   326  	rep := suite.rep
   327  	for i := 0; i <= 50; i++ {
   328  		rep.DonateAt("user1", "post1", NewInt(100*100000))
   329  		suite.MoveToNewRound()
   330  	}
   331  	suite.Equal(NewInt(9690802), rep.GetReputation("user1"))
   332  }
   333  
   334  func (suite *ReputationTestSuite) TestDonateAtGrowAndDown() {
   335  	rep := suite.rep
   336  	for i := 0; i <= 60; i++ {
   337  		rep.DonateAt("user1", "post1", NewInt(80*100000))
   338  		rep.DonateAt("user1", "post2", NewInt(20*100000))
   339  		suite.MoveToNewRound()
   340  	}
   341  	suite.Equal(NewInt(9270170), rep.GetReputation("user1"))
   342  
   343  	rep.IncFreeScore("majority", NewInt(1000000*100000))
   344  	for i := 0; i <= 1; i++ {
   345  		rep.DonateAt("user1", "trash", NewInt(1*100000))
   346  		rep.DonateAt("majority", "good", NewInt(1000000*100000))
   347  		suite.MoveToNewRound()
   348  	}
   349  	suite.Equal(NewInt(9254170), rep.GetReputation("user1"))
   350  
   351  	for i := 0; i <= 60; i++ {
   352  		// rep.DonateAt("user1", "good", NewInt(50 * 100000))
   353  		rep.DonateAt("user1", "trash", NewInt(100*100000))
   354  		rep.DonateAt("majority", "good", NewInt(1000000*100000))
   355  		suite.MoveToNewRound()
   356  	}
   357  	suite.Equal(NewInt(57205), rep.GetReputation("user1"))
   358  }
   359  
   360  func (suite *ReputationTestSuite) TestUpdateReputationDonateAt() {
   361  	rep := suite.rep
   362  
   363  	// panics
   364  	suite.Panics(func() { rep.DonateAt("", "123", NewInt(11)) })
   365  	suite.Panics(func() { rep.DonateAt("u31", "", NewInt(11)) })
   366  	suite.Panics(func() { rep.DonateAt("", "", NewInt(11)) })
   367  
   368  	donations := []struct {
   369  		from   Uid
   370  		to     Pid
   371  		amount int64
   372  	}{
   373  		{"user1", "post1", 10000},
   374  		{"user2", "post1", 3},
   375  		{"user3", "post1", 600},
   376  		{"user4", "post1", 999},
   377  		{"user5", "post1", 1},
   378  		{"user6", "post1", 2},
   379  		{"user7", "post2", 7777},
   380  		{"user8", "post2", 2},
   381  		{"user9", "post2", 2},
   382  		{"user10", "post2", 100},
   383  		{"user11", "post2", 1000000},
   384  	}
   385  	cases := []struct {
   386  		user     Uid
   387  		expected *userMeta
   388  	}{
   389  		{
   390  			"user1",
   391  			&userMeta{
   392  				Consumption:       NewInt(1000),
   393  				Hold:              NewInt(99),
   394  				Reputation:        NewInt(10),
   395  				LastDonationRound: 1,
   396  				LastSettledRound:  1,
   397  			},
   398  		},
   399  		{
   400  			"user3",
   401  			&userMeta{
   402  				Consumption:       NewInt(60),
   403  				Hold:              NewInt(5),
   404  				Reputation:        NewInt(10),
   405  				LastDonationRound: 1,
   406  				LastSettledRound:  1,
   407  			},
   408  		},
   409  		{
   410  			"user7",
   411  			&userMeta{
   412  				Consumption:       NewInt(778),
   413  				Hold:              NewInt(77),
   414  				Reputation:        NewInt(8),
   415  				LastDonationRound: 1,
   416  				LastSettledRound:  1,
   417  			},
   418  		},
   419  		{
   420  			"user11",
   421  			&userMeta{
   422  				Consumption:       NewInt(100000),
   423  				Hold:              NewInt(9999),
   424  				Reputation:        NewInt(10),
   425  				LastDonationRound: 1,
   426  				LastSettledRound:  1,
   427  			},
   428  		},
   429  	}
   430  	for _, donation := range donations {
   431  		rep.DonateAt(donation.from, donation.to, NewInt(donation.amount))
   432  	}
   433  	suite.MoveToNewRound()
   434  	for i, v := range cases {
   435  		user := rep.store.GetUserMeta(v.user)
   436  		rep.updateReputation(user, 2)
   437  		suite.Equal(v.expected, user, "case: %d", i)
   438  	}
   439  }
   440  
   441  func (suite *ReputationTestSuite) TestAppendDonation() {
   442  	rep := NewReputation(
   443  		NewReputationStore(internal.NewMockStore(), DefaultInitialReputation),
   444  		100000, 2,
   445  		DefaultRoundDurationSeconds, DefaultSampleWindowSize, DefaultDecayFactor).(ReputationImpl)
   446  	user := &userMeta{
   447  		Reputation: NewInt(100),
   448  		Unsettled:  []Donation{},
   449  	}
   450  	cases := []struct {
   451  		post           Pid
   452  		amount         LinoCoin
   453  		expectedImpact IF
   454  		expected       *userMeta
   455  	}{
   456  		{
   457  			"p1", NewInt(33), NewInt(33),
   458  			&userMeta{
   459  				Reputation: NewInt(100),
   460  				Unsettled: []Donation{
   461  					{Pid: "p1", Amount: NewInt(33), Impact: NewInt(33)},
   462  				},
   463  			},
   464  		},
   465  		{
   466  			"p2", NewInt(77), NewInt(67),
   467  			&userMeta{
   468  				Reputation: NewInt(100),
   469  				Unsettled: []Donation{
   470  					{Pid: "p1", Amount: NewInt(33), Impact: NewInt(33)},
   471  					{Pid: "p2", Amount: NewInt(77), Impact: NewInt(67)},
   472  				},
   473  			},
   474  		},
   475  		{
   476  			"p3", NewInt(100), NewInt(0),
   477  			&userMeta{
   478  				Reputation: NewInt(100),
   479  				Unsettled: []Donation{
   480  					{Pid: "p1", Amount: NewInt(33), Impact: NewInt(33)},
   481  					{Pid: "p2", Amount: NewInt(77), Impact: NewInt(67)},
   482  				},
   483  			},
   484  		},
   485  		{
   486  			"p1", NewInt(100), NewInt(0),
   487  			&userMeta{
   488  				Reputation: NewInt(100),
   489  				Unsettled: []Donation{
   490  					{Pid: "p1", Amount: NewInt(133), Impact: NewInt(33)},
   491  					{Pid: "p2", Amount: NewInt(77), Impact: NewInt(67)},
   492  				},
   493  			},
   494  		},
   495  		{
   496  			"p2", NewInt(1000), NewInt(0),
   497  			&userMeta{
   498  				Reputation: NewInt(100),
   499  				Unsettled: []Donation{
   500  					{Pid: "p1", Amount: NewInt(133), Impact: NewInt(33)},
   501  					{Pid: "p2", Amount: NewInt(1077), Impact: NewInt(67)},
   502  				},
   503  			},
   504  		},
   505  	}
   506  
   507  	for i, c := range cases {
   508  		impact := rep.appendDonation(user, c.post, c.amount)
   509  		suite.Equal(c.expectedImpact, impact, "case: %d", i)
   510  		suite.Equal(c.expected, user, "case: %d", i)
   511  	}
   512  }
   513  
   514  func (suite *ReputationTestSuite) TestIncRoundPostSumImpactAndUpdate() {
   515  	rep := suite.rep
   516  	for i := 0; i < 1000; i++ {
   517  		post := Pid(fmt.Sprintf("post%d", i))
   518  		rep.incRoundPostSumImpact(1, post, NewInt(int64(1)))
   519  	}
   520  	for i := 999; i >= 0; i-- {
   521  		post := Pid(fmt.Sprintf("post%d", 999-i))
   522  		rep.incRoundPostSumImpact(1, post, NewInt(int64(3)))
   523  	}
   524  	for i := 0; i < 1000; i++ {
   525  		post := Pid(fmt.Sprintf("post%d", i))
   526  		if i%173 == 0 {
   527  			rep.incRoundPostSumImpact(1, post, NewInt(10000))
   528  		}
   529  	}
   530  
   531  	for i := 0; i < 1000; i++ {
   532  		post := Pid(fmt.Sprintf("post%d", i))
   533  		meta := rep.store.GetRoundPostMeta(1, post)
   534  		if i%173 == 0 {
   535  			suite.Equal(NewInt(10004), meta.SumIF)
   536  		} else {
   537  			suite.Equal(NewInt(4), meta.SumIF)
   538  		}
   539  	}
   540  
   541  	round := rep.store.GetRoundMeta(1)
   542  	suite.Nil(round.Result)
   543  	suite.Equal(NewInt(4*1000+10000*6), round.SumIF)
   544  	suite.Equal(Time(0), round.StartAt)
   545  	suite.Equal(30, len(round.TopN))
   546  	for _, v := range round.TopN {
   547  		id := -1
   548  		fmt.Sscanf(string(v.Pid), "post%d", &id)
   549  		if id%173 == 0 {
   550  			suite.Equal(NewInt(10004), v.SumIF)
   551  		} else {
   552  			suite.Equal(NewInt(4), v.SumIF)
   553  		}
   554  	}
   555  
   556  	suite.MoveToNewRound()
   557  	roundFinal := rep.store.GetRoundMeta(1)
   558  	suite.Equal(6, len(roundFinal.Result))
   559  	for _, v := range round.Result {
   560  		id := -1
   561  		fmt.Sscanf(string(v), "post%d", &id)
   562  		suite.True(id%173 == 0)
   563  	}
   564  }
   565  
   566  func (suite *ReputationTestSuite) TestFirstBlock1() {
   567  	rep := suite.rep
   568  	newBlockTime := Time(0)
   569  	rep.Update(0)
   570  	rid, startAt := rep.GetCurrentRound()
   571  	suite.Equal(RoundId(1), rid)
   572  	suite.Equal(newBlockTime, startAt)
   573  	suite.Equal(rep.GetReputation("me"), NewInt(DefaultInitialReputation))
   574  }
   575  
   576  func (suite *ReputationTestSuite) TestFirstBlock2() {
   577  	rep := suite.rep
   578  	newBlockTime := time.Date(1995, time.February, 5, 11, 11, 0, 0, time.UTC)
   579  	rep.Update(Time(newBlockTime.Unix()))
   580  	rid, startAt := rep.GetCurrentRound()
   581  	suite.Equal(RoundId(2), rid)
   582  	suite.Equal(Time(newBlockTime.Unix()), startAt)
   583  
   584  	nextBlockTime := time.Date(1995, time.February, 6, 12, 11, 0, 0, time.UTC)
   585  	suite.Equal(newBlockTime.Add(time.Duration(suite.roundDurationSeconds)*time.Second), nextBlockTime)
   586  	rep.Update(Time(nextBlockTime.Unix()))
   587  	rid, startAt = rep.GetCurrentRound()
   588  	suite.Equal(RoundId(3), rid)
   589  	suite.Equal(Time(nextBlockTime.Unix()), startAt)
   590  }
   591  
   592  func (suite *ReputationTestSuite) TestIncFreeScore() {
   593  	rep := suite.rep
   594  	rep.IncFreeScore("user1", NewInt(3000))
   595  	suite.Equal(NewInt(3000+DefaultInitialReputation), rep.GetReputation("user1"))
   596  }
   597  
   598  func (suite *ReputationTestSuite) TestDonationReturnDp1() {
   599  	rep := suite.rep
   600  	var user1 Uid = "user1"
   601  	var post1 Pid = "post1"
   602  	var post2 Pid = "post2"
   603  
   604  	dp1 := rep.DonateAt(user1, post1, NewInt(DefaultInitialReputation))
   605  	dp2 := rep.DonateAt(user1, post1, NewInt(DefaultInitialReputation))
   606  	dp3 := rep.DonateAt(user1, post2, NewInt(DefaultInitialReputation))
   607  	suite.Equal(NewInt(DefaultInitialReputation), dp1)
   608  	suite.Equal(NewInt(0), dp2)
   609  	suite.Equal(NewInt(0), dp3)
   610  }
   611  
   612  func (suite *ReputationTestSuite) TestDonationReturnDp2() {
   613  	rep := suite.rep
   614  	var user1 Uid = "user1"
   615  	var user2 Uid = "user2"
   616  	var post1 Pid = "post1"
   617  	var post2 Pid = "post2"
   618  
   619  	dp1 := rep.DonateAt(user1, post1, NewInt(10000))
   620  	dp2 := rep.DonateAt(user1, post2, NewInt(10000))
   621  	dpu2 := rep.DonateAt(user2, post1, NewInt(10000))
   622  	suite.Equal(NewInt(DefaultInitialReputation), dp1)
   623  	suite.Equal(NewInt(0), dp2)
   624  	suite.Equal(NewInt(DefaultInitialReputation), dpu2)
   625  
   626  	suite.MoveToNewRound()
   627  
   628  	// round 2
   629  	dp3 := rep.DonateAt(user1, post2, NewInt(3))
   630  	dp4 := rep.DonateAt(user1, post1, NewInt(4))
   631  	dp5 := rep.DonateAt(user1, post1, NewInt(5))
   632  	dpu2 = rep.DonateAt(user2, post2, NewInt(17))
   633  	suite.Equal(NewInt(3), dp3)
   634  	suite.Equal(NewInt(4), dp4)
   635  	suite.Equal(NewInt(3), dp5)
   636  	suite.Equal(NewInt(10), dpu2)
   637  }
   638  
   639  func (suite *ReputationTestSuite) TestBigIntEMA() {
   640  	suite.Panics(func() { IntEMA(NewInt(1000), NewInt(333), 0) })
   641  	cases := []struct {
   642  		prev     Int
   643  		new      Int
   644  		w        int64
   645  		expected Int
   646  	}{
   647  		{
   648  			prev:     NewInt(333),
   649  			new:      NewInt(333),
   650  			w:        10,
   651  			expected: NewInt(333),
   652  		},
   653  		{
   654  			prev:     NewInt(0),
   655  			new:      NewInt(10),
   656  			w:        10,
   657  			expected: NewInt(1),
   658  		},
   659  		{
   660  			prev:     NewInt(10),
   661  			new:      NewInt(110),
   662  			w:        10,
   663  			expected: NewInt(20),
   664  		},
   665  		{
   666  			prev:     NewInt(4),
   667  			new:      NewInt(77),
   668  			w:        7,
   669  			expected: NewInt(14),
   670  		},
   671  	}
   672  
   673  	for i, v := range cases {
   674  		suite.Equal(v.expected, IntEMA(v.prev, v.new, v.w), "case: %d", i)
   675  	}
   676  }
   677  
   678  func (suite *ReputationTestSuite) TestBubbleUp() {
   679  	cases := []struct {
   680  		posts    []PostIFPair
   681  		pos      int
   682  		expected []PostIFPair
   683  	}{
   684  		{
   685  			posts:    nil,
   686  			pos:      -1,
   687  			expected: nil,
   688  		},
   689  		{
   690  			posts:    []PostIFPair{{"1", NewInt(3)}},
   691  			pos:      0,
   692  			expected: []PostIFPair{{"1", NewInt(3)}},
   693  		},
   694  		{
   695  			posts:    []PostIFPair{{"1", NewInt(3)}, {"2", NewInt(0)}},
   696  			pos:      0,
   697  			expected: []PostIFPair{{"1", NewInt(3)}, {"2", NewInt(0)}},
   698  		},
   699  		{
   700  			posts:    []PostIFPair{{"1", NewInt(3)}, {"2", NewInt(4)}},
   701  			pos:      1,
   702  			expected: []PostIFPair{{"2", NewInt(4)}, {"1", NewInt(3)}},
   703  		},
   704  		{
   705  			posts: []PostIFPair{
   706  				{"1", NewInt(9)},
   707  				{"3", NewInt(8)},
   708  				{"5", NewInt(7)},
   709  				{"2", NewInt(6)},
   710  				{"8", NewInt(100)},
   711  				{"0", NewInt(5)},
   712  				{"11", NewInt(4)},
   713  			},
   714  			pos: 4,
   715  			expected: []PostIFPair{
   716  				{"8", NewInt(100)},
   717  				{"1", NewInt(9)},
   718  				{"3", NewInt(8)},
   719  				{"5", NewInt(7)},
   720  				{"2", NewInt(6)},
   721  				{"0", NewInt(5)},
   722  				{"11", NewInt(4)},
   723  			},
   724  		},
   725  		{
   726  			posts: []PostIFPair{
   727  				{"1", NewInt(9)},
   728  				{"3", NewInt(8)},
   729  				{"5", NewInt(7)},
   730  				{"2", NewInt(6)},
   731  				{"0", NewInt(5)},
   732  				{"11", NewInt(4)},
   733  				{"8", NewInt(100)},
   734  			},
   735  			pos: 6,
   736  			expected: []PostIFPair{
   737  				{"8", NewInt(100)},
   738  				{"1", NewInt(9)},
   739  				{"3", NewInt(8)},
   740  				{"5", NewInt(7)},
   741  				{"2", NewInt(6)},
   742  				{"0", NewInt(5)},
   743  				{"11", NewInt(4)},
   744  			},
   745  		},
   746  	}
   747  	for i, v := range cases {
   748  		bubbleUp(v.posts, v.pos)
   749  		suite.Equal(v.expected, v.posts, "case: %d", i)
   750  	}
   751  }
   752  
   753  // func (suite *ReputationTestSuite) simPostZipf(nposts uint64) *rand.Zipf {
   754  // 	// zipf posts, with s = 2, v = 50. number of seed: 193 if nposts = 10000
   755  // 	zipf := rand.NewZipf(rand.New(rand.NewSource(121212)), 2, 50, uint64(nposts))
   756  // 	return zipf
   757  // 	// print distribution.
   758  // 	// count := make(map[uint64]int)
   759  // 	// for i := 0; i < nposts; i++ {
   760  // 	// 	v := zipf.Uint64()
   761  // 	// 	count[v]++
   762  // 	// }
   763  // 	// probs := make([]float64, nposts)
   764  // 	// for k, v := range count {
   765  // 	// 	probs[k] = float64(v) * 100 / float64(nposts)
   766  // 	// }
   767  // 	// total := float64(0.0)
   768  // 	// for i, v := range probs {
   769  // 	// 	total += v
   770  // 	// 	if total >= 80 {
   771  // 	// 		fmt.Printf("80: %d\n", i)
   772  // 	// 		break
   773  // 	// 	}
   774  // 	// }
   775  // 	// fmt.Println(probs)
   776  // }
   777  
   778  // simulations
   779  // func (suite *ReputationTestSuite) TestSimulation() {
   780  // 	rep := NewReputation(
   781  // 		NewReputationStore(internal.NewMockStore(), DefaultInitialReputation),
   782  // 		200, 30, DefaultRoundDurationSeconds, DefaultSampleWindowSize, DefaultDecayFactor)
   783  // 	suite.rep = rep.(ReputationImpl)
   784  // 	// zipf posts.
   785  // 	nPosts := uint64(1000)
   786  // 	zipf := suite.simPostZipf(nPosts)
   787  // 	nUsers := int(5 * nPosts)
   788  // 	toUID := func(i int) Uid {
   789  // 		return fmt.Sprintf("user%d", i)
   790  // 	}
   791  // 	toPID := func(i uint64) Pid {
   792  // 		return fmt.Sprintf("post%d", i)
   793  // 	}
   794  
   795  // 	for j := 0; j < 3; j++ {
   796  // 		for i := 0; i < nUsers; i++ {
   797  // 			rep.DonateAt(toUID(i), toPID(zipf.Uint64()), NewInt(10*100000))
   798  // 		}
   799  // 		suite.MoveToNewRound()
   800  // 		fmt.Println(j)
   801  // 	}
   802  
   803  // 	for i := 0; i < nUsers; i++ {
   804  // 		fmt.Println(rep.GetReputation(toUID(i)))
   805  // 	}
   806  
   807  // }
   808  
   809  // benchmarks
   810  func BenchmarkDonateAt1(b *testing.B) {
   811  	suite := ReputationTestSuite{}
   812  	suite.SetupTest()
   813  	for n := 0; n < b.N; n++ {
   814  		suite.rep.DonateAt("user1", "post2", NewInt(100*100000))
   815  	}
   816  }
   817  
   818  func BenchmarkDonateAtWorstCase(b *testing.B) {
   819  	suite := ReputationTestSuite{}
   820  	suite.SetupTest()
   821  
   822  	posts := make([]Pid, b.N)
   823  	for i := 0; i < b.N; i++ {
   824  		posts[i] = Pid(fmt.Sprintf("post%d", i))
   825  	}
   826  
   827  	b.ResetTimer()
   828  	for n := 0; n < b.N; n++ {
   829  		suite.rep.DonateAt("user1", posts[n], NewInt(10000*100000))
   830  	}
   831  }