github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/tests/strategies_test.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "math" 6 "net/http" 7 "testing" 8 9 "github.com/DapperCollectives/CAST/backend/main/models" 10 "github.com/DapperCollectives/CAST/backend/main/shared" 11 "github.com/DapperCollectives/CAST/backend/main/strategies" 12 utils "github.com/DapperCollectives/CAST/backend/tests/test_utils" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 type Strategy interface { 17 TallyVotes(votes []*models.VoteWithBalance, p *models.ProposalResults, proposal *models.Proposal) (models.ProposalResults, error) 18 GetVotes(votes []*models.VoteWithBalance, proposal *models.Proposal) ([]*models.VoteWithBalance, error) 19 GetVoteWeightForBalance(vote *models.VoteWithBalance, proposal *models.Proposal) (float64, error) 20 InitStrategy(f *shared.FlowAdapter, db *shared.Database) 21 FetchBalance(b *models.Balance, p *models.Proposal) (*models.Balance, error) 22 RequiresSnapshot() bool 23 } 24 25 var strategyMap = map[string]Strategy{ 26 "token-weighted-default": &strategies.TokenWeightedDefault{}, 27 "staked-token-weighted-default": &strategies.StakedTokenWeightedDefault{}, 28 "one-address-one-vote": &strategies.OneAddressOneVote{}, 29 "balance-of-nfts": &strategies.BalanceOfNfts{}, 30 } 31 32 /* Token Weighted Default */ 33 func TestTokenWeightedDefaultStrategy(t *testing.T) { 34 clearTable("communities") 35 clearTable("community_users") 36 clearTable("proposals") 37 clearTable("votes") 38 clearTable("balances") 39 40 communityId := otu.AddCommunities(1, "dao")[0] 41 proposalIds, proposals := otu.AddProposalsForStrategy(communityId, "token-weighted-default", 2) 42 votes := otu.GenerateListOfVotes(proposalIds[0], 10) 43 proposalId := proposalIds[0] 44 proposalIdTwo := proposalIds[1] 45 choices := proposals[0].Choices 46 47 err := otu.AddDummyVotesAndBalances(votes) 48 if err != nil { 49 t.Errorf("Error adding votes and balances: %s", err) 50 } 51 52 t.Run("Test Tallying Results", func(t *testing.T) { 53 // Tally results 54 strategyName := "token-weighted-default" 55 56 s := strategyMap[strategyName] 57 proposalWithChoices := models.NewProposalResults(proposalId, choices) 58 _results, err := s.TallyVotes(votes, proposalWithChoices, proposals[0]) 59 if err != nil { 60 t.Errorf("Error tallying votes: %v", err) 61 } 62 63 // Fetch Proposal Results 64 response := otu.GetProposalResultsAPI(proposalId) 65 CheckResponseCode(t, http.StatusOK, response.Code) 66 67 var results models.ProposalResults 68 json.Unmarshal(response.Body.Bytes(), &results) 69 70 assert.Equal(t, _results.Proposal_id, results.Proposal_id) 71 assert.Equal(t, _results.Results_float["a"], results.Results_float["a"]) 72 assert.Equal(t, _results.Results_float["b"], results.Results_float["b"]) 73 }) 74 75 t.Run("Test Fetching Votes for Proposal", func(t *testing.T) { 76 response := otu.GetVotesForProposalAPI(proposalId) 77 78 CheckResponseCode(t, http.StatusOK, response.Code) 79 80 var body utils.PaginatedResponseWithVotes 81 json.Unmarshal(response.Body.Bytes(), &body) 82 83 // Validate vote weights are returned correctly 84 for i, v := range body.Data { 85 _vote := (votes)[i] 86 expectedWeight := float64(*_vote.PrimaryAccountBalance) * math.Pow(10, -8) 87 assert.Equal(t, expectedWeight, *v.Weight) 88 } 89 }) 90 91 t.Run("Test Fetching Vote for Address", func(t *testing.T) { 92 _vote := (votes)[0] 93 response := otu.GetVoteForProposalByAddressAPI(proposalId, _vote.Addr) 94 95 CheckResponseCode(t, http.StatusOK, response.Code) 96 97 var vote models.VoteWithBalance 98 json.Unmarshal(response.Body.Bytes(), &vote) 99 100 _expectedWeight := float64(*_vote.PrimaryAccountBalance) * math.Pow(10, -8) 101 assert.Equal(t, _expectedWeight, *vote.Weight) 102 }) 103 104 t.Run("Test Fetching Votes For Address", func(t *testing.T) { 105 _vote := (votes)[0] 106 proposalIds := []int{proposalId, proposalIdTwo} 107 108 response := otu.GetVotesForAddressAPI(_vote.Addr, proposalIds) 109 CheckResponseCode(t, http.StatusOK, response.Code) 110 111 var votes []models.VoteWithBalance 112 json.Unmarshal(response.Body.Bytes(), &votes) 113 114 for _, v := range votes { 115 _expectedWeight := float64(*_vote.PrimaryAccountBalance) * math.Pow(10, -8) 116 assert.Equal(t, _expectedWeight, *v.Weight) 117 } 118 }) 119 } 120 121 /* Balance of NFT */ 122 func TestBalanceOfNFTsStrategy(t *testing.T) { 123 clearTable("communities") 124 clearTable("community_users") 125 clearTable("proposals") 126 clearTable("votes") 127 clearTable("balances") 128 clearTable("nfts") 129 130 communityId, community := otu.AddCommunitiesWithNFTContract(1, "user1") 131 proposalIds, proposals := otu.AddProposalsForStrategy(communityId[0], "balance-of-nfts", 1) 132 proposalId := proposalIds[0] 133 choices := proposals[0].Choices 134 135 var contract = &shared.Contract{ 136 Name: community.Contract_name, 137 Addr: community.Contract_addr, 138 Public_path: community.Public_path, 139 } 140 141 votes, err := otu.GenerateListOfVotesWithNFTs(proposalId, 5, contract) 142 if err != nil { 143 t.Error(err) 144 } 145 146 otu.AddDummyVotesAndNFTs(votes) 147 t.Run("Test Tallying Results For NFT Balance Strategy", func(t *testing.T) { 148 strategyName := "balance-of-nfts" 149 150 s := strategyMap[strategyName] 151 s.InitStrategy(otu.A.FlowAdapter, otu.A.DB) 152 proposalWithChoices := models.NewProposalResults(proposalId, choices) 153 _results, err := s.TallyVotes(votes, proposalWithChoices, proposals[0]) 154 if err != nil { 155 t.Errorf("Error tallying votes: %v", err) 156 } 157 158 response := otu.GetProposalResultsAPI(proposalId) 159 CheckResponseCode(t, http.StatusOK, response.Code) 160 161 var results models.ProposalResults 162 json.Unmarshal(response.Body.Bytes(), &results) 163 164 assert.Equal(t, _results.Results_float["a"], results.Results_float["a"]) 165 }) 166 167 t.Run("Test Fetching Votes for Proposal", func(t *testing.T) { 168 response := otu.GetVotesForProposalAPI(proposalId) 169 CheckResponseCode(t, http.StatusOK, response.Code) 170 171 var body utils.PaginatedResponseWithVotes 172 json.Unmarshal(response.Body.Bytes(), &body) 173 174 // Validate vote weights are returned correctly 175 for _, v := range body.Data { 176 expectedWeight := float64(1.00) 177 assert.Equal(t, expectedWeight, *v.Weight) 178 } 179 }) 180 181 t.Run("Test Fetching Vote for Address", func(t *testing.T) { 182 _vote := (votes)[0] 183 response := otu.GetVoteForProposalByAddressAPI(proposalId, _vote.Addr) 184 185 CheckResponseCode(t, http.StatusOK, response.Code) 186 187 var vote models.VoteWithBalance 188 json.Unmarshal(response.Body.Bytes(), &vote) 189 190 expectedWeight := float64(1.00) 191 assert.Equal(t, expectedWeight, *vote.Weight) 192 }) 193 194 // t.Run("Attempt to cheat the NFT strategy", func(t *testing.T) { 195 // proposalWithChoices := models.NewProposalResults(proposalId, choices) 196 // _ = otu.TallyResultsForBalanceOfNfts(votes, proposalWithChoices) 197 198 // response := otu.GetProposalResultsAPI(proposalId) 199 // CheckResponseCode(t, http.StatusOK, response.Code) 200 201 // var correctResults models.ProposalResults 202 // json.Unmarshal(response.Body.Bytes(), &correctResults) 203 204 // otu.SetupAccountForNFTs("user6") 205 // otu.TransferNFT("user2", "user3", 1) 206 207 // cheatVote, err := otu.GenerateSingleVoteWithNFT(proposalId, 3, contract) 208 // if err != nil { 209 // t.Error(err) 210 // } 211 // votesWithCheat := append(*votes, *cheatVote) 212 // cheatResults := otu.TallyResultsForBalanceOfNfts(&votesWithCheat, proposalWithChoices) 213 214 // response = otu.GetProposalResultsAPI(proposalId) 215 // CheckResponseCode(t, http.StatusOK, response.Code) 216 217 // json.Unmarshal(response.Body.Bytes(), &cheatResults) 218 219 // //weight should be the same as before the cheat vote was added 220 // //because the cheat vote should be ignored by the server 221 // //therefor the cheatResults should be the same as the correctResults 222 223 // assert.Equal(t, correctResults.Proposal_id, cheatResults.Proposal_id) 224 // assert.Equal(t, correctResults.Results_float["a"], cheatResults.Results_float["a"]) 225 // assert.Equal(t, correctResults.Results_float["b"], cheatResults.Results_float["b"]) 226 // }) 227 } 228 229 /* Staked Token Weighted Default */ 230 func TestStakedTokenWeightedDefaultStrategy(t *testing.T) { 231 clearTable("communities") 232 clearTable("community_users") 233 clearTable("proposals") 234 clearTable("votes") 235 clearTable("balances") 236 237 communityId := otu.AddCommunities(1, "dao")[0] 238 proposalIds, proposals := otu.AddProposalsForStrategy(communityId, "staked-token-weighted-default", 2) 239 proposalIdTwo := proposalIds[1] 240 proposalId := proposalIds[0] 241 choices := proposals[0].Choices 242 votes := otu.GenerateListOfVotes(proposalId, 10) 243 otu.AddDummyVotesAndBalances(votes) 244 245 t.Run("Test Tallying Results", func(t *testing.T) { 246 // Tally results 247 strategyName := "staked-token-weighted-default" 248 249 s := strategyMap[strategyName] 250 proposalWithChoices := models.NewProposalResults(proposalId, choices) 251 _results, err := s.TallyVotes(votes, proposalWithChoices, proposals[0]) 252 if err != nil { 253 t.Errorf("Error tallying votes: %v", err) 254 } 255 256 // Fetch Proposal Results 257 response := otu.GetProposalResultsAPI(proposalId) 258 CheckResponseCode(t, http.StatusOK, response.Code) 259 260 var results models.ProposalResults 261 json.Unmarshal(response.Body.Bytes(), &results) 262 263 assert.Equal(t, _results, results) 264 }) 265 266 t.Run("Test Fetching Votes for Proposal", func(t *testing.T) { 267 response := otu.GetVotesForProposalAPI(proposalId) 268 CheckResponseCode(t, http.StatusOK, response.Code) 269 270 var body utils.PaginatedResponseWithVotes 271 json.Unmarshal(response.Body.Bytes(), &body) 272 273 // Validate vote weights are returned correctly 274 for i, v := range body.Data { 275 _vote := (votes)[i] 276 expectedWeight := float64(*_vote.StakingBalance) * math.Pow(10, -8) 277 assert.Equal(t, expectedWeight, *v.Weight) 278 } 279 }) 280 281 t.Run("Test Fetching Vote for Address", func(t *testing.T) { 282 _vote := (votes)[0] 283 response := otu.GetVoteForProposalByAddressAPI(proposalId, _vote.Addr) 284 285 CheckResponseCode(t, http.StatusOK, response.Code) 286 287 var vote models.VoteWithBalance 288 json.Unmarshal(response.Body.Bytes(), &vote) 289 290 _expectedWeight := float64(*_vote.StakingBalance) * math.Pow(10, -8) 291 assert.Equal(t, _expectedWeight, *vote.Weight) 292 }) 293 294 t.Run("Test Fetching Votes For Address", func(t *testing.T) { 295 _vote := (votes)[0] 296 proposalIds := []int{proposalId, proposalIdTwo} 297 298 response := otu.GetVotesForAddressAPI(_vote.Addr, proposalIds) 299 CheckResponseCode(t, http.StatusOK, response.Code) 300 301 var votes []models.VoteWithBalance 302 json.Unmarshal(response.Body.Bytes(), &votes) 303 304 for _, v := range votes { 305 _expectedWeight := float64(*_vote.StakingBalance) * math.Pow(10, -8) 306 assert.Equal(t, _expectedWeight, *v.Weight) 307 } 308 }) 309 } 310 311 /* One Token One Vote */ 312 // func TestOneTokenOneVoteStrategy(t *testing.T) { 313 // clearTable("communities") 314 // clearTable("community_users") 315 // clearTable("proposals") 316 // clearTable("votes") 317 // clearTable("balances") 318 319 // communityId := otu.AddCommunities(1)[0] 320 // proposalIds, proposals := otu.AddProposalsForStrategy(communityId, "one-address-one-vote", 2) 321 // proposalIdTwo := proposalIds[1] 322 // proposalId := proposalIds[0] 323 // choices := proposals[0].Choices 324 // votes := otu.GenerateListOfVotes(proposalId, 10) 325 // otu.AddDummyVotesAndBalances(votes) 326 327 // t.Run("Test Tallying Results", func(t *testing.T) { 328 // //Tally Results 329 // proposalWithChoices := models.NewProposalResults(proposalId, choices) 330 // _results := otu.TallyResultsForOneAddressOneVote(votes, proposalWithChoices) 331 332 // // Fetch Proposal Results 333 // response := otu.GetProposalResultsAPI(proposalId) 334 // CheckResponseCode(t, http.StatusOK, response.Code) 335 336 // var results models.ProposalResults 337 // json.Unmarshal(response.Body.Bytes(), &results) 338 339 // assert.Equal(t, _results.Proposal_id, results.Proposal_id) 340 // assert.Equal(t, _results.Results["a"], results.Results["a"]) 341 // assert.Equal(t, _results.Results["b"], results.Results["b"]) 342 // }) 343 344 // t.Run("Test Fetching Votes for Proposal", func(t *testing.T) { 345 // response := otu.GetVotesForProposalAPI(proposalId) 346 347 // CheckResponseCode(t, http.StatusOK, response.Code) 348 349 // var body utils.PaginatedResponseWithVotes 350 // json.Unmarshal(response.Body.Bytes(), &body) 351 352 // // Validate vote weights are returned correctly 353 // for _, v := range body.Data { 354 // expectedWeight := float64(1.00) 355 // assert.Equal(t, expectedWeight, *v.Weight) 356 // } 357 // }) 358 359 // t.Run("Test Fetching Vote for Address", func(t *testing.T) { 360 // _vote := (*votes)[0] 361 // response := otu.GetVoteForProposalByAddressAPI(proposalId, _vote.Addr) 362 363 // CheckResponseCode(t, http.StatusOK, response.Code) 364 365 // var vote models.VoteWithBalance 366 // json.Unmarshal(response.Body.Bytes(), &vote) 367 368 // expectedWeight := float64(1.00) 369 // assert.Equal(t, expectedWeight, *vote.Weight) 370 // }) 371 372 // t.Run("Test Fetching Votes for Address", func(t *testing.T) { 373 // _vote := (*votes)[0] 374 // proposalIds := []int{proposalId, proposalIdTwo} 375 // response := otu.GetVotesForAddressAPI(_vote.Addr, proposalIds) 376 377 // CheckResponseCode(t, http.StatusOK, response.Code) 378 379 // var votes []models.VoteWithBalance 380 // json.Unmarshal(response.Body.Bytes(), &votes) 381 382 // for _, v := range votes { 383 // expectedWeight := float64(1.00) 384 // assert.Equal(t, expectedWeight, *v.Weight) 385 // } 386 // }) 387 // }