code.vegaprotocol.io/vega@v0.79.0/commands/batch_proposal_submission_test.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 commands_test 17 18 import ( 19 "errors" 20 "testing" 21 22 "code.vegaprotocol.io/vega/commands" 23 vgrand "code.vegaprotocol.io/vega/libs/rand" 24 "code.vegaprotocol.io/vega/libs/test" 25 vegapb "code.vegaprotocol.io/vega/protos/vega" 26 commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" 27 28 "github.com/stretchr/testify/assert" 29 ) 30 31 func TestCheckBatchProposalSubmission(t *testing.T) { 32 t.Run("Submitting a nil command fails", testNilBatchProposalSubmissionFails) 33 t.Run("Submitting a proposal without terms fails", testBatchProposalSubmissionWithoutTermsFails) 34 t.Run("Submitting a proposal change without change fails", testBatchProposalSubmissionWithoutChangesFails) 35 t.Run("Submitting a proposal without rational fails", testBatchProposalSubmissionWithoutRationalFails) 36 t.Run("Submitting a proposal with rational succeeds", testBatchProposalSubmissionWithRationalSucceeds) 37 t.Run("Submitting a proposal with rational description succeeds", testBatchProposalSubmissionWithRationalDescriptionSucceeds) 38 t.Run("Submitting a proposal with incorrect rational description fails", testBatchProposalSubmissionWithIncorrectRationalDescriptionFails) 39 t.Run("Submitting a proposal with rational URL and hash succeeds", testBatchProposalSubmissionWithRationalDescriptionAndTitleSucceeds) 40 t.Run("Submitting a proposal with non-positive closing timestamp fails", testBatchProposalSubmissionWithNonPositiveClosingTimestampFails) 41 t.Run("Submitting a proposal with positive closing timestamp succeeds", testBatchProposalSubmissionWithPositiveClosingTimestampSucceeds) 42 t.Run("Submitting a proposal with non-positive enactment timestamp fails", testBatchProposalSubmissionWithNonPositiveEnactmentTimestampFails) 43 t.Run("Submitting a proposal with positive enactment timestamp succeeds", testBatchProposalSubmissionWithPositiveEnactmentTimestampSucceeds) 44 t.Run("Submitting a proposal with closing timestamp after enactment timestamp fails", testBatchProposalSubmissionWithClosingTimestampAfterEnactmentTimestampFails) 45 t.Run("Submitting a proposal with closing timestamp before enactment timestamp succeeds", testBatchProposalSubmissionWithClosingTimestampBeforeEnactmentTimestampSucceeds) 46 t.Run("Submitting a proposal with closing timestamp at enactment timestamp succeeds", testProposalSubmissionWithClosingTimestampAtEnactmentTimestampSucceeds) 47 } 48 49 func testNilBatchProposalSubmissionFails(t *testing.T) { 50 err := checkBatchProposalSubmission(nil) 51 52 assert.Contains(t, err.Get("batch_proposal_submission"), commands.ErrIsRequired) 53 } 54 55 func testBatchProposalSubmissionWithoutTermsFails(t *testing.T) { 56 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{}) 57 58 assert.Contains(t, err.Get("batch_proposal_submission.terms"), commands.ErrIsRequired) 59 } 60 61 func testBatchProposalSubmissionWithoutChangesFails(t *testing.T) { 62 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 63 Terms: &commandspb.BatchProposalSubmissionTerms{}, 64 }) 65 66 assert.Contains(t, err.Get("batch_proposal_submission.terms.changes"), commands.ErrIsRequired) 67 } 68 69 func testBatchProposalSubmissionWithoutRationalFails(t *testing.T) { 70 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{}) 71 72 assert.Contains(t, err.Get("batch_proposal_submission.rationale"), commands.ErrIsRequired) 73 } 74 75 func testBatchProposalSubmissionWithRationalSucceeds(t *testing.T) { 76 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 77 Rationale: &vegapb.ProposalRationale{}, 78 }) 79 80 assert.Empty(t, err.Get("batch_proposal_submission.rationale")) 81 } 82 83 func testBatchProposalSubmissionWithRationalDescriptionSucceeds(t *testing.T) { 84 tcs := []struct { 85 name string 86 description string 87 }{ 88 { 89 name: "with description of 10 characters", 90 description: vgrand.RandomStr(10), 91 }, { 92 name: "with description of 1024 characters", 93 description: vgrand.RandomStr(1024), 94 }, 95 } 96 97 for _, tc := range tcs { 98 t.Run(tc.name, func(tt *testing.T) { 99 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 100 Rationale: &vegapb.ProposalRationale{ 101 Description: tc.description, 102 }, 103 }) 104 105 assert.Empty(tt, err.Get("batch_proposal_submission.rationale.description")) 106 }) 107 } 108 } 109 110 func testBatchProposalSubmissionWithIncorrectRationalDescriptionFails(t *testing.T) { 111 tcs := []struct { 112 name string 113 description string 114 expectedErr error 115 }{ 116 { 117 name: "with empty description", 118 description: "", 119 expectedErr: commands.ErrIsRequired, 120 }, { 121 name: "with blank description", 122 description: " ", 123 expectedErr: commands.ErrIsRequired, 124 }, { 125 name: "with description > 1024", 126 description: vgrand.RandomStr(20420), 127 expectedErr: commands.ErrMustNotExceed20000Chars, 128 }, 129 } 130 131 for _, tc := range tcs { 132 t.Run(tc.name, func(tt *testing.T) { 133 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 134 Rationale: &vegapb.ProposalRationale{ 135 Description: tc.description, 136 }, 137 }) 138 139 assert.Contains(tt, err.Get("batch_proposal_submission.rationale.description"), tc.expectedErr) 140 }) 141 } 142 } 143 144 func testBatchProposalSubmissionWithRationalDescriptionAndTitleSucceeds(t *testing.T) { 145 tcs := []struct { 146 name string 147 shouldErr bool 148 submission *commandspb.BatchProposalSubmission 149 }{ 150 { 151 name: "NewMarket with rational Title and Description", 152 submission: &commandspb.BatchProposalSubmission{ 153 Terms: &commandspb.BatchProposalSubmissionTerms{ 154 Changes: []*vegapb.BatchProposalTermsChange{ 155 { 156 Change: &vegapb.BatchProposalTermsChange_NewMarket{}, 157 }, 158 }, 159 }, 160 Rationale: &vegapb.ProposalRationale{ 161 Title: vgrand.RandomStr(10), 162 Description: vgrand.RandomStr(10), 163 }, 164 }, 165 }, { 166 name: "NewMarket without rational Title and Description", 167 shouldErr: true, 168 submission: &commandspb.BatchProposalSubmission{ 169 Terms: &commandspb.BatchProposalSubmissionTerms{ 170 Changes: []*vegapb.BatchProposalTermsChange{ 171 { 172 Change: &vegapb.BatchProposalTermsChange_NewMarket{}, 173 }, 174 }, 175 }, 176 Rationale: &vegapb.ProposalRationale{}, 177 }, 178 }, { 179 name: "with UpdateMarket with rational Title and Description", 180 submission: &commandspb.BatchProposalSubmission{ 181 Terms: &commandspb.BatchProposalSubmissionTerms{ 182 Changes: []*vegapb.BatchProposalTermsChange{ 183 { 184 Change: &vegapb.BatchProposalTermsChange_UpdateMarket{}, 185 }, 186 }, 187 }, 188 Rationale: &vegapb.ProposalRationale{ 189 Title: vgrand.RandomStr(10), 190 Description: vgrand.RandomStr(10), 191 }, 192 }, 193 }, { 194 name: "with UpdateMarket without rational Title and Description", 195 shouldErr: true, 196 submission: &commandspb.BatchProposalSubmission{ 197 Terms: &commandspb.BatchProposalSubmissionTerms{ 198 Changes: []*vegapb.BatchProposalTermsChange{ 199 { 200 Change: &vegapb.BatchProposalTermsChange_UpdateMarket{}, 201 }, 202 }, 203 }, 204 Rationale: &vegapb.ProposalRationale{}, 205 }, 206 }, { 207 name: "with UpdateNetworkParameter with rational Title and Description", 208 submission: &commandspb.BatchProposalSubmission{ 209 Terms: &commandspb.BatchProposalSubmissionTerms{ 210 Changes: []*vegapb.BatchProposalTermsChange{ 211 { 212 Change: &vegapb.BatchProposalTermsChange_UpdateNetworkParameter{}, 213 }, 214 }, 215 }, 216 Rationale: &vegapb.ProposalRationale{ 217 Title: vgrand.RandomStr(10), 218 Description: vgrand.RandomStr(10), 219 }, 220 }, 221 }, { 222 name: "with UpdateNetworkParameter without rational Title and Description", 223 shouldErr: true, 224 submission: &commandspb.BatchProposalSubmission{ 225 Terms: &commandspb.BatchProposalSubmissionTerms{ 226 Changes: []*vegapb.BatchProposalTermsChange{ 227 { 228 Change: &vegapb.BatchProposalTermsChange_UpdateNetworkParameter{}, 229 }, 230 }, 231 }, 232 Rationale: &vegapb.ProposalRationale{}, 233 }, 234 }, { 235 name: "with NewFreeform with rational Title and Description", 236 submission: &commandspb.BatchProposalSubmission{ 237 Terms: &commandspb.BatchProposalSubmissionTerms{ 238 Changes: []*vegapb.BatchProposalTermsChange{ 239 { 240 Change: &vegapb.BatchProposalTermsChange_NewFreeform{}, 241 }, 242 }, 243 }, 244 Rationale: &vegapb.ProposalRationale{ 245 Title: vgrand.RandomStr(10), 246 Description: vgrand.RandomStr(10), 247 }, 248 }, 249 }, 250 } 251 252 for _, tc := range tcs { 253 t.Run(tc.name, func(tt *testing.T) { 254 err := checkBatchProposalSubmission(tc.submission) 255 if !tc.shouldErr { 256 assert.Empty(tt, err.Get("batch_proposal_submission.rationale.title"), tc.name) 257 assert.Empty(tt, err.Get("batch_proposal_submission.rationale.description"), tc.name) 258 } else { 259 assert.Contains(tt, err.Get("batch_proposal_submission.rationale.title"), commands.ErrIsRequired, tc.name) 260 assert.Contains(tt, err.Get("batch_proposal_submission.rationale.description"), commands.ErrIsRequired, tc.name) 261 } 262 }) 263 } 264 } 265 266 func testBatchProposalSubmissionWithNonPositiveClosingTimestampFails(t *testing.T) { 267 testCases := []struct { 268 msg string 269 value int64 270 }{ 271 { 272 msg: "with 0 as closing timestamp", 273 value: 0, 274 }, { 275 msg: "with negative closing timestamp", 276 value: test.RandomNegativeI64(), 277 }, 278 } 279 for _, tc := range testCases { 280 t.Run(tc.msg, func(t *testing.T) { 281 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 282 Terms: &commandspb.BatchProposalSubmissionTerms{ 283 ClosingTimestamp: tc.value, 284 Changes: []*vegapb.BatchProposalTermsChange{{}}, 285 }, 286 Rationale: &vegapb.ProposalRationale{ 287 Title: vgrand.RandomStr(10), 288 Description: vgrand.RandomStr(10), 289 }, 290 }) 291 292 assert.Contains(t, err.Get("batch_proposal_submission.terms.closing_timestamp"), commands.ErrMustBePositive) 293 }) 294 } 295 } 296 297 func testBatchProposalSubmissionWithPositiveClosingTimestampSucceeds(t *testing.T) { 298 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 299 Terms: &commandspb.BatchProposalSubmissionTerms{ 300 ClosingTimestamp: test.RandomPositiveI64(), 301 Changes: []*vegapb.BatchProposalTermsChange{{}}, 302 }, 303 Rationale: &vegapb.ProposalRationale{ 304 Title: vgrand.RandomStr(10), 305 Description: vgrand.RandomStr(10), 306 }, 307 }) 308 309 assert.NotContains(t, err.Get("proposal_submission.terms.closing_timestamp"), commands.ErrMustBePositive) 310 } 311 312 func testBatchProposalSubmissionWithNonPositiveEnactmentTimestampFails(t *testing.T) { 313 testCases := []struct { 314 msg string 315 value int64 316 }{ 317 { 318 msg: "with 0 as closing timestamp", 319 value: 0, 320 }, { 321 msg: "with negative closing timestamp", 322 value: test.RandomNegativeI64(), 323 }, 324 } 325 for _, tc := range testCases { 326 t.Run(tc.msg, func(t *testing.T) { 327 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 328 Terms: &commandspb.BatchProposalSubmissionTerms{ 329 ClosingTimestamp: test.RandomPositiveI64(), 330 Changes: []*vegapb.BatchProposalTermsChange{{ 331 EnactmentTimestamp: tc.value, 332 }}, 333 }, 334 Rationale: &vegapb.ProposalRationale{ 335 Title: vgrand.RandomStr(10), 336 Description: vgrand.RandomStr(10), 337 }, 338 }) 339 340 assert.Contains(t, err.Get("batch_proposal_submission.terms.enactment_timestamp"), commands.ErrMustBePositive) 341 }) 342 } 343 } 344 345 func testBatchProposalSubmissionWithPositiveEnactmentTimestampSucceeds(t *testing.T) { 346 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 347 Terms: &commandspb.BatchProposalSubmissionTerms{ 348 ClosingTimestamp: test.RandomPositiveI64(), 349 Changes: []*vegapb.BatchProposalTermsChange{{ 350 EnactmentTimestamp: test.RandomPositiveI64(), 351 }}, 352 }, 353 Rationale: &vegapb.ProposalRationale{ 354 Title: vgrand.RandomStr(10), 355 Description: vgrand.RandomStr(10), 356 }, 357 }) 358 359 assert.NotContains(t, err.Get("proposal_submission.terms.enactment_timestamp"), commands.ErrMustBePositive) 360 } 361 362 func testBatchProposalSubmissionWithClosingTimestampAfterEnactmentTimestampFails(t *testing.T) { 363 closingTime := test.RandomPositiveI64() 364 enactmentTime := test.RandomPositiveI64Before(closingTime) 365 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 366 Terms: &commandspb.BatchProposalSubmissionTerms{ 367 ClosingTimestamp: closingTime, 368 Changes: []*vegapb.BatchProposalTermsChange{{ 369 EnactmentTimestamp: enactmentTime, 370 }}, 371 }, 372 Rationale: &vegapb.ProposalRationale{ 373 Title: vgrand.RandomStr(10), 374 Description: vgrand.RandomStr(10), 375 }, 376 }) 377 378 assert.Contains(t, err.Get("batch_proposal_submission.terms.closing_timestamp"), 379 errors.New("cannot be after enactment time"), 380 ) 381 } 382 383 func testBatchProposalSubmissionWithClosingTimestampBeforeEnactmentTimestampSucceeds(t *testing.T) { 384 enactmentTime := test.RandomPositiveI64() 385 closingTime := test.RandomPositiveI64Before(enactmentTime) 386 387 err := checkBatchProposalSubmission(&commandspb.BatchProposalSubmission{ 388 Terms: &commandspb.BatchProposalSubmissionTerms{ 389 ClosingTimestamp: closingTime, 390 Changes: []*vegapb.BatchProposalTermsChange{{ 391 EnactmentTimestamp: enactmentTime, 392 }}, 393 }, 394 Rationale: &vegapb.ProposalRationale{ 395 Title: vgrand.RandomStr(10), 396 Description: vgrand.RandomStr(10), 397 }, 398 }) 399 400 assert.NotContains(t, err.Get("batch_proposal_submission.terms.closing_timestamp"), 401 errors.New("cannot be after enactment time"), 402 ) 403 } 404 405 func checkBatchProposalSubmission(cmd *commandspb.BatchProposalSubmission) commands.Errors { 406 err := commands.CheckBatchProposalSubmission(cmd) 407 408 var e commands.Errors 409 if ok := errors.As(err, &e); !ok { 410 return commands.NewErrors() 411 } 412 413 return e 414 }