github.com/lino-network/lino@v0.6.11/x/developer/manager/manager_test.go (about)

     1  package developer // To test private filed `storage`
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"testing"
     9  	"time"
    10  
    11  	codec "github.com/cosmos/cosmos-sdk/codec"
    12  	sdk "github.com/cosmos/cosmos-sdk/types"
    13  	"github.com/stretchr/testify/mock"
    14  	"github.com/stretchr/testify/suite"
    15  
    16  	"github.com/lino-network/lino/param"
    17  	mparam "github.com/lino-network/lino/param/mocks"
    18  	"github.com/lino-network/lino/testsuites"
    19  	"github.com/lino-network/lino/testutils"
    20  	linotypes "github.com/lino-network/lino/types"
    21  	maccount "github.com/lino-network/lino/x/account/mocks"
    22  	"github.com/lino-network/lino/x/developer/model"
    23  	"github.com/lino-network/lino/x/developer/types"
    24  	mprice "github.com/lino-network/lino/x/price/mocks"
    25  	mvote "github.com/lino-network/lino/x/vote/mocks"
    26  	votetypes "github.com/lino-network/lino/x/vote/types"
    27  )
    28  
    29  var (
    30  	storeKeyStr      = "testStoreKey"
    31  	storeKey         = sdk.NewKVStoreKey(storeKeyStr)
    32  	appTest          = linotypes.AccountKey("testapp")
    33  	appDoesNotExists = linotypes.AccountKey("testapp-does-not-exist")
    34  	appWithoutIDA    = linotypes.AccountKey("testapp-without-ida")
    35  	appHasRevokedIDA = linotypes.AccountKey("testapp-revoked-ida")
    36  )
    37  
    38  type DeveloperDumper struct{}
    39  
    40  func (dumper DeveloperDumper) NewDumper() *testutils.Dumper {
    41  	return model.NewDeveloperDumper(model.NewDeveloperStorage(storeKey))
    42  }
    43  
    44  type DeveloperManagerSuite struct {
    45  	testsuites.GoldenTestSuite
    46  	manager        DeveloperManager
    47  	mParamKeeper   *mparam.ParamKeeper
    48  	mVoteKeeper    *mvote.VoteKeeper
    49  	mAccountKeeper *maccount.AccountKeeper
    50  	mPriceKeeper   *mprice.PriceKeeper
    51  }
    52  
    53  func NewDeveloperManagerSuite() *DeveloperManagerSuite {
    54  	return &DeveloperManagerSuite{
    55  		GoldenTestSuite: testsuites.NewGoldenTestSuite(DeveloperDumper{}, storeKey),
    56  	}
    57  }
    58  
    59  func (suite *DeveloperManagerSuite) SetupTest() {
    60  	suite.mParamKeeper = new(mparam.ParamKeeper)
    61  	suite.mVoteKeeper = new(mvote.VoteKeeper)
    62  	suite.mAccountKeeper = new(maccount.AccountKeeper)
    63  	suite.mPriceKeeper = new(mprice.PriceKeeper)
    64  	suite.manager = NewDeveloperManager(storeKey, suite.mParamKeeper, suite.mVoteKeeper, suite.mAccountKeeper, suite.mPriceKeeper)
    65  	suite.SetupCtx(0, time.Unix(0, 0), storeKey)
    66  }
    67  
    68  func TestDeveloperManagerSuite(t *testing.T) {
    69  	suite.Run(t, NewDeveloperManagerSuite())
    70  }
    71  
    72  func (suite *DeveloperManagerSuite) TestInitGenesis() {
    73  	testCases := []struct {
    74  		reservePoolAmount linotypes.Coin
    75  		name              string
    76  		expected          sdk.Error
    77  		expectedStore     *model.ReservePool
    78  	}{
    79  		{
    80  			name:              "Success Valid Genesis",
    81  			reservePoolAmount: linotypes.NewCoin(sdk.NewInt(1)),
    82  			expected:          nil,
    83  			expectedStore:     &model.ReservePool{Total: linotypes.NewCoin(sdk.NewInt(1))},
    84  		},
    85  		{
    86  			name:              "Fail Invalid Genesis negative reservePoolAmount",
    87  			reservePoolAmount: linotypes.NewCoin(sdk.NewInt(-1)),
    88  			expected:          types.ErrInvalidReserveAmount(linotypes.NewCoin(sdk.NewInt(-1))),
    89  			expectedStore:     nil,
    90  		},
    91  	}
    92  	for _, c := range testCases {
    93  		suite.Run(c.name, func() {
    94  			suite.SetupCtx(0, time.Unix(0, 0), storeKey)
    95  			suite.Equal(c.expected, suite.manager.InitGenesis(suite.Ctx, c.reservePoolAmount))
    96  			suite.Golden()
    97  		})
    98  	}
    99  }
   100  
   101  func (suite *DeveloperManagerSuite) TestDoesDeveloperExist() {
   102  	devDeleted := linotypes.AccountKey("testapp-deleted")
   103  	testCases := []struct {
   104  		name     string
   105  		username linotypes.AccountKey
   106  		expected bool
   107  	}{
   108  		{
   109  			name:     "Developer exists not deleted",
   110  			username: appTest,
   111  			expected: true,
   112  		},
   113  		{
   114  			name:     "Developer exist deleted",
   115  			username: devDeleted,
   116  			expected: false,
   117  		},
   118  		{
   119  			name:     "Developer does not exist",
   120  			username: appDoesNotExists,
   121  			expected: false,
   122  		},
   123  	}
   124  	for _, c := range testCases {
   125  		suite.Run(c.name, func() {
   126  			suite.LoadState(false)
   127  			suite.Equal(c.expected, suite.manager.DoesDeveloperExist(suite.Ctx, c.username))
   128  			suite.Golden()
   129  			suite.AssertStateUnchanged(false)
   130  		})
   131  	}
   132  }
   133  
   134  func (suite *DeveloperManagerSuite) TestGetDeveloper() {
   135  	testCases := []struct {
   136  		name        string
   137  		username    linotypes.AccountKey
   138  		expected    model.Developer
   139  		expectedErr sdk.Error
   140  	}{
   141  		{
   142  			name:     "Developer exists",
   143  			username: appTest,
   144  			expected: model.Developer{
   145  				Username:       appTest,
   146  				IsDeleted:      false,
   147  				Deposit:        linotypes.NewCoinFromInt64(0),
   148  				AppConsumption: linotypes.NewMiniDollar(0),
   149  			},
   150  			expectedErr: nil,
   151  		},
   152  		{
   153  			name:        "Developer does not exist",
   154  			username:    appDoesNotExists,
   155  			expected:    model.Developer{},
   156  			expectedErr: types.ErrDeveloperNotFound(),
   157  		},
   158  	}
   159  	for _, c := range testCases {
   160  		suite.Run(c.name, func() {
   161  			suite.LoadState(false)
   162  			got, err := suite.manager.GetDeveloper(suite.Ctx, c.username)
   163  			suite.Equal(c.expectedErr, err)
   164  			suite.Equal(c.expected, got)
   165  			suite.Golden()
   166  			suite.AssertStateUnchanged(false)
   167  		})
   168  	}
   169  }
   170  
   171  func (suite *DeveloperManagerSuite) TestGetLiveDeveloper() {
   172  	testCases := []struct {
   173  		name     string
   174  		expected []model.Developer
   175  	}{
   176  		{
   177  			name: "All developers",
   178  			expected: []model.Developer{
   179  				{
   180  					Username:       "test",
   181  					Deposit:        linotypes.NewCoinFromInt64(0),
   182  					AppConsumption: linotypes.NewMiniDollar(0),
   183  					IsDeleted:      false,
   184  				},
   185  			},
   186  		},
   187  	}
   188  	for _, c := range testCases {
   189  		suite.Run(c.name, func() {
   190  			suite.LoadState(true)
   191  			suite.Equal(c.expected, suite.manager.GetLiveDevelopers(suite.Ctx))
   192  			suite.Golden()
   193  			suite.AssertStateUnchanged(true)
   194  		})
   195  	}
   196  }
   197  
   198  func (suite *DeveloperManagerSuite) TestRegisterDeveloper() {
   199  	username := linotypes.AccountKey("test_username")
   200  	duplicateUsername := linotypes.AccountKey("test")
   201  	userRoleUsername := linotypes.AccountKey("test2")
   202  	voterDuty := votetypes.DutyVoter
   203  	voterDutyErr := linotypes.NewError(linotypes.CodeTestDummyError, "")
   204  	invalidVoterDuty := votetypes.DutyApp
   205  
   206  	minDeposit := linotypes.NewCoinFromInt64(50)
   207  	params := param.DeveloperParam{
   208  		DeveloperMinDeposit: minDeposit,
   209  	}
   210  	stake := linotypes.NewCoinFromInt64(100)
   211  	noEnoughStake := linotypes.NewCoinFromInt64(10)
   212  	testCases := []struct {
   213  		name                 string
   214  		username             linotypes.AccountKey
   215  		website              string
   216  		description          string
   217  		appMetaData          string
   218  		accountExist         bool
   219  		voterDutyErr         *sdk.Error
   220  		voterDuty            *votetypes.VoterDuty
   221  		developerParam       *param.DeveloperParam
   222  		developerParamError  sdk.Error
   223  		voteLinoStake        *linotypes.Coin
   224  		voteLinoStakeError   sdk.Error
   225  		voteAssignDutyCalled bool
   226  		voteAssignDutyError  sdk.Error
   227  		expected             sdk.Error
   228  	}{
   229  		{
   230  			name:         "Fail Account does not exist",
   231  			accountExist: false,
   232  			username:     username,
   233  			expected:     types.ErrAccountNotFound(),
   234  		},
   235  		{
   236  			name:         "Fail Developer already exists",
   237  			accountExist: true,
   238  			username:     duplicateUsername,
   239  			expected:     types.ErrDeveloperAlreadyExist(duplicateUsername),
   240  		},
   241  		{
   242  			name:         "Fail Account is not a Voter",
   243  			accountExist: true,
   244  			voterDuty:    nil,
   245  			voterDutyErr: &voterDutyErr,
   246  			username:     username,
   247  			expected:     types.ErrInvalidVoterDuty(),
   248  		},
   249  		{
   250  			name:         "Fail Account is not a Duty Voter",
   251  			accountExist: true,
   252  			voterDuty:    &invalidVoterDuty,
   253  			username:     username,
   254  			expected:     types.ErrInvalidVoterDuty(),
   255  		},
   256  		{
   257  			name:         "Fail Account has user role",
   258  			accountExist: true,
   259  			voterDuty:    &voterDuty,
   260  			username:     userRoleUsername,
   261  			expected:     types.ErrInvalidUserRole(),
   262  		},
   263  		{
   264  			name:                "Fail Error from paramHolder",
   265  			accountExist:        true,
   266  			voterDuty:           &voterDuty,
   267  			developerParam:      &params,
   268  			developerParamError: sdk.ErrInternal("test"),
   269  			username:            username,
   270  			expected:            sdk.ErrInternal("test"),
   271  		},
   272  		{
   273  			name:                "Fail Error from vote.GetLinoStake",
   274  			accountExist:        true,
   275  			voterDuty:           &voterDuty,
   276  			developerParam:      &params,
   277  			developerParamError: nil,
   278  			voteLinoStake:       &stake,
   279  			voteLinoStakeError:  sdk.ErrInternal("test linostake"),
   280  			username:            username,
   281  			expected:            sdk.ErrInternal("test linostake"),
   282  		},
   283  		{
   284  			name:                "Fail not enough stake",
   285  			accountExist:        true,
   286  			voterDuty:           &voterDuty,
   287  			developerParam:      &params,
   288  			developerParamError: nil,
   289  			voteLinoStake:       &noEnoughStake,
   290  			voteLinoStakeError:  nil,
   291  			username:            username,
   292  			expected:            types.ErrInsufficientDeveloperDeposit(),
   293  		},
   294  		{
   295  			name:                 "Fail Error from vote.AssignDuty",
   296  			accountExist:         true,
   297  			voterDuty:            &voterDuty,
   298  			developerParam:       &params,
   299  			developerParamError:  nil,
   300  			voteLinoStake:        &stake,
   301  			voteLinoStakeError:   nil,
   302  			voteAssignDutyCalled: true,
   303  			voteAssignDutyError:  sdk.ErrInternal("test assign duty"),
   304  			username:             username,
   305  			expected:             sdk.ErrInternal("test assign duty"),
   306  		},
   307  		{
   308  			name:                 "Success",
   309  			accountExist:         true,
   310  			voterDuty:            &voterDuty,
   311  			developerParam:       &params,
   312  			developerParamError:  nil,
   313  			voteLinoStake:        &stake,
   314  			voteAssignDutyCalled: true,
   315  			voteLinoStakeError:   nil,
   316  			voteAssignDutyError:  nil,
   317  			username:             username,
   318  			website:              "test website",
   319  			description:          "test description",
   320  			appMetaData:          "test meta",
   321  			expected:             nil,
   322  		},
   323  	}
   324  	for _, c := range testCases {
   325  		suite.Run(c.name, func() {
   326  			suite.LoadState(false)
   327  			suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.username).Return(c.accountExist).Once()
   328  			if c.voterDuty != nil {
   329  				suite.mVoteKeeper.On("GetVoterDuty", mock.Anything, c.username).Return(*c.voterDuty, nil).Once()
   330  			}
   331  			if c.voterDutyErr != nil {
   332  				suite.mVoteKeeper.On("GetVoterDuty", mock.Anything, c.username).Return(voterDuty, *c.voterDutyErr).Once()
   333  			}
   334  			if c.developerParam != nil {
   335  				suite.mParamKeeper.On("GetDeveloperParam", mock.Anything).Return(c.developerParam, c.developerParamError).Once()
   336  			}
   337  			if c.voteLinoStake != nil {
   338  				suite.mVoteKeeper.On("GetLinoStake", mock.Anything, c.username).Return(*c.voteLinoStake, c.voteLinoStakeError).Once()
   339  			}
   340  			if c.voteAssignDutyCalled {
   341  				suite.mVoteKeeper.On("AssignDuty", mock.Anything, c.username, votetypes.DutyApp, params.DeveloperMinDeposit).Return(c.voteAssignDutyError).Once()
   342  			}
   343  			suite.Equal(c.expected, suite.manager.RegisterDeveloper(suite.Ctx, c.username, c.website, c.description, c.appMetaData))
   344  			suite.mAccountKeeper.AssertExpectations(suite.T())
   345  			suite.mVoteKeeper.AssertExpectations(suite.T())
   346  			suite.mParamKeeper.AssertExpectations(suite.T())
   347  			suite.Golden()
   348  			if c.expected != nil {
   349  				suite.AssertStateUnchanged(false)
   350  			}
   351  		})
   352  	}
   353  }
   354  
   355  func (suite *DeveloperManagerSuite) TestUpdateDeveloper() {
   356  	username := linotypes.AccountKey("test")
   357  	usernameDoesNotExist := linotypes.AccountKey("test-no")
   358  	usernameDeleted := linotypes.AccountKey("test-deleted")
   359  	website := "test website"
   360  	description := "test description"
   361  	meta := "test meta"
   362  	testCases := []struct {
   363  		name     string
   364  		username linotypes.AccountKey
   365  		expected sdk.Error
   366  	}{
   367  		{
   368  			name:     "Fail developer doesnt exist",
   369  			username: usernameDoesNotExist,
   370  			expected: types.ErrDeveloperNotFound(),
   371  		}, {
   372  			name:     "Fail developer deleted",
   373  			username: usernameDeleted,
   374  			expected: types.ErrDeveloperNotFound(),
   375  		}, {
   376  			name:     "Success",
   377  			username: username,
   378  			expected: nil,
   379  		},
   380  	}
   381  	for _, c := range testCases {
   382  		suite.Run(c.name, func() {
   383  			suite.LoadState(false)
   384  			suite.Equal(c.expected, suite.manager.UpdateDeveloper(suite.Ctx, c.username, website, description, meta))
   385  			suite.Golden()
   386  			if c.expected != nil {
   387  				suite.AssertStateUnchanged(false)
   388  			}
   389  		})
   390  	}
   391  }
   392  
   393  func (suite *DeveloperManagerSuite) TestIssueIDA() {
   394  	app := linotypes.AccountKey("testapp")
   395  	appDoesNotExists := linotypes.AccountKey("testapp-does-not-exist")
   396  	appHasIDA := linotypes.AccountKey("testapp-has-ida")
   397  	idaName := "test-lemon"
   398  	var idaPrice int64 = 100
   399  	testCases := []struct {
   400  		name     string
   401  		appName  linotypes.AccountKey
   402  		expected sdk.Error
   403  	}{
   404  		{
   405  			name:     "Fail Developer doesnt exist",
   406  			appName:  appDoesNotExists,
   407  			expected: types.ErrDeveloperNotFound(),
   408  		},
   409  		{
   410  			name:     "Fail Developer has already issued IDA",
   411  			appName:  appHasIDA,
   412  			expected: types.ErrIDAIssuedBefore(),
   413  		},
   414  		{
   415  			name:     "Success",
   416  			appName:  app,
   417  			expected: nil,
   418  		},
   419  	}
   420  	for _, c := range testCases {
   421  		suite.Run(c.name, func() {
   422  			suite.LoadState(false)
   423  			suite.Equal(c.expected, suite.manager.IssueIDA(suite.Ctx, c.appName, idaName, idaPrice))
   424  			suite.Golden()
   425  			if c.expected != nil {
   426  				suite.AssertStateUnchanged(false)
   427  			}
   428  		})
   429  	}
   430  }
   431  
   432  func (suite *DeveloperManagerSuite) TestMintIDA() {
   433  	amount := linotypes.NewCoinFromInt64(1)
   434  	zeroMiniDollar := linotypes.NewMiniDollar(0)
   435  	validMiniDollar := linotypes.NewMiniDollar(1)
   436  	testCases := []struct {
   437  		name                    string
   438  		appName                 linotypes.AccountKey
   439  		amount                  linotypes.Coin
   440  		expected                sdk.Error
   441  		coinToMiniDollar        *linotypes.MiniDollar
   442  		minusCoinFromUserCalled bool
   443  		minusCoinFromUser       sdk.Error
   444  	}{
   445  		{
   446  			name:     "Fail Developer doesnt exist",
   447  			appName:  appDoesNotExists,
   448  			amount:   amount,
   449  			expected: types.ErrDeveloperNotFound(),
   450  		},
   451  		{
   452  			name:     "Fail App doesnt have IDA",
   453  			appName:  appWithoutIDA,
   454  			amount:   amount,
   455  			expected: types.ErrIDANotFound(),
   456  		},
   457  		{
   458  			name:     "Fail App has revoked IDA",
   459  			appName:  appHasRevokedIDA,
   460  			amount:   amount,
   461  			expected: types.ErrIDARevoked(),
   462  		},
   463  		{
   464  			name:             "Fail priceCoinToMiniDollar returns 0",
   465  			appName:          appTest,
   466  			amount:           amount,
   467  			coinToMiniDollar: &zeroMiniDollar,
   468  			expected:         types.ErrExchangeMiniDollarZeroAmount(),
   469  		},
   470  		{
   471  			name:                    "Fail accMinusCoinFromUsername returns error",
   472  			appName:                 appTest,
   473  			amount:                  amount,
   474  			coinToMiniDollar:        &validMiniDollar,
   475  			minusCoinFromUserCalled: true,
   476  			minusCoinFromUser:       sdk.ErrInternal("minus coin from username failed"),
   477  			expected:                sdk.ErrInternal("minus coin from username failed"),
   478  		},
   479  		{
   480  			name:                    "Success",
   481  			appName:                 appTest,
   482  			amount:                  amount,
   483  			coinToMiniDollar:        &validMiniDollar,
   484  			minusCoinFromUserCalled: true,
   485  			minusCoinFromUser:       nil,
   486  			expected:                nil,
   487  		},
   488  	}
   489  	for _, c := range testCases {
   490  		suite.Run(c.name, func() {
   491  			if c.coinToMiniDollar != nil {
   492  				suite.mPriceKeeper.On("CoinToMiniDollar", mock.Anything,
   493  					c.amount).Return(*c.coinToMiniDollar, nil).Once()
   494  			}
   495  			if c.minusCoinFromUserCalled {
   496  				suite.mAccountKeeper.On("MoveToPool", mock.Anything,
   497  					linotypes.DevIDAReservePool, linotypes.NewAccOrAddrFromAcc(c.appName),
   498  					c.amount).Return(c.minusCoinFromUser).Once()
   499  			}
   500  			suite.LoadState(false)
   501  			suite.Equal(c.expected, suite.manager.MintIDA(suite.Ctx, c.appName, c.amount))
   502  			suite.Golden()
   503  			if c.expected != nil {
   504  				suite.AssertStateUnchanged(false)
   505  			}
   506  			suite.mPriceKeeper.AssertExpectations(suite.T())
   507  			suite.mAccountKeeper.AssertExpectations(suite.T())
   508  		})
   509  	}
   510  }
   511  
   512  func (suite *DeveloperManagerSuite) TestIDAConvertFromLino() {
   513  	amount := linotypes.NewCoinFromInt64(1)
   514  	// zeroMiniDollar := linotypes.NewMiniDollar(0)
   515  	validMiniDollar := linotypes.NewMiniDollar(1)
   516  	user1 := linotypes.AccountKey("user1")
   517  	testCases := []struct {
   518  		name                    string
   519  		appName                 linotypes.AccountKey
   520  		user                    linotypes.AccountKey
   521  		amount                  linotypes.Coin
   522  		expected                sdk.Error
   523  		coinToMiniDollar        *linotypes.MiniDollar
   524  		minusCoinFromUserCalled bool
   525  		minusCoinFromUser       sdk.Error
   526  	}{
   527  		{
   528  			name:     "Fail Developer doesnt exist",
   529  			appName:  appDoesNotExists,
   530  			user:     user1,
   531  			amount:   amount,
   532  			expected: types.ErrDeveloperNotFound(),
   533  		},
   534  		{
   535  			name:     "Fail App doesnt have IDA",
   536  			appName:  appWithoutIDA,
   537  			user:     user1,
   538  			amount:   amount,
   539  			expected: types.ErrIDANotFound(),
   540  		},
   541  		{
   542  			name:     "Fail App has revoked IDA",
   543  			appName:  appHasRevokedIDA,
   544  			user:     user1,
   545  			amount:   amount,
   546  			expected: types.ErrIDARevoked(),
   547  		},
   548  		{
   549  			name:                    "Fail accMinusCoinFromUsername returns error",
   550  			appName:                 appTest,
   551  			user:                    user1,
   552  			amount:                  amount,
   553  			coinToMiniDollar:        &validMiniDollar,
   554  			minusCoinFromUserCalled: true,
   555  			minusCoinFromUser:       sdk.ErrInternal("minus coin from username failed"),
   556  			expected:                sdk.ErrInternal("minus coin from username failed"),
   557  		},
   558  		{
   559  			name:                    "Success",
   560  			appName:                 appTest,
   561  			user:                    user1,
   562  			amount:                  amount,
   563  			coinToMiniDollar:        &validMiniDollar,
   564  			minusCoinFromUserCalled: true,
   565  			minusCoinFromUser:       nil,
   566  			expected:                nil,
   567  		},
   568  	}
   569  	for _, c := range testCases {
   570  		suite.Run(c.name, func() {
   571  			suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.user).Return(true).Maybe()
   572  			suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.appName).Return(true).Maybe()
   573  			if c.minusCoinFromUserCalled {
   574  				suite.mAccountKeeper.On(
   575  					"MoveCoin", mock.Anything,
   576  					linotypes.NewAccOrAddrFromAcc(c.user),
   577  					linotypes.NewAccOrAddrFromAcc(c.appName), c.amount).Return(nil).Once()
   578  			}
   579  			if c.coinToMiniDollar != nil {
   580  				suite.mPriceKeeper.On("CoinToMiniDollar", mock.Anything,
   581  					c.amount).Return(*c.coinToMiniDollar, nil).Once()
   582  			}
   583  			if c.minusCoinFromUserCalled {
   584  				suite.mAccountKeeper.On("MoveToPool", mock.Anything,
   585  					linotypes.DevIDAReservePool, linotypes.NewAccOrAddrFromAcc(c.appName),
   586  					c.amount).Return(c.minusCoinFromUser).Once()
   587  			}
   588  			suite.LoadState(false)
   589  			suite.Equal(c.expected, suite.manager.IDAConvertFromLino(
   590  				suite.Ctx, c.user, c.appName, c.amount))
   591  			suite.Golden()
   592  			if c.expected != nil {
   593  				suite.AssertStateUnchanged(false)
   594  			}
   595  			suite.mPriceKeeper.AssertExpectations(suite.T())
   596  			suite.mAccountKeeper.AssertExpectations(suite.T())
   597  		})
   598  	}
   599  }
   600  
   601  func (suite *DeveloperManagerSuite) TestPrivateAppIDAMove() {
   602  	to := linotypes.AccountKey("to")
   603  	from := linotypes.AccountKey("from")
   604  	fromNotEnough := linotypes.AccountKey("from-not-enough")
   605  	fromUnauthed := linotypes.AccountKey("from-unauthed")
   606  	fromNotFound := linotypes.AccountKey("from-not-found")
   607  	toNotFound := linotypes.AccountKey("to-not-found")
   608  	amount := linotypes.NewMiniDollar(100)
   609  	testCases := []struct {
   610  		name     string
   611  		app      linotypes.AccountKey
   612  		from     linotypes.AccountKey
   613  		to       linotypes.AccountKey
   614  		amount   linotypes.MiniDollar
   615  		expected sdk.Error
   616  	}{
   617  		{
   618  			name:     "Fail negative amount",
   619  			app:      appTest,
   620  			from:     from,
   621  			to:       to,
   622  			amount:   linotypes.NewMiniDollar(-1),
   623  			expected: linotypes.ErrInvalidIDAAmount(),
   624  		},
   625  		{
   626  			name:     "Fail from not found",
   627  			app:      appTest,
   628  			from:     fromNotFound,
   629  			to:       to,
   630  			amount:   amount,
   631  			expected: types.ErrNotEnoughIDA(),
   632  		},
   633  		{
   634  			name:     "Fail from unauthed",
   635  			app:      appTest,
   636  			from:     fromUnauthed,
   637  			to:       to,
   638  			amount:   amount,
   639  			expected: types.ErrIDAUnauthed(),
   640  		},
   641  		{
   642  			name:     "Fail from balance not enough",
   643  			app:      appTest,
   644  			from:     fromNotEnough,
   645  			to:       to,
   646  			amount:   amount,
   647  			expected: types.ErrNotEnoughIDA(),
   648  		},
   649  		{
   650  			name:     "Succ should add to existing to account bank",
   651  			app:      appTest,
   652  			from:     from,
   653  			to:       to,
   654  			amount:   amount,
   655  			expected: nil,
   656  		},
   657  		{
   658  			name:     "Succ should create new to account bank",
   659  			app:      appTest,
   660  			from:     from,
   661  			to:       toNotFound,
   662  			amount:   amount,
   663  			expected: nil,
   664  		},
   665  	}
   666  	for _, c := range testCases {
   667  		suite.Run(c.name, func() {
   668  			suite.LoadState(false, "IDABasic")
   669  			suite.Equal(c.expected, suite.manager.appIDAMove(suite.Ctx, c.app, c.from, c.to, c.amount))
   670  			suite.Golden()
   671  			if c.expected != nil {
   672  				suite.AssertStateUnchanged(false, "IDABasic")
   673  			}
   674  		})
   675  	}
   676  }
   677  
   678  func (suite *DeveloperManagerSuite) TestAppTransferIDA() {
   679  	to := linotypes.AccountKey("to")
   680  	from := linotypes.AccountKey("from")
   681  	app1affiliated := linotypes.AccountKey("testapp-affiliated")
   682  	exists := true
   683  	doesntExists := false
   684  	amount := sdk.NewInt(1)
   685  	testCases := []struct {
   686  		name       string
   687  		appName    linotypes.AccountKey
   688  		signer     linotypes.AccountKey
   689  		from       linotypes.AccountKey
   690  		to         linotypes.AccountKey
   691  		fromExists *bool
   692  		toExists   *bool
   693  		expected   sdk.Error
   694  	}{
   695  		{
   696  			name:     "Fail from and to both not sender",
   697  			appName:  appTest,
   698  			signer:   appTest,
   699  			from:     from,
   700  			to:       to,
   701  			expected: types.ErrInvalidTransferTarget(),
   702  		},
   703  		{
   704  			name:     "Fail App does not exist",
   705  			appName:  appDoesNotExists,
   706  			signer:   appDoesNotExists,
   707  			from:     from,
   708  			to:       appDoesNotExists,
   709  			expected: types.ErrDeveloperNotFound(),
   710  		},
   711  		{
   712  			name:     "Fail App doesnt have IDA",
   713  			appName:  appWithoutIDA,
   714  			signer:   appWithoutIDA,
   715  			from:     from,
   716  			to:       appWithoutIDA,
   717  			expected: types.ErrIDANotFound(),
   718  		},
   719  		{
   720  			name:     "Fail App has revoked IDA",
   721  			appName:  appHasRevokedIDA,
   722  			signer:   appHasRevokedIDA,
   723  			from:     from,
   724  			to:       appHasRevokedIDA,
   725  			expected: types.ErrIDARevoked(),
   726  		},
   727  		{
   728  			name:       "Fail from account doesnt exist",
   729  			appName:    appTest,
   730  			signer:     appTest,
   731  			from:       from,
   732  			to:         appTest,
   733  			fromExists: &doesntExists,
   734  			expected:   types.ErrAccountNotFound(),
   735  		},
   736  		{
   737  			name:       "Fail to account doesnt exist",
   738  			appName:    appTest,
   739  			signer:     appTest,
   740  			from:       from,
   741  			to:         appTest,
   742  			fromExists: &exists,
   743  			toExists:   &doesntExists,
   744  			expected:   types.ErrAccountNotFound(),
   745  		},
   746  		{
   747  			name:       "Fail signer does not match",
   748  			appName:    appTest,
   749  			signer:     from,
   750  			from:       from,
   751  			to:         appTest,
   752  			fromExists: &exists,
   753  			toExists:   &exists,
   754  			expected:   types.ErrInvalidSigner(),
   755  		},
   756  		{
   757  			name:       "Success Transfer from App",
   758  			appName:    appTest,
   759  			signer:     appTest,
   760  			from:       appTest,
   761  			to:         to,
   762  			fromExists: &exists,
   763  			toExists:   &exists,
   764  			expected:   nil,
   765  		},
   766  		{
   767  			name:       "Success Transfer to App",
   768  			appName:    appTest,
   769  			signer:     appTest,
   770  			from:       from,
   771  			to:         appTest,
   772  			fromExists: &exists,
   773  			toExists:   &exists,
   774  			expected:   nil,
   775  		},
   776  		{
   777  			name:       "Success Transfer to App by affiliated",
   778  			appName:    appTest,
   779  			signer:     app1affiliated,
   780  			from:       from,
   781  			to:         appTest,
   782  			fromExists: &exists,
   783  			toExists:   &exists,
   784  			expected:   nil,
   785  		},
   786  	}
   787  	for _, c := range testCases {
   788  		suite.Run(c.name, func() {
   789  			if c.fromExists != nil {
   790  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.from).Return(*c.fromExists).Once()
   791  			}
   792  			if c.toExists != nil {
   793  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.to).Return(*c.toExists).Once()
   794  			}
   795  			suite.LoadState(false, "IDABasic")
   796  			suite.Equal(c.expected, suite.manager.AppTransferIDA(
   797  				suite.Ctx, c.appName, c.signer, amount, c.from, c.to))
   798  			suite.Golden()
   799  			if c.expected != nil {
   800  				suite.AssertStateUnchanged(false, "IDABasic")
   801  			}
   802  			suite.mAccountKeeper.AssertExpectations(suite.T())
   803  		})
   804  	}
   805  }
   806  
   807  func (suite *DeveloperManagerSuite) TestMoveIDA() {
   808  	to := linotypes.AccountKey("to")
   809  	from := linotypes.AccountKey("from")
   810  	fromNotEnough := linotypes.AccountKey("from-not-enough")
   811  	fromUnauthed := linotypes.AccountKey("from-unauthed")
   812  	fromNotFound := linotypes.AccountKey("from-not-found")
   813  	toNotFound := linotypes.AccountKey("to-not-found")
   814  	amount := linotypes.NewMiniDollar(100)
   815  	exists := true
   816  	doesntExists := false
   817  	testCases := []struct {
   818  		name       string
   819  		app        linotypes.AccountKey
   820  		from       linotypes.AccountKey
   821  		to         linotypes.AccountKey
   822  		amount     linotypes.MiniDollar
   823  		fromExists *bool
   824  		toExists   *bool
   825  		expected   sdk.Error
   826  	}{
   827  		{
   828  			name:     "Fail Developer doesnt exist",
   829  			app:      appDoesNotExists,
   830  			amount:   amount,
   831  			expected: types.ErrDeveloperNotFound(),
   832  		},
   833  		{
   834  			name:     "Fail App doesnt have IDA",
   835  			app:      appWithoutIDA,
   836  			amount:   amount,
   837  			expected: types.ErrIDANotFound(),
   838  		},
   839  		{
   840  			name:     "Fail App has revoked IDA",
   841  			app:      appHasRevokedIDA,
   842  			amount:   amount,
   843  			expected: types.ErrIDARevoked(),
   844  		},
   845  		{
   846  			name:       "Fail from account doesnt exist",
   847  			app:        appTest,
   848  			from:       from,
   849  			to:         appTest,
   850  			fromExists: &doesntExists,
   851  			expected:   types.ErrAccountNotFound(),
   852  		},
   853  		{
   854  			name:       "Fail to account doesnt exist",
   855  			app:        appTest,
   856  			from:       from,
   857  			to:         appTest,
   858  			fromExists: &exists,
   859  			toExists:   &doesntExists,
   860  			expected:   types.ErrAccountNotFound(),
   861  		},
   862  		{
   863  			name:       "Fail negative amount",
   864  			app:        appTest,
   865  			from:       from,
   866  			to:         to,
   867  			fromExists: &exists,
   868  			toExists:   &exists,
   869  			amount:     linotypes.NewMiniDollar(-1),
   870  			expected:   linotypes.ErrInvalidIDAAmount(),
   871  		},
   872  		{
   873  			name:       "Fail from bank not found",
   874  			app:        appTest,
   875  			from:       fromNotFound,
   876  			to:         to,
   877  			amount:     amount,
   878  			fromExists: &exists,
   879  			toExists:   &exists,
   880  			expected:   types.ErrNotEnoughIDA(),
   881  		},
   882  		{
   883  			name:       "Fail from bank unauthed",
   884  			app:        appTest,
   885  			from:       fromUnauthed,
   886  			to:         to,
   887  			amount:     amount,
   888  			fromExists: &exists,
   889  			toExists:   &exists,
   890  			expected:   types.ErrIDAUnauthed(),
   891  		},
   892  		{
   893  			name:       "Fail from bank balance not enough",
   894  			app:        appTest,
   895  			from:       fromNotEnough,
   896  			to:         to,
   897  			amount:     amount,
   898  			fromExists: &exists,
   899  			toExists:   &exists,
   900  			expected:   types.ErrNotEnoughIDA(),
   901  		},
   902  		{
   903  			name:       "Succes should add to existing to account bank",
   904  			app:        appTest,
   905  			from:       from,
   906  			to:         to,
   907  			amount:     amount,
   908  			fromExists: &exists,
   909  			toExists:   &exists,
   910  			expected:   nil,
   911  		},
   912  		{
   913  			name:       "Succes should create new to account bank",
   914  			app:        appTest,
   915  			from:       from,
   916  			to:         toNotFound,
   917  			amount:     amount,
   918  			fromExists: &exists,
   919  			toExists:   &exists,
   920  			expected:   nil,
   921  		},
   922  	}
   923  	for _, c := range testCases {
   924  		suite.Run(c.name, func() {
   925  			if c.fromExists != nil {
   926  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.from).Return(*c.fromExists).Once()
   927  			}
   928  			if c.toExists != nil {
   929  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.to).Return(*c.toExists).Once()
   930  			}
   931  			suite.LoadState(false, "IDABasic")
   932  			suite.Equal(c.expected, suite.manager.MoveIDA(suite.Ctx, c.app, c.from, c.to, c.amount))
   933  			suite.Golden()
   934  			if c.expected != nil {
   935  				suite.AssertStateUnchanged(false, "IDABasic")
   936  			}
   937  			suite.mAccountKeeper.AssertExpectations(suite.T())
   938  		})
   939  	}
   940  }
   941  
   942  // This also includes GetMiniIDAPrice and GetIDA since they are really simple
   943  func (suite *DeveloperManagerSuite) TestPrivateValidAppIDA() {
   944  	zeroPrices := linotypes.NewMiniDollar(0)
   945  	zeroIDA := model.AppIDA{}
   946  	price := linotypes.NewMiniDollar(10000)
   947  	ida := model.AppIDA{
   948  		App:             appTest,
   949  		Name:            "test-lemon",
   950  		MiniIDAPrice:    price,
   951  		RevokeCoinPrice: zeroPrices,
   952  	}
   953  	testCases := []struct {
   954  		name          string
   955  		app           linotypes.AccountKey
   956  		expectedError sdk.Error
   957  		expectedPrice linotypes.MiniDollar
   958  		expectedIDA   model.AppIDA
   959  	}{
   960  		{
   961  			name:          "Fail Developer doesnt exist",
   962  			app:           appDoesNotExists,
   963  			expectedError: types.ErrDeveloperNotFound(),
   964  			expectedPrice: zeroPrices,
   965  			expectedIDA:   zeroIDA,
   966  		},
   967  		{
   968  			name:          "Fail App doesnt have IDA",
   969  			app:           appWithoutIDA,
   970  			expectedError: types.ErrIDANotFound(),
   971  			expectedPrice: zeroPrices,
   972  			expectedIDA:   zeroIDA,
   973  		},
   974  		{
   975  			name:          "Fail App has revoked IDA",
   976  			app:           appHasRevokedIDA,
   977  			expectedError: types.ErrIDARevoked(),
   978  			expectedPrice: zeroPrices,
   979  			expectedIDA:   zeroIDA,
   980  		},
   981  		{
   982  			name:          "Success",
   983  			app:           appTest,
   984  			expectedError: nil,
   985  			expectedPrice: price,
   986  			expectedIDA:   ida,
   987  		},
   988  	}
   989  	for _, c := range testCases {
   990  		suite.Run(c.name, func() {
   991  			suite.LoadState(false, "IDABasic")
   992  			var ida *model.AppIDA = nil
   993  			if c.expectedError == nil {
   994  				ida = &c.expectedIDA
   995  			}
   996  			i, e := suite.manager.validAppIDA(suite.Ctx, c.app)
   997  			suite.Equal(ida, i)
   998  			suite.Equal(c.expectedError, e)
   999  			price, e := suite.manager.GetMiniIDAPrice(suite.Ctx, c.app)
  1000  			suite.Equal(c.expectedPrice, price)
  1001  			suite.Equal(c.expectedError, e)
  1002  			id, e := suite.manager.GetIDA(suite.Ctx, c.app)
  1003  			suite.Equal(c.expectedIDA, id)
  1004  			suite.Equal(c.expectedError, e)
  1005  			suite.Golden()
  1006  			suite.AssertStateUnchanged(false, "IDABasic")
  1007  		})
  1008  	}
  1009  }
  1010  
  1011  // This also tests GetIDABank
  1012  func (suite *DeveloperManagerSuite) TestBurnIDA() {
  1013  	zeroCoin := linotypes.NewCoinFromInt64(0)
  1014  	coin := linotypes.NewCoinFromInt64(1)
  1015  	coinAmountMoreThanPool := linotypes.NewCoinFromInt64(11)
  1016  	used := linotypes.NewMiniDollar(100)
  1017  	userNotEnough := linotypes.AccountKey("from-not-enough")
  1018  	user := linotypes.AccountKey("from")
  1019  	amount := linotypes.NewMiniDollar(10000)
  1020  	exists := true
  1021  	noExists := false
  1022  	testCases := []struct {
  1023  		name          string
  1024  		app           linotypes.AccountKey
  1025  		user          linotypes.AccountKey
  1026  		amount        linotypes.MiniDollar
  1027  		expectedError sdk.Error
  1028  		expectedCoin  linotypes.Coin
  1029  		accountExists *bool
  1030  		bought        *linotypes.Coin
  1031  		used          *linotypes.MiniDollar
  1032  	}{
  1033  		{
  1034  			name:          "Fail app does not exist",
  1035  			app:           appDoesNotExists,
  1036  			user:          user,
  1037  			amount:        amount,
  1038  			expectedCoin:  zeroCoin,
  1039  			expectedError: types.ErrDeveloperNotFound(),
  1040  		},
  1041  		{
  1042  			name:          "Fail user does not exist",
  1043  			app:           appTest,
  1044  			user:          user,
  1045  			amount:        amount,
  1046  			expectedCoin:  zeroCoin,
  1047  			accountExists: &noExists,
  1048  			expectedError: types.ErrAccountNotFound(),
  1049  		},
  1050  		{
  1051  			name:          "Fail user does not have enough mini dollar",
  1052  			app:           appTest,
  1053  			user:          userNotEnough,
  1054  			amount:        amount,
  1055  			expectedCoin:  zeroCoin,
  1056  			accountExists: &exists,
  1057  			expectedError: types.ErrNotEnoughIDA(),
  1058  		},
  1059  		{
  1060  			name:          "Fail cannot burn 0 coin",
  1061  			app:           appTest,
  1062  			user:          user,
  1063  			amount:        amount,
  1064  			expectedCoin:  zeroCoin,
  1065  			accountExists: &exists,
  1066  			expectedError: types.ErrBurnZeroIDA(),
  1067  			bought:        &zeroCoin,
  1068  			used:          &used,
  1069  		},
  1070  		{
  1071  			name:          "Fail burn amount more than reserve pool",
  1072  			app:           appTest,
  1073  			user:          user,
  1074  			amount:        amount,
  1075  			expectedCoin:  zeroCoin,
  1076  			accountExists: &exists,
  1077  			expectedError: types.ErrInsuffientReservePool(),
  1078  			bought:        &coinAmountMoreThanPool,
  1079  			used:          &used,
  1080  		},
  1081  		{
  1082  			name:          "Success",
  1083  			app:           appTest,
  1084  			user:          user,
  1085  			amount:        amount,
  1086  			expectedCoin:  coin,
  1087  			accountExists: &exists,
  1088  			expectedError: nil,
  1089  			bought:        &coin,
  1090  			used:          &used,
  1091  		},
  1092  	}
  1093  	for _, c := range testCases {
  1094  		suite.Run(c.name, func() {
  1095  			if c.accountExists != nil {
  1096  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.user).Return(*c.accountExists).Once()
  1097  			}
  1098  			if c.bought != nil {
  1099  				suite.mPriceKeeper.On("MiniDollarToCoin", mock.Anything, c.amount).Return(*c.bought, *c.used, nil).Once()
  1100  			}
  1101  			if c.expectedCoin.IsPositive() {
  1102  				suite.mAccountKeeper.On("MoveFromPool", mock.Anything,
  1103  					linotypes.DevIDAReservePool, linotypes.NewAccOrAddrFromAcc(c.user),
  1104  					c.expectedCoin).Return(nil).Once()
  1105  			}
  1106  			suite.LoadState(false, "IDABasic")
  1107  			coin, err := suite.manager.BurnIDA(suite.Ctx, c.app, c.user, c.amount)
  1108  			suite.Equal(c.expectedCoin, coin)
  1109  			suite.Equal(c.expectedError, err)
  1110  			suite.Golden()
  1111  			if c.expectedError != nil {
  1112  				suite.AssertStateUnchanged(false, "IDABasic")
  1113  			}
  1114  			suite.mAccountKeeper.AssertExpectations(suite.T())
  1115  			suite.mPriceKeeper.AssertExpectations(suite.T())
  1116  		})
  1117  	}
  1118  }
  1119  
  1120  func (suite *DeveloperManagerSuite) TestGetIDABank() {
  1121  	zeroBank := model.IDABank{}
  1122  	user := linotypes.AccountKey("from")
  1123  	bank := model.IDABank{
  1124  		Balance: linotypes.NewMiniDollar(100000),
  1125  	}
  1126  	exists := true
  1127  	noExists := false
  1128  	testCases := []struct {
  1129  		name          string
  1130  		app           linotypes.AccountKey
  1131  		user          linotypes.AccountKey
  1132  		expectedError sdk.Error
  1133  		expectedBank  model.IDABank
  1134  		accountExists *bool
  1135  	}{
  1136  		{
  1137  			name:          "Fail app does not exist",
  1138  			app:           appDoesNotExists,
  1139  			user:          user,
  1140  			expectedError: types.ErrDeveloperNotFound(),
  1141  			expectedBank:  zeroBank,
  1142  		},
  1143  		{
  1144  			name:          "Fail user does not exist",
  1145  			app:           appTest,
  1146  			user:          user,
  1147  			accountExists: &noExists,
  1148  			expectedBank:  zeroBank,
  1149  			expectedError: types.ErrAccountNotFound(),
  1150  		},
  1151  		{
  1152  			name:          "Success",
  1153  			app:           appTest,
  1154  			user:          user,
  1155  			accountExists: &exists,
  1156  			expectedBank:  bank,
  1157  			expectedError: nil,
  1158  		},
  1159  	}
  1160  	for _, c := range testCases {
  1161  		suite.Run(c.name, func() {
  1162  			if c.accountExists != nil {
  1163  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.user).Return(*c.accountExists).Once()
  1164  			}
  1165  			suite.LoadState(false, "IDABasic")
  1166  			bank, err := suite.manager.GetIDABank(suite.Ctx, c.app, c.user)
  1167  			suite.Equal(c.expectedBank, bank)
  1168  			suite.Equal(c.expectedError, err)
  1169  			suite.Golden()
  1170  			suite.AssertStateUnchanged(false, "IDABasic")
  1171  			suite.mAccountKeeper.AssertExpectations(suite.T())
  1172  		})
  1173  	}
  1174  }
  1175  
  1176  func (suite *DeveloperManagerSuite) TestUpdateAffiliated() {
  1177  	t := true
  1178  	f := false
  1179  	username := linotypes.AccountKey("testuser")
  1180  	appMaxAffiliated := linotypes.AccountKey("testapp-max-affiliated")
  1181  	userAffiliated := linotypes.AccountKey("testuser-affiliated")
  1182  	userDeveloper := linotypes.AccountKey("testuser-developer")
  1183  	userDeactivate := linotypes.AccountKey("testuser-deactivate")
  1184  	voterDuty := votetypes.DutyVoter
  1185  	invalidVoterDuty := votetypes.DutyApp
  1186  	testCases := []struct {
  1187  		name          string
  1188  		appName       linotypes.AccountKey
  1189  		username      linotypes.AccountKey
  1190  		activate      bool
  1191  		expected      sdk.Error
  1192  		accountExists *bool
  1193  		vote          *votetypes.VoterDuty
  1194  	}{
  1195  		{
  1196  			name:     "Fail app doesnt exist",
  1197  			appName:  appDoesNotExists,
  1198  			username: username,
  1199  			expected: types.ErrDeveloperNotFound(),
  1200  		},
  1201  		{
  1202  			name:          "Fail user doesnt exist",
  1203  			appName:       appTest,
  1204  			username:      username,
  1205  			accountExists: &f,
  1206  			expected:      types.ErrAccountNotFound(),
  1207  		},
  1208  		{
  1209  			name:          "Fail max affiliated account reached",
  1210  			appName:       appMaxAffiliated,
  1211  			username:      username,
  1212  			accountExists: &t,
  1213  			expected:      types.ErrMaxAffiliatedExceeded(),
  1214  		},
  1215  		{
  1216  			name:          "Fail activate user is already affiliated with some app",
  1217  			appName:       appTest,
  1218  			username:      userAffiliated,
  1219  			accountExists: &t,
  1220  			activate:      t,
  1221  			expected:      types.ErrInvalidAffiliatedAccount("is affiliated already"),
  1222  		},
  1223  		{
  1224  			name:          "Fail activate user is already a developer",
  1225  			appName:       appTest,
  1226  			username:      userDeveloper,
  1227  			accountExists: &t,
  1228  			activate:      t,
  1229  			expected:      types.ErrInvalidAffiliatedAccount("is/was developer"),
  1230  		},
  1231  		{
  1232  			name:          "Fail activate user has some other duty",
  1233  			appName:       appTest,
  1234  			username:      username,
  1235  			accountExists: &t,
  1236  			vote:          &invalidVoterDuty,
  1237  			activate:      t,
  1238  			expected:      types.ErrInvalidAffiliatedAccount("on duty of something else"),
  1239  		},
  1240  		{
  1241  			name:          "Success activate",
  1242  			appName:       appTest,
  1243  			username:      username,
  1244  			accountExists: &t,
  1245  			vote:          &voterDuty,
  1246  			activate:      t,
  1247  			expected:      nil,
  1248  		},
  1249  		{
  1250  			name:          "Fail deactivate user isnt affiliated with any app",
  1251  			appName:       appTest,
  1252  			username:      username,
  1253  			accountExists: &t,
  1254  			vote:          &voterDuty,
  1255  			activate:      f,
  1256  			expected:      types.ErrInvalidUserRole(),
  1257  		},
  1258  		{
  1259  			name:          "Fail deactivate user has different affiliated account",
  1260  			appName:       appTest,
  1261  			username:      userAffiliated,
  1262  			accountExists: &t,
  1263  			vote:          &voterDuty,
  1264  			activate:      f,
  1265  			expected:      types.ErrInvalidAffiliatedAccount("not affiliated account of provided app"),
  1266  		},
  1267  		{
  1268  			name:          "Success deactivate",
  1269  			appName:       appTest,
  1270  			username:      userDeactivate,
  1271  			accountExists: &t,
  1272  			vote:          &voterDuty,
  1273  			activate:      f,
  1274  			expected:      nil,
  1275  		},
  1276  	}
  1277  	for _, c := range testCases {
  1278  		suite.Run(c.name, func() {
  1279  			if c.accountExists != nil {
  1280  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.username).Return(*c.accountExists).Once()
  1281  			}
  1282  			if c.vote != nil && c.activate {
  1283  				suite.mVoteKeeper.On("GetVoterDuty", mock.Anything, c.username).Return(*c.vote, nil).Once()
  1284  			}
  1285  			suite.LoadState(false, "AffiliatedBasic")
  1286  			suite.Equal(c.expected, suite.manager.UpdateAffiliated(suite.Ctx, c.appName, c.username, c.activate))
  1287  			suite.Golden()
  1288  			if c.expected != nil {
  1289  				suite.AssertStateUnchanged(false, "AffiliatedBasic")
  1290  			}
  1291  			suite.mAccountKeeper.AssertExpectations(suite.T())
  1292  			suite.mVoteKeeper.AssertExpectations(suite.T())
  1293  		})
  1294  	}
  1295  }
  1296  
  1297  func (suite *DeveloperManagerSuite) TestGetAffiliatingApp() {
  1298  	userDev := linotypes.AccountKey("testuser-developer")
  1299  	username := linotypes.AccountKey("testuser-affiliated")
  1300  	app := linotypes.AccountKey("testapp-a")
  1301  	usernameNotAf := linotypes.AccountKey("testuser")
  1302  	testCases := []struct {
  1303  		name          string
  1304  		username      linotypes.AccountKey
  1305  		expectedApp   linotypes.AccountKey
  1306  		expectedError sdk.Error
  1307  	}{
  1308  		{
  1309  			name:          "Fail no affiliation found",
  1310  			username:      usernameNotAf,
  1311  			expectedApp:   "",
  1312  			expectedError: types.ErrInvalidUserRole(),
  1313  		},
  1314  		{
  1315  			name:          "Success is developer",
  1316  			username:      userDev,
  1317  			expectedApp:   userDev,
  1318  			expectedError: nil,
  1319  		},
  1320  		{
  1321  			name:          "Success found affiliated app",
  1322  			username:      username,
  1323  			expectedApp:   app,
  1324  			expectedError: nil,
  1325  		},
  1326  	}
  1327  	for _, c := range testCases {
  1328  		suite.Run(c.name, func() {
  1329  			suite.LoadState(false, "AffiliatedBasic")
  1330  			app, err := suite.manager.GetAffiliatingApp(suite.Ctx, c.username)
  1331  			suite.Equal(c.expectedApp, app)
  1332  			suite.Equal(c.expectedError, err)
  1333  			suite.Golden()
  1334  			suite.AssertStateUnchanged(false, "AffiliatedBasic")
  1335  		})
  1336  	}
  1337  }
  1338  
  1339  func (suite *DeveloperManagerSuite) TestGetAffiliated() {
  1340  	testCases := []struct {
  1341  		name     string
  1342  		app      linotypes.AccountKey
  1343  		expected []linotypes.AccountKey
  1344  	}{
  1345  		{
  1346  			name:     "Success developer doesnt exist",
  1347  			app:      appDoesNotExists,
  1348  			expected: nil,
  1349  		},
  1350  		{
  1351  			name: "Success get all affiliated account",
  1352  			app:  appTest,
  1353  			expected: []linotypes.AccountKey{
  1354  				linotypes.AccountKey("testuser-deactivate"),
  1355  			},
  1356  		},
  1357  	}
  1358  	for _, c := range testCases {
  1359  		suite.Run(c.name, func() {
  1360  			suite.LoadState(false, "AffiliatedBasic")
  1361  			suite.Equal(c.expected, suite.manager.GetAffiliated(suite.Ctx, c.app))
  1362  			suite.Golden()
  1363  			suite.AssertStateUnchanged(false, "AffiliatedBasic")
  1364  		})
  1365  	}
  1366  }
  1367  
  1368  func (suite *DeveloperManagerSuite) TestUpdateIDAAuth() {
  1369  	t := true
  1370  	f := false
  1371  	userAf := linotypes.AccountKey("testuser-affiliated")
  1372  	user := linotypes.AccountKey("testuser")
  1373  	testCases := []struct {
  1374  		name     string
  1375  		app      linotypes.AccountKey
  1376  		username linotypes.AccountKey
  1377  		active   bool
  1378  		expected sdk.Error
  1379  		aExists  *bool
  1380  	}{
  1381  		{
  1382  			name:     "Fail developer doesnt exist",
  1383  			app:      appDoesNotExists,
  1384  			expected: types.ErrDeveloperNotFound(),
  1385  		},
  1386  		{
  1387  			name:     "Fail account doesnt exist",
  1388  			app:      appTest,
  1389  			aExists:  &f,
  1390  			expected: types.ErrAccountNotFound(),
  1391  		},
  1392  		{
  1393  			name:     "Fail user is affiliated account",
  1394  			app:      appTest,
  1395  			username: userAf,
  1396  			aExists:  &t,
  1397  			expected: types.ErrInvalidIDAAuth(),
  1398  		},
  1399  		{
  1400  			name:     "Fail bank already has the target active state",
  1401  			app:      appTest,
  1402  			username: user,
  1403  			aExists:  &t,
  1404  			active:   true,
  1405  			expected: types.ErrInvalidIDAAuth(),
  1406  		},
  1407  		{
  1408  			name:     "Success",
  1409  			app:      appTest,
  1410  			username: user,
  1411  			aExists:  &t,
  1412  			active:   false,
  1413  			expected: nil,
  1414  		},
  1415  	}
  1416  	for _, c := range testCases {
  1417  		suite.Run(c.name, func() {
  1418  			if c.aExists != nil {
  1419  				suite.mAccountKeeper.On("DoesAccountExist", mock.Anything, c.username).Return(*c.aExists).Once()
  1420  			}
  1421  			suite.LoadState(false, "IDABasic")
  1422  			suite.Equal(c.expected, suite.manager.UpdateIDAAuth(suite.Ctx, c.app, c.username, c.active))
  1423  			suite.Golden()
  1424  			if c.expected != nil {
  1425  				suite.AssertStateUnchanged(false, "IDABasic")
  1426  			}
  1427  			suite.mAccountKeeper.AssertExpectations(suite.T())
  1428  		})
  1429  	}
  1430  }
  1431  
  1432  func (suite *DeveloperManagerSuite) TestReportConsumption() {
  1433  	testCases := []struct {
  1434  		name        string
  1435  		app         linotypes.AccountKey
  1436  		consumption linotypes.MiniDollar
  1437  		expected    sdk.Error
  1438  	}{
  1439  		{
  1440  			name:     "Fail developer doesnt exist",
  1441  			app:      appDoesNotExists,
  1442  			expected: types.ErrDeveloperNotFound(),
  1443  		},
  1444  		{
  1445  			name:        "Success",
  1446  			app:         appTest,
  1447  			consumption: linotypes.NewMiniDollar(10),
  1448  			expected:    nil,
  1449  		},
  1450  	}
  1451  	for _, c := range testCases {
  1452  		suite.Run(c.name, func() {
  1453  			suite.LoadState(false, "DeveloperBasic")
  1454  			suite.Equal(c.expected, suite.manager.ReportConsumption(suite.Ctx, c.app, c.consumption))
  1455  			suite.Golden()
  1456  			if c.expected != nil {
  1457  				suite.AssertStateUnchanged(false, "DeveloperBasic")
  1458  			}
  1459  		})
  1460  	}
  1461  }
  1462  
  1463  func (suite *DeveloperManagerSuite) TestMonthlyDistributeDevInflation() {
  1464  	zeroCoin := linotypes.NewCoinFromInt64(0)
  1465  	inflation := linotypes.NewCoinFromInt64(100)
  1466  	testCases := []struct {
  1467  		name                string
  1468  		expected            sdk.Error
  1469  		totalInflation      *linotypes.Coin
  1470  		totalInflationError sdk.Error
  1471  		shares              []linotypes.Coin
  1472  		addCoinError        sdk.Error
  1473  	}{
  1474  		{
  1475  			name:                "Fail error get inflation pool",
  1476  			expected:            sdk.ErrInternal(""),
  1477  			totalInflation:      &zeroCoin,
  1478  			totalInflationError: sdk.ErrInternal(""),
  1479  		},
  1480  		{
  1481  			name:                "Fail error when move coin to account",
  1482  			expected:            sdk.ErrInternal(""),
  1483  			totalInflation:      &inflation,
  1484  			totalInflationError: nil,
  1485  			shares: []linotypes.Coin{
  1486  				linotypes.NewCoinFromInt64(50),
  1487  			},
  1488  			addCoinError: sdk.ErrInternal(""),
  1489  		},
  1490  		{
  1491  			name:     "Succ no developers",
  1492  			expected: nil,
  1493  		},
  1494  		{
  1495  			name:                "Success even distribution",
  1496  			expected:            nil,
  1497  			totalInflation:      &inflation,
  1498  			totalInflationError: nil,
  1499  			shares: []linotypes.Coin{
  1500  				linotypes.NewCoinFromInt64(50),
  1501  				linotypes.NewCoinFromInt64(50),
  1502  			},
  1503  		},
  1504  		{
  1505  			name:                "Success even distribution with remainder",
  1506  			expected:            nil,
  1507  			totalInflation:      &inflation,
  1508  			totalInflationError: nil,
  1509  			shares: []linotypes.Coin{
  1510  				linotypes.NewCoinFromInt64(33),
  1511  				linotypes.NewCoinFromInt64(33),
  1512  				linotypes.NewCoinFromInt64(34),
  1513  			},
  1514  		},
  1515  		{
  1516  			name:                "Success distribute according to consumption",
  1517  			expected:            nil,
  1518  			totalInflation:      &inflation,
  1519  			totalInflationError: nil,
  1520  			shares: []linotypes.Coin{
  1521  				linotypes.NewCoinFromInt64(14),
  1522  				linotypes.NewCoinFromInt64(29),
  1523  				linotypes.NewCoinFromInt64(57),
  1524  			},
  1525  		},
  1526  	}
  1527  	for _, c := range testCases {
  1528  		suite.Run(c.name, func() {
  1529  			if c.totalInflation != nil {
  1530  				suite.mAccountKeeper.On("GetPool",
  1531  					mock.Anything, linotypes.InflationDeveloperPool).Return(
  1532  					*c.totalInflation, c.totalInflationError).Once()
  1533  			}
  1534  			for i, share := range c.shares {
  1535  				suite.mAccountKeeper.On("MoveFromPool", mock.Anything,
  1536  					linotypes.InflationDeveloperPool,
  1537  					linotypes.NewAccOrAddrFromAcc(
  1538  						linotypes.AccountKey(fmt.Sprintf("testapp-%d", i))),
  1539  					share).Return(c.addCoinError).Once()
  1540  			}
  1541  			suite.LoadState(true)
  1542  			suite.Equal(c.expected, suite.manager.MonthlyDistributeDevInflation(suite.Ctx))
  1543  			suite.Golden()
  1544  			suite.mAccountKeeper.AssertExpectations(suite.T())
  1545  		})
  1546  	}
  1547  }
  1548  
  1549  func (suite *DeveloperManagerSuite) TestImportExport() {
  1550  	// background data
  1551  	suite.manager.storage.SetDeveloper(suite.Ctx, model.Developer{
  1552  		Username:       "app1",
  1553  		Deposit:        linotypes.NewCoinFromInt64(0),
  1554  		AppConsumption: linotypes.NewMiniDollar(1234),
  1555  		Website:        "web1",
  1556  		Description:    "app1 is good",
  1557  		AppMetaData:    "app1 meta",
  1558  		IsDeleted:      false,
  1559  		NAffiliated:    233,
  1560  	})
  1561  	suite.manager.storage.SetDeveloper(suite.Ctx, model.Developer{
  1562  		Username:       "app2",
  1563  		Deposit:        linotypes.NewCoinFromInt64(0),
  1564  		AppConsumption: linotypes.NewMiniDollar(5678),
  1565  		Website:        "web2",
  1566  		Description:    "app2 is good",
  1567  		AppMetaData:    "app2 meta",
  1568  		IsDeleted:      true,
  1569  		NAffiliated:    567,
  1570  	})
  1571  	suite.manager.storage.SetDeveloper(suite.Ctx, model.Developer{
  1572  		Username:       "app3",
  1573  		Deposit:        linotypes.NewCoinFromInt64(0),
  1574  		AppConsumption: linotypes.NewMiniDollar(10),
  1575  		Website:        "web3",
  1576  		Description:    "app3 is good",
  1577  		AppMetaData:    "app3 meta",
  1578  		IsDeleted:      true,
  1579  		NAffiliated:    567,
  1580  	})
  1581  	suite.manager.storage.SetIDA(suite.Ctx, model.AppIDA{
  1582  		App:             "app1",
  1583  		Name:            "lemon",
  1584  		MiniIDAPrice:    linotypes.NewMiniDollar(1234),
  1585  		IsRevoked:       true,
  1586  		RevokeCoinPrice: linotypes.NewMiniDollar(556),
  1587  	})
  1588  	suite.manager.storage.SetIDA(suite.Ctx, model.AppIDA{
  1589  		App:             "app2",
  1590  		Name:            "candy",
  1591  		MiniIDAPrice:    linotypes.NewMiniDollar(45),
  1592  		IsRevoked:       false,
  1593  		RevokeCoinPrice: linotypes.NewMiniDollar(0),
  1594  	})
  1595  
  1596  	suite.manager.storage.SetAffiliatedAcc(suite.Ctx, "app1", "user1")
  1597  	suite.manager.storage.SetAffiliatedAcc(suite.Ctx, "app1", "user2")
  1598  	suite.manager.storage.SetAffiliatedAcc(suite.Ctx, "app2", "user3")
  1599  
  1600  	suite.manager.storage.SetIDABank(suite.Ctx, "app1", "user1", &model.IDABank{
  1601  		Balance:  linotypes.NewMiniDollar(123),
  1602  		Unauthed: false,
  1603  	})
  1604  	suite.manager.storage.SetIDABank(suite.Ctx, "app2", "user1", &model.IDABank{
  1605  		Balance:  linotypes.NewMiniDollar(456),
  1606  		Unauthed: false,
  1607  	})
  1608  	suite.manager.storage.SetIDABank(suite.Ctx, "app1", "user2", &model.IDABank{
  1609  		Balance:  linotypes.NewMiniDollar(789),
  1610  		Unauthed: false,
  1611  	})
  1612  
  1613  	suite.manager.storage.SetIDAStats(suite.Ctx, "app1", model.AppIDAStats{
  1614  		Total: linotypes.NewMiniDollar(1000),
  1615  	})
  1616  	suite.manager.storage.SetIDAStats(suite.Ctx, "app2", model.AppIDAStats{
  1617  		Total: linotypes.NewMiniDollar(2000),
  1618  	})
  1619  
  1620  	suite.manager.storage.SetUserRole(suite.Ctx, "user1", &model.Role{
  1621  		AffiliatedApp: "app1",
  1622  	})
  1623  	suite.manager.storage.SetUserRole(suite.Ctx, "user2", &model.Role{
  1624  		AffiliatedApp: "app1",
  1625  	})
  1626  	suite.manager.storage.SetUserRole(suite.Ctx, "user3", &model.Role{
  1627  		AffiliatedApp: "app2",
  1628  	})
  1629  
  1630  	suite.manager.storage.SetReservePool(suite.Ctx, &model.ReservePool{
  1631  		Total:           linotypes.NewCoinFromInt64(123),
  1632  		TotalMiniDollar: linotypes.NewMiniDollar(456),
  1633  	})
  1634  
  1635  	cdc := codec.New()
  1636  	dir, err2 := ioutil.TempDir("", "test")
  1637  	suite.Require().Nil(err2)
  1638  	defer os.RemoveAll(dir) // clean up
  1639  
  1640  	tmpfn := filepath.Join(dir, "tmpfile")
  1641  	err2 = suite.manager.ExportToFile(suite.Ctx, cdc, tmpfn)
  1642  	suite.Nil(err2)
  1643  
  1644  	// reset all state.
  1645  	suite.SetupTest()
  1646  	err2 = suite.manager.ImportFromFile(suite.Ctx, cdc, tmpfn)
  1647  	suite.Nil(err2)
  1648  
  1649  	suite.Golden()
  1650  }