// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. // See LICENSE.txt for license information. package app import ( "archive/zip" "bytes" "errors" "fmt" "image" "io" "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" "github.com/mattermost/mattermost/server/public/model" "github.com/mattermost/mattermost/server/public/shared/request" "github.com/mattermost/mattermost/server/v8/channels/store" storemocks "github.com/mattermost/mattermost/server/v8/channels/store/storetest/mocks" "github.com/mattermost/mattermost/server/v8/channels/utils/fileutils" eMocks "github.com/mattermost/mattermost/server/v8/einterfaces/mocks" "github.com/mattermost/mattermost/server/v8/platform/services/searchengine/mocks" ) func TestGeneratePublicLinkHash(t *testing.T) { mainHelper.Parallel(t) filename1 := model.NewId() + "/" + model.NewRandomString(16) + ".txt" filename2 := model.NewId() + "/" + model.NewRandomString(16) + ".txt" salt1 := model.NewRandomString(32) salt2 := model.NewRandomString(32) hash1 := GeneratePublicLinkHash(filename1, salt1) hash2 := GeneratePublicLinkHash(filename2, salt1) hash3 := GeneratePublicLinkHash(filename1, salt2) hash := GeneratePublicLinkHash(filename1, salt1) assert.Equal(t, hash, hash1, "hash should be equal for the same file name and salt") assert.NotEqual(t, hash1, hash2, "hashes for different files should not be equal") assert.NotEqual(t, hash1, hash3, "hashes for the same file with different salts should not be equal") } func TestDoUploadFile(t *testing.T) { mainHelper.Parallel(t) th := Setup(t) defer th.TearDown() teamID := model.NewId() channelID := model.NewId() userID := model.NewId() filename := "test" data := []byte("abcd") info1, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true) require.Nil(t, err, "DoUploadFile should succeed with valid data") defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info1.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info1.Path) require.Nil(t, appErr) }() value := fmt.Sprintf("20070204/teams/%v/channels/%v/users/%v/%v/%v", teamID, channelID, userID, info1.Id, filename) assert.Equal(t, value, info1.Path, "stored file at incorrect path") info2, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true) require.Nil(t, err, "DoUploadFile should succeed with valid data") defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info2.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info2.Path) require.Nil(t, appErr) }() value = fmt.Sprintf("20070204/teams/%v/channels/%v/users/%v/%v/%v", teamID, channelID, userID, info2.Id, filename) assert.Equal(t, value, info2.Path, "stored file at incorrect path") info3, err := th.App.DoUploadFile(th.Context, time.Date(2008, 3, 5, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true) require.Nil(t, err, "DoUploadFile should succeed with valid data") defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info3.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info3.Path) require.Nil(t, appErr) }() value = fmt.Sprintf("20080305/teams/%v/channels/%v/users/%v/%v/%v", teamID, channelID, userID, info3.Id, filename) assert.Equal(t, value, info3.Path, "stored file at incorrect path") info4, err := th.App.DoUploadFile(th.Context, time.Date(2009, 3, 5, 1, 2, 3, 4, time.Local), "../../"+teamID, "../../"+channelID, "../../"+userID, "../../"+filename, data, true) require.Nil(t, err, "DoUploadFile should succeed with valid data") defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info4.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info4.Path) require.Nil(t, appErr) }() value = fmt.Sprintf("20090305/teams/%v/channels/%v/users/%v/%v/%v", teamID, channelID, userID, info4.Id, filename) assert.Equal(t, value, info4.Path, "stored file at incorrect path") info5, err := th.App.DoUploadFile(th.Context, time.Date(2008, 3, 5, 1, 2, 3, 4, time.Local), teamID, channelID, model.BookmarkFileOwner, filename, data, true) require.Nil(t, err, "DoUploadFile should succeed with valid data") defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info5.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info5.Path) require.Nil(t, appErr) }() value = fmt.Sprintf("%v/teams/%v/channels/%v/%v/%v", model.BookmarkFileOwner, teamID, channelID, info5.Id, filename) assert.Equal(t, value, info5.Path, "stored file at incorrect path") } func TestUploadFile(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() channelID := th.BasicChannel.Id filename := "test" data := []byte("abcd") info1, err := th.App.UploadFile(th.Context, data, "wrong", filename) require.NotNil(t, err, "Wrong Channel ID.") require.Nil(t, info1, "Channel ID does not exist.") info1, err = th.App.UploadFile(th.Context, data, "", filename) require.Nil(t, err, "empty channel IDs should be valid") require.NotNil(t, info1) info1, err = th.App.UploadFile(th.Context, data, channelID, filename) require.Nil(t, err, "UploadFile should succeed with valid data") defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info1.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info1.Path) require.Nil(t, appErr) }() value := fmt.Sprintf("%v/teams/noteam/channels/%v/users/nouser/%v/%v", time.Now().Format("20060102"), channelID, info1.Id, filename) assert.Equal(t, value, info1.Path, "Stored file at incorrect path") } func TestParseOldFilenames(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() fileID := model.NewId() tests := []struct { description string filenames []string channelID string userID string expected [][]string }{ { description: "Empty input should result in empty output", filenames: []string{}, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{}, }, { description: "Filename with invalid format should not parse", filenames: []string{"/path/to/some/file.png"}, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{}, }, { description: "ChannelId in Filename should not match", filenames: []string{ fmt.Sprintf("/%v/%v/%v/file.png", model.NewId(), th.BasicUser.Id, fileID), }, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{}, }, { description: "UserId in Filename should not match", filenames: []string{ fmt.Sprintf("/%v/%v/%v/file.png", th.BasicChannel.Id, model.NewId(), fileID), }, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{}, }, { description: "../ in filename should not parse", filenames: []string{ fmt.Sprintf("/%v/%v/%v/../../../file.png", th.BasicChannel.Id, th.BasicUser.Id, fileID), }, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{}, }, { description: "Should only parse valid filenames", filenames: []string{ fmt.Sprintf("/%v/%v/%v/../otherfile.png", th.BasicChannel.Id, th.BasicUser.Id, fileID), fmt.Sprintf("/%v/%v/%v/file.png", th.BasicChannel.Id, th.BasicUser.Id, fileID), }, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{ { th.BasicChannel.Id, th.BasicUser.Id, fileID, "file.png", }, }, }, { description: "Valid Filename should parse", filenames: []string{ fmt.Sprintf("/%v/%v/%v/file.png", th.BasicChannel.Id, th.BasicUser.Id, fileID), }, channelID: th.BasicChannel.Id, userID: th.BasicUser.Id, expected: [][]string{ { th.BasicChannel.Id, th.BasicUser.Id, fileID, "file.png", }, }, }, } for _, test := range tests { t.Run(test.description, func(tt *testing.T) { result := parseOldFilenames(th.Context, test.filenames, test.channelID, test.userID) require.Equal(tt, result, test.expected) }) } } func TestGetInfoForFilename(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() post := th.BasicPost teamID := th.BasicTeam.Id info := th.App.getInfoForFilename(th.Context, post, teamID, post.ChannelId, post.UserId, "someid", "somefile.png") assert.Nil(t, info, "Test non-existent file") } func TestFindTeamIdForFilename(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() teamID := th.App.findTeamIdForFilename(th.Context, th.BasicPost, "someid", "somefile.png") assert.Equal(t, th.BasicTeam.Id, teamID) _, err := th.App.CreateTeamWithUser(th.Context, &model.Team{Email: th.BasicUser.Email, Name: "zz" + model.NewId(), DisplayName: "Joram's Test Team", Type: model.TeamOpen}, th.BasicUser.Id) require.Nil(t, err) teamID = th.App.findTeamIdForFilename(th.Context, th.BasicPost, "someid", "somefile.png") assert.Equal(t, "", teamID) } func TestMigrateFilenamesToFileInfos(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() post := th.BasicPost infos := th.App.MigrateFilenamesToFileInfos(th.Context, post) assert.Equal(t, 0, len(infos)) post.Filenames = []string{fmt.Sprintf("/%v/%v/%v/blargh.png", th.BasicChannel.Id, th.BasicUser.Id, "someid")} infos = th.App.MigrateFilenamesToFileInfos(th.Context, post) assert.Equal(t, 0, len(infos)) path, _ := fileutils.FindDir("tests") file, fileErr := os.Open(filepath.Join(path, "test.png")) require.NoError(t, fileErr) defer file.Close() fileID := model.NewId() fpath := fmt.Sprintf("/teams/%v/channels/%v/users/%v/%v/test.png", th.BasicTeam.Id, th.BasicChannel.Id, th.BasicUser.Id, fileID) _, err := th.App.WriteFile(file, fpath) require.Nil(t, err) rpost, err := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/test.png", th.BasicChannel.Id, th.BasicUser.Id, fileID)}}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) infos = th.App.MigrateFilenamesToFileInfos(th.Context, rpost) assert.Equal(t, 1, len(infos)) rpost, err = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, ChannelId: th.BasicChannel.Id, Filenames: []string{fmt.Sprintf("/%v/%v/%v/../../test.png", th.BasicChannel.Id, th.BasicUser.Id, fileID)}}, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) require.Nil(t, err) infos = th.App.MigrateFilenamesToFileInfos(th.Context, rpost) assert.Equal(t, 0, len(infos)) } func TestWriteZipFile(t *testing.T) { mainHelper.Parallel(t) th := Setup(t) defer th.TearDown() t.Run("write no file", func(t *testing.T) { buf := new(bytes.Buffer) err := th.App.WriteZipFile(buf, []model.FileData{}) require.NoError(t, err) // Verify it's a valid zip file reader := bytes.NewReader(buf.Bytes()) z, err := zip.NewReader(reader, int64(buf.Len())) require.NoError(t, err) require.Len(t, z.File, 0) }) t.Run("write one file", func(t *testing.T) { buf := new(bytes.Buffer) err := th.App.WriteZipFile(buf, []model.FileData{ { Filename: "file1.txt", Body: []byte("content1"), }, }) require.NoError(t, err) // Verify the zip contents reader := bytes.NewReader(buf.Bytes()) z, err := zip.NewReader(reader, int64(buf.Len())) require.NoError(t, err) require.Len(t, z.File, 1) file := z.File[0] assert.Equal(t, "file1.txt", file.Name) now := time.Now().Truncate(time.Second) // Files are stored with a second precision // Confirm that the file was created in the last 10 seconds assert.GreaterOrEqual(t, file.Modified, now.Add(-10*time.Second)) assert.GreaterOrEqual(t, now, file.Modified) // Check file content fr, err := file.Open() require.NoError(t, err) t.Cleanup(func() { err = fr.Close() require.NoError(t, err) }) content, err := io.ReadAll(fr) require.NoError(t, err) assert.Equal(t, []byte("content1"), content) }) t.Run("write multiple files", func(t *testing.T) { buf := new(bytes.Buffer) fileDatas := []model.FileData{ { Filename: "file1.txt", Body: []byte("content1"), }, { Filename: "file2.txt", Body: []byte("content2"), }, { Filename: "dir/file3.txt", Body: []byte("content3"), }, } err := th.App.WriteZipFile(buf, fileDatas) require.NoError(t, err) // Verify the zip contents reader := bytes.NewReader(buf.Bytes()) z, err := zip.NewReader(reader, int64(buf.Len())) require.NoError(t, err) require.Len(t, z.File, 3) // Check each file for i, zf := range z.File { assert.Equal(t, fileDatas[i].Filename, zf.Name) fr, err := zf.Open() require.NoError(t, err) content, err := io.ReadAll(fr) require.NoError(t, err) assert.Equal(t, fileDatas[i].Body, content) err = fr.Close() require.NoError(t, err) } }) } func TestCopyFileInfos(t *testing.T) { mainHelper.Parallel(t) th := Setup(t) defer th.TearDown() teamID := model.NewId() channelID := model.NewId() userID := model.NewId() filename := "test" data := []byte("abcd") info1, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true) require.Nil(t, err) defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info1.Id) require.NoError(t, err) }() infoIds, err := th.App.CopyFileInfos(th.Context, userID, []string{info1.Id}) require.Nil(t, err) info2, err := th.App.GetFileInfo(th.Context, infoIds[0]) require.Nil(t, err) defer func() { err := th.App.Srv().Store().FileInfo().PermanentDelete(th.Context, info2.Id) require.NoError(t, err) appErr := th.App.RemoveFile(info2.Path) require.Nil(t, appErr) }() assert.NotEqual(t, info1.Id, info2.Id, "should not be equal") assert.Equal(t, info2.PostId, "", "should be empty string") } func TestGenerateThumbnailImage(t *testing.T) { mainHelper.Parallel(t) t.Run("test generating thumbnail image", func(t *testing.T) { // given th := Setup(t) defer th.TearDown() img := createDummyImage() dataPath := *th.App.Config().FileSettings.Directory thumbnailName := "thumb.jpg" thumbnailPath := filepath.Join(dataPath, thumbnailName) // when th.App.generateThumbnailImage(th.Context, img, "jpg", thumbnailName) defer os.Remove(thumbnailPath) // then outputImage, err := os.Stat(thumbnailPath) assert.NoError(t, err) assert.Equal(t, int64(721), outputImage.Size()) }) } func createDummyImage() *image.RGBA { width := 200 height := 100 upperLeftCorner := image.Point{0, 0} lowerRightCorner := image.Point{width, height} return image.NewRGBA(image.Rectangle{upperLeftCorner, lowerRightCorner}) } func TestSearchFilesInTeamForUser(t *testing.T) { mainHelper.Parallel(t) perPage := 5 searchTerm := "searchTerm" setup := func(t *testing.T, enableElasticsearch bool) (*TestHelper, []*model.FileInfo) { th := Setup(t).InitBasic() fileInfos := make([]*model.FileInfo, 7) for i := 0; i < cap(fileInfos); i++ { fileInfo, err := th.App.Srv().Store().FileInfo().Save(th.Context, &model.FileInfo{ CreatorId: th.BasicUser.Id, PostId: th.BasicPost.Id, ChannelId: th.BasicPost.ChannelId, Name: searchTerm, Path: searchTerm, Extension: "jpg", MimeType: "image/jpeg", }) time.Sleep(1 * time.Millisecond) require.NoError(t, err) fileInfos[i] = fileInfo } if enableElasticsearch { th.App.Srv().SetLicense(model.NewTestLicense("elastic_search")) th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ElasticsearchSettings.EnableIndexing = true *cfg.ElasticsearchSettings.EnableSearching = true }) } else { th.App.UpdateConfig(func(cfg *model.Config) { *cfg.ElasticsearchSettings.EnableSearching = false }) } return th, fileInfos } t.Run("should return everything as first page of fileInfos from database", func(t *testing.T) { th, fileInfos := setup(t, false) defer th.TearDown() page := 0 results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, []string{ fileInfos[6].Id, fileInfos[5].Id, fileInfos[4].Id, fileInfos[3].Id, fileInfos[2].Id, fileInfos[1].Id, fileInfos[0].Id, }, results.Order) }) t.Run("should not return later pages of fileInfos from database", func(t *testing.T) { th, _ := setup(t, false) defer th.TearDown() page := 1 results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, []string{}, results.Order) }) t.Run("should return first page of fileInfos from ElasticSearch", func(t *testing.T) { th, fileInfos := setup(t, true) defer th.TearDown() page := 0 resultsPage := []string{ fileInfos[6].Id, fileInfos[5].Id, fileInfos[4].Id, fileInfos[3].Id, fileInfos[2].Id, } es := &mocks.SearchEngineInterface{} es.On("SearchFiles", mock.Anything, mock.Anything, page, perPage).Return(resultsPage, nil) es.On("Start").Return(nil).Maybe() es.On("IsActive").Return(true) es.On("IsSearchEnabled").Return(true) th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es defer func() { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, resultsPage, results.Order) es.AssertExpectations(t) }) t.Run("should return later pages of fileInfos from ElasticSearch", func(t *testing.T) { th, fileInfos := setup(t, true) defer th.TearDown() page := 1 resultsPage := []string{ fileInfos[1].Id, fileInfos[0].Id, } es := &mocks.SearchEngineInterface{} es.On("SearchFiles", mock.Anything, mock.Anything, page, perPage).Return(resultsPage, nil) es.On("Start").Return(nil).Maybe() es.On("IsActive").Return(true) es.On("IsSearchEnabled").Return(true) th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es defer func() { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, resultsPage, results.Order) es.AssertExpectations(t) }) t.Run("should fall back to database if ElasticSearch fails on first page", func(t *testing.T) { th, fileInfos := setup(t, true) defer th.TearDown() page := 0 es := &mocks.SearchEngineInterface{} es.On("SearchFiles", mock.Anything, mock.Anything, page, perPage).Return(nil, &model.AppError{}) es.On("GetName").Return("mock") es.On("Start").Return(nil).Maybe() es.On("IsActive").Return(true) es.On("IsSearchEnabled").Return(true) th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es defer func() { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) require.NotNil(t, results) assert.Equal(t, []string{ fileInfos[6].Id, fileInfos[5].Id, fileInfos[4].Id, fileInfos[3].Id, fileInfos[2].Id, fileInfos[1].Id, fileInfos[0].Id, }, results.Order) es.AssertExpectations(t) }) t.Run("should return nothing if ElasticSearch fails on later pages", func(t *testing.T) { th, _ := setup(t, true) defer th.TearDown() page := 1 es := &mocks.SearchEngineInterface{} es.On("SearchFiles", mock.Anything, mock.Anything, page, perPage).Return(nil, &model.AppError{}) es.On("GetName").Return("mock") es.On("Start").Return(nil).Maybe() es.On("IsActive").Return(true) es.On("IsSearchEnabled").Return(true) th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = es defer func() { th.App.Srv().Platform().SearchEngine.ElasticsearchEngine = nil }() results, err := th.App.SearchFilesInTeamForUser(th.Context, searchTerm, th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, page, perPage) require.Nil(t, err) assert.Equal(t, []string{}, results.Order) es.AssertExpectations(t) }) } func TestExtractContentFromFileInfo(t *testing.T) { mainHelper.Parallel(t) app := &App{} fi := &model.FileInfo{ MimeType: "image/jpeg", } // Test that we don't process images. require.NoError(t, app.ExtractContentFromFileInfo(request.TestContext(t), fi)) } func TestGetLastAccessibleFileTime(t *testing.T) { mainHelper.Parallel(t) th := SetupWithStoreMock(t) defer th.TearDown() r, err := th.App.GetLastAccessibleFileTime() require.Nil(t, err) assert.Equal(t, int64(0), r) th.App.Srv().SetLicense(model.NewTestLicense("cloud")) mockStore := th.App.Srv().Store().(*storemocks.Store) mockSystemStore := storemocks.SystemStore{} mockStore.On("System").Return(&mockSystemStore) mockSystemStore.On("GetByName", mock.Anything).Return(nil, store.NewErrNotFound("", "")) r, err = th.App.GetLastAccessibleFileTime() require.Nil(t, err) assert.Equal(t, int64(0), r) mockSystemStore = storemocks.SystemStore{} mockStore.On("System").Return(&mockSystemStore) mockSystemStore.On("GetByName", mock.Anything).Return(nil, errors.New("test")) _, err = th.App.GetLastAccessibleFileTime() require.NotNil(t, err) mockSystemStore = storemocks.SystemStore{} mockStore.On("System").Return(&mockSystemStore) mockSystemStore.On("GetByName", mock.Anything).Return(&model.System{Name: model.SystemLastAccessibleFileTime, Value: "10"}, nil) r, err = th.App.GetLastAccessibleFileTime() require.Nil(t, err) assert.Equal(t, int64(10), r) } func TestComputeLastAccessibleFileTime(t *testing.T) { mainHelper.Parallel(t) t.Run("Updates the time, if cloud limit is applicable", func(t *testing.T) { th := SetupWithStoreMock(t) defer th.TearDown() th.App.Srv().SetLicense(model.NewTestLicense("cloud")) cloud := &eMocks.CloudInterface{} th.App.Srv().Cloud = cloud cloud.Mock.On("GetCloudLimits", mock.Anything).Return(&model.ProductLimits{ Files: &model.FilesLimits{ TotalStorage: model.NewPointer(int64(1)), }, }, nil) mockStore := th.App.Srv().Store().(*storemocks.Store) mockFileStore := storemocks.FileInfoStore{} mockFileStore.On("GetUptoNSizeFileTime", mock.Anything).Return(int64(1), nil) mockSystemStore := storemocks.SystemStore{} mockSystemStore.On("SaveOrUpdate", mock.Anything).Return(nil) mockStore.On("FileInfo").Return(&mockFileStore) mockStore.On("System").Return(&mockSystemStore) err := th.App.ComputeLastAccessibleFileTime() require.NoError(t, err) mockSystemStore.AssertCalled(t, "SaveOrUpdate", mock.Anything) }) t.Run("Removes the time, if cloud limit is not applicable", func(t *testing.T) { th := SetupWithStoreMock(t) defer th.TearDown() th.App.Srv().SetLicense(model.NewTestLicense("cloud")) cloud := &eMocks.CloudInterface{} th.App.Srv().Cloud = cloud cloud.Mock.On("GetCloudLimits", mock.Anything).Return(nil, nil) mockStore := th.App.Srv().Store().(*storemocks.Store) mockFileStore := storemocks.FileInfoStore{} mockFileStore.On("GetUptoNSizeFileTime", mock.Anything).Return(int64(1), nil) mockSystemStore := storemocks.SystemStore{} mockSystemStore.On("GetByName", mock.Anything).Return(&model.System{Name: model.SystemLastAccessibleFileTime, Value: "10"}, nil) mockSystemStore.On("PermanentDeleteByName", mock.Anything).Return(nil, nil) mockSystemStore.On("SaveOrUpdate", mock.Anything).Return(nil) mockStore.On("FileInfo").Return(&mockFileStore) mockStore.On("System").Return(&mockSystemStore) err := th.App.ComputeLastAccessibleFileTime() require.NoError(t, err) mockSystemStore.AssertNotCalled(t, "SaveOrUpdate", mock.Anything) mockSystemStore.AssertCalled(t, "PermanentDeleteByName", mock.Anything) }) } func TestSetFileSearchableContent(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() fileInfo, err := th.App.Srv().Store().FileInfo().Save(th.Context, &model.FileInfo{ CreatorId: th.BasicUser.Id, PostId: th.BasicPost.Id, ChannelId: th.BasicPost.ChannelId, Name: "test", Path: "test", Extension: "jpg", MimeType: "image/jpeg", }) require.NoError(t, err) result, appErr := th.App.SearchFilesInTeamForUser(th.Context, "searchable", th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, 0, 60) require.Nil(t, appErr) assert.Equal(t, 0, len(result.Order)) appErr = th.App.SetFileSearchableContent(th.Context, fileInfo.Id, "searchable") require.Nil(t, appErr) result, appErr = th.App.SearchFilesInTeamForUser(th.Context, "searchable", th.BasicUser.Id, th.BasicTeam.Id, false, false, 0, 0, 60) require.Nil(t, appErr) assert.Equal(t, 1, len(result.Order)) } func TestPermanentDeleteFilesByPost(t *testing.T) { mainHelper.Parallel(t) th := Setup(t).InitBasic() defer th.TearDown() t.Run("should delete files for post", func(t *testing.T) { // Create a post with a file attachment. teamID := th.BasicTeam.Id channelID := th.BasicChannel.Id userID := th.BasicUser.Id filename := "test" data := []byte("abcd") info1, err := th.App.DoUploadFile(th.Context, time.Date(2007, 2, 4, 1, 2, 3, 4, time.Local), teamID, channelID, userID, filename, data, true) require.Nil(t, err) post := &model.Post{ Message: "asd", ChannelId: channelID, PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), UserId: userID, CreateAt: 0, FileIds: []string{info1.Id}, } post, err = th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) err = th.App.PermanentDeleteFilesByPost(th.Context, post.Id) require.Nil(t, err) _, err = th.App.GetFileInfo(th.Context, info1.Id) require.NotNil(t, err) }) t.Run("should not delete files for post that doesn't exist", func(t *testing.T) { err := th.App.PermanentDeleteFilesByPost(th.Context, "postId1") assert.Nil(t, err) }) t.Run("should handle empty file list", func(t *testing.T) { post := &model.Post{ Message: "asd", ChannelId: th.BasicChannel.Id, PendingPostId: model.NewId() + ":" + fmt.Sprint(model.GetMillis()), UserId: th.BasicUser.Id, CreateAt: 0, } post, err := th.App.CreatePost(th.Context, post, th.BasicChannel, model.CreatePostFlags{SetOnline: true}) assert.Nil(t, err) err = th.App.PermanentDeleteFilesByPost(th.Context, post.Id) assert.Nil(t, err) }) }