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>
2610 lines
94 KiB
Go
2610 lines
94 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func getBaseConfig(th *TestHelper) model.ContentFlaggingSettingsRequest {
|
|
config := model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
config.ReviewerSettings.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.AdditionalSettings.ReporterCommentRequired = model.NewPointer(false)
|
|
config.AdditionalSettings.HideFlaggedContent = model.NewPointer(false)
|
|
config.AdditionalSettings.Reasons = &[]string{"spam", "harassment", "inappropriate"}
|
|
return config
|
|
}
|
|
|
|
func setBaseConfig(th *TestHelper) *model.AppError {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig(th))
|
|
if appErr != nil {
|
|
return appErr
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func setupFlaggedPost(th *TestHelper) (*model.Post, *model.AppError) {
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
appErr := th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
if appErr != nil {
|
|
return nil, appErr
|
|
}
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
return post, nil
|
|
}
|
|
|
|
func TestContentFlaggingEnabledForTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t)
|
|
defer th.TearDown()
|
|
|
|
t.Run("should return true for common reviewers", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(true),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
CommonReviewerIds: []string{"reviewer_user_id_1", "reviewer_user_id_2"},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
status, appErr := th.App.ContentFlaggingEnabledForTeam("team1")
|
|
require.Nil(t, appErr)
|
|
require.True(t, status, "expected team post reporting feature to be enabled for common reviewers")
|
|
})
|
|
|
|
t.Run("should return true when configured for specified team", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(false),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
TeamReviewersSetting: map[string]*model.TeamReviewerSetting{
|
|
"team1": {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{"reviewer_user_id_1"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
status, appErr := th.App.ContentFlaggingEnabledForTeam("team1")
|
|
require.Nil(t, appErr)
|
|
require.True(t, status, "expected team post reporting feature to be disabled for team without reviewers")
|
|
})
|
|
|
|
t.Run("should return true when using Additional Reviewers", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(false),
|
|
TeamAdminsAsReviewers: model.NewPointer(true),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
TeamReviewersSetting: map[string]*model.TeamReviewerSetting{
|
|
"team1": {
|
|
Enabled: model.NewPointer(true),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
status, appErr := th.App.ContentFlaggingEnabledForTeam("team1")
|
|
require.Nil(t, appErr)
|
|
require.True(t, status)
|
|
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
appErr = th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
status, appErr = th.App.ContentFlaggingEnabledForTeam("team1")
|
|
require.Nil(t, appErr)
|
|
require.True(t, status)
|
|
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
appErr = th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
status, appErr = th.App.ContentFlaggingEnabledForTeam("team1")
|
|
require.Nil(t, appErr)
|
|
require.True(t, status)
|
|
})
|
|
|
|
t.Run("should return true for default state", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
status, appErr := th.App.ContentFlaggingEnabledForTeam("team1")
|
|
require.Nil(t, appErr)
|
|
require.True(t, status, "expected team post reporting feature to be enabled for common reviewers")
|
|
})
|
|
}
|
|
|
|
func TestAssignFlaggedPostReviewer(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should successfully assign reviewer to pending flagged post", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify status was updated to assigned
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusAssigned+`"`, string(statusValue.Value))
|
|
|
|
// Verify reviewer property was created
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReviewerUserID].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, reviewerValues, 1)
|
|
require.Equal(t, `"`+th.BasicUser.Id+`"`, string(reviewerValues[0].Value))
|
|
})
|
|
|
|
t.Run("should successfully reassign reviewer to already assigned flagged post", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
// First assignment
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Second assignment (reassignment)
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser2.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify status remains assigned
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusAssigned+`"`, string(statusValue.Value))
|
|
|
|
// Verify reviewer property was updated
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReviewerUserID].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, reviewerValues, 1)
|
|
require.Equal(t, `"`+th.BasicUser2.Id+`"`, string(reviewerValues[0].Value))
|
|
})
|
|
|
|
t.Run("should fail when trying to assign reviewer to non-flagged post", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
appErr := th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, http.StatusNotFound, appErr.StatusCode)
|
|
})
|
|
|
|
t.Run("should handle assignment with same reviewer ID", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
// Assign reviewer
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Assign same reviewer again
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify status remains assigned
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusAssigned+`"`, string(statusValue.Value))
|
|
|
|
// Verify reviewer property still exists with correct value
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReviewerUserID].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, reviewerValues, 1)
|
|
require.Equal(t, `"`+th.BasicUser.Id+`"`, string(reviewerValues[0].Value))
|
|
})
|
|
|
|
t.Run("should handle assignment with empty reviewer ID", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, "", th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify status was updated to assigned
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusAssigned+`"`, string(statusValue.Value))
|
|
|
|
// Verify reviewer property was created with empty value
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReviewerUserID].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, reviewerValues, 1)
|
|
require.Equal(t, `""`, string(reviewerValues[0].Value))
|
|
})
|
|
|
|
t.Run("should handle assignment with invalid post ID", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
appErr := th.App.AssignFlaggedPostReviewer(th.Context, "invalid_post_id", th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, http.StatusNotFound, appErr.StatusCode)
|
|
})
|
|
|
|
t.Run("should allow assigning reviewer at all stages", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
|
|
// Set the status to Assigned
|
|
statusValue.Value = json.RawMessage(fmt.Sprintf(`"%s"`, model.ContentFlaggingStatusAssigned))
|
|
_, err := th.App.Srv().propertyService.UpdatePropertyValue(groupId, statusValue)
|
|
require.NoError(t, err)
|
|
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue, appErr = th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusAssigned+`"`, string(statusValue.Value))
|
|
|
|
// Set the status to Removed
|
|
statusValue.Value = json.RawMessage(fmt.Sprintf(`"%s"`, model.ContentFlaggingStatusRemoved))
|
|
_, err = th.App.Srv().propertyService.UpdatePropertyValue(groupId, statusValue)
|
|
require.NoError(t, err)
|
|
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue, appErr = th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusRemoved+`"`, string(statusValue.Value))
|
|
|
|
// Set the status to Retained
|
|
statusValue.Value = json.RawMessage(fmt.Sprintf(`"%s"`, model.ContentFlaggingStatusRetained))
|
|
_, err = th.App.Srv().propertyService.UpdatePropertyValue(groupId, statusValue)
|
|
require.NoError(t, err)
|
|
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue, appErr = th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusRetained+`"`, string(statusValue.Value))
|
|
})
|
|
}
|
|
|
|
func TestSaveContentFlaggingConfig(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should save content flagging config successfully", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ContentFlaggingSettingsBase: model.ContentFlaggingSettingsBase{
|
|
EnableContentFlagging: model.NewPointer(true),
|
|
AdditionalSettings: &model.AdditionalContentFlaggingSettings{
|
|
ReporterCommentRequired: model.NewPointer(true),
|
|
HideFlaggedContent: model.NewPointer(false),
|
|
Reasons: &[]string{"spam", "harassment", "inappropriate"},
|
|
},
|
|
},
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(true),
|
|
SystemAdminsAsReviewers: model.NewPointer(true),
|
|
TeamAdminsAsReviewers: model.NewPointer(false),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
CommonReviewerIds: []string{th.BasicUser.Id, th.BasicUser2.Id},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify system config was updated
|
|
savedConfig := th.App.Config()
|
|
require.Equal(t, *config.EnableContentFlagging, *savedConfig.ContentFlaggingSettings.EnableContentFlagging)
|
|
require.Equal(t, *config.ReviewerSettings.CommonReviewers, *savedConfig.ContentFlaggingSettings.ReviewerSettings.CommonReviewers)
|
|
require.Equal(t, *config.ReviewerSettings.SystemAdminsAsReviewers, *savedConfig.ContentFlaggingSettings.ReviewerSettings.SystemAdminsAsReviewers)
|
|
require.Equal(t, *config.ReviewerSettings.TeamAdminsAsReviewers, *savedConfig.ContentFlaggingSettings.ReviewerSettings.TeamAdminsAsReviewers)
|
|
require.Equal(t, *config.AdditionalSettings.ReporterCommentRequired, *savedConfig.ContentFlaggingSettings.AdditionalSettings.ReporterCommentRequired)
|
|
require.Equal(t, *config.AdditionalSettings.HideFlaggedContent, *savedConfig.ContentFlaggingSettings.AdditionalSettings.HideFlaggedContent)
|
|
require.Equal(t, *config.AdditionalSettings.Reasons, *savedConfig.ContentFlaggingSettings.AdditionalSettings.Reasons)
|
|
|
|
// Verify reviewer IDs were saved separately
|
|
reviewerIDs, appErr := th.App.GetContentFlaggingConfigReviewerIDs()
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, config.ReviewerSettings.CommonReviewerIds, reviewerIDs.CommonReviewerIds)
|
|
})
|
|
|
|
t.Run("should save config with team reviewers", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ContentFlaggingSettingsBase: model.ContentFlaggingSettingsBase{
|
|
EnableContentFlagging: model.NewPointer(true),
|
|
},
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(false),
|
|
SystemAdminsAsReviewers: model.NewPointer(false),
|
|
TeamAdminsAsReviewers: model.NewPointer(false),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
TeamReviewersSetting: map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser.Id},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify team reviewers were saved
|
|
reviewerIDs, appErr := th.App.GetContentFlaggingConfigReviewerIDs()
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, reviewerIDs.TeamReviewersSetting)
|
|
|
|
teamSettings := (reviewerIDs.TeamReviewersSetting)[th.BasicTeam.Id]
|
|
require.True(t, *teamSettings.Enabled)
|
|
require.Equal(t, []string{th.BasicUser.Id}, teamSettings.ReviewerIds)
|
|
})
|
|
|
|
t.Run("should handle empty config", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify defaults were applied
|
|
savedConfig := th.App.Config()
|
|
require.NotNil(t, savedConfig.ContentFlaggingSettings.EnableContentFlagging)
|
|
require.NotNil(t, savedConfig.ContentFlaggingSettings.ReviewerSettings.CommonReviewers)
|
|
})
|
|
}
|
|
|
|
func TestGetContentFlaggingConfigReviewerIDs(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should return reviewer IDs after saving config", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(true),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
CommonReviewerIds: []string{th.BasicUser.Id, th.BasicUser2.Id},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerIDs, appErr := th.App.GetContentFlaggingConfigReviewerIDs()
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, reviewerIDs)
|
|
require.Equal(t, []string{th.BasicUser.Id, th.BasicUser2.Id}, reviewerIDs.CommonReviewerIds)
|
|
})
|
|
|
|
t.Run("should return team reviewer settings", func(t *testing.T) {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
CommonReviewers: model.NewPointer(false),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
TeamReviewersSetting: map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser.Id},
|
|
},
|
|
"team2": {
|
|
Enabled: model.NewPointer(false),
|
|
ReviewerIds: []string{th.BasicUser2.Id},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerIDs, appErr := th.App.GetContentFlaggingConfigReviewerIDs()
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, reviewerIDs.TeamReviewersSetting)
|
|
|
|
teamSettings := reviewerIDs.TeamReviewersSetting
|
|
require.Len(t, teamSettings, 2)
|
|
|
|
// Check first team
|
|
team1Settings := teamSettings[th.BasicTeam.Id]
|
|
require.True(t, *team1Settings.Enabled)
|
|
require.Equal(t, []string{th.BasicUser.Id}, team1Settings.ReviewerIds)
|
|
|
|
// Check second team
|
|
team2Settings := teamSettings["team2"]
|
|
require.False(t, *team2Settings.Enabled)
|
|
require.Equal(t, []string{th.BasicUser2.Id}, team2Settings.ReviewerIds)
|
|
})
|
|
|
|
t.Run("should return empty settings when no config saved", func(t *testing.T) {
|
|
// Clear any existing config by saving empty config
|
|
config := model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewerIDs, appErr := th.App.GetContentFlaggingConfigReviewerIDs()
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, reviewerIDs)
|
|
|
|
// Should have default empty values
|
|
if reviewerIDs.CommonReviewerIds != nil {
|
|
require.Empty(t, reviewerIDs.CommonReviewerIds)
|
|
}
|
|
if reviewerIDs.TeamReviewersSetting != nil {
|
|
require.Empty(t, reviewerIDs.TeamReviewersSetting)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetContentReviewChannels(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
getBaseConfig := func() model.ContentFlaggingSettingsRequest {
|
|
config := model.ContentFlaggingSettingsRequest{
|
|
ReviewerSettings: &model.ReviewSettingsRequest{
|
|
ReviewerSettings: model.ReviewerSettings{
|
|
TeamAdminsAsReviewers: model.NewPointer(true),
|
|
SystemAdminsAsReviewers: model.NewPointer(true),
|
|
CommonReviewers: model.NewPointer(true),
|
|
},
|
|
ReviewerIDsSettings: model.ReviewerIDsSettings{
|
|
CommonReviewerIds: []string{th.BasicUser.Id, th.BasicUser2.Id},
|
|
},
|
|
},
|
|
}
|
|
config.SetDefaults()
|
|
return config
|
|
}
|
|
|
|
t.Run("should return channels for common reviewers", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, contentReviewBot)
|
|
|
|
channels, appErr := th.App.getContentReviewChannels(th.Context, th.BasicTeam.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, channels, 2)
|
|
|
|
for _, channel := range channels {
|
|
require.Equal(t, model.ChannelTypeDirect, channel.Type)
|
|
otherUserId := channel.GetOtherUserIdForDM(contentReviewBot.UserId)
|
|
require.True(t, otherUserId == th.BasicUser.Id || otherUserId == th.BasicUser2.Id)
|
|
}
|
|
})
|
|
|
|
t.Run("should return channels for system admins as additional reviewers", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Sysadmin explicitly need to be a team member to be returned as reviewer
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
defer func() {
|
|
_ = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
}()
|
|
|
|
require.Nil(t, appErr)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, contentReviewBot)
|
|
|
|
channels, appErr := th.App.getContentReviewChannels(th.Context, th.BasicTeam.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, channels, 3)
|
|
|
|
require.Equal(t, model.ChannelTypeDirect, channels[0].Type)
|
|
require.Equal(t, model.ChannelTypeDirect, channels[1].Type)
|
|
require.Equal(t, model.ChannelTypeDirect, channels[2].Type)
|
|
|
|
reviewerIds := []string{
|
|
channels[0].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
channels[1].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
channels[2].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
}
|
|
require.Contains(t, reviewerIds, th.BasicUser.Id)
|
|
require.Contains(t, reviewerIds, th.BasicUser2.Id)
|
|
require.Contains(t, reviewerIds, th.SystemAdminUser.Id)
|
|
})
|
|
|
|
t.Run("should return channels for team admins as additional reviewers", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Create a new user and make them team admin
|
|
teamAdmin := th.CreateUser()
|
|
defer func() {
|
|
_ = th.App.PermanentDeleteUser(th.Context, teamAdmin)
|
|
}()
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, teamAdmin.Id, "")
|
|
|
|
require.Nil(t, appErr)
|
|
|
|
_, appErr = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, teamAdmin.Id, model.TeamAdminRoleId)
|
|
require.Nil(t, appErr)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, contentReviewBot)
|
|
|
|
channels, appErr := th.App.getContentReviewChannels(th.Context, th.BasicTeam.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, channels, 3)
|
|
|
|
require.Equal(t, model.ChannelTypeDirect, channels[0].Type)
|
|
require.Equal(t, model.ChannelTypeDirect, channels[1].Type)
|
|
require.Equal(t, model.ChannelTypeDirect, channels[2].Type)
|
|
|
|
reviewerIds := []string{
|
|
channels[0].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
channels[1].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
channels[2].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
}
|
|
require.Contains(t, reviewerIds, th.BasicUser.Id)
|
|
require.Contains(t, reviewerIds, th.BasicUser2.Id)
|
|
require.Contains(t, reviewerIds, teamAdmin.Id)
|
|
})
|
|
|
|
t.Run("should return channels for team reviewers", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser2.Id},
|
|
},
|
|
}
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, contentReviewBot)
|
|
|
|
channels, appErr := th.App.getContentReviewChannels(th.Context, th.BasicTeam.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, channels, 1)
|
|
|
|
require.Equal(t, model.ChannelTypeDirect, channels[0].Type)
|
|
otherUserId := channels[0].GetOtherUserIdForDM(contentReviewBot.UserId)
|
|
require.Equal(t, th.BasicUser2.Id, otherUserId)
|
|
})
|
|
|
|
t.Run("should not return channels for team reviewers when disabled for the team", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(false),
|
|
ReviewerIds: []string{th.BasicUser.Id},
|
|
},
|
|
}
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, contentReviewBot)
|
|
|
|
channels, appErr := th.App.getContentReviewChannels(th.Context, th.BasicTeam.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, channels, 0)
|
|
})
|
|
|
|
t.Run("should return channels for additional reviewers with team reviewers", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser2.Id},
|
|
},
|
|
}
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
defer func() {
|
|
_ = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
}()
|
|
require.Nil(t, appErr)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, contentReviewBot)
|
|
|
|
channels, appErr := th.App.getContentReviewChannels(th.Context, th.BasicTeam.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, channels, 2)
|
|
|
|
reviewerIds := []string{
|
|
channels[0].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
channels[1].GetOtherUserIdForDM(contentReviewBot.UserId),
|
|
}
|
|
|
|
require.Contains(t, reviewerIds, th.BasicUser2.Id)
|
|
require.Contains(t, reviewerIds, th.SystemAdminUser.Id)
|
|
})
|
|
}
|
|
|
|
func TestGetReviewersForTeam(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should return common reviewers", func(t *testing.T) {
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id, th.BasicUser2.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 2)
|
|
require.Contains(t, reviewers, th.BasicUser.Id)
|
|
require.Contains(t, reviewers, th.BasicUser2.Id)
|
|
})
|
|
|
|
t.Run("should return system admins as additional reviewers", func(t *testing.T) {
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Sysadmin explicitly need to be a team member to be returned as reviewer
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 2)
|
|
require.Contains(t, reviewers, th.BasicUser.Id)
|
|
require.Contains(t, reviewers, th.SystemAdminUser.Id)
|
|
|
|
config.ReviewerSettings.CommonReviewerIds = []string{}
|
|
appErr = th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr = th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Contains(t, reviewers, th.SystemAdminUser.Id)
|
|
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
appErr = th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
// If sysadmin is not a team member, they should not be returned as a reviewer
|
|
appErr = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
reviewers, appErr = th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Contains(t, reviewers, th.BasicUser.Id)
|
|
})
|
|
|
|
t.Run("should return team admins as additional reviewers", func(t *testing.T) {
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Create a new user and make them team admin
|
|
teamAdmin := th.CreateUser()
|
|
defer func() {
|
|
_ = th.App.PermanentDeleteUser(th.Context, teamAdmin)
|
|
}()
|
|
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, teamAdmin.Id, "")
|
|
require.Nil(t, appErr)
|
|
|
|
_, appErr = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, teamAdmin.Id, model.TeamAdminRoleId)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 2)
|
|
require.Contains(t, reviewers, th.BasicUser.Id)
|
|
require.Contains(t, reviewers, teamAdmin.Id)
|
|
|
|
config.ReviewerSettings.CommonReviewerIds = []string{}
|
|
appErr = th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr = th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Contains(t, reviewers, teamAdmin.Id)
|
|
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
appErr = th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
// If team admin is not a team member, they should not be returned as a reviewer
|
|
appErr = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, teamAdmin.Id, "")
|
|
require.Nil(t, appErr)
|
|
reviewers, appErr = th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Contains(t, reviewers, th.BasicUser.Id)
|
|
})
|
|
|
|
t.Run("should return team reviewers", func(t *testing.T) {
|
|
team2 := th.CreateTeam()
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser2.Id},
|
|
},
|
|
}
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Reviewers configured for th.BasicTeam
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Contains(t, reviewers, th.BasicUser2.Id)
|
|
|
|
// NO reviewers configured for team2
|
|
reviewers, appErr = th.App.getReviewersForTeam(team2.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 0)
|
|
})
|
|
|
|
t.Run("should not return reviewers when disabled for the team", func(t *testing.T) {
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(false),
|
|
ReviewerIds: []string{th.BasicUser.Id},
|
|
},
|
|
}
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 0)
|
|
})
|
|
|
|
t.Run("should return additional reviewers with team reviewers", func(t *testing.T) {
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser2.Id},
|
|
},
|
|
}
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 2)
|
|
require.Contains(t, reviewers, th.BasicUser2.Id)
|
|
require.Contains(t, reviewers, th.SystemAdminUser.Id)
|
|
})
|
|
|
|
t.Run("should return unique reviewers", func(t *testing.T) {
|
|
config := &model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id, th.SystemAdminUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(*config)
|
|
require.Nil(t, appErr)
|
|
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
|
|
reviewers, appErr := th.App.getReviewersForTeam(th.BasicTeam.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 2)
|
|
require.Contains(t, reviewers, th.BasicUser.Id)
|
|
require.Contains(t, reviewers, th.SystemAdminUser.Id)
|
|
})
|
|
}
|
|
|
|
func TestCanFlagPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should be able to flag post which has not already been flagged", func(t *testing.T) {
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
appErr = th.App.canFlagPost(groupId, post.Id, "en")
|
|
require.Nil(t, appErr)
|
|
})
|
|
|
|
t.Run("should not be able to flag post which has already been flagged", func(t *testing.T) {
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
statusField, err := th.Server.propertyService.GetPropertyFieldByName(groupId, "", ContentFlaggingPropertyNameStatus)
|
|
require.NoError(t, err)
|
|
|
|
propertyValue, err := th.Server.propertyService.CreatePropertyValue(&model.PropertyValue{
|
|
TargetID: post.Id,
|
|
GroupID: groupId,
|
|
FieldID: statusField.ID,
|
|
TargetType: "post",
|
|
Value: json.RawMessage(`"` + model.ContentFlaggingStatusPending + `"`),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Can't fleg when post already flagged in pending status
|
|
appErr = th.App.canFlagPost(groupId, post.Id, "en")
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "Cannot flag this post as it is already flagged.", appErr.Id)
|
|
|
|
// Can't fleg when post already flagged in assigned status
|
|
propertyValue.Value = json.RawMessage(`"` + model.ContentFlaggingStatusAssigned + `"`)
|
|
_, err = th.Server.propertyService.UpdatePropertyValue(groupId, propertyValue)
|
|
require.NoError(t, err)
|
|
|
|
appErr = th.App.canFlagPost(groupId, post.Id, "en")
|
|
require.NotNil(t, appErr)
|
|
|
|
// Can't fleg when post already flagged in retained status
|
|
propertyValue.Value = json.RawMessage(`"` + model.ContentFlaggingStatusRetained + `"`)
|
|
_, err = th.Server.propertyService.UpdatePropertyValue(groupId, propertyValue)
|
|
require.NoError(t, err)
|
|
|
|
appErr = th.App.canFlagPost(groupId, post.Id, "en")
|
|
require.NotNil(t, appErr)
|
|
|
|
// Can't fleg when post already flagged in removed status
|
|
propertyValue.Value = json.RawMessage(`"` + model.ContentFlaggingStatusRemoved + `"`)
|
|
_, err = th.Server.propertyService.UpdatePropertyValue(groupId, propertyValue)
|
|
require.NoError(t, err)
|
|
|
|
appErr = th.App.canFlagPost(groupId, post.Id, "en")
|
|
require.NotNil(t, appErr)
|
|
})
|
|
}
|
|
|
|
func TestFlagPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
getBaseConfig := func() model.ContentFlaggingSettingsRequest {
|
|
cfg := model.ContentFlaggingSettingsRequest{}
|
|
cfg.SetDefaults()
|
|
cfg.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
cfg.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
cfg.AdditionalSettings.ReporterCommentRequired = model.NewPointer(false)
|
|
cfg.AdditionalSettings.HideFlaggedContent = model.NewPointer(false)
|
|
cfg.AdditionalSettings.Reasons = &[]string{"spam", "harassment", "inappropriate"}
|
|
return cfg
|
|
}
|
|
|
|
t.Run("should successfully flag a post with valid data", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify property values were created
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
// Check status property
|
|
statusValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[ContentFlaggingPropertyNameStatus].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, statusValues, 1)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusPending+`"`, string(statusValues[0].Value))
|
|
|
|
// Check reporting user property
|
|
userValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReportingUserID].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, userValues, 1)
|
|
require.Equal(t, `"`+th.BasicUser2.Id+`"`, string(userValues[0].Value))
|
|
|
|
// Check reason property
|
|
reasonValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReportingReason].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, reasonValues, 1)
|
|
require.Equal(t, `"spam"`, string(reasonValues[0].Value))
|
|
|
|
// Check comment property
|
|
commentValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReportingComment].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, commentValues, 1)
|
|
require.Equal(t, `"This is spam content"`, string(commentValues[0].Value))
|
|
})
|
|
|
|
t.Run("should fail with invalid reason", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "invalid_reason",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "api.content_flagging.error.reason_invalid", appErr.Id)
|
|
})
|
|
|
|
t.Run("should fail when comment is required but not provided", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.AdditionalSettings.ReporterCommentRequired = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.NotNil(t, appErr)
|
|
})
|
|
|
|
t.Run("should fail when trying to flag already flagged post", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "\"This is spam content\"",
|
|
}
|
|
|
|
// Flag the post first time
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
// Try to flag the same post again
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "Cannot flag this post as it is already flagged.", appErr.Id)
|
|
})
|
|
|
|
t.Run("should hide flagged content when configured", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.AdditionalSettings.HideFlaggedContent = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "\"This is spam content\"",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify post was deleted
|
|
deletedPost, appErr := th.App.GetSinglePost(th.Context, post.Id, false)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, deletedPost)
|
|
})
|
|
|
|
t.Run("should create content review post for reviewers", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "harassment",
|
|
Comment: "\"This is harassment\"",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
|
|
require.Nil(t, appErr)
|
|
|
|
// The reviewer posts are created async in a go routine. Wait for a short time to allow it to complete.
|
|
// 2 seconds is the minimum time when the test consistently passes locally and in CI.
|
|
time.Sleep(5 * time.Second)
|
|
|
|
// Get the content review bot
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
// Get direct channel between reviewer and bot
|
|
dmChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
|
|
// Check if review post was created in the DM channel
|
|
posts, appErr := th.App.GetPostsPage(th.Context, model.GetPostsOptions{
|
|
ChannelId: dmChannel.Id,
|
|
Page: 0,
|
|
PerPage: 10,
|
|
})
|
|
require.Nil(t, appErr)
|
|
require.NotEmpty(t, posts.Posts)
|
|
|
|
// Find the content review post
|
|
var reviewPost *model.Post
|
|
for _, p := range posts.Posts {
|
|
if p.Type == "custom_spillage_report" {
|
|
reviewPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, reviewPost)
|
|
})
|
|
|
|
t.Run("should work with empty comment when not required", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "inappropriate",
|
|
Comment: "",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify property values were created with empty comment
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
commentValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReportingComment].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, commentValues, 1)
|
|
require.Equal(t, `""`, string(commentValues[0].Value))
|
|
})
|
|
|
|
t.Run("should set reporting time property", func(t *testing.T) {
|
|
appErr := th.App.SaveContentFlaggingConfig(getBaseConfig())
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "\"Test comment\"",
|
|
}
|
|
|
|
beforeTime := model.GetMillis()
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
|
|
afterTime := model.GetMillis()
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify reporting time property was set
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
timeValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameReportingTime].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, timeValues, 1)
|
|
|
|
var reportingTime int64
|
|
err = json.Unmarshal(timeValues[0].Value, &reportingTime)
|
|
require.NoError(t, err)
|
|
require.True(t, reportingTime >= beforeTime && reportingTime <= afterTime)
|
|
})
|
|
}
|
|
|
|
func TestSearchReviewers(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
getBaseConfig := func() model.ContentFlaggingSettingsRequest {
|
|
config := model.ContentFlaggingSettingsRequest{}
|
|
config.SetDefaults()
|
|
return config
|
|
}
|
|
|
|
allProfilesSanitized := func(reviewers []*model.User) {
|
|
for _, reviewer := range reviewers {
|
|
require.Empty(t, reviewer.LastPasswordUpdate)
|
|
require.Empty(t, reviewer.NotifyProps)
|
|
}
|
|
}
|
|
|
|
t.Run("should return common reviewers when searching", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id, th.BasicUser2.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search for users by username
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, th.BasicUser.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Equal(t, th.BasicUser.Id, reviewers[0].Id)
|
|
|
|
// Search for users by partial username
|
|
reviewers, appErr = th.App.SearchReviewers(th.Context, th.BasicUser.Username[:3], th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.True(t, len(reviewers) >= 1)
|
|
allProfilesSanitized(reviewers)
|
|
|
|
// Verify the basic user is in the results
|
|
found := false
|
|
for _, reviewer := range reviewers {
|
|
if reviewer.Id == th.BasicUser.Id {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, found)
|
|
})
|
|
|
|
t.Run("should return team reviewers when common reviewers disabled", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser2.Id},
|
|
},
|
|
}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search for team reviewer
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, th.BasicUser2.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Equal(t, th.BasicUser2.Id, reviewers[0].Id)
|
|
allProfilesSanitized(reviewers)
|
|
|
|
// Search should not return users not configured as team reviewers
|
|
reviewers, appErr = th.App.SearchReviewers(th.Context, th.BasicUser.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 0)
|
|
})
|
|
|
|
t.Run("should return system admins as additional reviewers", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Add system admin to team
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
_ = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
}()
|
|
|
|
// Search for system admin
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, th.SystemAdminUser.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Equal(t, th.SystemAdminUser.Id, reviewers[0].Id)
|
|
allProfilesSanitized(reviewers)
|
|
})
|
|
|
|
t.Run("should return team admins as additional reviewers", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(true)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Create a new user and make them team admin
|
|
teamAdmin := th.CreateUser()
|
|
defer func() {
|
|
_ = th.App.PermanentDeleteUser(th.Context, teamAdmin)
|
|
}()
|
|
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, teamAdmin.Id, "")
|
|
require.Nil(t, appErr)
|
|
|
|
_, appErr = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, teamAdmin.Id, model.TeamAdminRoleId)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search for team admin
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, teamAdmin.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Equal(t, teamAdmin.Id, reviewers[0].Id)
|
|
allProfilesSanitized(reviewers)
|
|
})
|
|
|
|
t.Run("should return combined reviewers from multiple sources", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(true)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Add system admin to team
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
_ = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
}()
|
|
|
|
// Create a team admin
|
|
teamAdmin := th.CreateUser()
|
|
defer func() {
|
|
_ = th.App.PermanentDeleteUser(th.Context, teamAdmin)
|
|
}()
|
|
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, teamAdmin.Id, "")
|
|
require.Nil(t, appErr)
|
|
|
|
_, appErr = th.App.UpdateTeamMemberRoles(th.Context, th.BasicTeam.Id, teamAdmin.Id, model.TeamAdminRoleId)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search with empty term should return all reviewers
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, "", th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 3, len(reviewers))
|
|
allProfilesSanitized(reviewers)
|
|
|
|
// Verify all expected reviewers are present
|
|
reviewerIds := make([]string, len(reviewers))
|
|
for i, reviewer := range reviewers {
|
|
reviewerIds[i] = reviewer.Id
|
|
}
|
|
require.Contains(t, reviewerIds, th.BasicUser.Id)
|
|
require.Contains(t, reviewerIds, th.SystemAdminUser.Id)
|
|
require.Contains(t, reviewerIds, teamAdmin.Id)
|
|
})
|
|
|
|
t.Run("should deduplicate reviewers from multiple sources", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.SystemAdminUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Add system admin to team
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
_ = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
}()
|
|
|
|
// Search for system admin (who is both common reviewer and system admin)
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, th.SystemAdminUser.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 1)
|
|
require.Equal(t, th.SystemAdminUser.Id, reviewers[0].Id)
|
|
allProfilesSanitized(reviewers)
|
|
})
|
|
|
|
t.Run("should return empty results when no reviewers match search term", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search for non-existent user
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, "nonexistentuser", th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 0)
|
|
allProfilesSanitized(reviewers)
|
|
})
|
|
|
|
t.Run("should return empty results when no reviewers configured", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search should return no results
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, th.BasicUser.Username, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewers, 0)
|
|
allProfilesSanitized(reviewers)
|
|
})
|
|
|
|
t.Run("should work with team reviewers and additional reviewers combined", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamReviewersSetting = map[string]*model.TeamReviewerSetting{
|
|
th.BasicTeam.Id: {
|
|
Enabled: model.NewPointer(true),
|
|
ReviewerIds: []string{th.BasicUser.Id},
|
|
},
|
|
}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Add system admin to team
|
|
_, _, appErr = th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
defer func() {
|
|
_ = th.App.RemoveUserFromTeam(th.Context, th.BasicTeam.Id, th.SystemAdminUser.Id, "")
|
|
}()
|
|
|
|
// Search with empty term should return both team reviewer and system admin
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, "", th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.True(t, len(reviewers) >= 2)
|
|
allProfilesSanitized(reviewers)
|
|
|
|
reviewerIds := make([]string, len(reviewers))
|
|
for i, reviewer := range reviewers {
|
|
reviewerIds[i] = reviewer.Id
|
|
}
|
|
require.Contains(t, reviewerIds, th.BasicUser.Id)
|
|
require.Contains(t, reviewerIds, th.SystemAdminUser.Id)
|
|
})
|
|
|
|
t.Run("should handle search by email and full name", func(t *testing.T) {
|
|
config := getBaseConfig()
|
|
config.ReviewerSettings.CommonReviewers = model.NewPointer(true)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id}
|
|
config.ReviewerSettings.SystemAdminsAsReviewers = model.NewPointer(false)
|
|
config.ReviewerSettings.TeamAdminsAsReviewers = model.NewPointer(false)
|
|
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
// Search by first name (if the user has one set)
|
|
if th.BasicUser.FirstName != "" {
|
|
reviewers, appErr := th.App.SearchReviewers(th.Context, th.BasicUser.FirstName, th.BasicTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.True(t, len(reviewers) >= 1)
|
|
|
|
found := false
|
|
for _, reviewer := range reviewers {
|
|
if reviewer.Id == th.BasicUser.Id {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
require.True(t, found)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetReviewerPostsForFlaggedPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should return reviewer posts for flagged post", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
flaggedPostIdField, ok := mappedFields[contentFlaggingPropertyNameFlaggedPostId]
|
|
require.True(t, ok)
|
|
|
|
reviewerPostIds, appErr := th.App.getReviewerPostsForFlaggedPost(groupId, post.Id, flaggedPostIdField.ID)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewerPostIds, 1)
|
|
|
|
// Verify the reviewer post exists and has the correct properties
|
|
reviewerPost, appErr := th.App.GetSinglePost(th.Context, reviewerPostIds[0], false)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, model.ContentFlaggingPostType, reviewerPost.Type)
|
|
require.Contains(t, reviewerPost.GetProps(), POST_PROP_KEY_FLAGGED_POST_ID)
|
|
require.Equal(t, post.Id, reviewerPost.GetProps()[POST_PROP_KEY_FLAGGED_POST_ID])
|
|
})
|
|
|
|
t.Run("should return empty list when no reviewer posts exist", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
flaggedPostIdField, ok := mappedFields[contentFlaggingPropertyNameFlaggedPostId]
|
|
require.True(t, ok)
|
|
|
|
reviewerPostIds, appErr := th.App.getReviewerPostsForFlaggedPost(groupId, post.Id, flaggedPostIdField.ID)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewerPostIds, 0)
|
|
})
|
|
|
|
t.Run("should handle multiple reviewer posts for same flagged post", func(t *testing.T) {
|
|
// Create a config with multiple reviewers
|
|
config := getBaseConfig(th)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id, th.BasicUser2.Id}
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.SystemAdminUser.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
// Wait for async reviewer post creation to complete
|
|
time.Sleep(2 * time.Second)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
flaggedPostIdField, ok := mappedFields[contentFlaggingPropertyNameFlaggedPostId]
|
|
require.True(t, ok)
|
|
|
|
reviewerPostIds, appErr := th.App.getReviewerPostsForFlaggedPost(groupId, post.Id, flaggedPostIdField.ID)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewerPostIds, 2)
|
|
|
|
// Verify both reviewer posts exist and have correct properties
|
|
for _, postId := range reviewerPostIds {
|
|
reviewerPost, appErr := th.App.GetSinglePost(th.Context, postId, false)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, model.ContentFlaggingPostType, reviewerPost.Type)
|
|
require.Contains(t, reviewerPost.GetProps(), POST_PROP_KEY_FLAGGED_POST_ID)
|
|
require.Equal(t, post.Id, reviewerPost.GetProps()[POST_PROP_KEY_FLAGGED_POST_ID])
|
|
}
|
|
})
|
|
|
|
t.Run("should handle invalid flagged post ID", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
flaggedPostIdField, ok := mappedFields[contentFlaggingPropertyNameFlaggedPostId]
|
|
require.True(t, ok)
|
|
|
|
reviewerPostIds, appErr := th.App.getReviewerPostsForFlaggedPost(groupId, "invalid_post_id", flaggedPostIdField.ID)
|
|
require.Nil(t, appErr)
|
|
require.Len(t, reviewerPostIds, 0)
|
|
})
|
|
}
|
|
|
|
func TestPostReviewerMessage(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should post reviewer message to thread", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
testMessage := "Test reviewer message"
|
|
_, appErr = th.App.postReviewerMessage(th.Context, testMessage, groupId, post.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify message was posted to the reviewer thread
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
dmChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
|
|
posts, appErr := th.App.GetPostsPage(th.Context, model.GetPostsOptions{
|
|
ChannelId: dmChannel.Id,
|
|
Page: 0,
|
|
PerPage: 10,
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
// Find the original review post and the test message
|
|
var reviewPost *model.Post
|
|
var testMessagePost *model.Post
|
|
for _, p := range posts.Posts {
|
|
if p.Type == "custom_spillage_report" {
|
|
reviewPost = p
|
|
} else if p.RootId != "" && p.Message == testMessage {
|
|
testMessagePost = p
|
|
}
|
|
}
|
|
require.NotNil(t, reviewPost)
|
|
require.NotNil(t, testMessagePost)
|
|
require.Equal(t, reviewPost.Id, testMessagePost.RootId)
|
|
require.Equal(t, contentReviewBot.UserId, testMessagePost.UserId)
|
|
})
|
|
|
|
t.Run("should handle multiple reviewer channels", func(t *testing.T) {
|
|
// Create a config with multiple reviewers
|
|
config := getBaseConfig(th)
|
|
config.ReviewerSettings.CommonReviewerIds = []string{th.BasicUser.Id, th.BasicUser2.Id}
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.SystemAdminUser.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
// Wait for async reviewer post creation to complete
|
|
time.Sleep(2 * time.Second)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
testMessage := "Test message for multiple reviewers"
|
|
_, appErr = th.App.postReviewerMessage(th.Context, testMessage, groupId, post.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify message was posted to both reviewer threads
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
// Check first reviewer's channel
|
|
dmChannel1, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
|
|
posts1, appErr := th.App.GetPostsPage(th.Context, model.GetPostsOptions{
|
|
ChannelId: dmChannel1.Id,
|
|
Page: 0,
|
|
PerPage: 10,
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
// Check second reviewer's channel
|
|
dmChannel2, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser2.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
|
|
posts2, appErr := th.App.GetPostsPage(th.Context, model.GetPostsOptions{
|
|
ChannelId: dmChannel2.Id,
|
|
Page: 0,
|
|
PerPage: 10,
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify test message exists in both channels
|
|
var testMessagePost1, testMessagePost2 *model.Post
|
|
for _, p := range posts1.Posts {
|
|
if p.RootId != "" && p.Message == testMessage {
|
|
testMessagePost1 = p
|
|
break
|
|
}
|
|
}
|
|
for _, p := range posts2.Posts {
|
|
if p.RootId != "" && p.Message == testMessage {
|
|
testMessagePost2 = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, testMessagePost1)
|
|
require.NotNil(t, testMessagePost2)
|
|
require.Equal(t, contentReviewBot.UserId, testMessagePost1.UserId)
|
|
require.Equal(t, contentReviewBot.UserId, testMessagePost2.UserId)
|
|
})
|
|
|
|
t.Run("should handle case when no reviewer posts exist", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
testMessage := "Test message for non-flagged post"
|
|
_, appErr = th.App.postReviewerMessage(th.Context, testMessage, groupId, post.Id)
|
|
require.Nil(t, appErr)
|
|
})
|
|
|
|
t.Run("should handle message with special characters", func(t *testing.T) {
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
testMessage := "Test message with special chars: @user #channel ~team & <script>alert('xss')</script>"
|
|
_, appErr = th.App.postReviewerMessage(th.Context, testMessage, groupId, post.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify message was posted correctly with special characters preserved
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
dmChannel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, contentReviewBot.UserId)
|
|
require.Nil(t, appErr)
|
|
|
|
posts, appErr := th.App.GetPostsPage(th.Context, model.GetPostsOptions{
|
|
ChannelId: dmChannel.Id,
|
|
Page: 0,
|
|
PerPage: 10,
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
// Find the test message
|
|
var testMessagePost *model.Post
|
|
for _, p := range posts.Posts {
|
|
if p.RootId != "" && p.Message == testMessage {
|
|
testMessagePost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, testMessagePost)
|
|
require.Equal(t, testMessage, testMessagePost.Message)
|
|
})
|
|
}
|
|
|
|
// Helper function to setup notification config for testing
|
|
func setupNotificationConfig(th *TestHelper, eventTargetMapping map[model.ContentFlaggingEvent][]model.NotificationTarget) *model.AppError {
|
|
config := getBaseConfig(th)
|
|
config.NotificationSettings = &model.ContentFlaggingNotificationSettings{
|
|
EventTargetMapping: eventTargetMapping,
|
|
}
|
|
return th.App.SaveContentFlaggingConfig(config)
|
|
}
|
|
|
|
// Helper function to verify post message content and properties
|
|
func verifyNotificationPost(t *testing.T, post *model.Post, expectedMessage string, expectedUserId string, expectedChannelId string) {
|
|
require.NotNil(t, post)
|
|
require.Equal(t, expectedMessage, post.Message)
|
|
require.Equal(t, expectedUserId, post.UserId)
|
|
require.Equal(t, expectedChannelId, post.ChannelId)
|
|
require.True(t, post.CreateAt > 0)
|
|
require.True(t, post.UpdateAt > 0)
|
|
}
|
|
|
|
func TestSendFlaggedPostRemovalNotification(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should send notifications to all configured targets", func(t *testing.T) {
|
|
// Setup notification config for all targets
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentRemoved: {model.TargetReviewers, model.TargetAuthor, model.TargetReporter},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
actorComment := "This post violates community guidelines"
|
|
createdPosts := th.App.sendFlaggedPostRemovalNotification(th.Context, post, th.SystemAdminUser.Id, actorComment, groupId)
|
|
|
|
// Should create 3 posts: reviewer message, author message, reporter message
|
|
require.Len(t, createdPosts, 3)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify reviewer message
|
|
reviewerMessage := fmt.Sprintf("The flagged message was removed by @%s\n\nWith comment:\n\n> %s", th.SystemAdminUser.Username, actorComment)
|
|
var reviewerPost *model.Post
|
|
for _, p := range createdPosts {
|
|
if p.Message == reviewerMessage {
|
|
reviewerPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, reviewerPost)
|
|
verifyNotificationPost(t, reviewerPost, reviewerMessage, contentReviewBot.UserId, reviewerPost.ChannelId)
|
|
require.NotEmpty(t, reviewerPost.RootId) // Should be a thread reply to the flag review post
|
|
|
|
// Verify author message
|
|
authorMessage := fmt.Sprintf("Your post having ID `%s` in the channel `%s` which was flagged for review has been permanently removed by a reviewer.", post.Id, th.BasicChannel.DisplayName)
|
|
var authorPost *model.Post
|
|
for _, p := range createdPosts {
|
|
if p.Message == authorMessage {
|
|
authorPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, authorPost)
|
|
verifyNotificationPost(t, authorPost, authorMessage, contentReviewBot.UserId, authorPost.ChannelId)
|
|
|
|
// Verify reporter message
|
|
reporterMessage := fmt.Sprintf("The post having ID `%s` in the channel `%s` which you flagged for review has been permanently removed by a reviewer.", post.Id, th.BasicChannel.DisplayName)
|
|
var reporterPost *model.Post
|
|
for _, p := range createdPosts {
|
|
if p.Message == reporterMessage {
|
|
reporterPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, reporterPost)
|
|
verifyNotificationPost(t, reporterPost, reporterMessage, contentReviewBot.UserId, reporterPost.ChannelId)
|
|
})
|
|
|
|
t.Run("should send notifications only to configured targets", func(t *testing.T) {
|
|
// Setup notification config for only author
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentRemoved: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
// Setup notification config for only author
|
|
appErr = setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentRemoved: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
createdPosts := th.App.sendFlaggedPostRemovalNotification(th.Context, post, th.SystemAdminUser.Id, "Test comment", groupId)
|
|
|
|
// Should create only 1 post for author
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was removed by @%s\n\nWith comment:\n\n> %s", th.SystemAdminUser.Username, "Test comment")
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, contentReviewBot.UserId, createdPosts[0].ChannelId)
|
|
})
|
|
|
|
t.Run("should handle empty comment", func(t *testing.T) {
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentRemoved: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
createdPosts := th.App.sendFlaggedPostRemovalNotification(th.Context, post, th.SystemAdminUser.Id, "", groupId)
|
|
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was removed by @%s", th.SystemAdminUser.Username)
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, createdPosts[0].UserId, createdPosts[0].ChannelId)
|
|
})
|
|
|
|
t.Run("should handle special characters in comment", func(t *testing.T) {
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentRemoved: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
specialComment := "Comment with @mentions #channels ~teams & <script>alert('xss')</script>"
|
|
createdPosts := th.App.sendFlaggedPostRemovalNotification(th.Context, post, th.SystemAdminUser.Id, specialComment, groupId)
|
|
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was removed by @%s\n\nWith comment:\n\n> %s", th.SystemAdminUser.Username, specialComment)
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, createdPosts[0].UserId, createdPosts[0].ChannelId)
|
|
})
|
|
}
|
|
|
|
func TestSendKeepFlaggedPostNotification(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
|
|
t.Run("should send notifications to all configured targets", func(t *testing.T) {
|
|
// Setup notification config for all targets
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentDismissed: {model.TargetReviewers, model.TargetAuthor, model.TargetReporter},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
actorComment := "This post is acceptable after review"
|
|
createdPosts := th.App.sendKeepFlaggedPostNotification(th.Context, post, th.SystemAdminUser.Id, actorComment, groupId)
|
|
|
|
// Should create 3 posts: reviewer message, author message, reporter message
|
|
require.Len(t, createdPosts, 3)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify reviewer message
|
|
reviewerMessage := fmt.Sprintf("The flagged message was retained by @%s\n\nWith comment:\n\n> %s", th.SystemAdminUser.Username, actorComment)
|
|
var reviewerPost *model.Post
|
|
for _, p := range createdPosts {
|
|
if p.Message == reviewerMessage {
|
|
reviewerPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, reviewerPost)
|
|
verifyNotificationPost(t, reviewerPost, reviewerMessage, contentReviewBot.UserId, reviewerPost.ChannelId)
|
|
require.NotEmpty(t, reviewerPost.RootId) // Should be a thread reply
|
|
|
|
// Verify author message
|
|
authorMessage := fmt.Sprintf("Your post having ID `%s` in the channel `%s` which was flagged for review has been restored by a reviewer.", post.Id, th.BasicChannel.DisplayName)
|
|
var authorPost *model.Post
|
|
for _, p := range createdPosts {
|
|
if p.Message == authorMessage {
|
|
authorPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, authorPost)
|
|
verifyNotificationPost(t, authorPost, authorMessage, contentReviewBot.UserId, authorPost.ChannelId)
|
|
|
|
// Verify reporter message
|
|
reporterMessage := fmt.Sprintf("The post having ID `%s` in the channel `%s` which you flagged for review has been restored by a reviewer.", post.Id, th.BasicChannel.DisplayName)
|
|
var reporterPost *model.Post
|
|
for _, p := range createdPosts {
|
|
if p.Message == reporterMessage {
|
|
reporterPost = p
|
|
break
|
|
}
|
|
}
|
|
require.NotNil(t, reporterPost)
|
|
verifyNotificationPost(t, reporterPost, reporterMessage, contentReviewBot.UserId, reporterPost.ChannelId)
|
|
})
|
|
|
|
t.Run("should send notifications only to configured targets", func(t *testing.T) {
|
|
// Setup notification config for only reporter
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentDismissed: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
comment := "Test comment"
|
|
createdPosts := th.App.sendKeepFlaggedPostNotification(th.Context, post, th.SystemAdminUser.Id, comment, groupId)
|
|
|
|
// Should create only 1 post for reporter
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
contentReviewBot, appErr := th.App.getContentReviewBot(th.Context)
|
|
require.Nil(t, appErr)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was retained by @%s\n\nWith comment:\n\n> %s", th.SystemAdminUser.Username, comment)
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, contentReviewBot.UserId, createdPosts[0].ChannelId)
|
|
})
|
|
|
|
t.Run("should handle empty comment", func(t *testing.T) {
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentDismissed: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
createdPosts := th.App.sendKeepFlaggedPostNotification(th.Context, post, th.SystemAdminUser.Id, "", groupId)
|
|
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was retained by @%s", th.SystemAdminUser.Username)
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, createdPosts[0].UserId, createdPosts[0].ChannelId)
|
|
})
|
|
|
|
t.Run("should handle special characters in comment", func(t *testing.T) {
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentDismissed: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
specialComment := "Comment with @mentions #channels ~teams & <script>alert('xss')</script>"
|
|
createdPosts := th.App.sendKeepFlaggedPostNotification(th.Context, post, th.SystemAdminUser.Id, specialComment, groupId)
|
|
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was retained by @%s\n\nWith comment:\n\n> %s", th.SystemAdminUser.Username, specialComment)
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, createdPosts[0].UserId, createdPosts[0].ChannelId)
|
|
})
|
|
|
|
t.Run("should handle different actor users", func(t *testing.T) {
|
|
appErr := setupNotificationConfig(th, map[model.ContentFlaggingEvent][]model.NotificationTarget{
|
|
model.EventContentDismissed: {model.TargetReviewers},
|
|
})
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
// Use BasicUser as actor instead of SystemAdminUser
|
|
createdPosts := th.App.sendKeepFlaggedPostNotification(th.Context, post, th.BasicUser.Id, "Reviewed by different user", groupId)
|
|
|
|
require.Len(t, createdPosts, 1)
|
|
|
|
expectedMessage := fmt.Sprintf("The flagged message was retained by @%s\n\nWith comment:\n\n> %s", th.BasicUser.Username, "Reviewed by different user")
|
|
verifyNotificationPost(t, createdPosts[0], expectedMessage, createdPosts[0].UserId, createdPosts[0].ChannelId)
|
|
})
|
|
}
|
|
|
|
func TestPermanentDeleteFlaggedPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic()
|
|
defer th.TearDown()
|
|
|
|
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
|
|
require.Nil(t, setBaseConfig(th))
|
|
|
|
t.Run("should successfully permanently delete pending flagged post", func(t *testing.T) {
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "This post violates community guidelines",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify post content was scrubbed
|
|
updatedPost, appErr := th.App.GetSinglePost(th.Context, post.Id, true)
|
|
require.Nil(t, appErr) // Post should be deleted
|
|
require.Greater(t, updatedPost.DeleteAt, int64(0))
|
|
|
|
// Verify status was updated to removed
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusRemoved+`"`, string(statusValue.Value))
|
|
|
|
// Verify actor properties were created
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
// Check actor user property
|
|
actorValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameActorUserID].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, actorValues, 1)
|
|
require.Equal(t, `"`+th.SystemAdminUser.Id+`"`, string(actorValues[0].Value))
|
|
|
|
// Check actor comment property
|
|
commentValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameActorComment].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, commentValues, 1)
|
|
require.Equal(t, `"This post violates community guidelines"`, string(commentValues[0].Value))
|
|
|
|
// Check action time property
|
|
timeValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameActionTime].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, timeValues, 1)
|
|
|
|
var actionTime int64
|
|
err = json.Unmarshal(timeValues[0].Value, &actionTime)
|
|
require.NoError(t, err)
|
|
require.True(t, actionTime > 0)
|
|
})
|
|
|
|
t.Run("should successfully permanently delete pending flagged post when flagged posts are hidden", func(t *testing.T) {
|
|
baseConfig := getBaseConfig(th)
|
|
baseConfig.AdditionalSettings.HideFlaggedContent = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(baseConfig)
|
|
require.Nil(t, appErr)
|
|
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "This post violates community guidelines",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify post content was scrubbed
|
|
updatedPost, appErr := th.App.GetSinglePost(th.Context, post.Id, true)
|
|
require.Nil(t, appErr) // Post should be deleted
|
|
require.Greater(t, updatedPost.DeleteAt, int64(0))
|
|
})
|
|
|
|
t.Run("should successfully permanently delete assigned flagged post", func(t *testing.T) {
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
// Assign the post to a reviewer first
|
|
appErr = th.App.AssignFlaggedPostReviewer(th.Context, post.Id, th.BasicChannel.TeamId, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Assigned post needs removal",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify status was updated to removed
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusRemoved+`"`, string(statusValue.Value))
|
|
})
|
|
|
|
t.Run("should fail when trying to delete already removed post", func(t *testing.T) {
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
// Set status to removed
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue.Value = json.RawMessage(fmt.Sprintf(`"%s"`, model.ContentFlaggingStatusRemoved))
|
|
_, err := th.App.Srv().propertyService.UpdatePropertyValue(groupId, statusValue)
|
|
require.NoError(t, err)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Trying to delete already removed post",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "api.content_flagging.error.post_not_in_progress", appErr.Id)
|
|
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
|
|
})
|
|
|
|
t.Run("should fail when trying to delete already retained post", func(t *testing.T) {
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
// Set status to retained
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
|
|
statusValue.Value = json.RawMessage(fmt.Sprintf(`"%s"`, model.ContentFlaggingStatusRetained))
|
|
_, err := th.App.Srv().propertyService.UpdatePropertyValue(groupId, statusValue)
|
|
require.NoError(t, err)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Trying to delete retained post",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "api.content_flagging.error.post_not_in_progress", appErr.Id)
|
|
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
|
|
})
|
|
|
|
t.Run("should fail when trying to delete non-flagged post", func(t *testing.T) {
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Trying to delete non-flagged post",
|
|
}
|
|
|
|
appErr := th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, http.StatusNotFound, appErr.StatusCode)
|
|
})
|
|
|
|
t.Run("should handle empty comment", func(t *testing.T) {
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify empty comment was stored
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
commentValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameActorComment].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, commentValues, 1)
|
|
require.Equal(t, `""`, string(commentValues[0].Value))
|
|
})
|
|
|
|
t.Run("should handle special characters in comment", func(t *testing.T) {
|
|
post, appErr := setupFlaggedPost(th)
|
|
require.Nil(t, appErr)
|
|
|
|
specialComment := "Comment with @mentions #channels ~teams & <script>alert('xss')</script>"
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: specialComment,
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify special characters were properly escaped and stored
|
|
groupId, appErr := th.App.ContentFlaggingGroupId()
|
|
require.Nil(t, appErr)
|
|
|
|
mappedFields, appErr := th.App.GetContentFlaggingMappedFields(groupId)
|
|
require.Nil(t, appErr)
|
|
|
|
commentValues, err := th.Server.propertyService.SearchPropertyValues(groupId, model.PropertyValueSearchOpts{
|
|
TargetIDs: []string{post.Id},
|
|
PerPage: CONTENT_FLAGGING_MAX_PROPERTY_VALUES,
|
|
FieldID: mappedFields[contentFlaggingPropertyNameActorComment].ID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, commentValues, 1)
|
|
|
|
var storedComment string
|
|
err = json.Unmarshal(commentValues[0].Value, &storedComment)
|
|
require.NoError(t, err)
|
|
require.Equal(t, specialComment, storedComment)
|
|
})
|
|
|
|
t.Run("should handle post with file attachments", func(t *testing.T) {
|
|
// Create a post with file attachments
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
// Create some file infos and associate them with the post
|
|
fileInfo1 := &model.FileInfo{
|
|
Id: model.NewId(),
|
|
PostId: post.Id,
|
|
CreatorId: post.UserId,
|
|
Path: "test/file1.txt",
|
|
Name: "file1.txt",
|
|
Size: 100,
|
|
}
|
|
fileInfo2 := &model.FileInfo{
|
|
Id: model.NewId(),
|
|
PostId: post.Id,
|
|
CreatorId: post.UserId,
|
|
Path: "test/file2.txt",
|
|
Name: "file2.txt",
|
|
Size: 200,
|
|
}
|
|
|
|
_, err := th.App.Srv().Store().FileInfo().Save(th.Context, fileInfo1)
|
|
require.NoError(t, err)
|
|
_, err = th.App.Srv().Store().FileInfo().Save(th.Context, fileInfo2)
|
|
require.NoError(t, err)
|
|
|
|
// Update post to include file IDs
|
|
post.FileIds = []string{fileInfo1.Id, fileInfo2.Id}
|
|
_, appErr := th.App.UpdatePost(th.Context, post, &model.UpdatePostOptions{})
|
|
require.Nil(t, appErr)
|
|
|
|
// Flag the post
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Post with files needs removal",
|
|
}
|
|
|
|
require.Eventually(t, func() bool {
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, post)
|
|
require.Nil(t, appErr)
|
|
return true
|
|
}, 5*time.Second, 200*time.Millisecond)
|
|
|
|
// Verify post was deleted and status updated
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(post.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusRemoved+`"`, string(statusValue.Value))
|
|
|
|
// Verify file infos were also deleted
|
|
files, err := th.App.Srv().Store().FileInfo().GetByIds([]string{fileInfo1.Id, fileInfo2.Id}, true, false)
|
|
require.NoError(t, err)
|
|
require.Empty(t, files)
|
|
})
|
|
|
|
t.Run("should handle post with edit history", func(t *testing.T) {
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
// Create edit history for the post
|
|
editedPost := post.Clone()
|
|
editedPost.Message = "Edited message"
|
|
editedPost.EditAt = model.GetMillis()
|
|
|
|
_, appErr := th.App.UpdatePost(th.Context, editedPost, &model.UpdatePostOptions{})
|
|
require.Nil(t, appErr)
|
|
|
|
// Flag the post
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "inappropriate",
|
|
Comment: "This post is inappropriate",
|
|
}
|
|
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Post with edit history needs removal",
|
|
}
|
|
|
|
require.Eventually(t, func() bool {
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, editedPost)
|
|
require.Nil(t, appErr)
|
|
return true
|
|
}, 5*time.Second, 200*time.Millisecond)
|
|
|
|
// Verify status was updated
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(editedPost.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify statusValue.Value is a string
|
|
var stringValue string
|
|
err := json.Unmarshal(statusValue.Value, &stringValue)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, model.ContentFlaggingStatusRemoved, stringValue)
|
|
})
|
|
|
|
t.Run("should handle post that was already deleted", func(t *testing.T) {
|
|
config := getBaseConfig(th)
|
|
config.AdditionalSettings.HideFlaggedContent = model.NewPointer(true)
|
|
appErr := th.App.SaveContentFlaggingConfig(config)
|
|
require.Nil(t, appErr)
|
|
|
|
post := th.CreatePost(th.BasicChannel)
|
|
|
|
flagData := model.FlagContentRequest{
|
|
Reason: "spam",
|
|
Comment: "This is spam content",
|
|
}
|
|
|
|
// Flag the post (this will delete it due to HideFlaggedContent setting)
|
|
appErr = th.App.FlagPost(th.Context, post, th.BasicTeam.Id, th.BasicUser2.Id, flagData)
|
|
require.Nil(t, appErr)
|
|
|
|
var deletedPost *model.Post
|
|
|
|
require.Eventually(t, func() bool {
|
|
// Get the updated post (should be deleted)
|
|
deletedPost, appErr = th.App.GetSinglePost(th.Context, post.Id, true)
|
|
require.Nil(t, appErr)
|
|
require.True(t, deletedPost.DeleteAt > 0)
|
|
return true
|
|
}, 5*time.Second, 200*time.Millisecond)
|
|
|
|
actionRequest := &model.FlagContentActionRequest{
|
|
Comment: "Permanently delete already hidden post",
|
|
}
|
|
|
|
appErr = th.App.PermanentDeleteFlaggedPost(th.Context, actionRequest, th.SystemAdminUser.Id, deletedPost)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify status was updated to removed
|
|
statusValue, appErr := th.App.GetPostContentFlaggingPropertyValue(deletedPost.Id, ContentFlaggingPropertyNameStatus)
|
|
require.Nil(t, appErr)
|
|
|
|
// Verify statusValue.Value is a string
|
|
var stringValue string
|
|
err := json.Unmarshal(statusValue.Value, &stringValue)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, `"`+model.ContentFlaggingStatusRemoved+`"`, string(statusValue.Value))
|
|
})
|
|
}
|