github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/tests/leaderboard_test.go (about)

     1  package main
     2  
     3  import (
     4  	"encoding/json"
     5  	"net/http"
     6  	"sort"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/DapperCollectives/CAST/backend/main/models"
    11  	"github.com/DapperCollectives/CAST/backend/tests/test_utils"
    12  	"github.com/stretchr/testify/assert"
    13  )
    14  
    15  func resetTables() {
    16  	clearTable("communities")
    17  	clearTable("balances")
    18  	clearTable("community_users")
    19  	clearTable("proposals")
    20  	clearTable("votes")
    21  }
    22  
    23  func TestGetLeaderboardCurrentUser(t *testing.T) {
    24  	resetTables()
    25  
    26  	communityId := otu.AddCommunities(1, "dao")[0]
    27  	proposalId := otu.AddActiveProposals(communityId, 1)[0]
    28  	vote := otu.GenerateValidVotePayload("user1", proposalId, "a")
    29  	otu.CreateVoteAPI(proposalId, vote)
    30  
    31  	response := otu.GetCommunityLeaderboardAPI(communityId)
    32  	checkResponseCode(t, http.StatusOK, response.Code)
    33  
    34  	var p1 test_utils.PaginatedResponseWithLeaderboardUser
    35  	json.Unmarshal(response.Body.Bytes(), &p1)
    36  
    37  	assert.Equal(t, models.LeaderboardUser{}, p1.Data.CurrentUser)
    38  
    39  	response = otu.GetCommunityLeaderboardAPIWithCurrentUser(communityId, vote.Addr)
    40  	checkResponseCode(t, http.StatusOK, response.Code)
    41  
    42  	var p2 test_utils.PaginatedResponseWithLeaderboardUser
    43  	json.Unmarshal(response.Body.Bytes(), &p2)
    44  
    45  	assert.Equal(t, vote.Addr, p2.Data.CurrentUser.Addr)
    46  }
    47  
    48  func TestGetLeaderboardWithEarlyVotes(t *testing.T) {
    49  	resetTables()
    50  
    51  	communityId := otu.AddCommunities(1, "dao")[0]
    52  	earlyVoteBonus := 1
    53  	expectedUsers := 1
    54  	expectedProposals := 2
    55  
    56  	// user gets single vote for each proposal they voted on
    57  	expectedScore := expectedProposals + (expectedProposals * earlyVoteBonus)
    58  
    59  	otu.GenerateEarlyVoteAchievements(communityId, expectedProposals, expectedUsers)
    60  
    61  	response := otu.GetCommunityLeaderboardAPI(communityId)
    62  	checkResponseCode(t, http.StatusOK, response.Code)
    63  
    64  	var p test_utils.PaginatedResponseWithLeaderboardUser
    65  	json.Unmarshal(response.Body.Bytes(), &p)
    66  
    67  	users := p.Data.Users
    68  
    69  	receivedScore := users[0].Score
    70  
    71  	assert.Equal(t, expectedUsers, len(users))
    72  	assert.Equal(t, expectedScore, receivedScore)
    73  }
    74  
    75  func TestGetLeaderboardWithSingleStreak(t *testing.T) {
    76  	resetTables()
    77  
    78  	communityId := otu.AddCommunities(1, "dao")[0]
    79  	streaks := []int{3, 4}
    80  	streakBonus := 1
    81  	expectedUsers := len(streaks)
    82  	expectedScoreA := streaks[0] + (1 * streakBonus)
    83  	expectedScoreB := streaks[1] + (1 * streakBonus)
    84  
    85  	otu.GenerateSingleStreakAchievements(communityId, streaks)
    86  
    87  	response := otu.GetCommunityLeaderboardAPI(communityId)
    88  	checkResponseCode(t, http.StatusOK, response.Code)
    89  
    90  	var p test_utils.PaginatedResponseWithLeaderboardUser
    91  	json.Unmarshal(response.Body.Bytes(), &p)
    92  
    93  	users := p.Data.Users
    94  
    95  	// ensure scores ordered for assert
    96  	sort.Slice(users, func(i, j int) bool {
    97  		return users[i].Score < users[j].Score
    98  	})
    99  
   100  	receivedScoreA := users[0].Score
   101  	receivedScoreB := users[1].Score
   102  
   103  	assert.Equal(t, expectedUsers, len(users))
   104  	assert.Equal(t, expectedScoreA, receivedScoreA)
   105  	assert.Equal(t, expectedScoreB, receivedScoreB)
   106  }
   107  
   108  func TestGetLeaderboardWithMultiStreaks(t *testing.T) {
   109  	resetTables()
   110  
   111  	communityId := otu.AddCommunities(1, "dao")[0]
   112  	streaks := []int{3, 4}
   113  	streakBonus := 1
   114  	expectedUsers := 1
   115  
   116  	// user with 7 votes and 2 streaks
   117  	expectedUser1Score := 7 + (2 * streakBonus)
   118  
   119  	otu.GenerateMultiStreakAchievements(communityId, streaks)
   120  
   121  	response := otu.GetCommunityLeaderboardAPI(communityId)
   122  	checkResponseCode(t, http.StatusOK, response.Code)
   123  
   124  	var p test_utils.PaginatedResponseWithLeaderboardUser
   125  	json.Unmarshal(response.Body.Bytes(), &p)
   126  
   127  	users := p.Data.Users
   128  
   129  	receivedUser1Score := users[0].Score
   130  
   131  	assert.Equal(t, expectedUsers, len(users))
   132  	assert.Equal(t, expectedUser1Score, receivedUser1Score)
   133  }
   134  
   135  func TestGetLeaderboardWithWinningVote(t *testing.T) {
   136  	resetTables()
   137  
   138  	communityId := otu.AddCommunities(1, "dao")[0]
   139  	winningVoteBonus := 1
   140  
   141  	proposalId := otu.GenerateWinningVoteAchievement(communityId, "token-weighted-default")
   142  	otu.UpdateProposalEndTime(proposalId, time.Now().UTC())
   143  	otu.GetProposalResultsAPI(proposalId)
   144  
   145  	response := otu.GetCommunityLeaderboardAPI(communityId)
   146  	checkResponseCode(t, http.StatusOK, response.Code)
   147  
   148  	var p test_utils.PaginatedResponseWithLeaderboardUser
   149  	json.Unmarshal(response.Body.Bytes(), &p)
   150  
   151  	users := p.Data.Users
   152  
   153  	winningUserScore := 1 + 1*winningVoteBonus
   154  	losingUserScore := 1
   155  
   156  	receivedWinners := 0
   157  	receivedLosers := 0
   158  
   159  	for _, user := range users {
   160  		if user.Score == winningUserScore {
   161  			receivedWinners += 1
   162  		} else if user.Score == losingUserScore {
   163  			receivedLosers += 1
   164  		}
   165  	}
   166  
   167  	expectedWinners := 3
   168  	expectedLosers := 1
   169  
   170  	assert.Equal(t, expectedWinners, receivedWinners)
   171  	assert.Equal(t, expectedLosers, receivedLosers)
   172  }
   173  
   174  func TestGetLeaderboardWithCancelledProposal(t *testing.T) {
   175  	authorName := "user1"
   176  
   177  	t.Run("User should lose vote on cancelled proposal", func(t *testing.T) {
   178  		resetTables()
   179  		communityId := otu.AddCommunitiesWithUsers(1, authorName)[0]
   180  		proposalIds := otu.AddActiveProposals(communityId, 3)
   181  
   182  		for _, id := range proposalIds {
   183  			vote := otu.GenerateValidVotePayload(authorName, id, "a")
   184  			otu.CreateVoteAPI(id, vote)
   185  		}
   186  	
   187  		// Cancel proposal
   188  		cancelPayload := otu.GenerateCancelProposalStruct(authorName, proposalIds[1])
   189  		otu.UpdateProposalAPI(proposalIds[1], cancelPayload)
   190  		
   191  		response := otu.GetCommunityLeaderboardAPI(communityId)
   192  		checkResponseCode(t, http.StatusOK, response.Code)
   193  	
   194  		var p test_utils.PaginatedResponseWithLeaderboardUser
   195  		json.Unmarshal(response.Body.Bytes(), &p)
   196  	
   197  		assert.Equal(t, 2, p.Data.Users[0].Score)
   198  	})
   199  
   200  	t.Run("User should lose early vote achievement on cancelled proposal", func(t *testing.T) {
   201  		numProposals := 4
   202  		communityId := otu.AddCommunitiesWithUsers(1, authorName)[0]
   203  		proposalIds := otu.GenerateEarlyVoteAchievements(communityId, numProposals, 1)
   204  		
   205  		// Cancel a proposalId
   206  		cancelPayload := otu.GenerateCancelProposalStruct(authorName, proposalIds[1])
   207  		otu.UpdateProposalAPI(proposalIds[1], cancelPayload)
   208  		
   209  		response := otu.GetCommunityLeaderboardAPI(communityId)
   210  		checkResponseCode(t, http.StatusOK, response.Code)
   211  	
   212  		var p test_utils.PaginatedResponseWithLeaderboardUser
   213  		json.Unmarshal(response.Body.Bytes(), &p)
   214  
   215  		// User get votes + early votes, but no extra on cancelled proposal
   216  		expectedResult := (numProposals - 1) * 2
   217  		assert.Equal(t, expectedResult, p.Data.Users[0].Score)
   218  	})
   219  }
   220  
   221  func TestGetLeaderboardDefaultPaging(t *testing.T) {
   222  	resetTables()
   223  
   224  	communityId := otu.AddCommunities(1, "dao")[0]
   225  
   226  	numUsers := 6
   227  	numProposals := 1
   228  
   229  	otu.GenerateVotes(communityId, numProposals, numUsers)
   230  
   231  	response := otu.GetCommunityLeaderboardAPI(communityId)
   232  	checkResponseCode(t, http.StatusOK, response.Code)
   233  
   234  	var p test_utils.PaginatedResponseWithLeaderboardUser
   235  	json.Unmarshal(response.Body.Bytes(), &p)
   236  
   237  	expectedLength := 6
   238  	assert.Equal(t, expectedLength, len(p.Data.Users))
   239  }
   240  
   241  func TestGetLeaderboardPaging(t *testing.T) {
   242  	resetTables()
   243  
   244  	communityId := otu.AddCommunities(1, "dao")[0]
   245  
   246  	otu.AddActiveProposals(communityId, 1)
   247  
   248  	count := 3
   249  
   250  	// Leaderboard with no users because no votes yet
   251  	response1 := otu.GetCommunityLeaderboardAPIWithPaging(communityId, 0, count)
   252  	checkResponseCode(t, http.StatusOK, response1.Code)
   253  
   254  	var p1 test_utils.PaginatedResponseWithLeaderboardUser
   255  	json.Unmarshal(response1.Body.Bytes(), &p1)
   256  
   257  	numUsers := 6
   258  	numProposals := 1
   259  
   260  	otu.GenerateVotes(communityId, numProposals, numUsers)
   261  
   262  	// First Page
   263  	response2 := otu.GetCommunityLeaderboardAPIWithPaging(communityId, 0, count)
   264  	checkResponseCode(t, http.StatusOK, response2.Code)
   265  
   266  	var p2 test_utils.PaginatedResponseWithLeaderboardUser
   267  	json.Unmarshal(response2.Body.Bytes(), &p2)
   268  
   269  	// Second Page
   270  	response3 := otu.GetCommunityLeaderboardAPIWithPaging(communityId, 1, count)
   271  	checkResponseCode(t, http.StatusOK, response3.Code)
   272  
   273  	var p3 test_utils.PaginatedResponseWithLeaderboardUser
   274  	json.Unmarshal(response3.Body.Bytes(), &p3)
   275  
   276  	// Invalid Page
   277  	response4 := otu.GetCommunityLeaderboardAPIWithPaging(communityId, 3, count)
   278  	checkResponseCode(t, http.StatusOK, response4.Code)
   279  
   280  	var p4 test_utils.PaginatedResponseWithLeaderboardUser
   281  	json.Unmarshal(response4.Body.Bytes(), &p4)
   282  
   283  	expectedNoUsersLength := 0
   284  	expectedLength := 3
   285  	assert.Equal(t, expectedNoUsersLength, len(p1.Data.Users))
   286  	assert.Equal(t, expectedLength, len(p2.Data.Users))
   287  	assert.Equal(t, expectedLength, len(p3.Data.Users))
   288  	assert.Equal(t, expectedLength, len(p4.Data.Users))
   289  }