github.com/cosmos/cosmos-sdk@v0.50.10/x/gov/client/cli/util_test.go (about)

     1  package cli
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/base64"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/spf13/cobra"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	sdkmath "cosmossdk.io/math"
    17  
    18  	"github.com/cosmos/cosmos-sdk/client"
    19  	"github.com/cosmos/cosmos-sdk/codec"
    20  	codectypes "github.com/cosmos/cosmos-sdk/codec/types"
    21  	"github.com/cosmos/cosmos-sdk/testutil"
    22  	"github.com/cosmos/cosmos-sdk/testutil/testdata"
    23  	sdk "github.com/cosmos/cosmos-sdk/types"
    24  	banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
    25  	v1 "github.com/cosmos/cosmos-sdk/x/gov/types/v1"
    26  	"github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1"
    27  	stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
    28  )
    29  
    30  func TestParseSubmitLegacyProposal(t *testing.T) {
    31  	okJSON := testutil.WriteToNewTempFile(t, `
    32  {
    33    "title": "Test Proposal",
    34    "description": "My awesome proposal",
    35    "type": "Text",
    36    "deposit": "1000test"
    37  }
    38  `)
    39  
    40  	badJSON := testutil.WriteToNewTempFile(t, "bad json")
    41  	fs := NewCmdSubmitLegacyProposal().Flags()
    42  
    43  	// nonexistent json
    44  	fs.Set(FlagProposal, "fileDoesNotExist")
    45  	_, err := parseSubmitLegacyProposal(fs)
    46  	require.Error(t, err)
    47  
    48  	// invalid json
    49  	fs.Set(FlagProposal, badJSON.Name())
    50  	_, err = parseSubmitLegacyProposal(fs)
    51  	require.Error(t, err)
    52  
    53  	// ok json
    54  	fs.Set(FlagProposal, okJSON.Name())
    55  	proposal1, err := parseSubmitLegacyProposal(fs)
    56  	require.Nil(t, err, "unexpected error")
    57  	require.Equal(t, "Test Proposal", proposal1.Title)
    58  	require.Equal(t, "My awesome proposal", proposal1.Description)
    59  	require.Equal(t, "Text", proposal1.Type)
    60  	require.Equal(t, "1000test", proposal1.Deposit)
    61  
    62  	// flags that can't be used with --proposal
    63  	for _, incompatibleFlag := range ProposalFlags {
    64  		fs.Set(incompatibleFlag, "some value")
    65  		_, err := parseSubmitLegacyProposal(fs)
    66  		require.Error(t, err)
    67  		fs.Set(incompatibleFlag, "")
    68  	}
    69  
    70  	// no --proposal, only flags
    71  	fs.Set(FlagProposal, "")
    72  	flagTestCases := map[string]struct {
    73  		pTitle       string
    74  		pDescription string
    75  		pType        string
    76  		expErr       bool
    77  		errMsg       string
    78  	}{
    79  		"valid flags": {
    80  			pTitle:       proposal1.Title,
    81  			pDescription: proposal1.Description,
    82  			pType:        proposal1.Type,
    83  		},
    84  		"empty type": {
    85  			pTitle:       proposal1.Title,
    86  			pDescription: proposal1.Description,
    87  			expErr:       true,
    88  			errMsg:       "proposal type is required",
    89  		},
    90  		"empty title": {
    91  			pDescription: proposal1.Description,
    92  			pType:        proposal1.Type,
    93  			expErr:       true,
    94  			errMsg:       "proposal title is required",
    95  		},
    96  		"empty description": {
    97  			pTitle: proposal1.Title,
    98  			pType:  proposal1.Type,
    99  			expErr: true,
   100  			errMsg: "proposal description is required",
   101  		},
   102  	}
   103  	for name, tc := range flagTestCases {
   104  		t.Run(name, func(t *testing.T) {
   105  			fs.Set(FlagTitle, tc.pTitle)
   106  			fs.Set(FlagDescription, tc.pDescription)
   107  			fs.Set(FlagProposalType, tc.pType)
   108  			fs.Set(FlagDeposit, proposal1.Deposit)
   109  			proposal2, err := parseSubmitLegacyProposal(fs)
   110  
   111  			if tc.expErr {
   112  				require.Error(t, err)
   113  				require.Contains(t, err.Error(), tc.errMsg)
   114  			} else {
   115  				require.NoError(t, err)
   116  				require.Equal(t, proposal1.Title, proposal2.Title)
   117  				require.Equal(t, proposal1.Description, proposal2.Description)
   118  				require.Equal(t, proposal1.Type, proposal2.Type)
   119  				require.Equal(t, proposal1.Deposit, proposal2.Deposit)
   120  			}
   121  		})
   122  	}
   123  
   124  	err = okJSON.Close()
   125  	require.Nil(t, err, "unexpected error")
   126  	err = badJSON.Close()
   127  	require.Nil(t, err, "unexpected error")
   128  }
   129  
   130  func TestParseSubmitProposal(t *testing.T) {
   131  	_, _, addr := testdata.KeyTestPubAddr()
   132  	interfaceRegistry := codectypes.NewInterfaceRegistry()
   133  	cdc := codec.NewProtoCodec(interfaceRegistry)
   134  	banktypes.RegisterInterfaces(interfaceRegistry)
   135  	stakingtypes.RegisterInterfaces(interfaceRegistry)
   136  	v1beta1.RegisterInterfaces(interfaceRegistry)
   137  	v1.RegisterInterfaces(interfaceRegistry)
   138  	expectedMetadata := []byte{42}
   139  
   140  	okJSON := testutil.WriteToNewTempFile(t, fmt.Sprintf(`
   141  {
   142  	"messages": [
   143  		{
   144  			"@type": "/cosmos.bank.v1beta1.MsgSend",
   145  			"from_address": "%s",
   146  			"to_address": "%s",
   147  			"amount":[{"denom": "stake","amount": "10"}]
   148  		},
   149  		{
   150  			"@type": "/cosmos.staking.v1beta1.MsgDelegate",
   151  			"delegator_address": "%s",
   152  			"validator_address": "%s",
   153  			"amount":{"denom": "stake","amount": "10"}
   154  		},
   155  		{
   156  			"@type": "/cosmos.gov.v1.MsgExecLegacyContent",
   157  			"authority": "%s",
   158  			"content": {
   159  				"@type": "/cosmos.gov.v1beta1.TextProposal",
   160  				"title": "My awesome title",
   161  				"description": "My awesome description"
   162  			}
   163  		}
   164    	],
   165  	"metadata": "%s",
   166  	"title": "My awesome title",
   167  	"summary": "My awesome summary",
   168  	"deposit": "1000test",
   169  	"expedited": true
   170  }
   171  `, addr, addr, addr, addr, addr, base64.StdEncoding.EncodeToString(expectedMetadata)))
   172  
   173  	badJSON := testutil.WriteToNewTempFile(t, "bad json")
   174  
   175  	// nonexistent json
   176  	_, _, _, err := parseSubmitProposal(cdc, "fileDoesNotExist")
   177  	require.Error(t, err)
   178  
   179  	// invalid json
   180  	_, _, _, err = parseSubmitProposal(cdc, badJSON.Name())
   181  	require.Error(t, err)
   182  
   183  	// ok json
   184  	proposal, msgs, deposit, err := parseSubmitProposal(cdc, okJSON.Name())
   185  	require.NoError(t, err, "unexpected error")
   186  	require.Equal(t, sdk.NewCoins(sdk.NewCoin("test", sdkmath.NewInt(1000))), deposit)
   187  	require.Equal(t, base64.StdEncoding.EncodeToString(expectedMetadata), proposal.Metadata)
   188  	require.Len(t, msgs, 3)
   189  	msg1, ok := msgs[0].(*banktypes.MsgSend)
   190  	require.True(t, ok)
   191  	require.Equal(t, addr.String(), msg1.FromAddress)
   192  	require.Equal(t, addr.String(), msg1.ToAddress)
   193  	require.Equal(t, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10))), msg1.Amount)
   194  	msg2, ok := msgs[1].(*stakingtypes.MsgDelegate)
   195  	require.True(t, ok)
   196  	require.Equal(t, addr.String(), msg2.DelegatorAddress)
   197  	require.Equal(t, addr.String(), msg2.ValidatorAddress)
   198  	require.Equal(t, sdk.NewCoin("stake", sdkmath.NewInt(10)), msg2.Amount)
   199  	msg3, ok := msgs[2].(*v1.MsgExecLegacyContent)
   200  	require.True(t, ok)
   201  	require.Equal(t, addr.String(), msg3.Authority)
   202  	textProp, ok := msg3.Content.GetCachedValue().(*v1beta1.TextProposal)
   203  	require.True(t, ok)
   204  	require.Equal(t, "My awesome title", textProp.Title)
   205  	require.Equal(t, "My awesome description", textProp.Description)
   206  	require.Equal(t, "My awesome title", proposal.Title)
   207  	require.Equal(t, "My awesome summary", proposal.Summary)
   208  	require.Equal(t, true, proposal.Expedited)
   209  
   210  	err = okJSON.Close()
   211  	require.Nil(t, err, "unexpected error")
   212  	err = badJSON.Close()
   213  	require.Nil(t, err, "unexpected error")
   214  }
   215  
   216  func getCommandHelp(t *testing.T, cmd *cobra.Command) string {
   217  	// Create a pipe, so we can capture the help sent to stdout.
   218  	reader, writer, err := os.Pipe()
   219  	require.NoError(t, err, "creating os.Pipe()")
   220  	outChan := make(chan string)
   221  	defer func(origCmdOut io.Writer) {
   222  		cmd.SetOut(origCmdOut)
   223  		// Ignoring these errors since we're just ensuring cleanup here,
   224  		// and they will return an error if already called (which we don't care about).
   225  		_ = reader.Close()
   226  		_ = writer.Close()
   227  		close(outChan)
   228  	}(cmd.OutOrStdout())
   229  	cmd.SetOut(writer)
   230  
   231  	// Do the reading in a separate goroutine from the writing (a best practice).
   232  	go func() {
   233  		var b bytes.Buffer
   234  		_, buffErr := io.Copy(&b, reader)
   235  		if buffErr != nil {
   236  			// Due to complexities of goroutines and multiple channels, I'm sticking with a
   237  			// single channel and just putting the error in there (which I'll test for later).
   238  			b.WriteString("buffer error: " + buffErr.Error())
   239  		}
   240  		outChan <- b.String()
   241  	}()
   242  
   243  	err = cmd.Help()
   244  	require.NoError(t, err, "cmd.Help()")
   245  	require.NoError(t, writer.Close(), "pipe writer .Close()")
   246  	rv := <-outChan
   247  	require.NotContains(t, rv, "buffer error: ", "buffer output")
   248  	return rv
   249  }
   250  
   251  func TestAddGovPropFlagsToCmd(t *testing.T) {
   252  	cmd := &cobra.Command{
   253  		Short: "Just a test command that does nothing but we can add flags to it.",
   254  		Run: func(cmd *cobra.Command, args []string) {
   255  			t.Errorf("The cmd has run with the args %q, but Run shouldn't have been called.", args)
   256  		},
   257  	}
   258  	testFunc := func() {
   259  		AddGovPropFlagsToCmd(cmd)
   260  	}
   261  	require.NotPanics(t, testFunc, "AddGovPropFlagsToCmd")
   262  
   263  	help := getCommandHelp(t, cmd)
   264  
   265  	expDepositDesc := "The deposit to include with the governance proposal"
   266  	expMetadataDesc := "The metadata to include with the governance proposal"
   267  	expTitleDesc := "The title to put on the governance proposal"
   268  	expSummaryDesc := "The summary to include with the governance proposal"
   269  	// Regexp notes: (?m:...) = multi-line mode so ^ and $ match the beginning and end of each line.
   270  	// Each regexp assertion checks for a line containing only a specific flag and its description.
   271  	assert.Regexp(t, `(?m:^\s+--`+FlagDeposit+` string\s+`+expDepositDesc+`$)`, help, "help output")
   272  	assert.Regexp(t, `(?m:^\s+--`+FlagMetadata+` string\s+`+expMetadataDesc+`$)`, help, "help output")
   273  	assert.Regexp(t, `(?m:^\s+--`+FlagTitle+` string\s+`+expTitleDesc+`$)`, help, "help output")
   274  	assert.Regexp(t, `(?m:^\s+--`+FlagSummary+` string\s+`+expSummaryDesc+`$)`, help, "help output")
   275  }
   276  
   277  func TestReadGovPropFlags(t *testing.T) {
   278  	fromAddr := sdk.AccAddress("from_addr___________")
   279  	argDeposit := "--" + FlagDeposit
   280  	argMetadata := "--" + FlagMetadata
   281  	argTitle := "--" + FlagTitle
   282  	argSummary := "--" + FlagSummary
   283  
   284  	// cz is a shorter way to define coins objects for these tests.
   285  	cz := func(coins string) sdk.Coins {
   286  		rv, err := sdk.ParseCoinsNormalized(coins)
   287  		require.NoError(t, err, "ParseCoinsNormalized(%q)", coins)
   288  		return rv
   289  	}
   290  
   291  	tests := []struct {
   292  		name     string
   293  		fromAddr sdk.AccAddress
   294  		args     []string
   295  		exp      *v1.MsgSubmitProposal
   296  		expErr   []string
   297  	}{
   298  		{
   299  			name:     "no args no from",
   300  			fromAddr: nil,
   301  			args:     []string{},
   302  			exp: &v1.MsgSubmitProposal{
   303  				InitialDeposit: nil,
   304  				Proposer:       "",
   305  				Metadata:       "",
   306  				Title:          "",
   307  				Summary:        "",
   308  			},
   309  		},
   310  		{
   311  			name:     "only from defined",
   312  			fromAddr: fromAddr,
   313  			args:     []string{},
   314  			exp: &v1.MsgSubmitProposal{
   315  				InitialDeposit: nil,
   316  				Proposer:       fromAddr.String(),
   317  				Metadata:       "",
   318  				Title:          "",
   319  				Summary:        "",
   320  			},
   321  		},
   322  
   323  		// only deposit tests.
   324  		{
   325  			name:     "only deposit empty string",
   326  			fromAddr: nil,
   327  			args:     []string{argDeposit, ""},
   328  			exp: &v1.MsgSubmitProposal{
   329  				InitialDeposit: nil,
   330  				Proposer:       "",
   331  				Metadata:       "",
   332  				Title:          "",
   333  				Summary:        "",
   334  			},
   335  		},
   336  		{
   337  			name:     "only deposit one coin",
   338  			fromAddr: nil,
   339  			args:     []string{argDeposit, "1bigcoin"},
   340  			exp: &v1.MsgSubmitProposal{
   341  				InitialDeposit: cz("1bigcoin"),
   342  				Proposer:       "",
   343  				Metadata:       "",
   344  				Title:          "",
   345  				Summary:        "",
   346  			},
   347  		},
   348  		{
   349  			name:     "only deposit invalid coins",
   350  			fromAddr: nil,
   351  			args:     []string{argDeposit, "not really coins"},
   352  			expErr:   []string{"invalid deposit", "invalid decimal coin expression", "not really coins"},
   353  		},
   354  		{
   355  			name:     "only deposit two coins",
   356  			fromAddr: nil,
   357  			args:     []string{argDeposit, "1acoin,2bcoin"},
   358  			exp: &v1.MsgSubmitProposal{
   359  				InitialDeposit: cz("1acoin,2bcoin"),
   360  				Proposer:       "",
   361  				Metadata:       "",
   362  				Title:          "",
   363  				Summary:        "",
   364  			},
   365  		},
   366  		{
   367  			name:     "only deposit two coins other order",
   368  			fromAddr: nil,
   369  			args:     []string{argDeposit, "2bcoin,1acoin"},
   370  			exp: &v1.MsgSubmitProposal{
   371  				InitialDeposit: cz("1acoin,2bcoin"),
   372  				Proposer:       "",
   373  				Metadata:       "",
   374  				Title:          "",
   375  				Summary:        "",
   376  			},
   377  		},
   378  		{
   379  			name:     "only deposit coin 1 of 3 bad",
   380  			fromAddr: nil,
   381  			args:     []string{argDeposit, "1bad^coin,2bcoin,3ccoin"},
   382  			expErr:   []string{"invalid deposit", "invalid decimal coin expression", "1bad^coin"},
   383  		},
   384  		{
   385  			name:     "only deposit coin 2 of 3 bad",
   386  			fromAddr: nil,
   387  			args:     []string{argDeposit, "1acoin,2bad^coin,3ccoin"},
   388  			expErr:   []string{"invalid deposit", "invalid decimal coin expression", "2bad^coin"},
   389  		},
   390  		{
   391  			name:     "only deposit coin 3 of 3 bad",
   392  			fromAddr: nil,
   393  			args:     []string{argDeposit, "1acoin,2bcoin,3bad^coin"},
   394  			expErr:   []string{"invalid deposit", "invalid decimal coin expression", "3bad^coin"},
   395  		},
   396  		// As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag.
   397  		// So I don't have a test for the "could not read deposit" error case.
   398  
   399  		// only metadata tests.
   400  		{
   401  			name:     "only metadata empty",
   402  			fromAddr: nil,
   403  			args:     []string{argMetadata, ""},
   404  			exp: &v1.MsgSubmitProposal{
   405  				InitialDeposit: nil,
   406  				Proposer:       "",
   407  				Metadata:       "",
   408  				Title:          "",
   409  				Summary:        "",
   410  			},
   411  		},
   412  		{
   413  			name:     "only metadata simple",
   414  			fromAddr: nil,
   415  			args:     []string{argMetadata, "just some metadata"},
   416  			exp: &v1.MsgSubmitProposal{
   417  				InitialDeposit: nil,
   418  				Proposer:       "",
   419  				Metadata:       "just some metadata",
   420  				Title:          "",
   421  				Summary:        "",
   422  			},
   423  		},
   424  		{
   425  			name:     "only metadata super long",
   426  			fromAddr: nil,
   427  			args:     []string{argMetadata, strings.Repeat("Long", 1_000_000)},
   428  			exp: &v1.MsgSubmitProposal{
   429  				InitialDeposit: nil,
   430  				Proposer:       "",
   431  				Metadata:       strings.Repeat("Long", 1_000_000),
   432  				Title:          "",
   433  				Summary:        "",
   434  			},
   435  		},
   436  		// As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag.
   437  		// So I don't have a test for the "could not read metadata" error case.
   438  
   439  		// only title tests.
   440  		{
   441  			name:     "only title empty",
   442  			fromAddr: nil,
   443  			args:     []string{argTitle, ""},
   444  			exp: &v1.MsgSubmitProposal{
   445  				InitialDeposit: nil,
   446  				Proposer:       "",
   447  				Metadata:       "",
   448  				Title:          "",
   449  				Summary:        "",
   450  			},
   451  		},
   452  		{
   453  			name:     "only title simple",
   454  			fromAddr: nil,
   455  			args:     []string{argTitle, "just a title"},
   456  			exp: &v1.MsgSubmitProposal{
   457  				InitialDeposit: nil,
   458  				Proposer:       "",
   459  				Metadata:       "",
   460  				Title:          "just a title",
   461  				Summary:        "",
   462  			},
   463  		},
   464  		{
   465  			name:     "only title super long",
   466  			fromAddr: nil,
   467  			args:     []string{argTitle, strings.Repeat("Long", 1_000_000)},
   468  			exp: &v1.MsgSubmitProposal{
   469  				InitialDeposit: nil,
   470  				Proposer:       "",
   471  				Metadata:       "",
   472  				Title:          strings.Repeat("Long", 1_000_000),
   473  				Summary:        "",
   474  			},
   475  		},
   476  		// As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag.
   477  		// So I don't have a test for the "could not read title" error case.
   478  
   479  		// only summary tests.
   480  		{
   481  			name:     "only summary empty",
   482  			fromAddr: nil,
   483  			args:     []string{argSummary, ""},
   484  			exp: &v1.MsgSubmitProposal{
   485  				InitialDeposit: nil,
   486  				Proposer:       "",
   487  				Metadata:       "",
   488  				Title:          "",
   489  				Summary:        "",
   490  			},
   491  		},
   492  		{
   493  			name:     "only summary simple",
   494  			fromAddr: nil,
   495  			args:     []string{argSummary, "just a short summary"},
   496  			exp: &v1.MsgSubmitProposal{
   497  				InitialDeposit: nil,
   498  				Proposer:       "",
   499  				Metadata:       "",
   500  				Title:          "",
   501  				Summary:        "just a short summary",
   502  			},
   503  		},
   504  		{
   505  			name:     "only summary super long",
   506  			fromAddr: nil,
   507  			args:     []string{argSummary, strings.Repeat("Long", 1_000_000)},
   508  			exp: &v1.MsgSubmitProposal{
   509  				InitialDeposit: nil,
   510  				Proposer:       "",
   511  				Metadata:       "",
   512  				Title:          "",
   513  				Summary:        strings.Repeat("Long", 1_000_000),
   514  			},
   515  		},
   516  		// As far as I can tell, there's no way to make flagSet.GetString return an error for a defined string flag.
   517  		// So I don't have a test for the "could not read summary" error case.
   518  
   519  		// Combo tests.
   520  		{
   521  			name:     "all together order 1",
   522  			fromAddr: fromAddr,
   523  			args: []string{
   524  				argDeposit, "56depcoin",
   525  				argMetadata, "my proposal is cool",
   526  				argTitle, "Simple Gov Prop Title",
   527  				argSummary, "This is just a test summary on a simple gov prop.",
   528  			},
   529  			exp: &v1.MsgSubmitProposal{
   530  				InitialDeposit: cz("56depcoin"),
   531  				Proposer:       fromAddr.String(),
   532  				Metadata:       "my proposal is cool",
   533  				Title:          "Simple Gov Prop Title",
   534  				Summary:        "This is just a test summary on a simple gov prop.",
   535  			},
   536  		},
   537  		{
   538  			name:     "all together order 2",
   539  			fromAddr: fromAddr,
   540  			args: []string{
   541  				argTitle, "This title is a *bit* more complex.",
   542  				argSummary, "This\nis\na\ncrazy\nsummary",
   543  				argDeposit, "78coolcoin",
   544  				argMetadata, "this proposal is cooler",
   545  			},
   546  			exp: &v1.MsgSubmitProposal{
   547  				InitialDeposit: cz("78coolcoin"),
   548  				Proposer:       fromAddr.String(),
   549  				Metadata:       "this proposal is cooler",
   550  				Title:          "This title is a *bit* more complex.",
   551  				Summary:        "This\nis\na\ncrazy\nsummary",
   552  			},
   553  		},
   554  		{
   555  			name:     "all except proposer",
   556  			fromAddr: nil,
   557  			args: []string{
   558  				argMetadata, "https://example.com/lucky",
   559  				argDeposit, "33luckycoin",
   560  				argSummary, "This proposal will bring you luck and good fortune in the new year.",
   561  				argTitle, "Increase Luck",
   562  			},
   563  			exp: &v1.MsgSubmitProposal{
   564  				InitialDeposit: cz("33luckycoin"),
   565  				Proposer:       "",
   566  				Metadata:       "https://example.com/lucky",
   567  				Title:          "Increase Luck",
   568  				Summary:        "This proposal will bring you luck and good fortune in the new year.",
   569  			},
   570  		},
   571  		{
   572  			name:     "all except proposer but all empty",
   573  			fromAddr: nil,
   574  			args: []string{
   575  				argMetadata, "",
   576  				argDeposit, "",
   577  				argSummary, "",
   578  				argTitle, "",
   579  			},
   580  			exp: &v1.MsgSubmitProposal{
   581  				InitialDeposit: nil,
   582  				Proposer:       "",
   583  				Metadata:       "",
   584  				Title:          "",
   585  				Summary:        "",
   586  			},
   587  		},
   588  		{
   589  			name:     "all except deposit",
   590  			fromAddr: fromAddr,
   591  			args: []string{
   592  				argTitle, "This is a Title",
   593  				argSummary, "This is a useless summary",
   594  				argMetadata, "worthless metadata",
   595  			},
   596  			exp: &v1.MsgSubmitProposal{
   597  				InitialDeposit: nil,
   598  				Proposer:       fromAddr.String(),
   599  				Metadata:       "worthless metadata",
   600  				Title:          "This is a Title",
   601  				Summary:        "This is a useless summary",
   602  			},
   603  			expErr: nil,
   604  		},
   605  		{
   606  			name:     "all except metadata",
   607  			fromAddr: fromAddr,
   608  			args: []string{
   609  				argTitle, "Bland Title",
   610  				argSummary, "Boring summary",
   611  				argDeposit, "99mdcoin",
   612  			},
   613  			exp: &v1.MsgSubmitProposal{
   614  				InitialDeposit: cz("99mdcoin"),
   615  				Proposer:       fromAddr.String(),
   616  				Metadata:       "",
   617  				Title:          "Bland Title",
   618  				Summary:        "Boring summary",
   619  			},
   620  		},
   621  		{
   622  			name:     "all except title",
   623  			fromAddr: fromAddr,
   624  			args: []string{
   625  				argMetadata, "this metadata does not have the title either",
   626  				argDeposit, "71whatcoin",
   627  				argSummary, "This is a summary on a titleless proposal.",
   628  			},
   629  			exp: &v1.MsgSubmitProposal{
   630  				InitialDeposit: cz("71whatcoin"),
   631  				Proposer:       fromAddr.String(),
   632  				Metadata:       "this metadata does not have the title either",
   633  				Title:          "",
   634  				Summary:        "This is a summary on a titleless proposal.",
   635  			},
   636  			expErr: nil,
   637  		},
   638  		{
   639  			name:     "all except summary",
   640  			fromAddr: fromAddr,
   641  			args: []string{
   642  				argMetadata, "28",
   643  				argTitle, "Now This is What I Call A Governance Proposal 28",
   644  				argDeposit, "42musiccoin",
   645  			},
   646  			exp: &v1.MsgSubmitProposal{
   647  				InitialDeposit: cz("42musiccoin"),
   648  				Proposer:       fromAddr.String(),
   649  				Metadata:       "28",
   650  				Title:          "Now This is What I Call A Governance Proposal 28",
   651  				Summary:        "",
   652  			},
   653  			expErr: nil,
   654  		},
   655  	}
   656  
   657  	for _, tc := range tests {
   658  		t.Run(tc.name, func(t *testing.T) {
   659  			cmd := &cobra.Command{
   660  				Short: tc.name,
   661  				Run: func(cmd *cobra.Command, args []string) {
   662  					t.Errorf("The cmd for %q has run with the args %q, but Run shouldn't have been called.", tc.name, args)
   663  				},
   664  			}
   665  			AddGovPropFlagsToCmd(cmd)
   666  			err := cmd.ParseFlags(tc.args)
   667  			require.NoError(t, err, "parsing test case args using cmd: %q", tc.args)
   668  			flagSet := cmd.Flags()
   669  
   670  			clientCtx := client.Context{
   671  				FromAddress: tc.fromAddr,
   672  			}
   673  
   674  			var msg *v1.MsgSubmitProposal
   675  			testFunc := func() {
   676  				msg, err = ReadGovPropFlags(clientCtx, flagSet)
   677  			}
   678  			require.NotPanics(t, testFunc, "ReadGovPropFlags")
   679  			if len(tc.expErr) > 0 {
   680  				require.Error(t, err, "ReadGovPropFlags error")
   681  				for _, exp := range tc.expErr {
   682  					assert.ErrorContains(t, err, exp, "ReadGovPropFlags error")
   683  				}
   684  			} else {
   685  				require.NoError(t, err, "ReadGovPropFlags error")
   686  			}
   687  			assert.Equal(t, tc.exp, msg, "ReadGovPropFlags msg")
   688  		})
   689  	}
   690  }