github.com/Finschia/finschia-sdk@v0.48.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 9 "github.com/Finschia/finschia-sdk/client/flags" 10 "github.com/Finschia/finschia-sdk/crypto/hd" 11 "github.com/Finschia/finschia-sdk/crypto/keyring" 12 clitestutil "github.com/Finschia/finschia-sdk/testutil/cli" 13 "github.com/Finschia/finschia-sdk/testutil/network" 14 sdk "github.com/Finschia/finschia-sdk/types" 15 bankcli "github.com/Finschia/finschia-sdk/x/bank/client/cli" 16 "github.com/Finschia/finschia-sdk/x/collection" 17 "github.com/Finschia/finschia-sdk/x/collection/client/cli" 18 abci "github.com/tendermint/tendermint/abci/types" 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 getType := func(msg proto.Message) string { 139 return proto.MessageName(msg) 140 } 141 142 for _, e := range events { 143 if e.Type == getType(event) { 144 msg, err := sdk.ParseTypedEvent(e) 145 s.Require().NoError(err) 146 147 fn(msg) 148 return 149 } 150 } 151 152 s.Require().Failf("event not found", "%s", events) 153 } 154 155 func (s *IntegrationTestSuite) createContract(creator sdk.AccAddress) string { 156 val := s.network.Validators[0] 157 args := append([]string{ 158 creator.String(), 159 }, commonArgs...) 160 161 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdCreateContract(), args) 162 s.Require().NoError(err) 163 164 var res sdk.TxResponse 165 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 166 s.Require().EqualValues(0, res.Code, out.String()) 167 168 var event collection.EventCreatedContract 169 s.pickEvent(res.Events, &event, func(e proto.Message) { 170 event = *e.(*collection.EventCreatedContract) 171 }) 172 return event.ContractId 173 } 174 175 func (s *IntegrationTestSuite) createFTClass(contractID string, operator sdk.AccAddress) string { 176 val := s.network.Validators[0] 177 args := append([]string{ 178 contractID, 179 operator.String(), 180 fmt.Sprintf("--%s=%s", cli.FlagName, "tibetian fox"), 181 fmt.Sprintf("--%s=%s", cli.FlagTo, operator), 182 fmt.Sprintf("--%s=%v", cli.FlagMintable, true), 183 }, commonArgs...) 184 185 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdIssueFT(), args) 186 s.Require().NoError(err) 187 188 var res sdk.TxResponse 189 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 190 s.Require().EqualValues(0, res.Code, out.String()) 191 192 var event collection.EventCreatedFTClass 193 s.pickEvent(res.Events, &event, func(e proto.Message) { 194 event = *e.(*collection.EventCreatedFTClass) 195 }) 196 return collection.SplitTokenID(event.TokenId) 197 } 198 199 func (s *IntegrationTestSuite) createNFTClass(contractID string, operator sdk.AccAddress) string { 200 val := s.network.Validators[0] 201 args := append([]string{ 202 contractID, 203 operator.String(), 204 }, commonArgs...) 205 206 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdIssueNFT(), args) 207 s.Require().NoError(err) 208 209 var res sdk.TxResponse 210 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 211 s.Require().EqualValues(0, res.Code, out.String()) 212 213 var event collection.EventCreatedNFTClass 214 s.pickEvent(res.Events, &event, func(e proto.Message) { 215 event = *e.(*collection.EventCreatedNFTClass) 216 }) 217 return event.TokenType 218 } 219 220 func (s *IntegrationTestSuite) mintFT(contractID string, operator, to sdk.AccAddress, classID string, amount sdk.Int) { 221 val := s.network.Validators[0] 222 args := append([]string{ 223 contractID, 224 operator.String(), 225 to.String(), 226 classID, 227 amount.String(), 228 }, commonArgs...) 229 230 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdMintFT(), args) 231 s.Require().NoError(err) 232 233 var res sdk.TxResponse 234 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 235 s.Require().EqualValues(0, res.Code, out.String()) 236 } 237 238 func (s *IntegrationTestSuite) mintNFT(contractID string, operator, to sdk.AccAddress, classID string) string { 239 val := s.network.Validators[0] 240 args := append([]string{ 241 contractID, 242 operator.String(), 243 to.String(), 244 classID, 245 fmt.Sprintf("--%s=%s", cli.FlagName, "arctic fox"), 246 }, commonArgs...) 247 248 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdMintNFT(), args) 249 s.Require().NoError(err) 250 251 var res sdk.TxResponse 252 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 253 s.Require().EqualValues(0, res.Code, out.String()) 254 255 var event collection.EventMintedNFT 256 s.pickEvent(res.Events, &event, func(e proto.Message) { 257 event = *e.(*collection.EventMintedNFT) 258 }) 259 260 s.Require().Equal(1, len(event.Tokens)) 261 return event.Tokens[0].TokenId 262 } 263 264 func (s *IntegrationTestSuite) burnFT(contractID string, from sdk.AccAddress, amount collection.Coins) { 265 val := s.network.Validators[0] 266 args := append([]string{ 267 contractID, 268 from.String(), 269 amount.String(), 270 }, commonArgs...) 271 272 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdBurnFT(), args) 273 s.Require().NoError(err) 274 275 var res sdk.TxResponse 276 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 277 s.Require().EqualValues(0, res.Code, out.String()) 278 } 279 280 func (s *IntegrationTestSuite) attach(contractID string, holder sdk.AccAddress, subject, target string) { 281 val := s.network.Validators[0] 282 args := append([]string{ 283 contractID, 284 holder.String(), 285 subject, 286 target, 287 }, commonArgs...) 288 289 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdAttach(), args) 290 s.Require().NoError(err) 291 292 var res sdk.TxResponse 293 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 294 s.Require().EqualValues(0, res.Code, out.String()) 295 } 296 297 func (s *IntegrationTestSuite) grant(contractID string, granter, grantee sdk.AccAddress, permission collection.Permission) { 298 val := s.network.Validators[0] 299 args := append([]string{ 300 contractID, 301 granter.String(), 302 grantee.String(), 303 collection.LegacyPermission(permission).String(), 304 }, commonArgs...) 305 306 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdGrantPermission(), args) 307 s.Require().NoError(err) 308 309 var res sdk.TxResponse 310 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 311 s.Require().EqualValues(0, res.Code, out.String()) 312 } 313 314 // creates an account and send some coins to it for the future transactions. 315 func (s *IntegrationTestSuite) createAccount(uid string) sdk.AccAddress { 316 val := s.network.Validators[0] 317 keyInfo, _, err := val.ClientCtx.Keyring.NewMnemonic(uid, keyring.English, sdk.FullFundraiserPath, keyring.DefaultBIP39Passphrase, hd.Secp256k1) 318 s.Require().NoError(err) 319 addr := keyInfo.GetAddress() 320 321 fee := sdk.NewCoins(sdk.NewCoin(s.cfg.BondDenom, sdk.NewInt(1000000))) 322 args := append([]string{ 323 val.Address.String(), 324 addr.String(), 325 fee.String(), 326 fmt.Sprintf("--%s=%s", flags.FlagFrom, val.Address), 327 }, commonArgs...) 328 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, bankcli.NewSendTxCmd(), args) 329 s.Require().NoError(err) 330 331 var res sdk.TxResponse 332 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 333 s.Require().EqualValues(0, res.Code, out.String()) 334 335 return addr 336 } 337 338 func (s *IntegrationTestSuite) authorizeOperator(contractID string, holder, operator sdk.AccAddress) { 339 val := s.network.Validators[0] 340 args := append([]string{ 341 contractID, 342 holder.String(), 343 operator.String(), 344 }, commonArgs...) 345 346 out, err := clitestutil.ExecTestCLICmd(val.ClientCtx, cli.NewTxCmdAuthorizeOperator(), args) 347 s.Require().NoError(err) 348 349 var res sdk.TxResponse 350 s.Require().NoError(val.ClientCtx.Codec.UnmarshalJSON(out.Bytes(), &res), out.String()) 351 s.Require().EqualValues(0, res.Code, out.String()) 352 } 353 354 func (s *IntegrationTestSuite) TearDownSuite() { 355 s.T().Log("tearing down integration test suite") 356 s.network.Cleanup() 357 }