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  }