github.com/DapperCollectives/CAST/backend@v0.0.0-20230921221157-1350c8be7c96/tests/proposal_test.go (about) 1 package main 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "testing" 8 "time" 9 10 "github.com/DapperCollectives/CAST/backend/main/models" 11 "github.com/DapperCollectives/CAST/backend/main/shared" 12 "github.com/stretchr/testify/assert" 13 ) 14 15 /*****************/ 16 /* Proposals */ 17 /*****************/ 18 19 func TestGetProposal(t *testing.T) { 20 21 clearTable("communities") 22 clearTable("community_users") 23 clearTable("proposals") 24 communityId := otu.AddCommunities(1, "dao")[0] 25 26 t.Run("Requesting proposals for a community with none created should succeed and return an empty array", func(t *testing.T) { 27 response := otu.GetProposalsForCommunityAPI(communityId) 28 29 CheckResponseCode(t, http.StatusOK, response.Code) 30 31 var proposalsResponse shared.PaginatedResponse 32 json.Unmarshal(response.Body.Bytes(), &proposalsResponse) 33 34 assert.Equal(t, 0, proposalsResponse.Count) 35 }) 36 37 t.Run("Should throw an error if proposal with ID doesnt exist", func(t *testing.T) { 38 39 response := otu.GetProposalByIdAPI(communityId, 420) 40 41 CheckResponseCode(t, http.StatusForbidden, response.Code) 42 43 expectedErr := errIncompleteRequest 44 expectedErr.StatusCode = http.StatusForbidden 45 46 var e errorResponse 47 json.Unmarshal(response.Body.Bytes(), &e) 48 assert.Equal(t, expectedErr, e) 49 }) 50 51 t.Run("Should fetch existing proposal by ID", func(t *testing.T) { 52 proposalId := otu.AddProposals(communityId, 1)[0] 53 response := otu.GetProposalByIdAPI(communityId, proposalId) 54 CheckResponseCode(t, http.StatusOK, response.Code) 55 }) 56 } 57 58 func TestCreateProposal(t *testing.T) { 59 clearTable("communities") 60 clearTable("community_users") 61 clearTable("proposals") 62 communityId := otu.AddCommunitiesWithUsers(1, "user1")[0] 63 64 t.Run("Should be able to create a valid proposal", func(t *testing.T) { 65 proposalStruct := otu.GenerateProposalStruct("user1", communityId) 66 payload := otu.GenerateProposalPayload("user1", proposalStruct) 67 68 response := otu.CreateProposalAPI(payload) 69 CheckResponseCode(t, http.StatusCreated, response.Code) 70 71 var p models.Proposal 72 json.Unmarshal(response.Body.Bytes(), &p) 73 74 assert.NotNil(t, p.Cid) 75 76 assert.Equal(t, proposalStruct.Name, p.Name) 77 assert.Equal(t, *proposalStruct.Body, *p.Body) 78 assert.Equal(t, proposalStruct.Choices, p.Choices) 79 assert.Equal(t, proposalStruct.Community_id, p.Community_id) 80 assert.Equal(t, *proposalStruct.Strategy, *p.Strategy) 81 }) 82 83 t.Run("Should throw an error if signature is invalid", func(t *testing.T) { 84 proposalStruct := otu.GenerateProposalStruct("user1", communityId) 85 payload := otu.GenerateProposalPayload("user1", proposalStruct) 86 // Invalidate the signature by signing a new timestamp 87 newTimestamp := fmt.Sprint(time.Now().UnixNano()/int64(time.Millisecond) + 1234) 88 compositeSigs := otu.GenerateCompositeSignatures("user1", newTimestamp) 89 payload.Composite_signatures = compositeSigs 90 response := otu.CreateProposalAPI(payload) 91 92 CheckResponseCode(t, http.StatusForbidden, response.Code) 93 94 expectedErr := errForbidden 95 expectedErr.StatusCode = http.StatusForbidden 96 97 var e errorResponse 98 json.Unmarshal(response.Body.Bytes(), &e) 99 assert.Equal(t, expectedErr, e) 100 }) 101 102 t.Run("Should throw an error if timestamp is more than 60 seconds", func(t *testing.T) { 103 proposalStruct := otu.GenerateProposalStruct("user1", communityId) 104 payload := otu.GenerateProposalPayload("user1", proposalStruct) 105 // Invalidate by signing an old timestamp 106 newTimestamp := fmt.Sprint(time.Now().Add(-10*time.Minute).UnixNano() / int64(time.Millisecond)) 107 compositeSigs := otu.GenerateCompositeSignatures("user1", newTimestamp) 108 payload.Timestamp = newTimestamp 109 payload.Composite_signatures = compositeSigs 110 111 response := otu.CreateProposalAPI(payload) 112 113 CheckResponseCode(t, http.StatusForbidden, response.Code) 114 115 expectedErr := errForbidden 116 expectedErr.StatusCode = http.StatusForbidden 117 118 var e errorResponse 119 json.Unmarshal(response.Body.Bytes(), &e) 120 assert.Equal(t, expectedErr, e) 121 }) 122 } 123 124 func TestUpdateProposal(t *testing.T) { 125 clearTable("communities") 126 clearTable("community_users") 127 clearTable("proposals") 128 // Create a community 129 authorName := "user1" 130 communityId := otu.AddCommunitiesWithUsers(1, authorName)[0] 131 132 t.Run("A community author should be able to cancel a pending proposal they have created", func(t *testing.T) { 133 proposalStruct := otu.GenerateProposalStruct(authorName, communityId) 134 payload := otu.GenerateProposalPayload(authorName, proposalStruct) 135 response := otu.CreateProposalAPI(payload) 136 137 CheckResponseCode(t, http.StatusCreated, response.Code) 138 var p models.Proposal 139 json.Unmarshal(response.Body.Bytes(), &p) 140 141 // Get proposal after create 142 response = otu.GetProposalByIdAPI(communityId, p.ID) 143 var created models.Proposal 144 json.Unmarshal(response.Body.Bytes(), &created) 145 146 assert.Equal(t, "pending", *created.Computed_status) 147 148 cancelPayload := otu.GenerateCancelProposalStruct(authorName, p.ID) 149 response = otu.UpdateProposalAPI(p.ID, cancelPayload) 150 checkResponseCode(t, http.StatusOK, response.Code) 151 152 // Get proposal after update 153 response = otu.GetProposalByIdAPI(communityId, p.ID) 154 var cancelled models.Proposal 155 json.Unmarshal(response.Body.Bytes(), &cancelled) 156 assert.Equal(t, "cancelled", *cancelled.Computed_status) 157 }) 158 159 t.Run("A community author should be able to cancel an active proposal", func(t *testing.T) { 160 proposalStruct := otu.GenerateProposalStruct(authorName, communityId) 161 // Make proposal active 162 proposalStruct.Start_time = time.Now().AddDate(0, -1, 0) 163 164 payload := otu.GenerateProposalPayload(authorName, proposalStruct) 165 response := otu.CreateProposalAPI(payload) 166 167 CheckResponseCode(t, http.StatusCreated, response.Code) 168 var p models.Proposal 169 json.Unmarshal(response.Body.Bytes(), &p) 170 171 // Get proposal after create 172 response = otu.GetProposalByIdAPI(communityId, p.ID) 173 var created models.Proposal 174 json.Unmarshal(response.Body.Bytes(), &created) 175 176 assert.Equal(t, "active", *created.Computed_status) 177 178 cancelPayload := otu.GenerateCancelProposalStruct(authorName, communityId) 179 response = otu.UpdateProposalAPI(p.ID, cancelPayload) 180 checkResponseCode(t, http.StatusOK, response.Code) 181 182 // Get proposal after update 183 response = otu.GetProposalByIdAPI(communityId, p.ID) 184 var cancelled models.Proposal 185 json.Unmarshal(response.Body.Bytes(), &cancelled) 186 assert.Equal(t, "cancelled", *cancelled.Computed_status) 187 }) 188 189 t.Run("A community author should be able to cancel an active proposal created by another author", func(t *testing.T) { 190 proposalStruct := otu.GenerateProposalStruct(authorName, communityId) 191 // Make proposal active 192 proposalStruct.Start_time = time.Now().AddDate(0, -1, 0) 193 194 payload := otu.GenerateProposalPayload(authorName, proposalStruct) 195 response := otu.CreateProposalAPI(payload) 196 197 CheckResponseCode(t, http.StatusCreated, response.Code) 198 var p models.Proposal 199 json.Unmarshal(response.Body.Bytes(), &p) 200 201 //Generate second author 202 userStruct := otu.GenerateCommunityUserStruct("user2", "author") 203 userPayload := otu.GenerateCommunityUserPayload("user1", userStruct) 204 205 response = otu.CreateCommunityUserAPI(communityId, userPayload) 206 checkResponseCode(t, http.StatusCreated, response.Code) 207 208 // Get proposal after create 209 response = otu.GetProposalByIdAPI(communityId, p.ID) 210 var created models.Proposal 211 json.Unmarshal(response.Body.Bytes(), &created) 212 213 assert.Equal(t, "active", *created.Computed_status) 214 215 cancelPayload := otu.GenerateCancelProposalStruct("user2", communityId) 216 response = otu.UpdateProposalAPI(p.ID, cancelPayload) 217 checkResponseCode(t, http.StatusOK, response.Code) 218 219 // Get proposal after update 220 response = otu.GetProposalByIdAPI(communityId, p.ID) 221 var cancelled models.Proposal 222 json.Unmarshal(response.Body.Bytes(), &cancelled) 223 assert.Equal(t, "cancelled", *cancelled.Computed_status) 224 }) 225 } 226 227 func TestCreateManyProposals(t *testing.T) { 228 clearTable("communities") 229 clearTable("community_users") 230 clearTable("proposals") 231 232 // Create a community 233 authorName := "account" 234 communityId := otu.AddCommunitiesWithUsers(1, authorName)[0] 235 236 t.Run("A community author should be able to create many proposals", func(t *testing.T) { 237 numProposals := 4 238 239 for i := 0; i < numProposals; i++ { 240 proposalStruct := otu.GenerateProposalStruct(authorName, communityId) 241 payload := otu.GenerateProposalPayload(authorName, proposalStruct) 242 response := otu.CreateProposalAPI(payload) 243 244 CheckResponseCode(t, http.StatusCreated, response.Code) 245 var p models.Proposal 246 json.Unmarshal(response.Body.Bytes(), &p) 247 248 // Get proposal after create 249 response = otu.GetProposalByIdAPI(communityId, p.ID) 250 var created models.Proposal 251 json.Unmarshal(response.Body.Bytes(), &created) 252 253 assert.Equal(t, "pending", *created.Computed_status) 254 } 255 }) 256 257 }