github.com/Finschia/finschia-sdk@v0.49.1/x/collection/client/testutil/suite.go (about) 1 package testutil 2 3 import ( 4 "fmt" 5 6 "github.com/gogo/protobuf/proto" 7 "github.com/stretchr/testify/suite" 8 abci "github.com/tendermint/tendermint/abci/types" 9 10 "github.com/Finschia/finschia-sdk/client/flags" 11 "github.com/Finschia/finschia-sdk/crypto/hd" 12 "github.com/Finschia/finschia-sdk/crypto/keyring" 13 clitestutil "github.com/Finschia/finschia-sdk/testutil/cli" 14 "github.com/Finschia/finschia-sdk/testutil/network" 15 sdk "github.com/Finschia/finschia-sdk/types" 16 bankcli "github.com/Finschia/finschia-sdk/x/bank/client/cli" 17 "github.com/Finschia/finschia-sdk/x/collection" 18 "github.com/Finschia/finschia-sdk/x/collection/client/cli" 19 ) 20 21 type IntegrationTestSuite struct { 22 suite.Suite 23 24 cfg network.Config 25 network *network.Network 26 27 setupHeight int64 28 29 vendor sdk.AccAddress 30 operator sdk.AccAddress 31 customer sdk.AccAddress 32 stranger sdk.AccAddress 33 34 contractID string 35 ftClassID string 36 nftClassID string 37 38 balance sdk.Int 39 40 lenChain int 41 } 42 43 var commonArgs = []string{ 44 fmt.Sprintf("--%s=true", flags.FlagSkipConfirmation), 45 fmt.Sprintf("--%s=%s", flags.FlagBroadcastMode, flags.BroadcastBlock), 46 fmt.Sprintf("--%s=%s", flags.FlagFees, sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(100))).String()), 47 } 48 49 func NewIntegrationTestSuite(cfg network.Config) *IntegrationTestSuite { 50 return &IntegrationTestSuite{cfg: cfg} 51 } 52 53 func (s *IntegrationTestSuite) SetupSuite() { 54 s.T().Log("setting up integration test suite") 55 56 var gs collection.GenesisState 57 s.Require().NoError(s.cfg.Codec.UnmarshalJSON(s.cfg.GenesisState[collection.ModuleName], &gs)) 58 59 params := collection.Params{ 60 DepthLimit: 4, 61 WidthLimit: 4, 62 } 63 gs.Params = params 64 65 gsBz, err := s.cfg.Codec.MarshalJSON(&gs) 66 s.Require().NoError(err) 67 s.cfg.GenesisState[collection.ModuleName] = gsBz 68 69 s.network = network.New(s.T(), s.cfg) 70 _, err = s.network.WaitForHeight(1) 71 s.Require().NoError(err) 72 73 s.vendor = s.createAccount("vendor") 74 s.operator = s.createAccount("operator") 75 s.customer = s.createAccount("customer") 76 s.stranger = s.createAccount("stranger") 77 78 s.balance = sdk.NewInt(1000000) 79 80 // vendor creates 2 token classes 81 s.contractID = s.createContract(s.vendor) 82 s.ftClassID = s.createFTClass(s.contractID, s.vendor) 83 s.nftClassID = s.createNFTClass(s.contractID, s.vendor) 84 85 // mint & burn fts 86 for _, to := range []sdk.AccAddress{s.customer, s.operator, s.vendor, s.stranger} { 87 s.mintFT(s.contractID, s.vendor, to, s.ftClassID, s.balance) 88 89 if to.Equals(s.vendor) { 90 tokenID := collection.NewFTID(s.ftClassID) 91 amount := collection.NewCoins(collection.NewCoin(tokenID, s.balance)) 92 s.burnFT(s.contractID, s.vendor, amount) 93 s.mintFT(s.contractID, s.vendor, to, s.ftClassID, s.balance) 94 } 95 } 96 97 // mint nfts 98 s.lenChain = 2 99 for _, to := range []sdk.AccAddress{s.customer, s.operator, s.vendor, s.stranger} { 100 // mint N chains per account 101 numChains := 3 102 for n := 0; n < numChains; n++ { 103 ids := make([]string, s.lenChain) 104 for i := range ids { 105 ids[i] = s.mintNFT(s.contractID, s.vendor, to, s.nftClassID) 106 } 107 108 for i := range ids[1:] { 109 r := len(ids) - 1 - i 110 subject := ids[r] 111 target := ids[r-1] 112 s.attach(s.contractID, to, subject, target) 113 } 114 } 115 } 116 117 // grant all the permissions to operator 118 for _, pv := range collection.Permission_value { 119 permission := collection.Permission(pv) 120 if permission == collection.PermissionUnspecified { 121 continue 122 } 123 s.grant(s.contractID, s.vendor, s.operator, permission) 124 } 125 126 // customer and vendor approves the operator to manipulate its tokens, so vendor can do OperatorXXX (Send or Burn) later. 127 s.authorizeOperator(s.contractID, s.customer, s.operator) 128 s.authorizeOperator(s.contractID, s.vendor, s.operator) 129 // for the revocation. 130 s.authorizeOperator(s.contractID, s.operator, s.vendor) 131 132 s.setupHeight, err = s.network.LatestHeight() 133 s.Require().NoError(err) 134 s.Require().NoError(s.network.WaitForNextBlock()) 135 } 136 137 func (s *IntegrationTestSuite) pickEvent(events []abci.Event, event proto.Message, fn func(event proto.Message)) { 138 for _, e := range events { 139 if e.Type == proto.MessageName(event) { 140 msg, err := sdk.ParseTypedEvent(e) 141 s.Require().NoError(err) 142 143 fn(msg) 144 return 145 } 146 } 147 148 s.Require().Failf("event not found", "%s", events) 149 } 150 151 func (s *IntegrationTestSuite) createContract(creator sdk.AccAddress) string { 152 val := s.network.Validators[0] 153 args := append([]string{ 154 creator.String(), 155 }, commonArgs...) 156 157 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdCreateContract(), args) 158 s.Require().NoError(err) 159 160 var res sdk.TxResponse 161 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 162 s.Require().EqualValues(0, res.Code, out.String()) 163 164 var event collection.EventCreatedContract 165 s.pickEvent(res.Events, &event, func(e proto.Message) { 166 event = *e.(*collection.EventCreatedContract) 167 }) 168 return event.ContractId 169 } 170 171 func (s *IntegrationTestSuite) createFTClass(contractID string, operator sdk.AccAddress) string { 172 val := s.network.Validators[0] 173 args := append([]string{ 174 contractID, 175 operator.String(), 176 fmt.Sprintf("--%s=%s", cli.FlagName, "tibetian fox"), 177 fmt.Sprintf("--%s=%s", cli.FlagTo, operator), 178 fmt.Sprintf("--%s=%v", cli.FlagMintable, true), 179 }, commonArgs...) 180 181 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdIssueFT(), args) 182 s.Require().NoError(err) 183 184 var res sdk.TxResponse 185 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 186 s.Require().EqualValues(0, res.Code, out.String()) 187 188 var event collection.EventCreatedFTClass 189 s.pickEvent(res.Events, &event, func(e proto.Message) { 190 event = *e.(*collection.EventCreatedFTClass) 191 }) 192 return collection.SplitTokenID(event.TokenId) 193 } 194 195 func (s *IntegrationTestSuite) createNFTClass(contractID string, operator sdk.AccAddress) string { 196 val := s.network.Validators[0] 197 args := append([]string{ 198 contractID, 199 operator.String(), 200 }, commonArgs...) 201 202 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdIssueNFT(), args) 203 s.Require().NoError(err) 204 205 var res sdk.TxResponse 206 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 207 s.Require().EqualValues(0, res.Code, out.String()) 208 209 var event collection.EventCreatedNFTClass 210 s.pickEvent(res.Events, &event, func(e proto.Message) { 211 event = *e.(*collection.EventCreatedNFTClass) 212 }) 213 return event.TokenType 214 } 215 216 func (s *IntegrationTestSuite) mintFT(contractID string, operator, to sdk.AccAddress, classID string, amount sdk.Int) { 217 val := s.network.Validators[0] 218 args := append([]string{ 219 contractID, 220 operator.String(), 221 to.String(), 222 classID, 223 amount.String(), 224 }, commonArgs...) 225 226 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdMintFT(), args) 227 s.Require().NoError(err) 228 229 var res sdk.TxResponse 230 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 231 s.Require().EqualValues(0, res.Code, out.String()) 232 } 233 234 func (s *IntegrationTestSuite) mintNFT(contractID string, operator, to sdk.AccAddress, classID string) string { 235 val := s.network.Validators[0] 236 args := append([]string{ 237 contractID, 238 operator.String(), 239 to.String(), 240 classID, 241 fmt.Sprintf("--%s=%s", cli.FlagName, "arctic fox"), 242 }, commonArgs...) 243 244 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdMintNFT(), args) 245 s.Require().NoError(err) 246 247 var res sdk.TxResponse 248 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 249 s.Require().EqualValues(0, res.Code, out.String()) 250 251 var event collection.EventMintedNFT 252 s.pickEvent(res.Events, &event, func(e proto.Message) { 253 event = *e.(*collection.EventMintedNFT) 254 }) 255 256 s.Require().Equal(1, len(event.Tokens)) 257 return event.Tokens[0].TokenId 258 } 259 260 func (s *IntegrationTestSuite) burnFT(contractID string, from sdk.AccAddress, amount collection.Coins) { 261 val := s.network.Validators[0] 262 args := append([]string{ 263 contractID, 264 from.String(), 265 amount.String(), 266 }, commonArgs...) 267 268 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdBurnFT(), args) 269 s.Require().NoError(err) 270 271 var res sdk.TxResponse 272 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 273 s.Require().EqualValues(0, res.Code, out.String()) 274 } 275 276 func (s *IntegrationTestSuite) attach(contractID string, holder sdk.AccAddress, subject, target string) { 277 val := s.network.Validators[0] 278 args := append([]string{ 279 contractID, 280 holder.String(), 281 subject, 282 target, 283 }, commonArgs...) 284 285 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdAttach(), args) 286 s.Require().NoError(err) 287 288 var res sdk.TxResponse 289 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 290 s.Require().EqualValues(0, res.Code, out.String()) 291 } 292 293 func (s *IntegrationTestSuite) grant(contractID string, granter, grantee sdk.AccAddress, permission collection.Permission) { 294 val := s.network.Validators[0] 295 args := append([]string{ 296 contractID, 297 granter.String(), 298 grantee.String(), 299 collection.LegacyPermission(permission).String(), 300 }, commonArgs...) 301 302 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdGrantPermission(), args) 303 s.Require().NoError(err) 304 305 var res sdk.TxResponse 306 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 307 s.Require().EqualValues(0, res.Code, out.String()) 308 } 309 310 // creates an account and send some coins to it for the future transactions. 311 func (s *IntegrationTestSuite) createAccount(uid string) sdk.AccAddress { 312 val := s.network.Validators[0] 313 keyInfo, _, err := val.ClientCtx.Keyring.NewMnemonic(uid, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 314 s.Require().NoError(err) 315 addr := keyInfo.GetAddress() 316 317 fee := sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1000000))) 318 args := append([]string{ 319 val.Address.String(), 320 addr.String(), 321 fee.String(), 322 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), 323 }, commonArgs...) 324 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, bankcli.NewSendTxCmd(), args) 325 s.Require().NoError(err) 326 327 var res sdk.TxResponse 328 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 329 s.Require().EqualValues(0, res.Code, out.String()) 330 331 return addr 332 } 333 334 func (s *IntegrationTestSuite) authorizeOperator(contractID string, holder, operator sdk.AccAddress) { 335 val := s.network.Validators[0] 336 args := append([]string{ 337 contractID, 338 holder.String(), 339 operator.String(), 340 }, commonArgs...) 341 342 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdAuthorizeOperator(), args) 343 s.Require().NoError(err) 344 345 var res sdk.TxResponse 346 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 347 s.Require().EqualValues(0, res.Code, out.String()) 348 } 349 350 func (s *IntegrationTestSuite) TearDownSuite() { 351 s.T().Log("tearing down integration test suite") 352 s.network.Cleanup() 353 }