Files
team/server/channels/api4/content_flagging_report_test.go
zeekay 8feb24ea1f One name for this fork, and it is ours
The server module named github.com/hanzoai/team/server/v8 — a path that
resolves to the retired workspace's repository, not this one — while
server/public still published github.com/mattermost/mattermost/server/public.
Both are github.com/hanzoteam/server/... now, which is where this code
actually lives.

That second half is what made the binary unbootable. Upstream's public path
stayed claimed by upstream, mattermost-plugin-ai imported it, and the build
linked public/model twice; public/plugin's gob.Register then ran twice under
one name and panicked at init, before main. Reverting the rename traded the
brand for a working binary. Rebranding the plugin alongside it needs no such
trade: the agents fork is github.com/hanzoteam/agents/v2 against our public
module, so no module named mattermost/mattermost is in the graph at all.

Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and
it is ours.
2026-08-28 23:35:36 -07:00

233 lines
7.7 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package api4
import (
"archive/zip"
"bytes"
"context"
"io"
"net/http"
"testing"
"github.com/goccy/go-yaml"
"github.com/hanzoteam/server/server/public/model"
"github.com/stretchr/testify/require"
)
func TestGenerateFlaggedPostReport(t *testing.T) {
th := Setup(t).InitBasic(t)
client := th.Client
th.App.Srv().SetLicense(model.NewTestLicenseSKU(model.LicenseShortSkuEnterpriseAdvanced))
defer th.RemoveLicense(t)
t.Run("Should return 501 when feature is disabled", func(t *testing.T) {
th.App.UpdateConfig(func(config *model.Config) {
config.ContentFlaggingSettings.EnableContentFlagging = model.NewPointer(false)
config.ContentFlaggingSettings.SetDefaults()
})
post := th.CreatePost(t)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{})
require.Error(t, err)
require.Equal(t, http.StatusNotImplemented, resp.StatusCode)
require.Empty(t, report)
})
t.Run("Should return 400 when post ID is invalid", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), "invalid", &model.FlagContentActionRequest{})
require.Error(t, err)
require.Equal(t, http.StatusBadRequest, resp.StatusCode)
require.Empty(t, report)
})
t.Run("Should return 403 when user is not a reviewer", func(t *testing.T) {
appErr := setNonReviewerConfig(th)
require.Nil(t, appErr)
post := th.CreatePost(t)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{})
require.Error(t, err)
require.Equal(t, http.StatusForbidden, resp.StatusCode)
require.Empty(t, report)
})
t.Run("Should return 404 when post is not flagged", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post := th.CreatePost(t)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{})
require.Error(t, err)
require.Equal(t, http.StatusNotFound, resp.StatusCode)
require.Empty(t, report)
})
t.Run("Should successfully generate report for a flagged post", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post := th.CreatePost(t)
flagPostViaAPI(t, client, post.Id)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{Comment: "investigation note"})
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.NotEmpty(t, report)
zr, err := zip.NewReader(bytes.NewReader(report), int64(len(report)))
require.NoError(t, err)
entries := map[string]bool{}
for _, f := range zr.File {
entries[f.Name] = true
}
require.Contains(t, entries, "report_metadata.yaml")
require.Contains(t, entries, "post/post.yaml")
require.Contains(t, entries, "content_review.yaml")
})
t.Run("Should successfully generate report when user is a team reviewer", func(t *testing.T) {
appErr := setBasicTeamReviewerConfig(th)
require.Nil(t, appErr)
post := th.CreatePost(t)
flagPostViaAPI(t, client, post.Id)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{Comment: "investigation note"})
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.NotEmpty(t, report)
})
t.Run("Should include file attachments in the generated report", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post, fileInfo := uploadFileAndCreatePost(t, th, client)
flagPostViaAPI(t, client, post.Id)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{Comment: "investigation note"})
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.NotEmpty(t, report)
zr, err := zip.NewReader(bytes.NewReader(report), int64(len(report)))
require.NoError(t, err)
var foundAttachment bool
for _, f := range zr.File {
if f.Name == "post/attachments/"+fileInfo.Id+"_"+fileInfo.Name {
foundAttachment = true
break
}
}
require.True(t, foundAttachment, "attachment for the flagged post should be present in the report archive")
})
t.Run("Should include reviewer decision from request action", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post := th.CreatePost(t)
flagPostViaAPI(t, client, post.Id)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{
Comment: "investigation note",
Action: model.ContentFlaggingActionRemove,
})
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.NotEmpty(t, report)
zr, err := zip.NewReader(bytes.NewReader(report), int64(len(report)))
require.NoError(t, err)
var review model.FlaggedPostReportContentReview
var found bool
for _, f := range zr.File {
if f.Name != "content_review.yaml" {
continue
}
rc, err := f.Open()
require.NoError(t, err)
b, err := io.ReadAll(rc)
require.NoError(t, err)
_ = rc.Close()
require.NoError(t, yaml.Unmarshal(b, &review))
found = true
break
}
require.True(t, found, "content_review.yaml should be present in the report archive")
require.Equal(t, "remove", review.ActorDecision)
require.Equal(t, th.BasicUser.Id, review.ActorUserId)
require.Equal(t, th.BasicUser.Username, review.ActorUsername)
})
t.Run("Should include edit history entries in the generated report", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post := th.CreatePost(t)
post.Message = "Updated message to create edit history"
_, _, err := client.UpdatePost(context.Background(), post.Id, post)
require.NoError(t, err)
editHistory, appErr := th.App.GetEditHistoryForPost(post.Id)
require.Nil(t, appErr)
require.NotEmpty(t, editHistory)
editId := editHistory[0].Id
flagPostViaAPI(t, client, post.Id)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{Comment: "investigation note"})
require.NoError(t, err)
require.Equal(t, http.StatusOK, resp.StatusCode)
require.NotEmpty(t, report)
zr, err := zip.NewReader(bytes.NewReader(report), int64(len(report)))
require.NoError(t, err)
var foundEdit bool
for _, f := range zr.File {
if f.Name == "edit_history/"+editId+"/post.yaml" {
foundEdit = true
break
}
}
require.True(t, foundEdit, "edit history entry should be present in the report archive")
})
t.Run("Should not allow generating a report for a post in a DM channel", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post := createDmPost(t, th, client)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{})
require.Error(t, err)
CheckBadRequestStatus(t, resp)
CheckErrorID(t, err, "api.data_spillage.error.invalid_channel_type")
require.Empty(t, report)
})
t.Run("Should not allow generating a report for a post in a GM channel", func(t *testing.T) {
appErr := setBasicCommonReviewerConfig(th)
require.Nil(t, appErr)
post := createGmPost(t, th, client)
report, resp, err := client.GenerateFlaggedPostReport(context.Background(), post.Id, &model.FlagContentActionRequest{})
require.Error(t, err)
CheckBadRequestStatus(t, resp)
CheckErrorID(t, err, "api.data_spillage.error.invalid_channel_type")
require.Empty(t, report)
})
}