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

     1  package manager
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"testing"
     8  	"time"
     9  
    10  	codec "github.com/cosmos/cosmos-sdk/codec"
    11  	sdk "github.com/cosmos/cosmos-sdk/types"
    12  	"github.com/stretchr/testify/mock"
    13  	"github.com/stretchr/testify/suite"
    14  
    15  	"github.com/lino-network/lino/testsuites"
    16  	"github.com/lino-network/lino/testutils"
    17  	linotypes "github.com/lino-network/lino/types"
    18  	acc "github.com/lino-network/lino/x/account/mocks"
    19  	dev "github.com/lino-network/lino/x/developer/mocks"
    20  	global "github.com/lino-network/lino/x/global/mocks"
    21  	"github.com/lino-network/lino/x/post/model"
    22  	types "github.com/lino-network/lino/x/post/types"
    23  	price "github.com/lino-network/lino/x/price/mocks"
    24  	rep "github.com/lino-network/lino/x/reputation/mocks"
    25  	vote "github.com/lino-network/lino/x/vote/mocks"
    26  )
    27  
    28  // var dummyErr = linotypes.NewError(linotypes.CodeTestDummyError, "")
    29  var (
    30  	storeKey = sdk.NewKVStoreKey("post")
    31  )
    32  
    33  type PostManagerTestSuite struct {
    34  	testsuites.GoldenTestSuite
    35  	pm PostManager
    36  	// deps
    37  	am     *acc.AccountKeeper
    38  	dev    *dev.DeveloperKeeper
    39  	global *global.GlobalKeeper
    40  	price  *price.PriceKeeper
    41  	rep    *rep.ReputationKeeper
    42  	vote   *vote.VoteKeeper
    43  	// mock data
    44  	user1          linotypes.AccountKey
    45  	user2          linotypes.AccountKey
    46  	unreg1         linotypes.AccountKey
    47  	app1affiliated linotypes.AccountKey
    48  	app2affiliated linotypes.AccountKey
    49  	app1           linotypes.AccountKey
    50  	app2           linotypes.AccountKey
    51  	app3           linotypes.AccountKey
    52  	rate           sdk.Dec
    53  	app1IDAPrice   linotypes.MiniDollar
    54  }
    55  
    56  type PostDumper struct{}
    57  
    58  func (dumper PostDumper) NewDumper() *testutils.Dumper {
    59  	return model.NewPostDumper(model.NewPostStorage(storeKey))
    60  }
    61  
    62  func TestPostManagerTestSuite(t *testing.T) {
    63  	suite.Run(t, &PostManagerTestSuite{
    64  		GoldenTestSuite: testsuites.NewGoldenTestSuite(PostDumper{}, storeKey),
    65  	})
    66  }
    67  
    68  func (suite *PostManagerTestSuite) SetupTest() {
    69  	suite.SetupCtx(0, time.Unix(0, 0), storeKey)
    70  	suite.am = &acc.AccountKeeper{}
    71  	suite.dev = &dev.DeveloperKeeper{}
    72  	suite.global = &global.GlobalKeeper{}
    73  	suite.price = &price.PriceKeeper{}
    74  	suite.rep = &rep.ReputationKeeper{}
    75  	suite.vote = &vote.VoteKeeper{}
    76  	suite.pm = NewPostManager(storeKey, suite.am, suite.global, suite.dev, suite.rep, suite.price, suite.vote)
    77  
    78  	// background
    79  	suite.user1 = linotypes.AccountKey("user1")
    80  	suite.user2 = linotypes.AccountKey("user2")
    81  	suite.unreg1 = linotypes.AccountKey("user3")
    82  	suite.app1affiliated = linotypes.AccountKey("app1affi")
    83  	suite.app2affiliated = linotypes.AccountKey("app2affi")
    84  	suite.app1 = linotypes.AccountKey("app1")
    85  	suite.app2 = linotypes.AccountKey("app2")
    86  	suite.app3 = linotypes.AccountKey("app3")
    87  
    88  	// reg accounts
    89  	for _, v := range []linotypes.AccountKey{suite.user1, suite.user2, suite.app1, suite.app2, suite.app3, suite.app1affiliated} {
    90  		suite.am.On("DoesAccountExist", mock.Anything, v).Return(true).Maybe()
    91  	}
    92  	// unreg accounts
    93  	for _, v := range []linotypes.AccountKey{suite.unreg1} {
    94  		suite.am.On("DoesAccountExist", mock.Anything, v).Return(false).Maybe()
    95  	}
    96  
    97  	// reg dev
    98  	for _, v := range []linotypes.AccountKey{suite.app1, suite.app2, suite.app3} {
    99  		suite.dev.On("GetAffiliatingApp", mock.Anything, v).Return(v, nil).Maybe()
   100  		suite.dev.On("DoesDeveloperExist", mock.Anything, v).Return(true).Maybe()
   101  	}
   102  	// unreg devs
   103  	for _, v := range []linotypes.AccountKey{suite.unreg1, suite.user1, suite.user2} {
   104  		suite.dev.On("DoesDeveloperExist", mock.Anything, v).Return(false).Maybe()
   105  	}
   106  
   107  	// affiliated
   108  	suite.dev.On("GetAffiliatingApp",
   109  		mock.Anything, suite.app1affiliated).Return(suite.app1, nil).Maybe()
   110  	suite.dev.On("GetAffiliatingApp",
   111  		mock.Anything, suite.app2affiliated).Return(suite.app2, nil).Maybe()
   112  	for _, v := range []linotypes.AccountKey{suite.user1, suite.user2, suite.unreg1} {
   113  		suite.dev.On("GetAffiliatingApp",
   114  			mock.Anything, v).Return(linotypes.AccountKey(""), types.ErrInvalidSigner()).Maybe()
   115  	}
   116  
   117  	suite.app1IDAPrice = linotypes.NewMiniDollar(10)
   118  	// app1, app2 has issued IDA
   119  	suite.dev.On("GetMiniIDAPrice", mock.Anything, suite.app1).Return(
   120  		suite.app1IDAPrice, nil).Maybe()
   121  	suite.dev.On("GetMiniIDAPrice", mock.Anything, suite.app2).Return(
   122  		linotypes.NewMiniDollar(7), nil).Maybe()
   123  	// unreg ida
   124  	suite.dev.On("GetMiniIDAPrice", mock.Anything, suite.app3).Return(
   125  		linotypes.NewMiniDollar(7), types.ErrDeveloperNotFound(suite.app3)).Maybe()
   126  
   127  	rate, err := sdk.NewDecFromStr("0.099")
   128  	suite.Require().Nil(err)
   129  	suite.rate = rate
   130  }
   131  
   132  func (suite *PostManagerTestSuite) TestCreatePost() {
   133  	user1 := suite.user1
   134  	user2 := suite.user2
   135  	unreg1 := suite.unreg1
   136  	app1 := suite.app1
   137  	app2 := suite.app2
   138  	testCases := []struct {
   139  		testName     string
   140  		postID       string
   141  		title        string
   142  		content      string
   143  		author       linotypes.AccountKey
   144  		createdby    linotypes.AccountKey
   145  		expectResult sdk.Error
   146  	}{
   147  		{
   148  			testName:     "user does not exists",
   149  			postID:       "postID",
   150  			author:       unreg1,
   151  			title:        "title1",
   152  			content:      "content1",
   153  			createdby:    unreg1,
   154  			expectResult: types.ErrAccountNotFound(unreg1),
   155  		},
   156  		{
   157  			testName:     "createdBy does not exists",
   158  			postID:       "postID",
   159  			title:        "title2",
   160  			content:      "content2",
   161  			author:       user2,
   162  			createdby:    unreg1,
   163  			expectResult: types.ErrAccountNotFound(unreg1),
   164  		},
   165  		{
   166  			testName:     "createdBy is not an app",
   167  			postID:       "postID",
   168  			content:      "content3",
   169  			title:        "title3",
   170  			author:       user1,
   171  			createdby:    user2,
   172  			expectResult: types.ErrDeveloperNotFound(user2),
   173  		},
   174  		{
   175  			testName:     "creates (postID, user1) successfully, by author",
   176  			postID:       "postID",
   177  			content:      "content4",
   178  			title:        "title4",
   179  			author:       user1,
   180  			createdby:    user1,
   181  			expectResult: nil,
   182  		},
   183  		{
   184  			testName:     "creates (postID, user2) successfully, by app",
   185  			postID:       "postID",
   186  			content:      "content5",
   187  			title:        "title5",
   188  			author:       user2,
   189  			createdby:    app1,
   190  			expectResult: nil,
   191  		},
   192  		{
   193  			testName:     "(postID, user1) already exists",
   194  			postID:       "postID",
   195  			content:      "content6",
   196  			title:        "title6",
   197  			author:       user1,
   198  			createdby:    app1,
   199  			expectResult: types.ErrPostAlreadyExist(linotypes.GetPermlink(user1, "postID")),
   200  		},
   201  		{
   202  			testName:     "(postID, user2) already exists case 1",
   203  			postID:       "postID",
   204  			content:      "content7",
   205  			title:        "title7",
   206  			author:       user2,
   207  			createdby:    user2,
   208  			expectResult: types.ErrPostAlreadyExist(linotypes.GetPermlink(user2, "postID")),
   209  		},
   210  		{
   211  			testName:     "creates (postID2, user2) successfully",
   212  			postID:       "postID2",
   213  			content:      "content8",
   214  			title:        "title8",
   215  			author:       user2,
   216  			createdby:    app1,
   217  			expectResult: nil,
   218  		},
   219  		{
   220  			testName:     "creates (postID2, user1) successfully",
   221  			postID:       "postID2",
   222  			title:        "title9",
   223  			content:      "content9",
   224  			author:       user1,
   225  			createdby:    app2,
   226  			expectResult: nil,
   227  		},
   228  		{
   229  			testName:     "created by affiliated account",
   230  			postID:       "postIDaffiliated",
   231  			title:        "created by affiliaed",
   232  			content:      "content9",
   233  			author:       user1,
   234  			createdby:    suite.app1affiliated,
   235  			expectResult: nil,
   236  		},
   237  	}
   238  
   239  	for _, tc := range testCases {
   240  		// test valid postInfo
   241  		msg := types.CreatePostMsg{
   242  			PostID:    tc.postID,
   243  			Title:     tc.title,
   244  			Content:   tc.content,
   245  			Author:    tc.author,
   246  			CreatedBy: tc.createdby,
   247  		}
   248  		err := suite.pm.CreatePost(
   249  			suite.Ctx, msg.Author, msg.PostID, msg.CreatedBy, msg.Content, msg.Title)
   250  		suite.Equal(tc.expectResult, err, "%s", tc.testName)
   251  		if tc.expectResult == nil {
   252  			post, err := suite.pm.postStorage.GetPost(
   253  				suite.Ctx, linotypes.GetPermlink(tc.author, tc.postID))
   254  			suite.Nil(err)
   255  			app := tc.createdby
   256  			if app != tc.author {
   257  				app, err = suite.dev.GetAffiliatingApp(suite.Ctx, tc.createdby)
   258  				suite.Require().Nil(err, tc.createdby)
   259  			} else {
   260  				app = tc.author
   261  			}
   262  			suite.Equal(&model.Post{
   263  				PostID:    tc.postID,
   264  				Title:     tc.title,
   265  				Content:   tc.content,
   266  				Author:    tc.author,
   267  				CreatedBy: app,
   268  				CreatedAt: suite.Ctx.BlockHeader().Time.Unix(),
   269  				UpdatedAt: suite.Ctx.BlockHeader().Time.Unix(),
   270  			}, post, "%s", tc.testName)
   271  		}
   272  	}
   273  }
   274  
   275  func (suite *PostManagerTestSuite) TestUpdatePost() {
   276  	user1 := suite.user1
   277  	user2 := suite.user2
   278  	app1 := suite.app1
   279  	postID := "post1"
   280  	err := suite.pm.CreatePost(suite.Ctx, user1, postID, app1, "content", "title")
   281  	suite.Require().Nil(err)
   282  	baseTime := suite.Ctx.BlockHeader().Time.Unix()
   283  
   284  	testCases := []struct {
   285  		testName   string
   286  		author     linotypes.AccountKey
   287  		postID     string
   288  		title      string
   289  		content    string
   290  		expectErr  sdk.Error
   291  		updateTime int64
   292  	}{
   293  		{
   294  			testName:   "update: author update",
   295  			author:     user1,
   296  			postID:     postID,
   297  			title:      "update to this title",
   298  			content:    "update to this content",
   299  			expectErr:  nil,
   300  			updateTime: baseTime + 10,
   301  		},
   302  		{
   303  			testName:   "update with invalid post id",
   304  			author:     user1,
   305  			postID:     "invalid",
   306  			expectErr:  types.ErrPostNotFound(linotypes.GetPermlink(user1, "invalid")),
   307  			updateTime: baseTime + 100,
   308  		},
   309  		{
   310  			testName:   "update with invalid author",
   311  			author:     user2,
   312  			postID:     postID,
   313  			expectErr:  types.ErrPostNotFound(linotypes.GetPermlink(user2, postID)),
   314  			updateTime: baseTime + 1000,
   315  		},
   316  		{
   317  			testName:   "update with account that does not exist",
   318  			author:     suite.unreg1,
   319  			postID:     postID,
   320  			expectErr:  types.ErrPostNotFound(linotypes.GetPermlink(suite.unreg1, postID)),
   321  			updateTime: baseTime + 10000,
   322  		},
   323  	}
   324  
   325  	for _, tc := range testCases {
   326  		suite.NextBlock(time.Unix(tc.updateTime, 0))
   327  		err := suite.pm.UpdatePost(suite.Ctx, tc.author, tc.postID, tc.title, tc.content)
   328  		suite.Equal(tc.expectErr, err)
   329  		if tc.expectErr == nil {
   330  			post, err := suite.pm.postStorage.GetPost(
   331  				suite.Ctx, linotypes.GetPermlink(tc.author, tc.postID))
   332  			suite.Nil(err)
   333  			suite.Equal(&model.Post{
   334  				PostID:    tc.postID,
   335  				Title:     tc.title,
   336  				Content:   tc.content,
   337  				Author:    tc.author,
   338  				CreatedBy: app1,
   339  				CreatedAt: baseTime,
   340  				UpdatedAt: tc.updateTime,
   341  			}, post, "%s", tc.testName)
   342  		}
   343  	}
   344  }
   345  
   346  func (suite *PostManagerTestSuite) TestDeletePost() {
   347  	user1 := suite.user1
   348  	app1 := suite.app1
   349  	postID := "post1"
   350  	err := suite.pm.CreatePost(suite.Ctx, user1, postID, app1, "content", "title")
   351  	suite.Require().Nil(err)
   352  
   353  	testCases := []struct {
   354  		testName  string
   355  		author    linotypes.AccountKey
   356  		postID    string
   357  		expectErr sdk.Error
   358  	}{
   359  		{
   360  			testName:  "post not exists",
   361  			author:    suite.user2,
   362  			postID:    "post2",
   363  			expectErr: types.ErrPostNotFound(linotypes.GetPermlink(suite.user2, "post2")),
   364  		},
   365  		{
   366  			testName:  "delete successfully",
   367  			author:    user1,
   368  			postID:    postID,
   369  			expectErr: nil,
   370  		},
   371  		{
   372  			testName:  "delete post already deleted",
   373  			author:    user1,
   374  			postID:    postID,
   375  			expectErr: types.ErrPostDeleted(linotypes.GetPermlink(user1, postID)),
   376  		},
   377  	}
   378  
   379  	for _, tc := range testCases {
   380  		err := suite.pm.DeletePost(suite.Ctx, linotypes.GetPermlink(tc.author, tc.postID))
   381  		suite.Equal(tc.expectErr, err, "%s", tc.testName)
   382  		if tc.expectErr == nil {
   383  			suite.False(suite.pm.DoesPostExist(
   384  				suite.Ctx, linotypes.GetPermlink(tc.author, tc.postID)))
   385  		}
   386  	}
   387  
   388  	// after deleting post, cannot create post with same permlink.
   389  	err = suite.pm.CreatePost(suite.Ctx, user1, postID, app1, "content", "title")
   390  	suite.Equal(types.ErrPostAlreadyExist(linotypes.GetPermlink(user1, postID)), err)
   391  
   392  	// after deleting post, cannot create post with same permlink.
   393  	_, err = suite.pm.GetPost(suite.Ctx, linotypes.GetPermlink(user1, postID))
   394  	suite.Equal(types.ErrPostDeleted(linotypes.GetPermlink(user1, postID)), err)
   395  }
   396  
   397  func (suite *PostManagerTestSuite) TestGetPost() {
   398  	user1 := suite.user1
   399  	app1 := suite.app1
   400  	postID := "post1"
   401  	err := suite.pm.CreatePost(suite.Ctx, user1, postID, app1, "content", "title")
   402  	suite.Require().Nil(err)
   403  	post, err := suite.pm.GetPost(suite.Ctx, linotypes.GetPermlink(user1, postID))
   404  	suite.Require().Nil(err)
   405  	suite.Equal(model.Post{
   406  		PostID:    postID,
   407  		Title:     "title",
   408  		Content:   "content",
   409  		Author:    user1,
   410  		CreatedBy: app1,
   411  		CreatedAt: suite.Ctx.BlockHeader().Time.Unix(),
   412  		UpdatedAt: suite.Ctx.BlockHeader().Time.Unix(),
   413  	}, post)
   414  	_, err = suite.pm.GetPost(suite.Ctx, linotypes.GetPermlink(suite.user2, postID))
   415  	suite.Require().NotNil(err)
   416  	_, err = suite.pm.GetPost(suite.Ctx, linotypes.GetPermlink(suite.user1, "post2"))
   417  	suite.Require().NotNil(err)
   418  	err = suite.pm.DeletePost(suite.Ctx, linotypes.GetPermlink(user1, postID))
   419  	suite.Require().Nil(err)
   420  	_, err = suite.pm.GetPost(suite.Ctx, linotypes.GetPermlink(user1, postID))
   421  	suite.Require().NotNil(err)
   422  }
   423  
   424  func (suite *PostManagerTestSuite) TestLinoDonateInvalid() {
   425  	user2 := suite.user2
   426  	user1 := suite.user1
   427  	app1 := suite.app1
   428  	postID := "post1"
   429  	err := suite.pm.CreatePost(suite.Ctx, user1, postID, app1, "content", "title")
   430  	suite.Require().Nil(err)
   431  
   432  	testCases := []struct {
   433  		testName  string
   434  		from      linotypes.AccountKey
   435  		amount    linotypes.Coin
   436  		author    linotypes.AccountKey
   437  		postID    string
   438  		app       linotypes.AccountKey
   439  		expectErr sdk.Error
   440  	}{
   441  		{
   442  			testName:  "user does not exists",
   443  			from:      suite.unreg1,
   444  			amount:    linotypes.NewCoinFromInt64(23),
   445  			author:    user1,
   446  			postID:    postID,
   447  			app:       app1,
   448  			expectErr: types.ErrAccountNotFound(suite.unreg1),
   449  		},
   450  		{
   451  			testName:  "author does not exists",
   452  			from:      user1,
   453  			amount:    linotypes.NewCoinFromInt64(23),
   454  			author:    suite.unreg1,
   455  			postID:    postID,
   456  			app:       app1,
   457  			expectErr: types.ErrAccountNotFound(suite.unreg1),
   458  		},
   459  		{
   460  			testName:  "post does not exists",
   461  			from:      user2,
   462  			amount:    linotypes.NewCoinFromInt64(23),
   463  			author:    user1,
   464  			postID:    "fakepost",
   465  			app:       app1,
   466  			expectErr: types.ErrPostNotFound(linotypes.GetPermlink(user1, "fakepost")),
   467  		},
   468  		{
   469  			testName:  "self donation",
   470  			from:      user1,
   471  			amount:    linotypes.NewCoinFromInt64(23),
   472  			author:    user1,
   473  			postID:    postID,
   474  			app:       app1,
   475  			expectErr: types.ErrCannotDonateToSelf(user1),
   476  		},
   477  		{
   478  			testName:  "app not found",
   479  			from:      user2,
   480  			amount:    linotypes.NewCoinFromInt64(23),
   481  			author:    user1,
   482  			postID:    postID,
   483  			app:       user2,
   484  			expectErr: types.ErrDeveloperNotFound(user2),
   485  		},
   486  		{
   487  			testName:  "negative amount",
   488  			from:      user2,
   489  			amount:    linotypes.NewCoinFromInt64(-23),
   490  			author:    user1,
   491  			postID:    postID,
   492  			app:       app1,
   493  			expectErr: types.ErrInvalidDonationAmount(linotypes.NewCoinFromInt64(-23)),
   494  		},
   495  		{
   496  			testName:  "zero amount",
   497  			from:      user2,
   498  			amount:    linotypes.NewCoinFromInt64(0),
   499  			author:    user1,
   500  			postID:    postID,
   501  			app:       app1,
   502  			expectErr: types.ErrInvalidDonationAmount(linotypes.NewCoinFromInt64(0)),
   503  		},
   504  		{
   505  			testName:  "amount too little",
   506  			from:      user2,
   507  			amount:    linotypes.NewCoinFromInt64(1),
   508  			author:    user1,
   509  			postID:    postID,
   510  			app:       app1,
   511  			expectErr: types.ErrDonateAmountTooLittle(),
   512  		},
   513  	}
   514  
   515  	for _, tc := range testCases {
   516  		err := suite.pm.LinoDonate(suite.Ctx, tc.from, tc.amount, tc.author, tc.postID, tc.app)
   517  		suite.Require().Equal(tc.expectErr, err, "%s", tc.testName)
   518  	}
   519  }
   520  
   521  func (suite *PostManagerTestSuite) TestLinoDonateOK() {
   522  	from := suite.user2
   523  	author := suite.user1
   524  	app := suite.app1
   525  	postID := "post1"
   526  	amount := linotypes.NewCoinFromInt64(100000)
   527  	tax := linotypes.DecToCoin(amount.ToDec().Mul(suite.rate))
   528  	income := amount.Minus(tax)
   529  	dollar := linotypes.NewMiniDollar(1000)
   530  	dp := linotypes.NewMiniDollar(33)
   531  	suite.price.On("CoinToMiniDollar", mock.Anything, amount).Return(dollar, nil)
   532  	err := suite.pm.CreatePost(suite.Ctx, author, postID, app, "content", "title")
   533  	suite.Require().Nil(err)
   534  
   535  	suite.rep.On("DonateAt",
   536  		mock.Anything, from, linotypes.GetPermlink(author, postID), dollar).Return(
   537  		dp, nil).Once()
   538  	suite.global.On("RegisterEventAtTime",
   539  		mock.Anything,
   540  		int64(linotypes.ConsumptionFreezingPeriodSec),
   541  		types.RewardEvent{
   542  			PostAuthor: author,
   543  			PostID:     postID,
   544  			Consumer:   from,
   545  			Evaluate:   dp,
   546  			FromApp:    app,
   547  		},
   548  	).Return(nil).Once()
   549  	suite.am.On("MoveCoin", mock.Anything,
   550  		linotypes.NewAccOrAddrFromAcc(from),
   551  		linotypes.NewAccOrAddrFromAcc(author), income).Return(nil).Once()
   552  	suite.am.On("MoveToPool", mock.Anything, linotypes.VoteFrictionPool,
   553  		linotypes.NewAccOrAddrFromAcc(from), tax).Return(nil).Once()
   554  	suite.vote.On("RecordFriction", mock.Anything, tax).Return(nil).Once()
   555  	err = suite.pm.LinoDonate(suite.Ctx, from, amount, author, postID, app)
   556  	suite.Nil(err)
   557  	suite.price.AssertExpectations(suite.T())
   558  	suite.rep.AssertExpectations(suite.T())
   559  	suite.global.AssertExpectations(suite.T())
   560  	suite.am.AssertExpectations(suite.T())
   561  }
   562  
   563  // TODO(yumin): need to test path that external module returns error for 100% code coverage.
   564  func (suite *PostManagerTestSuite) TestLinoDonateExternalFailure() {}
   565  
   566  func (suite *PostManagerTestSuite) TestIDADonateInvalid() {
   567  	user2 := suite.user2
   568  	user1 := suite.user1
   569  	app1 := suite.app1
   570  	// app2 := suite.app2
   571  	app3 := suite.app3
   572  	postID := "post1"
   573  	err := suite.pm.CreatePost(suite.Ctx, user1, postID, app1, "content", "title")
   574  	suite.Require().Nil(err)
   575  	suite.dev.On("BurnIDA", mock.Anything, app1, mock.Anything, mock.Anything).Return(linotypes.NewCoinFromInt64(0), nil)
   576  
   577  	testCases := []struct {
   578  		testName  string
   579  		from      linotypes.AccountKey
   580  		n         linotypes.MiniIDA
   581  		author    linotypes.AccountKey
   582  		postID    string
   583  		app       linotypes.AccountKey
   584  		signer    linotypes.AccountKey
   585  		expectErr sdk.Error
   586  	}{
   587  		{
   588  			testName:  "user does not exists",
   589  			from:      suite.unreg1,
   590  			n:         sdk.NewInt(1234),
   591  			author:    user1,
   592  			postID:    postID,
   593  			app:       app1,
   594  			signer:    app1,
   595  			expectErr: types.ErrAccountNotFound(suite.unreg1),
   596  		},
   597  		{
   598  			testName:  "author does not exists",
   599  			from:      user1,
   600  			n:         sdk.NewInt(1234),
   601  			author:    suite.unreg1,
   602  			postID:    postID,
   603  			app:       app1,
   604  			signer:    app1,
   605  			expectErr: types.ErrAccountNotFound(suite.unreg1),
   606  		},
   607  		{
   608  			testName:  "post does not exists",
   609  			from:      user2,
   610  			n:         sdk.NewInt(1234),
   611  			author:    user1,
   612  			postID:    "fakepost",
   613  			app:       app1,
   614  			signer:    app1,
   615  			expectErr: types.ErrPostNotFound(linotypes.GetPermlink(user1, "fakepost")),
   616  		},
   617  		{
   618  			testName:  "self donation",
   619  			from:      user1,
   620  			n:         sdk.NewInt(1234),
   621  			author:    user1,
   622  			postID:    postID,
   623  			app:       app1,
   624  			signer:    app1,
   625  			expectErr: types.ErrCannotDonateToSelf(user1),
   626  		},
   627  		{
   628  			testName:  "app not found",
   629  			from:      user2,
   630  			n:         sdk.NewInt(1234),
   631  			author:    user1,
   632  			postID:    postID,
   633  			app:       user2,
   634  			signer:    app1,
   635  			expectErr: types.ErrDeveloperNotFound(user2),
   636  		},
   637  		{
   638  			testName:  "ida not found",
   639  			from:      user2,
   640  			n:         sdk.NewInt(1234),
   641  			author:    user1,
   642  			postID:    postID,
   643  			app:       app3,
   644  			signer:    app3,
   645  			expectErr: types.ErrDeveloperNotFound(app3),
   646  		},
   647  		{
   648  			testName:  "negative amount",
   649  			from:      user2,
   650  			n:         sdk.NewInt(-1234),
   651  			author:    user1,
   652  			postID:    postID,
   653  			app:       app1,
   654  			signer:    app1,
   655  			expectErr: types.ErrNonPositiveIDAAmount(sdk.NewInt(-1234)),
   656  		},
   657  		{
   658  			testName:  "zero amount",
   659  			from:      user2,
   660  			n:         sdk.NewInt(0),
   661  			author:    user1,
   662  			postID:    postID,
   663  			app:       app1,
   664  			signer:    app1,
   665  			expectErr: types.ErrNonPositiveIDAAmount(sdk.NewInt(0)),
   666  		},
   667  		{
   668  			testName:  "little amount",
   669  			from:      user2,
   670  			n:         sdk.NewInt(1),
   671  			author:    user1,
   672  			postID:    postID,
   673  			app:       app1,
   674  			signer:    app1,
   675  			expectErr: types.ErrDonateAmountTooLittle(),
   676  		},
   677  		{
   678  			testName:  "invalid signer, not an appfiliated",
   679  			from:      user2,
   680  			n:         sdk.NewInt(1),
   681  			author:    user1,
   682  			postID:    postID,
   683  			app:       app1,
   684  			signer:    user2,
   685  			expectErr: types.ErrInvalidSigner(),
   686  		},
   687  		{
   688  			testName:  "invalid signer, wrong affiliated app",
   689  			from:      user2,
   690  			n:         sdk.NewInt(1),
   691  			author:    user1,
   692  			postID:    postID,
   693  			app:       app1,
   694  			signer:    suite.app2affiliated,
   695  			expectErr: types.ErrInvalidSigner(),
   696  		},
   697  	}
   698  
   699  	for _, tc := range testCases {
   700  		err := suite.pm.IDADonate(suite.Ctx, tc.from, tc.n, tc.author, tc.postID, tc.app, tc.signer)
   701  		suite.Equal(tc.expectErr, err, "%s", tc.testName)
   702  		if err != nil {
   703  			continue
   704  		}
   705  	}
   706  }
   707  
   708  func (suite *PostManagerTestSuite) TestIDADonateOK() {
   709  	from := suite.user2
   710  	author := suite.user1
   711  	app := suite.app1
   712  	postID := "post1"
   713  	var amount linotypes.IDAStr = "20"
   714  	miniIDA, err := amount.ToMiniIDA()
   715  	suite.Require().Nil(err)
   716  	dollar := linotypes.MiniIDAToMiniDollar(miniIDA, suite.app1IDAPrice)
   717  	tax := linotypes.NewMiniDollarFromInt(dollar.ToDec().Mul(suite.rate).RoundInt())
   718  	taxcoins := linotypes.NewCoinFromInt64(78)
   719  	income := dollar.Minus(tax)
   720  	dp := linotypes.NewMiniDollar(33)
   721  	err = suite.pm.CreatePost(suite.Ctx, author, postID, app, "content", "title")
   722  	suite.Require().Nil(err)
   723  
   724  	suite.dev.On("BurnIDA", mock.Anything, app, from, tax).Return(taxcoins, nil)
   725  	suite.rep.On("DonateAt",
   726  		mock.Anything, from, linotypes.GetPermlink(author, postID), dollar).Return(
   727  		dp, nil).Once()
   728  	suite.global.On("RegisterEventAtTime",
   729  		mock.Anything,
   730  		int64(linotypes.ConsumptionFreezingPeriodSec),
   731  		types.RewardEvent{
   732  			PostAuthor: author,
   733  			PostID:     postID,
   734  			Consumer:   from,
   735  			Evaluate:   dp,
   736  			FromApp:    app,
   737  		},
   738  	).Return(nil).Once()
   739  	suite.dev.On("MoveIDA", mock.Anything, app, from, author, income).Return(nil).Once()
   740  	suite.am.On("MoveToPool", mock.Anything, linotypes.VoteFrictionPool,
   741  		linotypes.NewAccOrAddrFromAcc(from), taxcoins).Return(nil).Once()
   742  	suite.vote.On("RecordFriction", mock.Anything, taxcoins).Return(nil).Once()
   743  	err = suite.pm.IDADonate(suite.Ctx, from, miniIDA, author, postID, app, suite.app1affiliated)
   744  	suite.Nil(err)
   745  	suite.price.AssertExpectations(suite.T())
   746  	suite.rep.AssertExpectations(suite.T())
   747  	suite.global.AssertExpectations(suite.T())
   748  	suite.am.AssertExpectations(suite.T())
   749  }
   750  
   751  func (suite *PostManagerTestSuite) TestImportExport() {
   752  	cdc := codec.New()
   753  	suite.LoadState(true)
   754  
   755  	dir, err2 := ioutil.TempDir("", "test")
   756  	suite.Require().Nil(err2)
   757  	defer os.RemoveAll(dir) // clean up
   758  
   759  	tmpfn := filepath.Join(dir, "tmpfile")
   760  	err2 = suite.pm.ExportToFile(suite.Ctx, cdc, tmpfn)
   761  	suite.Nil(err2)
   762  
   763  	// reset all state.
   764  	suite.SetupTest()
   765  	err2 = suite.pm.ImportFromFile(suite.Ctx, cdc, tmpfn)
   766  	suite.Nil(err2)
   767  
   768  	suite.Golden()
   769  	// suite.AssertStateUnchanged(false)
   770  }