github.com/haalcala/mattermost-server-change-repo/v5@v5.33.2/store/storetest/file_info_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package storetest
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/mattermost/mattermost-server/v5/model"
    13  	"github.com/mattermost/mattermost-server/v5/store"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  )
    18  
    19  func TestFileInfoStore(t *testing.T, ss store.Store) {
    20  	t.Run("FileInfoSaveGet", func(t *testing.T) { testFileInfoSaveGet(t, ss) })
    21  	t.Run("FileInfoSaveGetByPath", func(t *testing.T) { testFileInfoSaveGetByPath(t, ss) })
    22  	t.Run("FileInfoGetForPost", func(t *testing.T) { testFileInfoGetForPost(t, ss) })
    23  	t.Run("FileInfoGetForUser", func(t *testing.T) { testFileInfoGetForUser(t, ss) })
    24  	t.Run("FileInfoGetWithOptions", func(t *testing.T) { testFileInfoGetWithOptions(t, ss) })
    25  	t.Run("FileInfoAttachToPost", func(t *testing.T) { testFileInfoAttachToPost(t, ss) })
    26  	t.Run("FileInfoDeleteForPost", func(t *testing.T) { testFileInfoDeleteForPost(t, ss) })
    27  	t.Run("FileInfoPermanentDelete", func(t *testing.T) { testFileInfoPermanentDelete(t, ss) })
    28  	t.Run("FileInfoPermanentDeleteBatch", func(t *testing.T) { testFileInfoPermanentDeleteBatch(t, ss) })
    29  	t.Run("FileInfoPermanentDeleteByUser", func(t *testing.T) { testFileInfoPermanentDeleteByUser(t, ss) })
    30  }
    31  
    32  func testFileInfoSaveGet(t *testing.T, ss store.Store) {
    33  	info := &model.FileInfo{
    34  		CreatorId: model.NewId(),
    35  		Path:      "file.txt",
    36  	}
    37  
    38  	info, err := ss.FileInfo().Save(info)
    39  	require.NoError(t, err)
    40  	require.NotEqual(t, len(info.Id), 0)
    41  
    42  	defer func() {
    43  		ss.FileInfo().PermanentDelete(info.Id)
    44  	}()
    45  
    46  	rinfo, err := ss.FileInfo().Get(info.Id)
    47  	require.NoError(t, err)
    48  	require.Equal(t, info.Id, rinfo.Id)
    49  
    50  	info2, err := ss.FileInfo().Save(&model.FileInfo{
    51  		CreatorId: model.NewId(),
    52  		Path:      "file.txt",
    53  		DeleteAt:  123,
    54  	})
    55  	require.NoError(t, err)
    56  
    57  	_, err = ss.FileInfo().Get(info2.Id)
    58  	assert.Error(t, err)
    59  
    60  	defer func() {
    61  		ss.FileInfo().PermanentDelete(info2.Id)
    62  	}()
    63  }
    64  
    65  func testFileInfoSaveGetByPath(t *testing.T, ss store.Store) {
    66  	info := &model.FileInfo{
    67  		CreatorId: model.NewId(),
    68  		Path:      fmt.Sprintf("%v/file.txt", model.NewId()),
    69  	}
    70  
    71  	info, err := ss.FileInfo().Save(info)
    72  	require.NoError(t, err)
    73  	assert.NotEqual(t, len(info.Id), 0)
    74  	defer func() {
    75  		ss.FileInfo().PermanentDelete(info.Id)
    76  	}()
    77  
    78  	rinfo, err := ss.FileInfo().GetByPath(info.Path)
    79  	require.NoError(t, err)
    80  	assert.Equal(t, info.Id, rinfo.Id)
    81  
    82  	info2, err := ss.FileInfo().Save(&model.FileInfo{
    83  		CreatorId: model.NewId(),
    84  		Path:      "file.txt",
    85  		DeleteAt:  123,
    86  	})
    87  	require.NoError(t, err)
    88  
    89  	_, err = ss.FileInfo().GetByPath(info2.Id)
    90  	assert.Error(t, err)
    91  
    92  	defer func() {
    93  		ss.FileInfo().PermanentDelete(info2.Id)
    94  	}()
    95  }
    96  
    97  func testFileInfoGetForPost(t *testing.T, ss store.Store) {
    98  	userId := model.NewId()
    99  	postId := model.NewId()
   100  
   101  	infos := []*model.FileInfo{
   102  		{
   103  			PostId:    postId,
   104  			CreatorId: userId,
   105  			Path:      "file.txt",
   106  		},
   107  		{
   108  			PostId:    postId,
   109  			CreatorId: userId,
   110  			Path:      "file.txt",
   111  		},
   112  		{
   113  			PostId:    postId,
   114  			CreatorId: userId,
   115  			Path:      "file.txt",
   116  			DeleteAt:  123,
   117  		},
   118  		{
   119  			PostId:    model.NewId(),
   120  			CreatorId: userId,
   121  			Path:      "file.txt",
   122  		},
   123  	}
   124  
   125  	for i, info := range infos {
   126  		newInfo, err := ss.FileInfo().Save(info)
   127  		require.NoError(t, err)
   128  		infos[i] = newInfo
   129  		defer func(id string) {
   130  			ss.FileInfo().PermanentDelete(id)
   131  		}(newInfo.Id)
   132  	}
   133  
   134  	testCases := []struct {
   135  		Name           string
   136  		PostId         string
   137  		ReadFromMaster bool
   138  		IncludeDeleted bool
   139  		AllowFromCache bool
   140  		ExpectedPosts  int
   141  	}{
   142  		{
   143  			Name:           "Fetch from master, without deleted and without cache",
   144  			PostId:         postId,
   145  			ReadFromMaster: true,
   146  			IncludeDeleted: false,
   147  			AllowFromCache: false,
   148  			ExpectedPosts:  2,
   149  		},
   150  		{
   151  			Name:           "Fetch from master, with deleted and without cache",
   152  			PostId:         postId,
   153  			ReadFromMaster: true,
   154  			IncludeDeleted: true,
   155  			AllowFromCache: false,
   156  			ExpectedPosts:  3,
   157  		},
   158  		{
   159  			Name:           "Fetch from master, with deleted and with cache",
   160  			PostId:         postId,
   161  			ReadFromMaster: true,
   162  			IncludeDeleted: true,
   163  			AllowFromCache: true,
   164  			ExpectedPosts:  3,
   165  		},
   166  		{
   167  			Name:           "Fetch from replica, without deleted and without cache",
   168  			PostId:         postId,
   169  			ReadFromMaster: false,
   170  			IncludeDeleted: false,
   171  			AllowFromCache: false,
   172  			ExpectedPosts:  2,
   173  		},
   174  		{
   175  			Name:           "Fetch from replica, with deleted and without cache",
   176  			PostId:         postId,
   177  			ReadFromMaster: false,
   178  			IncludeDeleted: true,
   179  			AllowFromCache: false,
   180  			ExpectedPosts:  3,
   181  		},
   182  		{
   183  			Name:           "Fetch from replica, with deleted and without cache",
   184  			PostId:         postId,
   185  			ReadFromMaster: false,
   186  			IncludeDeleted: true,
   187  			AllowFromCache: true,
   188  			ExpectedPosts:  3,
   189  		},
   190  		{
   191  			Name:           "Fetch from replica, without deleted and with cache",
   192  			PostId:         postId,
   193  			ReadFromMaster: true,
   194  			IncludeDeleted: false,
   195  			AllowFromCache: true,
   196  			ExpectedPosts:  2,
   197  		},
   198  	}
   199  
   200  	for _, tc := range testCases {
   201  		t.Run(tc.Name, func(t *testing.T) {
   202  			postInfos, err := ss.FileInfo().GetForPost(
   203  				tc.PostId,
   204  				tc.ReadFromMaster,
   205  				tc.IncludeDeleted,
   206  				tc.AllowFromCache,
   207  			)
   208  			require.NoError(t, err)
   209  			assert.Len(t, postInfos, tc.ExpectedPosts)
   210  
   211  		})
   212  	}
   213  }
   214  
   215  func testFileInfoGetForUser(t *testing.T, ss store.Store) {
   216  	userId := model.NewId()
   217  	userId2 := model.NewId()
   218  	postId := model.NewId()
   219  
   220  	infos := []*model.FileInfo{
   221  		{
   222  			PostId:    postId,
   223  			CreatorId: userId,
   224  			Path:      "file.txt",
   225  		},
   226  		{
   227  			PostId:    postId,
   228  			CreatorId: userId,
   229  			Path:      "file.txt",
   230  		},
   231  		{
   232  			PostId:    postId,
   233  			CreatorId: userId,
   234  			Path:      "file.txt",
   235  		},
   236  		{
   237  			PostId:    model.NewId(),
   238  			CreatorId: userId2,
   239  			Path:      "file.txt",
   240  		},
   241  	}
   242  
   243  	for i, info := range infos {
   244  		newInfo, err := ss.FileInfo().Save(info)
   245  		require.NoError(t, err)
   246  		infos[i] = newInfo
   247  		defer func(id string) {
   248  			ss.FileInfo().PermanentDelete(id)
   249  		}(newInfo.Id)
   250  	}
   251  
   252  	userPosts, err := ss.FileInfo().GetForUser(userId)
   253  	require.NoError(t, err)
   254  	assert.Len(t, userPosts, 3)
   255  
   256  	userPosts, err = ss.FileInfo().GetForUser(userId2)
   257  	require.NoError(t, err)
   258  	assert.Len(t, userPosts, 1)
   259  }
   260  
   261  func testFileInfoGetWithOptions(t *testing.T, ss store.Store) {
   262  	makePost := func(chId string, user string) *model.Post {
   263  		post := model.Post{}
   264  		post.ChannelId = chId
   265  		post.UserId = user
   266  		_, err := ss.Post().Save(&post)
   267  		require.NoError(t, err)
   268  		return &post
   269  	}
   270  
   271  	makeFile := func(post *model.Post, user string, createAt int64, idPrefix string) model.FileInfo {
   272  		id := model.NewId()
   273  		id = idPrefix + id[1:] // hacky way to get sortable Ids to confirm secondary Id sort works
   274  		fileInfo := model.FileInfo{
   275  			Id:        id,
   276  			CreatorId: user,
   277  			Path:      "file.txt",
   278  			CreateAt:  createAt,
   279  		}
   280  		if post.Id != "" {
   281  			fileInfo.PostId = post.Id
   282  		}
   283  		_, err := ss.FileInfo().Save(&fileInfo)
   284  		require.NoError(t, err)
   285  		return fileInfo
   286  	}
   287  
   288  	userId1 := model.NewId()
   289  	userId2 := model.NewId()
   290  
   291  	channelId1 := model.NewId()
   292  	channelId2 := model.NewId()
   293  	channelId3 := model.NewId()
   294  
   295  	post1_1 := makePost(channelId1, userId1) // post 1 by user 1
   296  	post1_2 := makePost(channelId3, userId1) // post 2 by user 1
   297  	post2_1 := makePost(channelId2, userId2)
   298  	post2_2 := makePost(channelId3, userId2)
   299  
   300  	epoch := time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC)
   301  	file1_1 := makeFile(post1_1, userId1, epoch.AddDate(0, 0, 1).Unix(), "a")       // file 1 by user 1
   302  	file1_2 := makeFile(post1_2, userId1, epoch.AddDate(0, 0, 2).Unix(), "b")       // file 2 by user 1
   303  	file1_3 := makeFile(&model.Post{}, userId1, epoch.AddDate(0, 0, 3).Unix(), "c") // file that is not attached to a post
   304  	file2_1 := makeFile(post2_1, userId2, epoch.AddDate(0, 0, 4).Unix(), "d")       // file 2 by user 1
   305  	file2_2 := makeFile(post2_2, userId2, epoch.AddDate(0, 0, 5).Unix(), "e")
   306  
   307  	// delete a file
   308  	_, err := ss.FileInfo().DeleteForPost(file2_2.PostId)
   309  	require.NoError(t, err)
   310  
   311  	testCases := []struct {
   312  		Name            string
   313  		Page, PerPage   int
   314  		Opt             *model.GetFileInfosOptions
   315  		ExpectedFileIds []string
   316  	}{
   317  		{
   318  			Name:            "Get files with nil option",
   319  			Page:            0,
   320  			PerPage:         10,
   321  			Opt:             nil,
   322  			ExpectedFileIds: []string{file1_1.Id, file1_2.Id, file1_3.Id, file2_1.Id},
   323  		},
   324  		{
   325  			Name:            "Get files including deleted",
   326  			Page:            0,
   327  			PerPage:         10,
   328  			Opt:             &model.GetFileInfosOptions{IncludeDeleted: true},
   329  			ExpectedFileIds: []string{file1_1.Id, file1_2.Id, file1_3.Id, file2_1.Id, file2_2.Id},
   330  		},
   331  		{
   332  			Name:    "Get files including deleted filtered by channel",
   333  			Page:    0,
   334  			PerPage: 10,
   335  			Opt: &model.GetFileInfosOptions{
   336  				IncludeDeleted: true,
   337  				ChannelIds:     []string{channelId3},
   338  			},
   339  			ExpectedFileIds: []string{file1_2.Id, file2_2.Id},
   340  		},
   341  		{
   342  			Name:    "Get files including deleted filtered by channel and user",
   343  			Page:    0,
   344  			PerPage: 10,
   345  			Opt: &model.GetFileInfosOptions{
   346  				IncludeDeleted: true,
   347  				UserIds:        []string{userId1},
   348  				ChannelIds:     []string{channelId3},
   349  			},
   350  			ExpectedFileIds: []string{file1_2.Id},
   351  		},
   352  		{
   353  			Name:    "Get files including deleted sorted by created at",
   354  			Page:    0,
   355  			PerPage: 10,
   356  			Opt: &model.GetFileInfosOptions{
   357  				IncludeDeleted: true,
   358  				SortBy:         model.FILEINFO_SORT_BY_CREATED,
   359  			},
   360  			ExpectedFileIds: []string{file1_1.Id, file1_2.Id, file1_3.Id, file2_1.Id, file2_2.Id},
   361  		},
   362  		{
   363  			Name:    "Get files filtered by user ordered by created at descending",
   364  			Page:    0,
   365  			PerPage: 10,
   366  			Opt: &model.GetFileInfosOptions{
   367  				UserIds:        []string{userId1},
   368  				SortBy:         model.FILEINFO_SORT_BY_CREATED,
   369  				SortDescending: true,
   370  			},
   371  			ExpectedFileIds: []string{file1_3.Id, file1_2.Id, file1_1.Id},
   372  		},
   373  		{
   374  			Name:    "Get all files including deleted ordered by created descending 2nd page of 3 per page ",
   375  			Page:    1,
   376  			PerPage: 3,
   377  			Opt: &model.GetFileInfosOptions{
   378  				IncludeDeleted: true,
   379  				SortBy:         model.FILEINFO_SORT_BY_CREATED,
   380  				SortDescending: true,
   381  			},
   382  			ExpectedFileIds: []string{file1_2.Id, file1_1.Id},
   383  		},
   384  	}
   385  
   386  	for _, tc := range testCases {
   387  		t.Run(tc.Name, func(t *testing.T) {
   388  			fileInfos, err := ss.FileInfo().GetWithOptions(tc.Page, tc.PerPage, tc.Opt)
   389  			require.NoError(t, err)
   390  			require.Len(t, fileInfos, len(tc.ExpectedFileIds))
   391  			for i := range tc.ExpectedFileIds {
   392  				assert.Equal(t, tc.ExpectedFileIds[i], fileInfos[i].Id)
   393  			}
   394  		})
   395  	}
   396  }
   397  
   398  type byFileInfoId []*model.FileInfo
   399  
   400  func (a byFileInfoId) Len() int           { return len(a) }
   401  func (a byFileInfoId) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   402  func (a byFileInfoId) Less(i, j int) bool { return a[i].Id < a[j].Id }
   403  
   404  func testFileInfoAttachToPost(t *testing.T, ss store.Store) {
   405  	t.Run("should attach files", func(t *testing.T) {
   406  		userId := model.NewId()
   407  		postId := model.NewId()
   408  
   409  		info1, err := ss.FileInfo().Save(&model.FileInfo{
   410  			CreatorId: userId,
   411  			Path:      "file.txt",
   412  		})
   413  		require.NoError(t, err)
   414  		info2, err := ss.FileInfo().Save(&model.FileInfo{
   415  			CreatorId: userId,
   416  			Path:      "file2.txt",
   417  		})
   418  		require.NoError(t, err)
   419  
   420  		require.Equal(t, "", info1.PostId)
   421  		require.Equal(t, "", info2.PostId)
   422  
   423  		err = ss.FileInfo().AttachToPost(info1.Id, postId, userId)
   424  		assert.NoError(t, err)
   425  		info1.PostId = postId
   426  
   427  		err = ss.FileInfo().AttachToPost(info2.Id, postId, userId)
   428  		assert.NoError(t, err)
   429  		info2.PostId = postId
   430  
   431  		data, err := ss.FileInfo().GetForPost(postId, true, false, false)
   432  		require.NoError(t, err)
   433  
   434  		expected := []*model.FileInfo{info1, info2}
   435  		sort.Sort(byFileInfoId(expected))
   436  		sort.Sort(byFileInfoId(data))
   437  		assert.EqualValues(t, expected, data)
   438  	})
   439  
   440  	t.Run("should not attach files to multiple posts", func(t *testing.T) {
   441  		userId := model.NewId()
   442  		postId := model.NewId()
   443  
   444  		info, err := ss.FileInfo().Save(&model.FileInfo{
   445  			CreatorId: userId,
   446  			Path:      "file.txt",
   447  		})
   448  		require.NoError(t, err)
   449  
   450  		require.Equal(t, "", info.PostId)
   451  
   452  		err = ss.FileInfo().AttachToPost(info.Id, model.NewId(), userId)
   453  		require.NoError(t, err)
   454  
   455  		err = ss.FileInfo().AttachToPost(info.Id, postId, userId)
   456  		require.Error(t, err)
   457  	})
   458  
   459  	t.Run("should not attach files owned from a different user", func(t *testing.T) {
   460  		userId := model.NewId()
   461  		postId := model.NewId()
   462  
   463  		info, err := ss.FileInfo().Save(&model.FileInfo{
   464  			CreatorId: model.NewId(),
   465  			Path:      "file.txt",
   466  		})
   467  		require.NoError(t, err)
   468  
   469  		require.Equal(t, "", info.PostId)
   470  
   471  		err = ss.FileInfo().AttachToPost(info.Id, postId, userId)
   472  		assert.Error(t, err)
   473  	})
   474  
   475  	t.Run("should attach files uploaded by nouser", func(t *testing.T) {
   476  		postId := model.NewId()
   477  
   478  		info, err := ss.FileInfo().Save(&model.FileInfo{
   479  			CreatorId: "nouser",
   480  			Path:      "file.txt",
   481  		})
   482  		require.NoError(t, err)
   483  		assert.Equal(t, "", info.PostId)
   484  
   485  		err = ss.FileInfo().AttachToPost(info.Id, postId, model.NewId())
   486  		require.NoError(t, err)
   487  
   488  		data, err := ss.FileInfo().GetForPost(postId, true, false, false)
   489  		require.NoError(t, err)
   490  		info.PostId = postId
   491  		assert.EqualValues(t, []*model.FileInfo{info}, data)
   492  	})
   493  }
   494  
   495  func testFileInfoDeleteForPost(t *testing.T, ss store.Store) {
   496  	userId := model.NewId()
   497  	postId := model.NewId()
   498  
   499  	infos := []*model.FileInfo{
   500  		{
   501  			PostId:    postId,
   502  			CreatorId: userId,
   503  			Path:      "file.txt",
   504  		},
   505  		{
   506  			PostId:    postId,
   507  			CreatorId: userId,
   508  			Path:      "file.txt",
   509  		},
   510  		{
   511  			PostId:    postId,
   512  			CreatorId: userId,
   513  			Path:      "file.txt",
   514  			DeleteAt:  123,
   515  		},
   516  		{
   517  			PostId:    model.NewId(),
   518  			CreatorId: userId,
   519  			Path:      "file.txt",
   520  		},
   521  	}
   522  
   523  	for i, info := range infos {
   524  		newInfo, err := ss.FileInfo().Save(info)
   525  		require.NoError(t, err)
   526  		infos[i] = newInfo
   527  		defer func(id string) {
   528  			ss.FileInfo().PermanentDelete(id)
   529  		}(newInfo.Id)
   530  	}
   531  
   532  	_, err := ss.FileInfo().DeleteForPost(postId)
   533  	require.NoError(t, err)
   534  
   535  	infos, err = ss.FileInfo().GetForPost(postId, true, false, false)
   536  	require.NoError(t, err)
   537  	assert.Empty(t, infos)
   538  }
   539  
   540  func testFileInfoPermanentDelete(t *testing.T, ss store.Store) {
   541  	info, err := ss.FileInfo().Save(&model.FileInfo{
   542  		PostId:    model.NewId(),
   543  		CreatorId: model.NewId(),
   544  		Path:      "file.txt",
   545  	})
   546  	require.NoError(t, err)
   547  
   548  	err = ss.FileInfo().PermanentDelete(info.Id)
   549  	require.NoError(t, err)
   550  }
   551  
   552  func testFileInfoPermanentDeleteBatch(t *testing.T, ss store.Store) {
   553  	postId := model.NewId()
   554  
   555  	_, err := ss.FileInfo().Save(&model.FileInfo{
   556  		PostId:    postId,
   557  		CreatorId: model.NewId(),
   558  		Path:      "file.txt",
   559  		CreateAt:  1000,
   560  	})
   561  	require.NoError(t, err)
   562  
   563  	_, err = ss.FileInfo().Save(&model.FileInfo{
   564  		PostId:    postId,
   565  		CreatorId: model.NewId(),
   566  		Path:      "file.txt",
   567  		CreateAt:  1200,
   568  	})
   569  	require.NoError(t, err)
   570  
   571  	_, err = ss.FileInfo().Save(&model.FileInfo{
   572  		PostId:    postId,
   573  		CreatorId: model.NewId(),
   574  		Path:      "file.txt",
   575  		CreateAt:  2000,
   576  	})
   577  	require.NoError(t, err)
   578  
   579  	postFiles, err := ss.FileInfo().GetForPost(postId, true, false, false)
   580  	require.NoError(t, err)
   581  	assert.Len(t, postFiles, 3)
   582  
   583  	_, err = ss.FileInfo().PermanentDeleteBatch(1500, 1000)
   584  	require.NoError(t, err)
   585  
   586  	postFiles, err = ss.FileInfo().GetForPost(postId, true, false, false)
   587  	require.NoError(t, err)
   588  	assert.Len(t, postFiles, 1)
   589  }
   590  
   591  func testFileInfoPermanentDeleteByUser(t *testing.T, ss store.Store) {
   592  	userId := model.NewId()
   593  	postId := model.NewId()
   594  
   595  	_, err := ss.FileInfo().Save(&model.FileInfo{
   596  		PostId:    postId,
   597  		CreatorId: userId,
   598  		Path:      "file.txt",
   599  	})
   600  	require.NoError(t, err)
   601  
   602  	_, err = ss.FileInfo().PermanentDeleteByUser(userId)
   603  	require.NoError(t, err)
   604  }