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>
673 lines
15 KiB
Go
673 lines
15 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package model
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestChannelBookmarkIsValid(t *testing.T) {
|
|
testCases := []struct {
|
|
Description string
|
|
Bookmark *ChannelBookmark
|
|
ExpectedIsValid bool
|
|
}{
|
|
{
|
|
"nil bookmark",
|
|
&ChannelBookmark{},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark without create at timestamp",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: "",
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 0,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark without update at timestamp",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: "",
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 0,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with missing channel id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: "",
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with invalid channel id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: "invalid",
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with missing owner id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: "",
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with invalid user id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: "invalid",
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with missing displayname",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with missing type",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: "",
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with invalid type",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: "invalid",
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type link with missing link url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type link with invalid link url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "invalid",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type link with valid link url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "https://mattermost.com",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"bookmark of type link with empty image url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "https://mattermost.com",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"bookmark of type link with invalid image url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "https://mattermost.com",
|
|
ImageUrl: "invalid",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type link with valid image url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "https://mattermost.com",
|
|
ImageUrl: "https://mattermost.com/some-image-without-extension", // we don't care if the URL is an actual image as the client should handle the error
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"bookmark of type file with missing file id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkFile,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type file with invalid file id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: "invalid",
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkFile,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type file with valid file id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkFile,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
},
|
|
true,
|
|
},
|
|
{
|
|
"bookmark of type file with invalid original id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkFile,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
OriginalId: "invalid",
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type file with invalid parent id",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
FileId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkFile,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 4,
|
|
ParentId: "invalid",
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type link with a file Id attached",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: NewId(),
|
|
FileId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "http://somelink",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 0,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark of type file with a url",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: NewId(),
|
|
FileId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "http://somelink",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkFile,
|
|
CreateAt: 2,
|
|
UpdateAt: 3,
|
|
DeleteAt: 0,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with long display name > limit",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: NewId(),
|
|
FileId: "",
|
|
DisplayName: strings.Repeat("1", 65),
|
|
SortOrder: 0,
|
|
LinkUrl: "http://somelink",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 3,
|
|
UpdateAt: 3,
|
|
DeleteAt: 0,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with long display name < limit",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: NewId(),
|
|
FileId: "",
|
|
DisplayName: strings.Repeat("1", 64),
|
|
SortOrder: 0,
|
|
LinkUrl: "http://somelink",
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 3,
|
|
UpdateAt: 3,
|
|
DeleteAt: 0,
|
|
},
|
|
true,
|
|
},
|
|
|
|
{
|
|
"bookmark with link url > limit",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "not last test",
|
|
SortOrder: 0,
|
|
LinkUrl: "http://somelink?" + strings.Repeat("h", 1024),
|
|
ImageUrl: "",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 3,
|
|
UpdateAt: 3,
|
|
DeleteAt: 0,
|
|
},
|
|
false,
|
|
},
|
|
{
|
|
"bookmark with image url > limit",
|
|
&ChannelBookmark{
|
|
Id: NewId(),
|
|
OwnerId: NewId(),
|
|
ChannelId: NewId(),
|
|
FileId: "",
|
|
DisplayName: "last test",
|
|
SortOrder: 0,
|
|
LinkUrl: "",
|
|
ImageUrl: "http://somelink?" + strings.Repeat("h", 1024),
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 3,
|
|
UpdateAt: 3,
|
|
DeleteAt: 0,
|
|
},
|
|
false,
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.Description, func(t *testing.T) {
|
|
if testCase.ExpectedIsValid {
|
|
require.Nil(t, testCase.Bookmark.IsValid())
|
|
} else {
|
|
require.NotNil(t, testCase.Bookmark.IsValid())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestChannelBookmarkPreSave(t *testing.T) {
|
|
bookmark := &ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "https://mattermost.com",
|
|
Type: ChannelBookmarkLink,
|
|
DeleteAt: 0,
|
|
}
|
|
|
|
originalBookmark := &ChannelBookmark{
|
|
Id: bookmark.Id,
|
|
ChannelId: bookmark.ChannelId,
|
|
OwnerId: bookmark.OwnerId,
|
|
DisplayName: bookmark.DisplayName,
|
|
SortOrder: bookmark.SortOrder,
|
|
LinkUrl: bookmark.LinkUrl,
|
|
Type: bookmark.Type,
|
|
DeleteAt: bookmark.DeleteAt,
|
|
}
|
|
|
|
bookmark.PreSave()
|
|
assert.NotEqual(t, 0, bookmark.CreateAt)
|
|
assert.NotEqual(t, 0, bookmark.UpdateAt)
|
|
|
|
originalBookmark.CreateAt = bookmark.CreateAt
|
|
originalBookmark.UpdateAt = bookmark.UpdateAt
|
|
assert.Equal(t, originalBookmark, bookmark)
|
|
}
|
|
|
|
func TestChannelBookmarkPreUpdate(t *testing.T) {
|
|
bookmark := &ChannelBookmark{
|
|
Id: NewId(),
|
|
ChannelId: NewId(),
|
|
OwnerId: NewId(),
|
|
DisplayName: "display name",
|
|
SortOrder: 0,
|
|
LinkUrl: "https://mattermost.com",
|
|
Type: ChannelBookmarkLink,
|
|
CreateAt: 2,
|
|
DeleteAt: 0,
|
|
}
|
|
|
|
originalBookmark := &ChannelBookmark{
|
|
Id: bookmark.Id,
|
|
ChannelId: bookmark.ChannelId,
|
|
OwnerId: bookmark.OwnerId,
|
|
DisplayName: bookmark.DisplayName,
|
|
SortOrder: bookmark.SortOrder,
|
|
LinkUrl: bookmark.LinkUrl,
|
|
Type: bookmark.Type,
|
|
DeleteAt: bookmark.DeleteAt,
|
|
}
|
|
|
|
bookmark.PreSave()
|
|
assert.NotEqual(t, 0, bookmark.UpdateAt)
|
|
|
|
originalBookmark.CreateAt = bookmark.CreateAt
|
|
originalBookmark.UpdateAt = bookmark.UpdateAt
|
|
assert.Equal(t, originalBookmark, bookmark)
|
|
|
|
bookmark.PreUpdate()
|
|
assert.Greater(t, bookmark.UpdateAt, originalBookmark.UpdateAt)
|
|
}
|
|
|
|
func TestToBookmarkWithFileInfo(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
bookmark *ChannelBookmark
|
|
fileInfo *FileInfo
|
|
expectedEmoji string
|
|
}{
|
|
{
|
|
name: "emoji with colons",
|
|
bookmark: &ChannelBookmark{
|
|
Id: NewId(),
|
|
DisplayName: "test bookmark",
|
|
Emoji: ":smile:",
|
|
Type: ChannelBookmarkLink,
|
|
},
|
|
fileInfo: nil,
|
|
expectedEmoji: "smile",
|
|
},
|
|
{
|
|
name: "emoji without colons",
|
|
bookmark: &ChannelBookmark{
|
|
Id: NewId(),
|
|
DisplayName: "test bookmark",
|
|
Emoji: "smile",
|
|
Type: ChannelBookmarkLink,
|
|
},
|
|
fileInfo: nil,
|
|
expectedEmoji: "smile",
|
|
},
|
|
{
|
|
name: "empty emoji",
|
|
bookmark: &ChannelBookmark{
|
|
Id: NewId(),
|
|
DisplayName: "test bookmark",
|
|
Emoji: "",
|
|
Type: ChannelBookmarkLink,
|
|
},
|
|
fileInfo: nil,
|
|
expectedEmoji: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := tc.bookmark.ToBookmarkWithFileInfo(tc.fileInfo)
|
|
assert.Equal(t, tc.expectedEmoji, result.Emoji)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestChannelBookmarkPatch(t *testing.T) {
|
|
p := &ChannelBookmarkPatch{
|
|
DisplayName: NewPointer(NewId()),
|
|
SortOrder: NewPointer(int64(1)),
|
|
LinkUrl: NewPointer(NewId()),
|
|
}
|
|
|
|
b := ChannelBookmark{
|
|
Id: NewId(),
|
|
DisplayName: NewId(),
|
|
Type: ChannelBookmarkLink, // should not update
|
|
LinkUrl: NewId(),
|
|
}
|
|
b.Patch(p)
|
|
|
|
require.Empty(t, b.FileId)
|
|
require.Equal(t, *p.DisplayName, b.DisplayName)
|
|
require.Equal(t, *p.SortOrder, b.SortOrder)
|
|
require.Equal(t, *p.LinkUrl, b.LinkUrl)
|
|
require.Equal(t, ChannelBookmarkLink, b.Type)
|
|
}
|