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  }