Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1542 lines
43 KiB
Go
1542 lines
43 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package storetest
|
|
|
|
import (
|
|
"fmt"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/public/shared/request"
|
|
"github.com/mattermost/mattermost/server/v8/channels/store"
|
|
"github.com/mattermost/mattermost/server/v8/channels/utils"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestFileInfoStore(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
t.Cleanup(func() {
|
|
s.GetMaster().Exec("TRUNCATE FileInfo")
|
|
})
|
|
t.Run("FileInfoSaveGet", func(t *testing.T) { testFileInfoSaveGet(t, rctx, ss) })
|
|
t.Run("FileInfoSaveGetByPath", func(t *testing.T) { testFileInfoSaveGetByPath(t, rctx, ss) })
|
|
t.Run("FileInfoGetForPost", func(t *testing.T) { testFileInfoGetForPost(t, rctx, ss) })
|
|
t.Run("FileInfoGetForUser", func(t *testing.T) { testFileInfoGetForUser(t, rctx, ss) })
|
|
t.Run("FileInfoGetWithOptions", func(t *testing.T) { testFileInfoGetWithOptions(t, rctx, ss) })
|
|
t.Run("FileInfoAttachToPost", func(t *testing.T) { testFileInfoAttachToPost(t, rctx, ss) })
|
|
t.Run("FileInfoDeleteForPost", func(t *testing.T) { testFileInfoDeleteForPost(t, rctx, ss) })
|
|
t.Run("FileInfoPermanentDelete", func(t *testing.T) { testFileInfoPermanentDelete(t, rctx, ss) })
|
|
t.Run("FileInfoPermanentDeleteBatch", func(t *testing.T) { testFileInfoPermanentDeleteBatch(t, rctx, ss) })
|
|
t.Run("FileInfoPermanentDeleteByUser", func(t *testing.T) { testFileInfoPermanentDeleteByUser(t, rctx, ss) })
|
|
t.Run("FileInfoUpdateMinipreview", func(t *testing.T) { testFileInfoUpdateMinipreview(t, rctx, ss) })
|
|
t.Run("GetFilesBatchForIndexing", func(t *testing.T) { testFileInfoStoreGetFilesBatchForIndexing(t, rctx, ss) })
|
|
t.Run("CountAll", func(t *testing.T) { testFileInfoStoreCountAll(t, rctx, ss) })
|
|
t.Run("GetStorageUsage", func(t *testing.T) { testFileInfoGetStorageUsage(t, rctx, ss) })
|
|
t.Run("GetUptoNSizeFileTime", func(t *testing.T) { testGetUptoNSizeFileTime(t, rctx, ss, s) })
|
|
t.Run("FileInfoPermanentDeleteForPost", func(t *testing.T) { testPermanentDeleteForPost(t, rctx, ss) })
|
|
t.Run("FileInfoGetByIds", func(t *testing.T) { testGetByIds(t, rctx, ss) })
|
|
t.Run("FileInfoDeleteForPostByIds", func(t *testing.T) { testDeleteForPostByIds(t, rctx, ss) })
|
|
t.Run("FileInfoRestoreForPostByIds", func(t *testing.T) { testRestoreUndeleteForPostByIds(t, rctx, ss) })
|
|
}
|
|
|
|
func testFileInfoSaveGet(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
info := &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
}
|
|
|
|
info, err := ss.FileInfo().Save(rctx, info)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info.Id), 0)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info.Id)
|
|
}()
|
|
|
|
rinfo, err := ss.FileInfo().Get(info.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, info.Id, rinfo.Id)
|
|
|
|
info2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
DeleteAt: 123,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().Get(info2.Id)
|
|
assert.Error(t, err)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info2.Id)
|
|
}()
|
|
}
|
|
|
|
func testFileInfoSaveGetByPath(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
info := &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: fmt.Sprintf("%v/file.txt", model.NewId()),
|
|
}
|
|
|
|
info, err := ss.FileInfo().Save(rctx, info)
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, len(info.Id), 0)
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info.Id)
|
|
}()
|
|
|
|
rinfo, err := ss.FileInfo().GetByPath(info.Path)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, info.Id, rinfo.Id)
|
|
|
|
info2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
DeleteAt: 123,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().GetByPath(info2.Id)
|
|
assert.Error(t, err)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info2.Id)
|
|
}()
|
|
}
|
|
|
|
func testFileInfoGetForPost(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userID := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
infos := []*model.FileInfo{
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
DeleteAt: 123,
|
|
},
|
|
{
|
|
PostId: model.NewId(),
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
}
|
|
|
|
for i, info := range infos {
|
|
newInfo, err := ss.FileInfo().Save(rctx, info)
|
|
require.NoError(t, err)
|
|
infos[i] = newInfo
|
|
defer func(id string) {
|
|
ss.FileInfo().PermanentDelete(rctx, id)
|
|
}(newInfo.Id)
|
|
}
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
PostID string
|
|
ReadFromMaster bool
|
|
IncludeDeleted bool
|
|
AllowFromCache bool
|
|
ExpectedPosts int
|
|
}{
|
|
{
|
|
Name: "Fetch from master, without deleted and without cache",
|
|
PostID: postID,
|
|
ReadFromMaster: true,
|
|
IncludeDeleted: false,
|
|
AllowFromCache: false,
|
|
ExpectedPosts: 2,
|
|
},
|
|
{
|
|
Name: "Fetch from master, with deleted and without cache",
|
|
PostID: postID,
|
|
ReadFromMaster: true,
|
|
IncludeDeleted: true,
|
|
AllowFromCache: false,
|
|
ExpectedPosts: 3,
|
|
},
|
|
{
|
|
Name: "Fetch from master, with deleted and with cache",
|
|
PostID: postID,
|
|
ReadFromMaster: true,
|
|
IncludeDeleted: true,
|
|
AllowFromCache: true,
|
|
ExpectedPosts: 3,
|
|
},
|
|
{
|
|
Name: "Fetch from replica, without deleted and without cache",
|
|
PostID: postID,
|
|
ReadFromMaster: false,
|
|
IncludeDeleted: false,
|
|
AllowFromCache: false,
|
|
ExpectedPosts: 2,
|
|
},
|
|
{
|
|
Name: "Fetch from replica, with deleted and without cache",
|
|
PostID: postID,
|
|
ReadFromMaster: false,
|
|
IncludeDeleted: true,
|
|
AllowFromCache: false,
|
|
ExpectedPosts: 3,
|
|
},
|
|
{
|
|
Name: "Fetch from replica, with deleted and without cache",
|
|
PostID: postID,
|
|
ReadFromMaster: false,
|
|
IncludeDeleted: true,
|
|
AllowFromCache: true,
|
|
ExpectedPosts: 3,
|
|
},
|
|
{
|
|
Name: "Fetch from replica, without deleted and with cache",
|
|
PostID: postID,
|
|
ReadFromMaster: true,
|
|
IncludeDeleted: false,
|
|
AllowFromCache: true,
|
|
ExpectedPosts: 2,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
postInfos, err := ss.FileInfo().GetForPost(
|
|
tc.PostID,
|
|
tc.ReadFromMaster,
|
|
tc.IncludeDeleted,
|
|
tc.AllowFromCache,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Len(t, postInfos, tc.ExpectedPosts)
|
|
})
|
|
}
|
|
}
|
|
|
|
func testFileInfoGetForUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userID := model.NewId()
|
|
userID2 := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
infos := []*model.FileInfo{
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: model.NewId(),
|
|
ChannelId: channelID,
|
|
CreatorId: userID2,
|
|
Path: "file.txt",
|
|
},
|
|
}
|
|
|
|
for i, info := range infos {
|
|
newInfo, err := ss.FileInfo().Save(rctx, info)
|
|
require.NoError(t, err)
|
|
infos[i] = newInfo
|
|
defer func(id string) {
|
|
ss.FileInfo().PermanentDelete(rctx, id)
|
|
}(newInfo.Id)
|
|
}
|
|
|
|
userPosts, err := ss.FileInfo().GetForUser(userID)
|
|
require.NoError(t, err)
|
|
assert.Len(t, userPosts, 3)
|
|
|
|
userPosts, err = ss.FileInfo().GetForUser(userID2)
|
|
require.NoError(t, err)
|
|
assert.Len(t, userPosts, 1)
|
|
}
|
|
|
|
func testFileInfoGetWithOptions(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
makePost := func(chId string, user string) *model.Post {
|
|
post := model.Post{}
|
|
post.ChannelId = chId
|
|
post.UserId = user
|
|
_, err := ss.Post().Save(rctx, &post)
|
|
require.NoError(t, err)
|
|
return &post
|
|
}
|
|
|
|
makeFile := func(post *model.Post, user string, createAt int64, idPrefix string) model.FileInfo {
|
|
id := model.NewId()
|
|
id = idPrefix + id[1:] // hacky way to get sortable Ids to confirm secondary Id sort works
|
|
fileInfo := model.FileInfo{
|
|
Id: id,
|
|
CreatorId: user,
|
|
Path: "file.txt",
|
|
CreateAt: createAt,
|
|
}
|
|
if post.Id != "" {
|
|
fileInfo.PostId = post.Id
|
|
}
|
|
if post.ChannelId != "" {
|
|
fileInfo.ChannelId = post.ChannelId
|
|
}
|
|
_, err := ss.FileInfo().Save(rctx, &fileInfo)
|
|
require.NoError(t, err)
|
|
return fileInfo
|
|
}
|
|
|
|
userID1 := model.NewId()
|
|
userID2 := model.NewId()
|
|
|
|
channelID1 := model.NewId()
|
|
channelID2 := model.NewId()
|
|
channelID3 := model.NewId()
|
|
|
|
post1_1 := makePost(channelID1, userID1) // post 1 by user 1
|
|
post1_2 := makePost(channelID3, userID1) // post 2 by user 1
|
|
post2_1 := makePost(channelID2, userID2)
|
|
post2_2 := makePost(channelID3, userID2)
|
|
|
|
epoch := time.Date(2020, 1, 1, 1, 1, 1, 1, time.UTC)
|
|
file1_1 := makeFile(post1_1, userID1, epoch.AddDate(0, 0, 1).Unix(), "a") // file 1 by user 1
|
|
file1_2 := makeFile(post1_2, userID1, epoch.AddDate(0, 0, 2).Unix(), "b") // file 2 by user 1
|
|
file1_3 := makeFile(&model.Post{}, userID1, epoch.AddDate(0, 0, 3).Unix(), "c") // file that is not attached to a post
|
|
file2_1 := makeFile(post2_1, userID2, epoch.AddDate(0, 0, 4).Unix(), "d") // file 2 by user 1
|
|
file2_2 := makeFile(post2_2, userID2, epoch.AddDate(0, 0, 5).Unix(), "e")
|
|
|
|
// delete a file
|
|
_, err := ss.FileInfo().DeleteForPost(rctx, file2_2.PostId)
|
|
require.NoError(t, err)
|
|
|
|
testCases := []struct {
|
|
Name string
|
|
Page, PerPage int
|
|
Opt *model.GetFileInfosOptions
|
|
ExpectedFileIds []string
|
|
}{
|
|
{
|
|
Name: "Get files with nil option",
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Opt: nil,
|
|
ExpectedFileIds: []string{file1_1.Id, file1_2.Id, file1_3.Id, file2_1.Id},
|
|
},
|
|
{
|
|
Name: "Get files including deleted",
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Opt: &model.GetFileInfosOptions{IncludeDeleted: true},
|
|
ExpectedFileIds: []string{file1_1.Id, file1_2.Id, file1_3.Id, file2_1.Id, file2_2.Id},
|
|
},
|
|
{
|
|
Name: "Get files including deleted filtered by channel",
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Opt: &model.GetFileInfosOptions{
|
|
IncludeDeleted: true,
|
|
ChannelIds: []string{channelID3},
|
|
},
|
|
ExpectedFileIds: []string{file1_2.Id, file2_2.Id},
|
|
},
|
|
{
|
|
Name: "Get files including deleted filtered by channel and user",
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Opt: &model.GetFileInfosOptions{
|
|
IncludeDeleted: true,
|
|
UserIds: []string{userID1},
|
|
ChannelIds: []string{channelID3},
|
|
},
|
|
ExpectedFileIds: []string{file1_2.Id},
|
|
},
|
|
{
|
|
Name: "Get files including deleted sorted by created at",
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Opt: &model.GetFileInfosOptions{
|
|
IncludeDeleted: true,
|
|
SortBy: model.FileinfoSortByCreated,
|
|
},
|
|
ExpectedFileIds: []string{file1_1.Id, file1_2.Id, file1_3.Id, file2_1.Id, file2_2.Id},
|
|
},
|
|
{
|
|
Name: "Get files filtered by user ordered by created at descending",
|
|
Page: 0,
|
|
PerPage: 10,
|
|
Opt: &model.GetFileInfosOptions{
|
|
UserIds: []string{userID1},
|
|
SortBy: model.FileinfoSortByCreated,
|
|
SortDescending: true,
|
|
},
|
|
ExpectedFileIds: []string{file1_3.Id, file1_2.Id, file1_1.Id},
|
|
},
|
|
{
|
|
Name: "Get all files including deleted ordered by created descending 2nd page of 3 per page ",
|
|
Page: 1,
|
|
PerPage: 3,
|
|
Opt: &model.GetFileInfosOptions{
|
|
IncludeDeleted: true,
|
|
SortBy: model.FileinfoSortByCreated,
|
|
SortDescending: true,
|
|
},
|
|
ExpectedFileIds: []string{file1_2.Id, file1_1.Id},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.Name, func(t *testing.T) {
|
|
fileInfos, err := ss.FileInfo().GetWithOptions(tc.Page, tc.PerPage, tc.Opt)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos, len(tc.ExpectedFileIds))
|
|
for i := range tc.ExpectedFileIds {
|
|
assert.Equal(t, tc.ExpectedFileIds[i], fileInfos[i].Id)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type byFileInfoID []*model.FileInfo
|
|
|
|
func (a byFileInfoID) Len() int { return len(a) }
|
|
func (a byFileInfoID) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a byFileInfoID) Less(i, j int) bool { return a[i].Id < a[j].Id }
|
|
|
|
func testFileInfoAttachToPost(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("should attach files", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
info1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
info2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: userID,
|
|
Path: "file2.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "", info1.PostId)
|
|
require.Equal(t, "", info2.PostId)
|
|
|
|
err = ss.FileInfo().AttachToPost(rctx, info1.Id, postID, channelID, userID)
|
|
assert.NoError(t, err)
|
|
info1.PostId = postID
|
|
info1.ChannelId = channelID
|
|
|
|
err = ss.FileInfo().AttachToPost(rctx, info2.Id, postID, channelID, userID)
|
|
assert.NoError(t, err)
|
|
info2.PostId = postID
|
|
info2.ChannelId = channelID
|
|
|
|
data, err := ss.FileInfo().GetForPost(postID, true, false, false)
|
|
require.NoError(t, err)
|
|
|
|
expected := []*model.FileInfo{info1, info2}
|
|
sort.Sort(byFileInfoID(expected))
|
|
sort.Sort(byFileInfoID(data))
|
|
assert.EqualValues(t, expected, data)
|
|
})
|
|
|
|
t.Run("should not attach files to multiple posts", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
info, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "", info.PostId)
|
|
|
|
err = ss.FileInfo().AttachToPost(rctx, info.Id, model.NewId(), channelID, userID)
|
|
require.NoError(t, err)
|
|
|
|
err = ss.FileInfo().AttachToPost(rctx, info.Id, postID, channelID, userID)
|
|
require.Error(t, err)
|
|
})
|
|
|
|
t.Run("should not attach files owned from a different user", func(t *testing.T) {
|
|
userID := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
info, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, "", info.PostId)
|
|
|
|
err = ss.FileInfo().AttachToPost(rctx, info.Id, postID, channelID, userID)
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("should attach files uploaded by nouser", func(t *testing.T) {
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
info, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: "nouser",
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "", info.PostId)
|
|
|
|
err = ss.FileInfo().AttachToPost(rctx, info.Id, postID, channelID, model.NewId())
|
|
require.NoError(t, err)
|
|
|
|
data, err := ss.FileInfo().GetForPost(postID, true, false, false)
|
|
require.NoError(t, err)
|
|
info.PostId = postID
|
|
info.ChannelId = channelID
|
|
assert.EqualValues(t, []*model.FileInfo{info}, data)
|
|
})
|
|
}
|
|
|
|
func testFileInfoDeleteForPost(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userID := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
infos := []*model.FileInfo{
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
DeleteAt: 123,
|
|
},
|
|
{
|
|
PostId: model.NewId(),
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
},
|
|
}
|
|
|
|
for i, info := range infos {
|
|
newInfo, err := ss.FileInfo().Save(rctx, info)
|
|
require.NoError(t, err)
|
|
infos[i] = newInfo
|
|
defer func(id string) {
|
|
ss.FileInfo().PermanentDelete(rctx, id)
|
|
}(newInfo.Id)
|
|
}
|
|
|
|
_, err := ss.FileInfo().DeleteForPost(rctx, postID)
|
|
require.NoError(t, err)
|
|
|
|
infos, err = ss.FileInfo().GetForPost(postID, true, false, false)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, infos)
|
|
}
|
|
|
|
func testFileInfoPermanentDelete(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
info, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
ChannelId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = ss.FileInfo().PermanentDelete(rctx, info.Id)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testFileInfoPermanentDeleteBatch(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
_, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
CreateAt: 1000,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
CreateAt: 1200,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
CreateAt: 2000,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
bookmarkFile, err := ss.FileInfo().Save(rctx, &model.FileInfo{ // should not be deleted
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: model.BookmarkFileOwner,
|
|
Path: "file.txt",
|
|
CreateAt: 1000,
|
|
})
|
|
defer ss.FileInfo().PermanentDelete(rctx, bookmarkFile.Id)
|
|
require.NoError(t, err)
|
|
|
|
postFiles, err := ss.FileInfo().GetForPost(postID, true, false, false)
|
|
require.NoError(t, err)
|
|
assert.Len(t, postFiles, 4)
|
|
|
|
_, err = ss.FileInfo().PermanentDeleteBatch(rctx, 1500, 1000)
|
|
require.NoError(t, err)
|
|
|
|
postFiles, err = ss.FileInfo().GetForPost(postID, true, false, false)
|
|
require.NoError(t, err)
|
|
assert.Len(t, postFiles, 2)
|
|
}
|
|
|
|
func testFileInfoPermanentDeleteByUser(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
userID := model.NewId()
|
|
postID := model.NewId()
|
|
channelID := model.NewId()
|
|
|
|
_, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postID,
|
|
ChannelId: channelID,
|
|
CreatorId: userID,
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().PermanentDeleteByUser(rctx, userID)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func testFileInfoUpdateMinipreview(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
info := &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "image.png",
|
|
}
|
|
|
|
info, err := ss.FileInfo().Save(rctx, info)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info.Id), 0)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info.Id)
|
|
}()
|
|
|
|
rinfo, err := ss.FileInfo().Get(info.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, info.Id, rinfo.Id)
|
|
require.Nil(t, rinfo.MiniPreview)
|
|
|
|
miniPreview := []byte{0x0, 0x1, 0x2}
|
|
|
|
rinfo.MiniPreview = &miniPreview
|
|
|
|
rinfo, err = ss.FileInfo().Upsert(rctx, rinfo)
|
|
require.NoError(t, err)
|
|
require.Equal(t, info.Id, rinfo.Id)
|
|
|
|
tinfo, err := ss.FileInfo().Get(info.Id)
|
|
require.NoError(t, err)
|
|
require.Equal(t, info.Id, tinfo.Id)
|
|
require.Equal(t, *tinfo.MiniPreview, miniPreview)
|
|
}
|
|
|
|
func testFileInfoStoreGetFilesBatchForIndexing(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
c1 := &model.Channel{}
|
|
c1.TeamId = model.NewId()
|
|
c1.DisplayName = "Channel1"
|
|
c1.Name = "zz" + model.NewId() + "b"
|
|
c1.Type = model.ChannelTypeOpen
|
|
c1, _ = ss.Channel().Save(rctx, c1, -1)
|
|
|
|
c2 := &model.Channel{}
|
|
c2.TeamId = model.NewId()
|
|
c2.DisplayName = "Channel2"
|
|
c2.Name = "zz" + model.NewId() + "b"
|
|
c2.Type = model.ChannelTypeOpen
|
|
c2, _ = ss.Channel().Save(rctx, c2, -1)
|
|
|
|
o1 := &model.Post{}
|
|
o1.ChannelId = c1.Id
|
|
o1.UserId = model.NewId()
|
|
o1.Message = "zz" + model.NewId() + "AAAAAAAAAAA"
|
|
o1, err := ss.Post().Save(rctx, o1)
|
|
require.NoError(t, err)
|
|
f1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: o1.Id,
|
|
ChannelId: o1.ChannelId,
|
|
CreatorId: model.NewId(),
|
|
Path: "file1.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, f1.Id)
|
|
}()
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o2 := &model.Post{}
|
|
o2.ChannelId = c2.Id
|
|
o2.UserId = model.NewId()
|
|
o2.Message = "zz" + model.NewId() + "CCCCCCCCC"
|
|
o2, err = ss.Post().Save(rctx, o2)
|
|
require.NoError(t, err)
|
|
|
|
f2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: o2.Id,
|
|
ChannelId: o2.ChannelId,
|
|
CreatorId: model.NewId(),
|
|
Path: "file2.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, f2.Id)
|
|
}()
|
|
time.Sleep(2 * time.Millisecond)
|
|
|
|
o3 := &model.Post{}
|
|
o3.ChannelId = c1.Id
|
|
o3.UserId = model.NewId()
|
|
o3.RootId = o1.Id
|
|
o3.Message = "zz" + model.NewId() + "QQQQQQQQQQ"
|
|
o3, err = ss.Post().Save(rctx, o3)
|
|
require.NoError(t, err)
|
|
|
|
f3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: o3.Id,
|
|
ChannelId: o3.ChannelId,
|
|
CreatorId: model.NewId(),
|
|
Path: "file3.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, f3.Id)
|
|
}()
|
|
|
|
// Soft-deleting one file info
|
|
_, err = ss.FileInfo().DeleteForPost(rctx, f1.PostId)
|
|
require.NoError(t, err)
|
|
|
|
// Getting all
|
|
r, err := ss.FileInfo().GetFilesBatchForIndexing(f1.CreateAt-1, "", true, 100)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 3, "Expected 3 posts in results. Got %v", len(r))
|
|
|
|
r, err = ss.FileInfo().GetFilesBatchForIndexing(f1.CreateAt-1, "", false, 100)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 2, "Expected 2 posts in results. Got %v", len(r))
|
|
|
|
// Testing pagination
|
|
r, err = ss.FileInfo().GetFilesBatchForIndexing(f1.CreateAt-1, "", true, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 2, "Expected 2 posts in results. Got %v", len(r))
|
|
|
|
r, err = ss.FileInfo().GetFilesBatchForIndexing(r[1].CreateAt, r[1].Id, true, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 1, "Expected 1 post in results. Got %v", len(r))
|
|
|
|
r, err = ss.FileInfo().GetFilesBatchForIndexing(r[0].CreateAt, r[0].Id, true, 2)
|
|
require.NoError(t, err)
|
|
require.Len(t, r, 0, "Expected 0 posts in results. Got %v", len(r))
|
|
}
|
|
|
|
func testFileInfoStoreCountAll(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
_, err := ss.FileInfo().PermanentDeleteBatch(rctx, model.GetMillis(), 100000)
|
|
require.NoError(t, err)
|
|
f1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
ChannelId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Path: "file1.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
ChannelId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Path: "file2.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
ChannelId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Path: "file3.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, ss.FileInfo().RefreshFileStats())
|
|
count, err := ss.FileInfo().CountAll()
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(3), count)
|
|
|
|
_, err = ss.FileInfo().DeleteForPost(rctx, f1.PostId)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, ss.FileInfo().RefreshFileStats())
|
|
count, err = ss.FileInfo().CountAll()
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(2), count)
|
|
}
|
|
|
|
func testFileInfoGetStorageUsage(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
_, err := ss.FileInfo().PermanentDeleteBatch(rctx, model.GetMillis(), 100000)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, ss.FileInfo().RefreshFileStats())
|
|
usage, err := ss.FileInfo().GetStorageUsage(false, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(0), usage)
|
|
|
|
f1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
_, err = ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
_, err = ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file3.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, ss.FileInfo().RefreshFileStats())
|
|
usage, err = ss.FileInfo().GetStorageUsage(false, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(30), usage)
|
|
|
|
_, err = ss.FileInfo().DeleteForPost(rctx, f1.PostId)
|
|
require.NoError(t, err)
|
|
require.NoError(t, ss.FileInfo().RefreshFileStats())
|
|
usage, err = ss.FileInfo().GetStorageUsage(false, false)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(20), usage)
|
|
|
|
usage, err = ss.FileInfo().GetStorageUsage(false, true)
|
|
require.NoError(t, err)
|
|
require.Equal(t, int64(30), usage)
|
|
}
|
|
|
|
func testGetUptoNSizeFileTime(t *testing.T, rctx request.CTX, ss store.Store, s SqlStore) {
|
|
t.Skip("MM-53905")
|
|
|
|
_, err := ss.FileInfo().GetUptoNSizeFileTime(0)
|
|
assert.Error(t, err)
|
|
_, err = ss.FileInfo().GetUptoNSizeFileTime(-1)
|
|
assert.Error(t, err)
|
|
|
|
_, err = ss.FileInfo().PermanentDeleteBatch(rctx, model.GetMillis(), 100000)
|
|
require.NoError(t, err)
|
|
|
|
diff := int64(10000)
|
|
now := utils.MillisFromTime(time.Now()) + diff
|
|
|
|
f1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, f1.Id)
|
|
now = now + diff
|
|
f2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, f2.Id)
|
|
now = now + diff
|
|
f3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file3.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, f3.Id)
|
|
now = now + diff
|
|
tmp, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: model.NewId(),
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file4.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, tmp.Id)
|
|
|
|
createAt, err := ss.FileInfo().GetUptoNSizeFileTime(20)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, f3.CreateAt, createAt)
|
|
|
|
_, err = ss.FileInfo().GetUptoNSizeFileTime(5)
|
|
assert.Error(t, err)
|
|
assert.IsType(t, &store.ErrNotFound{}, err)
|
|
|
|
createAt, err = ss.FileInfo().GetUptoNSizeFileTime(1000)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, f1.CreateAt, createAt)
|
|
|
|
_, err = ss.FileInfo().DeleteForPost(rctx, f3.PostId)
|
|
require.NoError(t, err)
|
|
|
|
createAt, err = ss.FileInfo().GetUptoNSizeFileTime(20)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, f2.CreateAt, createAt)
|
|
}
|
|
|
|
func testPermanentDeleteForPost(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
postId := model.NewId()
|
|
|
|
_, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: utils.MillisFromTime(time.Now()),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = ss.FileInfo().PermanentDeleteForPost(rctx, postId)
|
|
require.NoError(t, err)
|
|
|
|
postInfos, err := ss.FileInfo().GetForPost(
|
|
postId,
|
|
true,
|
|
true,
|
|
false,
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Len(t, postInfos, 0)
|
|
}
|
|
|
|
func testGetByIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("Should get single file info", func(t *testing.T) {
|
|
info, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info.Id), 0)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info.Id)
|
|
}()
|
|
|
|
fileInfos, err := ss.FileInfo().GetByIds([]string{info.Id}, false, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos, 1)
|
|
require.Equal(t, info.Id, fileInfos[0].Id)
|
|
})
|
|
|
|
t.Run("Should get multiple file info", func(t *testing.T) {
|
|
info1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info1.Id), 0)
|
|
|
|
// waiting 1 second to add deterministic difference between the two file info's CreateAt time
|
|
time.Sleep(1 * time.Second)
|
|
|
|
info2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info2.Id), 0)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info1.Id)
|
|
ss.FileInfo().PermanentDelete(rctx, info2.Id)
|
|
}()
|
|
|
|
fileInfos, err := ss.FileInfo().GetByIds([]string{info1.Id, info2.Id}, false, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfos, 2)
|
|
require.Equal(t, info1.Id, fileInfos[1].Id)
|
|
require.Equal(t, info2.Id, fileInfos[0].Id)
|
|
})
|
|
|
|
t.Run("Should get deleted file infos when specified", func(t *testing.T) {
|
|
postId := model.NewId()
|
|
|
|
info1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
PostId: postId,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info1.Id), 0)
|
|
|
|
// waiting 1 second to add deterministic difference between the two file info's CreateAt time
|
|
time.Sleep(1 * time.Second)
|
|
|
|
info2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
CreatorId: model.NewId(),
|
|
Path: "file.txt",
|
|
PostId: postId,
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, len(info2.Id), 0)
|
|
|
|
defer func() {
|
|
ss.FileInfo().PermanentDelete(rctx, info1.Id)
|
|
ss.FileInfo().PermanentDelete(rctx, info2.Id)
|
|
}()
|
|
|
|
// we'll delete the two file infos
|
|
_, err = ss.FileInfo().DeleteForPost(rctx, postId)
|
|
require.NoError(t, err)
|
|
|
|
fileInfosIncludingDeleted, err := ss.FileInfo().GetByIds([]string{info1.Id, info2.Id}, true, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfosIncludingDeleted, 2)
|
|
require.Equal(t, info2.Id, fileInfosIncludingDeleted[0].Id)
|
|
require.Greater(t, fileInfosIncludingDeleted[0].DeleteAt, int64(0))
|
|
require.Equal(t, info1.Id, fileInfosIncludingDeleted[1].Id)
|
|
require.Greater(t, fileInfosIncludingDeleted[1].DeleteAt, int64(0))
|
|
|
|
// verifying that the file infos are not returned when IncludeDeleted is false
|
|
fileInfosExcludingDeleted, err := ss.FileInfo().GetByIds([]string{info1.Id, info2.Id}, false, true)
|
|
require.NoError(t, err)
|
|
require.Len(t, fileInfosExcludingDeleted, 0)
|
|
})
|
|
}
|
|
|
|
func testDeleteForPostByIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("base case", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
if fileInfo.Id == fileInfo1.Id || fileInfo.Id == fileInfo2.Id {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
} else {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("with empty array", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
})
|
|
|
|
t.Run("duplicate fileInfo Ids specified", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
if fileInfo.Id == fileInfo1.Id || fileInfo.Id == fileInfo2.Id {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
} else {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("non existent fileInfo IDs specified", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{model.NewId(), model.NewId()})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
})
|
|
|
|
t.Run("non existent postID specified", func(t *testing.T) {
|
|
err := ss.FileInfo().DeleteForPostByIds(rctx, model.NewId(), []string{model.NewId()})
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("delete already deleted fileInfos", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
if fileInfo.Id == fileInfo1.Id || fileInfo.Id == fileInfo2.Id {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
} else {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
}
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err = ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
if fileInfo.Id == fileInfo1.Id || fileInfo.Id == fileInfo2.Id {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
} else {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func testRestoreUndeleteForPostByIds(t *testing.T, rctx request.CTX, ss store.Store) {
|
|
t.Run("base case", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
if fileInfo.Id == fileInfo1.Id || fileInfo.Id == fileInfo2.Id {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
} else {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
}
|
|
|
|
// now we'll un-delete the files
|
|
err = ss.FileInfo().RestoreForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err = ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
require.Equal(t, fileInfo.DeleteAt, int64(0))
|
|
}
|
|
})
|
|
|
|
t.Run("with empty array it should not impact any post files", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
err = ss.FileInfo().RestoreForPostByIds(rctx, postId, []string{})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
})
|
|
|
|
t.Run("duplicate fileInfo Ids specified", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
// delete file infos
|
|
err = ss.FileInfo().DeleteForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id, fileInfo3.Id})
|
|
require.NoError(t, err)
|
|
|
|
// verify file infos are deleted
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
}
|
|
|
|
// undelete them specifying duplicate file info ids
|
|
err = ss.FileInfo().RestoreForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id, fileInfo2.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
// verify file infos are deleted
|
|
fileInfos, err = ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
if fileInfo.Id == fileInfo3.Id {
|
|
require.Greater(t, fileInfo.DeleteAt, int64(0))
|
|
} else {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("non existent fileInfo IDs and postId specified", func(t *testing.T) {
|
|
err := ss.FileInfo().RestoreForPostByIds(rctx, model.NewId(), []string{model.NewId(), model.NewId()})
|
|
require.NoError(t, err)
|
|
})
|
|
|
|
t.Run("undelete already undeleted fileInfos", func(t *testing.T) {
|
|
now := model.GetMillis()
|
|
postId := model.NewId()
|
|
|
|
fileInfo1, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo1.Id)
|
|
|
|
fileInfo2, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file2.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo2.Id)
|
|
|
|
fileInfo3, err := ss.FileInfo().Save(rctx, &model.FileInfo{
|
|
PostId: postId,
|
|
CreatorId: model.NewId(),
|
|
Size: 10,
|
|
Path: "file1.txt",
|
|
CreateAt: now,
|
|
})
|
|
require.NoError(t, err)
|
|
defer ss.FileInfo().PermanentDelete(rctx, fileInfo3.Id)
|
|
|
|
err = ss.FileInfo().RestoreForPostByIds(rctx, postId, []string{fileInfo1.Id, fileInfo2.Id})
|
|
require.NoError(t, err)
|
|
|
|
fileInfos, err := ss.FileInfo().GetForPost(postId, true, true, false)
|
|
require.NoError(t, err)
|
|
|
|
for _, fileInfo := range fileInfos {
|
|
require.Equal(t, int64(0), fileInfo.DeleteAt)
|
|
}
|
|
})
|
|
}
|