github.com/mad-app/mattermost-server@v5.11.1+incompatible/store/storetest/reaction_store.go (about)

     1  // Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package storetest
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/mattermost/mattermost-server/model"
    10  	"github.com/mattermost/mattermost-server/store"
    11  )
    12  
    13  func TestReactionStore(t *testing.T, ss store.Store) {
    14  	t.Run("ReactionSave", func(t *testing.T) { testReactionSave(t, ss) })
    15  	t.Run("ReactionDelete", func(t *testing.T) { testReactionDelete(t, ss) })
    16  	t.Run("ReactionGetForPost", func(t *testing.T) { testReactionGetForPost(t, ss) })
    17  	t.Run("ReactionDeleteAllWithEmojiName", func(t *testing.T) { testReactionDeleteAllWithEmojiName(t, ss) })
    18  	t.Run("PermanentDeleteBatch", func(t *testing.T) { testReactionStorePermanentDeleteBatch(t, ss) })
    19  	t.Run("ReactionBulkGetForPosts", func(t *testing.T) { testReactionBulkGetForPosts(t, ss) })
    20  }
    21  
    22  func testReactionSave(t *testing.T, ss store.Store) {
    23  	post := store.Must(ss.Post().Save(&model.Post{
    24  		ChannelId: model.NewId(),
    25  		UserId:    model.NewId(),
    26  	})).(*model.Post)
    27  	firstUpdateAt := post.UpdateAt
    28  
    29  	reaction1 := &model.Reaction{
    30  		UserId:    model.NewId(),
    31  		PostId:    post.Id,
    32  		EmojiName: model.NewId(),
    33  	}
    34  	if result := <-ss.Reaction().Save(reaction1); result.Err != nil {
    35  		t.Fatal(result.Err)
    36  	} else if saved := result.Data.(*model.Reaction); saved.UserId != reaction1.UserId ||
    37  		saved.PostId != reaction1.PostId || saved.EmojiName != reaction1.EmojiName {
    38  		t.Fatal("should've saved reaction and returned it")
    39  	}
    40  
    41  	var secondUpdateAt int64
    42  	if postList := store.Must(ss.Post().Get(reaction1.PostId)).(*model.PostList); !postList.Posts[post.Id].HasReactions {
    43  		t.Fatal("should've set HasReactions = true on post")
    44  	} else if postList.Posts[post.Id].UpdateAt == firstUpdateAt {
    45  		t.Fatal("should've marked post as updated when HasReactions changed")
    46  	} else {
    47  		secondUpdateAt = postList.Posts[post.Id].UpdateAt
    48  	}
    49  
    50  	if result := <-ss.Reaction().Save(reaction1); result.Err != nil {
    51  		t.Log(result.Err)
    52  		t.Fatal("should've allowed saving a duplicate reaction")
    53  	}
    54  
    55  	// different user
    56  	reaction2 := &model.Reaction{
    57  		UserId:    model.NewId(),
    58  		PostId:    reaction1.PostId,
    59  		EmojiName: reaction1.EmojiName,
    60  	}
    61  	if result := <-ss.Reaction().Save(reaction2); result.Err != nil {
    62  		t.Fatal(result.Err)
    63  	}
    64  
    65  	if postList := store.Must(ss.Post().Get(reaction2.PostId)).(*model.PostList); postList.Posts[post.Id].UpdateAt == secondUpdateAt {
    66  		t.Fatal("should've marked post as updated even if HasReactions doesn't change")
    67  	}
    68  
    69  	// different post
    70  	reaction3 := &model.Reaction{
    71  		UserId:    reaction1.UserId,
    72  		PostId:    model.NewId(),
    73  		EmojiName: reaction1.EmojiName,
    74  	}
    75  	if result := <-ss.Reaction().Save(reaction3); result.Err != nil {
    76  		t.Fatal(result.Err)
    77  	}
    78  
    79  	// different emoji
    80  	reaction4 := &model.Reaction{
    81  		UserId:    reaction1.UserId,
    82  		PostId:    reaction1.PostId,
    83  		EmojiName: model.NewId(),
    84  	}
    85  	if result := <-ss.Reaction().Save(reaction4); result.Err != nil {
    86  		t.Fatal(result.Err)
    87  	}
    88  
    89  	// invalid reaction
    90  	reaction5 := &model.Reaction{
    91  		UserId: reaction1.UserId,
    92  		PostId: reaction1.PostId,
    93  	}
    94  	if result := <-ss.Reaction().Save(reaction5); result.Err == nil {
    95  		t.Fatal("should've failed for invalid reaction")
    96  	}
    97  }
    98  
    99  func testReactionDelete(t *testing.T, ss store.Store) {
   100  	post := store.Must(ss.Post().Save(&model.Post{
   101  		ChannelId: model.NewId(),
   102  		UserId:    model.NewId(),
   103  	})).(*model.Post)
   104  
   105  	reaction := &model.Reaction{
   106  		UserId:    model.NewId(),
   107  		PostId:    post.Id,
   108  		EmojiName: model.NewId(),
   109  	}
   110  
   111  	store.Must(ss.Reaction().Save(reaction))
   112  	firstUpdateAt := store.Must(ss.Post().Get(reaction.PostId)).(*model.PostList).Posts[post.Id].UpdateAt
   113  
   114  	if result := <-ss.Reaction().Delete(reaction); result.Err != nil {
   115  		t.Fatal(result.Err)
   116  	}
   117  
   118  	if result := <-ss.Reaction().GetForPost(post.Id, false); result.Err != nil {
   119  		t.Fatal(result.Err)
   120  	} else if len(result.Data.([]*model.Reaction)) != 0 {
   121  		t.Fatal("should've deleted reaction")
   122  	}
   123  
   124  	if postList := store.Must(ss.Post().Get(post.Id)).(*model.PostList); postList.Posts[post.Id].HasReactions {
   125  		t.Fatal("should've set HasReactions = false on post")
   126  	} else if postList.Posts[post.Id].UpdateAt == firstUpdateAt {
   127  		t.Fatal("should mark post as updated after deleting reactions")
   128  	}
   129  }
   130  
   131  func testReactionGetForPost(t *testing.T, ss store.Store) {
   132  	postId := model.NewId()
   133  
   134  	userId := model.NewId()
   135  
   136  	reactions := []*model.Reaction{
   137  		{
   138  			UserId:    userId,
   139  			PostId:    postId,
   140  			EmojiName: "smile",
   141  		},
   142  		{
   143  			UserId:    model.NewId(),
   144  			PostId:    postId,
   145  			EmojiName: "smile",
   146  		},
   147  		{
   148  			UserId:    userId,
   149  			PostId:    postId,
   150  			EmojiName: "sad",
   151  		},
   152  		{
   153  			UserId:    userId,
   154  			PostId:    model.NewId(),
   155  			EmojiName: "angry",
   156  		},
   157  	}
   158  
   159  	for _, reaction := range reactions {
   160  		store.Must(ss.Reaction().Save(reaction))
   161  	}
   162  
   163  	if result := <-ss.Reaction().GetForPost(postId, false); result.Err != nil {
   164  		t.Fatal(result.Err)
   165  	} else if returned := result.Data.([]*model.Reaction); len(returned) != 3 {
   166  		t.Fatal("should've returned 3 reactions")
   167  	} else {
   168  		for _, reaction := range reactions {
   169  			found := false
   170  
   171  			for _, returnedReaction := range returned {
   172  				if returnedReaction.UserId == reaction.UserId && returnedReaction.PostId == reaction.PostId &&
   173  					returnedReaction.EmojiName == reaction.EmojiName {
   174  					found = true
   175  					break
   176  				}
   177  			}
   178  
   179  			if !found && reaction.PostId == postId {
   180  				t.Fatalf("should've returned reaction for post %v", reaction)
   181  			} else if found && reaction.PostId != postId {
   182  				t.Fatal("shouldn't have returned reaction for another post")
   183  			}
   184  		}
   185  	}
   186  
   187  	// Should return cached item
   188  	if result := <-ss.Reaction().GetForPost(postId, true); result.Err != nil {
   189  		t.Fatal(result.Err)
   190  	} else if returned := result.Data.([]*model.Reaction); len(returned) != 3 {
   191  		t.Fatal("should've returned 3 reactions")
   192  	} else {
   193  		for _, reaction := range reactions {
   194  			found := false
   195  
   196  			for _, returnedReaction := range returned {
   197  				if returnedReaction.UserId == reaction.UserId && returnedReaction.PostId == reaction.PostId &&
   198  					returnedReaction.EmojiName == reaction.EmojiName {
   199  					found = true
   200  					break
   201  				}
   202  			}
   203  
   204  			if !found && reaction.PostId == postId {
   205  				t.Fatalf("should've returned reaction for post %v", reaction)
   206  			} else if found && reaction.PostId != postId {
   207  				t.Fatal("shouldn't have returned reaction for another post")
   208  			}
   209  		}
   210  	}
   211  }
   212  
   213  func testReactionDeleteAllWithEmojiName(t *testing.T, ss store.Store) {
   214  	emojiToDelete := model.NewId()
   215  
   216  	post := store.Must(ss.Post().Save(&model.Post{
   217  		ChannelId: model.NewId(),
   218  		UserId:    model.NewId(),
   219  	})).(*model.Post)
   220  	post2 := store.Must(ss.Post().Save(&model.Post{
   221  		ChannelId: model.NewId(),
   222  		UserId:    model.NewId(),
   223  	})).(*model.Post)
   224  	post3 := store.Must(ss.Post().Save(&model.Post{
   225  		ChannelId: model.NewId(),
   226  		UserId:    model.NewId(),
   227  	})).(*model.Post)
   228  
   229  	userId := model.NewId()
   230  
   231  	reactions := []*model.Reaction{
   232  		{
   233  			UserId:    userId,
   234  			PostId:    post.Id,
   235  			EmojiName: emojiToDelete,
   236  		},
   237  		{
   238  			UserId:    model.NewId(),
   239  			PostId:    post.Id,
   240  			EmojiName: emojiToDelete,
   241  		},
   242  		{
   243  			UserId:    userId,
   244  			PostId:    post.Id,
   245  			EmojiName: "sad",
   246  		},
   247  		{
   248  			UserId:    userId,
   249  			PostId:    post2.Id,
   250  			EmojiName: "angry",
   251  		},
   252  		{
   253  			UserId:    userId,
   254  			PostId:    post3.Id,
   255  			EmojiName: emojiToDelete,
   256  		},
   257  	}
   258  
   259  	for _, reaction := range reactions {
   260  		store.Must(ss.Reaction().Save(reaction))
   261  	}
   262  
   263  	if result := <-ss.Reaction().DeleteAllWithEmojiName(emojiToDelete); result.Err != nil {
   264  		t.Fatal(result.Err)
   265  	}
   266  
   267  	// check that the reactions were deleted
   268  	if returned := store.Must(ss.Reaction().GetForPost(post.Id, false)).([]*model.Reaction); len(returned) != 1 {
   269  		t.Fatal("should've only removed reactions with emoji name")
   270  	} else {
   271  		for _, reaction := range returned {
   272  			if reaction.EmojiName == "smile" {
   273  				t.Fatal("should've removed reaction with emoji name")
   274  			}
   275  		}
   276  	}
   277  
   278  	if returned := store.Must(ss.Reaction().GetForPost(post2.Id, false)).([]*model.Reaction); len(returned) != 1 {
   279  		t.Fatal("should've only removed reactions with emoji name")
   280  	}
   281  
   282  	if returned := store.Must(ss.Reaction().GetForPost(post3.Id, false)).([]*model.Reaction); len(returned) != 0 {
   283  		t.Fatal("should've only removed reactions with emoji name")
   284  	}
   285  
   286  	// check that the posts are updated
   287  	if postList := store.Must(ss.Post().Get(post.Id)).(*model.PostList); !postList.Posts[post.Id].HasReactions {
   288  		t.Fatal("post should still have reactions")
   289  	}
   290  
   291  	if postList := store.Must(ss.Post().Get(post2.Id)).(*model.PostList); !postList.Posts[post2.Id].HasReactions {
   292  		t.Fatal("post should still have reactions")
   293  	}
   294  
   295  	if postList := store.Must(ss.Post().Get(post3.Id)).(*model.PostList); postList.Posts[post3.Id].HasReactions {
   296  		t.Fatal("post shouldn't have reactions any more")
   297  	}
   298  }
   299  
   300  func testReactionStorePermanentDeleteBatch(t *testing.T, ss store.Store) {
   301  	post := store.Must(ss.Post().Save(&model.Post{
   302  		ChannelId: model.NewId(),
   303  		UserId:    model.NewId(),
   304  	})).(*model.Post)
   305  
   306  	reactions := []*model.Reaction{
   307  		{
   308  			UserId:    model.NewId(),
   309  			PostId:    post.Id,
   310  			EmojiName: "sad",
   311  			CreateAt:  1000,
   312  		},
   313  		{
   314  			UserId:    model.NewId(),
   315  			PostId:    post.Id,
   316  			EmojiName: "sad",
   317  			CreateAt:  1500,
   318  		},
   319  		{
   320  			UserId:    model.NewId(),
   321  			PostId:    post.Id,
   322  			EmojiName: "sad",
   323  			CreateAt:  2000,
   324  		},
   325  		{
   326  			UserId:    model.NewId(),
   327  			PostId:    post.Id,
   328  			EmojiName: "sad",
   329  			CreateAt:  2000,
   330  		},
   331  	}
   332  
   333  	// Need to hang on to a reaction to delete later in order to clear the cache, as "allowFromCache" isn't honoured any more.
   334  	var lastReaction *model.Reaction
   335  	for _, reaction := range reactions {
   336  		lastReaction = store.Must(ss.Reaction().Save(reaction)).(*model.Reaction)
   337  	}
   338  
   339  	if returned := store.Must(ss.Reaction().GetForPost(post.Id, false)).([]*model.Reaction); len(returned) != 4 {
   340  		t.Fatal("expected 4 reactions")
   341  	}
   342  
   343  	store.Must(ss.Reaction().PermanentDeleteBatch(1800, 1000))
   344  
   345  	// This is to force a clear of the cache.
   346  	store.Must(ss.Reaction().Delete(lastReaction))
   347  
   348  	if returned := store.Must(ss.Reaction().GetForPost(post.Id, false)).([]*model.Reaction); len(returned) != 1 {
   349  		t.Fatalf("expected 1 reaction. Got: %v", len(returned))
   350  	}
   351  }
   352  
   353  func testReactionBulkGetForPosts(t *testing.T, ss store.Store) {
   354  	postId := model.NewId()
   355  	post2Id := model.NewId()
   356  	post3Id := model.NewId()
   357  	post4Id := model.NewId()
   358  
   359  	userId := model.NewId()
   360  
   361  	reactions := []*model.Reaction{
   362  		{
   363  			UserId:    userId,
   364  			PostId:    postId,
   365  			EmojiName: "smile",
   366  		},
   367  		{
   368  			UserId:    model.NewId(),
   369  			PostId:    post2Id,
   370  			EmojiName: "smile",
   371  		},
   372  		{
   373  			UserId:    userId,
   374  			PostId:    post3Id,
   375  			EmojiName: "sad",
   376  		},
   377  		{
   378  			UserId:    userId,
   379  			PostId:    postId,
   380  			EmojiName: "angry",
   381  		},
   382  		{
   383  			UserId:    userId,
   384  			PostId:    post2Id,
   385  			EmojiName: "angry",
   386  		},
   387  		{
   388  			UserId:    userId,
   389  			PostId:    post4Id,
   390  			EmojiName: "angry",
   391  		},
   392  	}
   393  
   394  	for _, reaction := range reactions {
   395  		store.Must(ss.Reaction().Save(reaction))
   396  	}
   397  
   398  	postIds := []string{postId, post2Id, post3Id}
   399  	if result := <-ss.Reaction().BulkGetForPosts(postIds); result.Err != nil {
   400  		t.Fatal(result.Err)
   401  	} else if returned := result.Data.([]*model.Reaction); len(returned) != 5 {
   402  		t.Fatal("should've returned 5 reactions")
   403  	} else {
   404  		post4IdFound := false
   405  		for _, reaction := range returned {
   406  			if reaction.PostId == post4Id {
   407  				post4IdFound = true
   408  				break
   409  			}
   410  		}
   411  
   412  		if post4IdFound {
   413  			t.Fatal("Wrong reaction returned")
   414  		}
   415  	}
   416  
   417  }