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  }