github.com/fibonacci-chain/fbc@v0.0.0-20231124064014-c7636198c1e9/x/wasm/types/proposal_test.go (about)

     1  package types
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"strings"
     7  	"testing"
     8  
     9  	sdk "github.com/fibonacci-chain/fbc/libs/cosmos-sdk/types"
    10  	govtypes "github.com/fibonacci-chain/fbc/x/gov/types"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  	"gopkg.in/yaml.v2"
    14  )
    15  
    16  func TestValidateProposalCommons(t *testing.T) {
    17  	type commonProposal struct {
    18  		Title, Description string
    19  	}
    20  
    21  	specs := map[string]struct {
    22  		src    commonProposal
    23  		expErr bool
    24  	}{
    25  		"all good": {src: commonProposal{
    26  			Title:       "Foo",
    27  			Description: "Bar",
    28  		}},
    29  		"prevent empty title": {
    30  			src: commonProposal{
    31  				Description: "Bar",
    32  			},
    33  			expErr: true,
    34  		},
    35  		"prevent white space only title": {
    36  			src: commonProposal{
    37  				Title:       " ",
    38  				Description: "Bar",
    39  			},
    40  			expErr: true,
    41  		},
    42  		"prevent leading white spaces in title": {
    43  			src: commonProposal{
    44  				Title:       " Foo",
    45  				Description: "Bar",
    46  			},
    47  			expErr: true,
    48  		},
    49  		"prevent title exceeds max length ": {
    50  			src: commonProposal{
    51  				Title:       strings.Repeat("a", govtypes.MaxTitleLength+1),
    52  				Description: "Bar",
    53  			},
    54  			expErr: true,
    55  		},
    56  		"prevent empty description": {
    57  			src: commonProposal{
    58  				Title: "Foo",
    59  			},
    60  			expErr: true,
    61  		},
    62  		"prevent leading white spaces in description": {
    63  			src: commonProposal{
    64  				Title:       "Foo",
    65  				Description: " Bar",
    66  			},
    67  			expErr: true,
    68  		},
    69  		"prevent white space only description": {
    70  			src: commonProposal{
    71  				Title:       "Foo",
    72  				Description: " ",
    73  			},
    74  			expErr: true,
    75  		},
    76  		"prevent descr exceeds max length ": {
    77  			src: commonProposal{
    78  				Title:       "Foo",
    79  				Description: strings.Repeat("a", govtypes.MaxDescriptionLength+1),
    80  			},
    81  			expErr: true,
    82  		},
    83  	}
    84  	for msg, spec := range specs {
    85  		t.Run(msg, func(t *testing.T) {
    86  			err := validateProposalCommons(spec.src.Title, spec.src.Description)
    87  			if spec.expErr {
    88  				require.Error(t, err)
    89  			} else {
    90  				require.NoError(t, err)
    91  			}
    92  		})
    93  	}
    94  }
    95  
    96  func TestValidateStoreCodeProposal(t *testing.T) {
    97  	var (
    98  		anyAddress     sdk.AccAddress = bytes.Repeat([]byte{0x0}, ContractAddrLen)
    99  		invalidAddress                = "invalid address"
   100  	)
   101  
   102  	specs := map[string]struct {
   103  		src    *StoreCodeProposal
   104  		expErr bool
   105  	}{
   106  		"all good": {
   107  			src: StoreCodeProposalFixture(),
   108  		},
   109  		"with instantiate permission": {
   110  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   111  				accessConfig := AccessTypeOnlyAddress.With(anyAddress)
   112  				p.InstantiatePermission = &accessConfig
   113  			}),
   114  		},
   115  		"base data missing": {
   116  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   117  				p.Title = ""
   118  			}),
   119  			expErr: true,
   120  		},
   121  		"run_as missing": {
   122  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   123  				p.RunAs = ""
   124  			}),
   125  			expErr: true,
   126  		},
   127  		"run_as invalid": {
   128  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   129  				p.RunAs = invalidAddress
   130  			}),
   131  			expErr: true,
   132  		},
   133  		"wasm code missing": {
   134  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   135  				p.WASMByteCode = nil
   136  			}),
   137  			expErr: true,
   138  		},
   139  		"wasm code invalid": {
   140  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   141  				p.WASMByteCode = bytes.Repeat([]byte{0x0}, MaxWasmSize+1)
   142  			}),
   143  			expErr: true,
   144  		},
   145  		"with invalid instantiate permission": {
   146  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   147  				p.InstantiatePermission = &AccessConfig{}
   148  			}),
   149  			expErr: true,
   150  		},
   151  	}
   152  	for msg, spec := range specs {
   153  		t.Run(msg, func(t *testing.T) {
   154  			err := spec.src.ValidateBasic()
   155  			if spec.expErr {
   156  				require.Error(t, err)
   157  			} else {
   158  				require.NoError(t, err)
   159  			}
   160  		})
   161  	}
   162  }
   163  
   164  func TestValidateInstantiateContractProposal(t *testing.T) {
   165  	invalidAddress := "invalid address"
   166  
   167  	specs := map[string]struct {
   168  		src    *InstantiateContractProposal
   169  		expErr bool
   170  	}{
   171  		"all good": {
   172  			src: InstantiateContractProposalFixture(),
   173  		},
   174  		"without admin": {
   175  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   176  				p.Admin = ""
   177  			}),
   178  		},
   179  		"without init msg": {
   180  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   181  				p.Msg = nil
   182  			}),
   183  			expErr: true,
   184  		},
   185  		"with invalid init msg": {
   186  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   187  				p.Msg = []byte("not a json string")
   188  			}),
   189  			expErr: true,
   190  		},
   191  		"without init funds": {
   192  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   193  				p.Funds = nil
   194  			}),
   195  		},
   196  		"base data missing": {
   197  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   198  				p.Title = ""
   199  			}),
   200  			expErr: true,
   201  		},
   202  		"run_as missing": {
   203  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   204  				p.RunAs = ""
   205  			}),
   206  			expErr: true,
   207  		},
   208  		"run_as invalid": {
   209  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   210  				p.RunAs = invalidAddress
   211  			}),
   212  			expErr: true,
   213  		},
   214  		"admin invalid": {
   215  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   216  				p.Admin = invalidAddress
   217  			}),
   218  			expErr: true,
   219  		},
   220  		"code id empty": {
   221  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   222  				p.CodeID = 0
   223  			}),
   224  			expErr: true,
   225  		},
   226  		"label empty": {
   227  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   228  				p.Label = ""
   229  			}),
   230  			expErr: true,
   231  		},
   232  		"init funds negative": {
   233  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   234  				p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(-1)}}
   235  			}),
   236  			expErr: true,
   237  		},
   238  		"init funds with duplicates": {
   239  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   240  				p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(1)}, {Denom: "foo", Amount: sdk.NewInt(2)}}
   241  			}),
   242  			expErr: true,
   243  		},
   244  	}
   245  	for msg, spec := range specs {
   246  		t.Run(msg, func(t *testing.T) {
   247  			err := spec.src.ValidateBasic()
   248  			if spec.expErr {
   249  				require.Error(t, err)
   250  			} else {
   251  				require.NoError(t, err)
   252  			}
   253  		})
   254  	}
   255  }
   256  
   257  func TestValidateMigrateContractProposal(t *testing.T) {
   258  	invalidAddress := "invalid address2"
   259  
   260  	specs := map[string]struct {
   261  		src    *MigrateContractProposal
   262  		expErr bool
   263  	}{
   264  		"all good": {
   265  			src: MigrateContractProposalFixture(),
   266  		},
   267  		"without migrate msg": {
   268  			src: MigrateContractProposalFixture(func(p *MigrateContractProposal) {
   269  				p.Msg = nil
   270  			}),
   271  			expErr: true,
   272  		},
   273  		"migrate msg with invalid json": {
   274  			src: MigrateContractProposalFixture(func(p *MigrateContractProposal) {
   275  				p.Msg = []byte("not a json message")
   276  			}),
   277  			expErr: true,
   278  		},
   279  		"base data missing": {
   280  			src: MigrateContractProposalFixture(func(p *MigrateContractProposal) {
   281  				p.Title = ""
   282  			}),
   283  			expErr: true,
   284  		},
   285  		"contract missing": {
   286  			src: MigrateContractProposalFixture(func(p *MigrateContractProposal) {
   287  				p.Contract = ""
   288  			}),
   289  			expErr: true,
   290  		},
   291  		"contract invalid": {
   292  			src: MigrateContractProposalFixture(func(p *MigrateContractProposal) {
   293  				p.Contract = invalidAddress
   294  			}),
   295  			expErr: true,
   296  		},
   297  		"code id empty": {
   298  			src: MigrateContractProposalFixture(func(p *MigrateContractProposal) {
   299  				p.CodeID = 0
   300  			}),
   301  			expErr: true,
   302  		},
   303  	}
   304  	for msg, spec := range specs {
   305  		t.Run(msg, func(t *testing.T) {
   306  			err := spec.src.ValidateBasic()
   307  			if spec.expErr {
   308  				require.Error(t, err)
   309  			} else {
   310  				require.NoError(t, err)
   311  			}
   312  		})
   313  	}
   314  }
   315  
   316  func TestValidateSudoContractProposal(t *testing.T) {
   317  	invalidAddress := "invalid address"
   318  
   319  	specs := map[string]struct {
   320  		src    *SudoContractProposal
   321  		expErr bool
   322  	}{
   323  		"all good": {
   324  			src: SudoContractProposalFixture(),
   325  		},
   326  		"msg is nil": {
   327  			src: SudoContractProposalFixture(func(p *SudoContractProposal) {
   328  				p.Msg = nil
   329  			}),
   330  			expErr: true,
   331  		},
   332  		"msg with invalid json": {
   333  			src: SudoContractProposalFixture(func(p *SudoContractProposal) {
   334  				p.Msg = []byte("not a json message")
   335  			}),
   336  			expErr: true,
   337  		},
   338  		"base data missing": {
   339  			src: SudoContractProposalFixture(func(p *SudoContractProposal) {
   340  				p.Title = ""
   341  			}),
   342  			expErr: true,
   343  		},
   344  		"contract missing": {
   345  			src: SudoContractProposalFixture(func(p *SudoContractProposal) {
   346  				p.Contract = ""
   347  			}),
   348  			expErr: true,
   349  		},
   350  		"contract invalid": {
   351  			src: SudoContractProposalFixture(func(p *SudoContractProposal) {
   352  				p.Contract = invalidAddress
   353  			}),
   354  			expErr: true,
   355  		},
   356  	}
   357  	for msg, spec := range specs {
   358  		t.Run(msg, func(t *testing.T) {
   359  			err := spec.src.ValidateBasic()
   360  			if spec.expErr {
   361  				require.Error(t, err)
   362  			} else {
   363  				require.NoError(t, err)
   364  			}
   365  		})
   366  	}
   367  }
   368  
   369  func TestValidateExecuteContractProposal(t *testing.T) {
   370  	invalidAddress := "invalid address"
   371  
   372  	specs := map[string]struct {
   373  		src    *ExecuteContractProposal
   374  		expErr bool
   375  	}{
   376  		"all good": {
   377  			src: ExecuteContractProposalFixture(),
   378  		},
   379  		"msg is nil": {
   380  			src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) {
   381  				p.Msg = nil
   382  			}),
   383  			expErr: true,
   384  		},
   385  		"msg with invalid json": {
   386  			src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) {
   387  				p.Msg = []byte("not a valid json message")
   388  			}),
   389  			expErr: true,
   390  		},
   391  		"base data missing": {
   392  			src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) {
   393  				p.Title = ""
   394  			}),
   395  			expErr: true,
   396  		},
   397  		"contract missing": {
   398  			src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) {
   399  				p.Contract = ""
   400  			}),
   401  			expErr: true,
   402  		},
   403  		"contract invalid": {
   404  			src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) {
   405  				p.Contract = invalidAddress
   406  			}),
   407  			expErr: true,
   408  		},
   409  		"run as is invalid": {
   410  			src: ExecuteContractProposalFixture(func(p *ExecuteContractProposal) {
   411  				p.RunAs = invalidAddress
   412  			}),
   413  			expErr: true,
   414  		},
   415  	}
   416  	for msg, spec := range specs {
   417  		t.Run(msg, func(t *testing.T) {
   418  			err := spec.src.ValidateBasic()
   419  			if spec.expErr {
   420  				require.Error(t, err)
   421  			} else {
   422  				require.NoError(t, err)
   423  			}
   424  		})
   425  	}
   426  }
   427  
   428  func TestValidateUpdateAdminProposal(t *testing.T) {
   429  	invalidAddress := "invalid address"
   430  
   431  	specs := map[string]struct {
   432  		src    *UpdateAdminProposal
   433  		expErr bool
   434  	}{
   435  		"all good": {
   436  			src: UpdateAdminProposalFixture(),
   437  		},
   438  		"base data missing": {
   439  			src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) {
   440  				p.Title = ""
   441  			}),
   442  			expErr: true,
   443  		},
   444  		"contract missing": {
   445  			src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) {
   446  				p.Contract = ""
   447  			}),
   448  			expErr: true,
   449  		},
   450  		"contract invalid": {
   451  			src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) {
   452  				p.Contract = invalidAddress
   453  			}),
   454  			expErr: true,
   455  		},
   456  		"admin missing": {
   457  			src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) {
   458  				p.NewAdmin = ""
   459  			}),
   460  			expErr: true,
   461  		},
   462  		"admin invalid": {
   463  			src: UpdateAdminProposalFixture(func(p *UpdateAdminProposal) {
   464  				p.NewAdmin = invalidAddress
   465  			}),
   466  			expErr: true,
   467  		},
   468  	}
   469  	for msg, spec := range specs {
   470  		t.Run(msg, func(t *testing.T) {
   471  			err := spec.src.ValidateBasic()
   472  			if spec.expErr {
   473  				require.Error(t, err)
   474  			} else {
   475  				require.NoError(t, err)
   476  			}
   477  		})
   478  	}
   479  }
   480  
   481  func TestValidateClearAdminProposal(t *testing.T) {
   482  	invalidAddress := "invalid address"
   483  
   484  	specs := map[string]struct {
   485  		src    *ClearAdminProposal
   486  		expErr bool
   487  	}{
   488  		"all good": {
   489  			src: ClearAdminProposalFixture(),
   490  		},
   491  		"base data missing": {
   492  			src: ClearAdminProposalFixture(func(p *ClearAdminProposal) {
   493  				p.Title = ""
   494  			}),
   495  			expErr: true,
   496  		},
   497  		"contract missing": {
   498  			src: ClearAdminProposalFixture(func(p *ClearAdminProposal) {
   499  				p.Contract = ""
   500  			}),
   501  			expErr: true,
   502  		},
   503  		"contract invalid": {
   504  			src: ClearAdminProposalFixture(func(p *ClearAdminProposal) {
   505  				p.Contract = invalidAddress
   506  			}),
   507  			expErr: true,
   508  		},
   509  	}
   510  	for msg, spec := range specs {
   511  		t.Run(msg, func(t *testing.T) {
   512  			err := spec.src.ValidateBasic()
   513  			if spec.expErr {
   514  				require.Error(t, err)
   515  			} else {
   516  				require.NoError(t, err)
   517  			}
   518  		})
   519  	}
   520  }
   521  
   522  func TestProposalStrings(t *testing.T) {
   523  	specs := map[string]struct {
   524  		src govtypes.Content
   525  		exp string
   526  	}{
   527  		"store code": {
   528  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   529  				p.WASMByteCode = []byte{0o1, 0o2, 0o3, 0o4, 0o5, 0o6, 0o7, 0x08, 0x09, 0x0a}
   530  			}),
   531  			exp: `Store Code Proposal:
   532    Title:       Foo
   533    Description: Bar
   534    Run as:      cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   535    WasmCode:    0102030405060708090A
   536  `,
   537  		},
   538  		"instantiate contract": {
   539  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   540  				p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(1)}, {Denom: "bar", Amount: sdk.NewInt(2)}}
   541  			}),
   542  			exp: `Instantiate Code Proposal:
   543    Title:       Foo
   544    Description: Bar
   545    Run as:      cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   546    Admin:       cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   547    Code id:     1
   548    Label:       testing
   549    Msg:         "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\",\"beneficiary\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}"
   550    Funds:       1foo,2bar
   551  `,
   552  		},
   553  		"instantiate contract without funds": {
   554  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Funds = nil }),
   555  			exp: `Instantiate Code Proposal:
   556    Title:       Foo
   557    Description: Bar
   558    Run as:      cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   559    Admin:       cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   560    Code id:     1
   561    Label:       testing
   562    Msg:         "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\",\"beneficiary\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}"
   563    Funds:       
   564  `,
   565  		},
   566  		"instantiate contract without admin": {
   567  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Admin = "" }),
   568  			exp: `Instantiate Code Proposal:
   569    Title:       Foo
   570    Description: Bar
   571    Run as:      cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   572    Admin:       
   573    Code id:     1
   574    Label:       testing
   575    Msg:         "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\",\"beneficiary\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}"
   576    Funds:       
   577  `,
   578  		},
   579  		"migrate contract": {
   580  			src: MigrateContractProposalFixture(),
   581  			exp: `Migrate Contract Proposal:
   582    Title:       Foo
   583    Description: Bar
   584    Contract:    cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr
   585    Code id:     1
   586    Msg:         "{\"verifier\":\"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4\"}"
   587  `,
   588  		},
   589  		"update admin": {
   590  			src: UpdateAdminProposalFixture(),
   591  			exp: `Update Contract Admin Proposal:
   592    Title:       Foo
   593    Description: Bar
   594    Contract:    cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr
   595    New Admin:   cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   596  `,
   597  		},
   598  		"clear admin": {
   599  			src: ClearAdminProposalFixture(),
   600  			exp: `Clear Contract Admin Proposal:
   601    Title:       Foo
   602    Description: Bar
   603    Contract:    cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr
   604  `,
   605  		},
   606  		"pin codes": {
   607  			src: &PinCodesProposal{
   608  				Title:       "Foo",
   609  				Description: "Bar",
   610  				CodeIDs:     []uint64{1, 2, 3},
   611  			},
   612  			exp: `Pin Wasm Codes Proposal:
   613    Title:       Foo
   614    Description: Bar
   615    Codes:       [1 2 3]
   616  `,
   617  		},
   618  		"unpin codes": {
   619  			src: &UnpinCodesProposal{
   620  				Title:       "Foo",
   621  				Description: "Bar",
   622  				CodeIDs:     []uint64{3, 2, 1},
   623  			},
   624  			exp: `Unpin Wasm Codes Proposal:
   625    Title:       Foo
   626    Description: Bar
   627    Codes:       [3 2 1]
   628  `,
   629  		},
   630  		"update deployment whitelist": {
   631  			src: &UpdateDeploymentWhitelistProposal{
   632  				Title:            "Foo",
   633  				Description:      "Bar",
   634  				DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"},
   635  			},
   636  			exp: `title:"Foo" description:"Bar" distributorAddrs:"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02" distributorAddrs:"ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf" `,
   637  		},
   638  	}
   639  	for msg, spec := range specs {
   640  		t.Run(msg, func(t *testing.T) {
   641  			assert.Equal(t, spec.exp, spec.src.String())
   642  		})
   643  	}
   644  }
   645  
   646  func TestProposalYaml(t *testing.T) {
   647  	specs := map[string]struct {
   648  		src govtypes.Content
   649  		exp string
   650  	}{
   651  		"store code": {
   652  			src: StoreCodeProposalFixture(func(p *StoreCodeProposal) {
   653  				p.WASMByteCode = []byte{0o1, 0o2, 0o3, 0o4, 0o5, 0o6, 0o7, 0x08, 0x09, 0x0a}
   654  			}),
   655  			exp: `title: Foo
   656  description: Bar
   657  run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   658  wasm_byte_code: AQIDBAUGBwgJCg==
   659  instantiate_permission: null
   660  `,
   661  		},
   662  		"instantiate contract": {
   663  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) {
   664  				p.Funds = sdk.CoinAdapters{{Denom: "foo", Amount: sdk.NewInt(1)}, {Denom: "bar", Amount: sdk.NewInt(2)}}
   665  			}),
   666  			exp: `title: Foo
   667  description: Bar
   668  run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   669  admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   670  code_id: 1
   671  label: testing
   672  msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4","beneficiary":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}'
   673  funds:
   674  - denom: foo
   675    amount: "0.000000000000000001"
   676  - denom: bar
   677    amount: "0.000000000000000002"
   678  `,
   679  		},
   680  		"instantiate contract without funds": {
   681  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Funds = nil }),
   682  			exp: `title: Foo
   683  description: Bar
   684  run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   685  admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   686  code_id: 1
   687  label: testing
   688  msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4","beneficiary":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}'
   689  funds: []
   690  `,
   691  		},
   692  		"instantiate contract without admin": {
   693  			src: InstantiateContractProposalFixture(func(p *InstantiateContractProposal) { p.Admin = "" }),
   694  			exp: `title: Foo
   695  description: Bar
   696  run_as: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   697  admin: ""
   698  code_id: 1
   699  label: testing
   700  msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4","beneficiary":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}'
   701  funds: []
   702  `,
   703  		},
   704  		"migrate contract": {
   705  			src: MigrateContractProposalFixture(),
   706  			exp: `title: Foo
   707  description: Bar
   708  contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr
   709  code_id: 1
   710  msg: '{"verifier":"cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4"}'
   711  `,
   712  		},
   713  		"update admin": {
   714  			src: UpdateAdminProposalFixture(),
   715  			exp: `title: Foo
   716  description: Bar
   717  new_admin: cosmos1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs2m6sx4
   718  contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr
   719  `,
   720  		},
   721  		"clear admin": {
   722  			src: ClearAdminProposalFixture(),
   723  			exp: `title: Foo
   724  description: Bar
   725  contract: cosmos14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s4hmalr
   726  `,
   727  		},
   728  		"pin codes": {
   729  			src: &PinCodesProposal{
   730  				Title:       "Foo",
   731  				Description: "Bar",
   732  				CodeIDs:     []uint64{1, 2, 3},
   733  			},
   734  			exp: `title: Foo
   735  description: Bar
   736  code_ids:
   737  - 1
   738  - 2
   739  - 3
   740  `,
   741  		},
   742  		"update deployment whitelist": {
   743  			src: &UpdateDeploymentWhitelistProposal{
   744  				Title:            "Foo",
   745  				Description:      "Bar",
   746  				DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"},
   747  			},
   748  			exp: `title: Foo
   749  description: Bar
   750  distributor_addresses:
   751  - fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02
   752  - ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf
   753  `,
   754  		},
   755  	}
   756  	for msg, spec := range specs {
   757  		t.Run(msg, func(t *testing.T) {
   758  			v, err := yaml.Marshal(&spec.src)
   759  			require.NoError(t, err)
   760  			assert.Equal(t, spec.exp, string(v))
   761  		})
   762  	}
   763  }
   764  
   765  func TestConvertToProposals(t *testing.T) {
   766  	cases := map[string]struct {
   767  		input     string
   768  		isError   bool
   769  		proposals []ProposalType
   770  	}{
   771  		"one proper item": {
   772  			input:     "UpdateAdmin",
   773  			proposals: []ProposalType{ProposalTypeUpdateAdmin},
   774  		},
   775  		"multiple proper items": {
   776  			input:     "StoreCode,InstantiateContract,MigrateContract,UpdateDeploymentWhitelist",
   777  			proposals: []ProposalType{ProposalTypeStoreCode, ProposalTypeInstantiateContract, ProposalTypeMigrateContract, ProposalTypeUpdateDeploymentWhitelist},
   778  		},
   779  		"empty trailing item": {
   780  			input:   "StoreCode,",
   781  			isError: true,
   782  		},
   783  		"invalid item": {
   784  			input:   "StoreCode,InvalidProposalType",
   785  			isError: true,
   786  		},
   787  	}
   788  
   789  	for name, tc := range cases {
   790  		t.Run(name, func(t *testing.T) {
   791  			chunks := strings.Split(tc.input, ",")
   792  			proposals, err := ConvertToProposals(chunks)
   793  			if tc.isError {
   794  				require.Error(t, err)
   795  			} else {
   796  				require.NoError(t, err)
   797  				require.Equal(t, proposals, tc.proposals)
   798  			}
   799  		})
   800  	}
   801  }
   802  
   803  func TestUnmarshalContentFromJson(t *testing.T) {
   804  	specs := map[string]struct {
   805  		src string
   806  		got govtypes.Content
   807  		exp govtypes.Content
   808  	}{
   809  		"instantiate ": {
   810  			src: `
   811  {
   812  	"title": "foo",
   813  	"description": "bar",
   814  	"admin": "myAdminAddress",
   815  	"code_id": 1,
   816  	"funds": [{"denom": "ALX", "amount": "2"},{"denom": "BLX","amount": "3"}],
   817  	"msg": {},
   818  	"label": "testing",
   819  	"run_as": "myRunAsAddress"
   820  }`,
   821  			got: &InstantiateContractProposal{},
   822  			exp: &InstantiateContractProposal{
   823  				Title:       "foo",
   824  				Description: "bar",
   825  				RunAs:       "myRunAsAddress",
   826  				Admin:       "myAdminAddress",
   827  				CodeID:      1,
   828  				Label:       "testing",
   829  				Msg:         []byte("{}"),
   830  				Funds:       sdk.CoinAdapters{{"ALX", sdk.NewInt(2)}, {"BLX", sdk.NewInt(3)}},
   831  			},
   832  		},
   833  		"migrate ": {
   834  			src: `
   835  {
   836  	"title": "foo",
   837  	"description": "bar",
   838  	"code_id": 1,
   839  	"contract": "myContractAddr",
   840  	"msg": {},
   841  	"run_as": "myRunAsAddress"
   842  }`,
   843  			got: &MigrateContractProposal{},
   844  			exp: &MigrateContractProposal{
   845  				Title:       "foo",
   846  				Description: "bar",
   847  				Contract:    "myContractAddr",
   848  				CodeID:      1,
   849  				Msg:         []byte("{}"),
   850  			},
   851  		},
   852  		"update deployment whitelit": {
   853  			src: `
   854  {
   855  	"title": "foo",
   856  	"description": "bar",
   857  	"distributorAddrs": ["fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"]
   858  }`,
   859  			got: &UpdateDeploymentWhitelistProposal{},
   860  			exp: &UpdateDeploymentWhitelistProposal{
   861  				Title:            "foo",
   862  				Description:      "bar",
   863  				DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"},
   864  			},
   865  		},
   866  	}
   867  	for name, spec := range specs {
   868  		t.Run(name, func(t *testing.T) {
   869  			require.NoError(t, json.Unmarshal([]byte(spec.src), spec.got))
   870  			assert.Equal(t, spec.exp, spec.got)
   871  		})
   872  	}
   873  }
   874  
   875  func TestProposalJsonSignBytes(t *testing.T) {
   876  	const myInnerMsg = `{"foo":"bar"}`
   877  	specs := map[string]struct {
   878  		src govtypes.Content
   879  		exp string
   880  	}{
   881  		"instantiate contract": {
   882  			src: &InstantiateContractProposal{Msg: RawContractMessage(myInnerMsg)},
   883  			exp: `
   884  {
   885  	"type":"fbexchain/gov/MsgSubmitProposal",
   886  	"value":{"content":{"type":"wasm/InstantiateContractProposal","value":{"funds":[],"msg":{"foo":"bar"}}},"initial_deposit":[],"proposer":""}
   887  }`,
   888  		},
   889  		"migrate contract": {
   890  			src: &MigrateContractProposal{Msg: RawContractMessage(myInnerMsg)},
   891  			exp: `
   892  {
   893  	"type":"fbexchain/gov/MsgSubmitProposal",
   894  	"value":{"content":{"type":"wasm/MigrateContractProposal","value":{"msg":{"foo":"bar"}}},"initial_deposit":[],"proposer":""}
   895  }`,
   896  		},
   897  		"update wasm deployment whitelist": {
   898  			src: &UpdateDeploymentWhitelistProposal{DistributorAddrs: []string{"fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02", "ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"}},
   899  			exp: `
   900  {	
   901  	"type":"fbexchain/gov/MsgSubmitProposal",
   902  	"value":{"content":{"type":"wasm/UpdateDeploymentWhitelistProposal","value":{"distributorAddrs":["fb1cftp8q8g4aa65nw9s5trwexe77d9t6cr8ndu02","ex10q0rk5qnyag7wfvvt7rtphlw589m7frs3hvqmf"]}},"initial_deposit":[],"proposer":""}
   903  }`,
   904  		},
   905  	}
   906  	for name, spec := range specs {
   907  		t.Run(name, func(t *testing.T) {
   908  			msg := govtypes.NewMsgSubmitProposal(spec.src, sdk.NewCoins(), []byte{})
   909  
   910  			bz := msg.GetSignBytes()
   911  			assert.JSONEq(t, spec.exp, string(bz), "raw: %s", string(bz))
   912  		})
   913  	}
   914  }