Files
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

811 lines
27 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package api4
import (
"context"
"fmt"
"strings"
"testing"
"github.com/hanzoteam/server/server/public/model"
"github.com/stretchr/testify/require"
)
func setupViewTest(t *testing.T) *TestHelper {
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.IntegratedBoards = true
}).InitBasic(t)
return th
}
func makeTestViewForAPI(t *testing.T) *model.View {
t.Helper()
kanban := &model.KanbanProps{
GroupBy: model.KanbanGroupBy{
FieldID: model.NewId(),
Columns: []model.KanbanColumn{
{ID: model.NewId(), Name: "Todo", OptionIDs: []string{model.NewId()}},
{ID: model.NewId(), Name: "Done", OptionIDs: []string{model.NewId()}},
},
},
}
props, err := kanban.ToProps()
require.NoError(t, err)
return &model.View{
Title: "Test Kanban",
Type: model.ViewTypeKanban,
Props: props,
}
}
func TestCreateView(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("creates a view in a public channel", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
require.NotNil(t, created)
require.Equal(t, view.Title, created.Title)
require.Equal(t, th.BasicChannel.Id, created.ChannelId)
require.Equal(t, th.BasicUser.Id, created.CreatorId)
require.NotEmpty(t, created.Id)
})
t.Run("creates a view in a private channel", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, resp, err := th.Client.CreateView(context.Background(), th.BasicPrivateChannel.Id, view)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
require.NotNil(t, created)
require.Equal(t, th.BasicPrivateChannel.Id, created.ChannelId)
})
t.Run("regular user in DM can create view", func(t *testing.T) {
dm, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id)
require.Nil(t, appErr)
view := makeTestViewForAPI(t)
created, resp, err := th.Client.CreateView(context.Background(), dm.Id, view)
require.NoError(t, err)
CheckCreatedStatus(t, resp)
require.NotNil(t, created)
require.Equal(t, dm.Id, created.ChannelId)
})
t.Run("invalid body returns 400", func(t *testing.T) {
_, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, nil)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("empty title returns 400", func(t *testing.T) {
view := makeTestViewForAPI(t)
view.Title = ""
_, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("whitespace-only title returns 400", func(t *testing.T) {
view := makeTestViewForAPI(t)
view.Title = " "
_, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("oversized title returns 400", func(t *testing.T) {
view := makeTestViewForAPI(t)
view.Title = strings.Repeat("a", model.ViewTitleMaxRunes+1)
_, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
view := makeTestViewForAPI(t)
_, resp, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("permission denied without create_post", func(t *testing.T) {
th.RemovePermissionFromRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
defer th.AddPermissionToRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
view := makeTestViewForAPI(t)
_, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("unauthenticated returns 401", func(t *testing.T) {
client := th.CreateClient()
view := makeTestViewForAPI(t)
_, resp, err := client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
})
t.Run("exceeding max views per channel returns 400", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
for i := range model.MaxViewsPerChannel {
view := makeTestViewForAPI(t)
_, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err, "failed to create view %d", i)
}
// The next one should fail
view := makeTestViewForAPI(t)
_, resp, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
}
func TestGetViewsForChannel(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("lists views for channel", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
v1 := makeTestViewForAPI(t)
_, _, err := th.Client.CreateView(context.Background(), channel.Id, v1)
require.NoError(t, err)
v2 := makeTestViewForAPI(t)
v2.Title = "Second View"
_, _, err = th.Client.CreateView(context.Background(), channel.Id, v2)
require.NoError(t, err)
views, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, views, 2)
})
t.Run("empty channel returns empty list", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
views, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Empty(t, views)
})
t.Run("permission denied for non-member", func(t *testing.T) {
channel := th.CreatePrivateChannel(t)
// BasicUser2 is not a member of this newly created private channel
user2Client := th.CreateClient()
th.LoginBasic2WithClient(t, user2Client)
_, resp, err := user2Client.GetViewsForChannel(context.Background(), channel.Id, model.ViewQueryOpts{})
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("pagination", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
// Create 3 views
for i := range 3 {
v := makeTestViewForAPI(t)
v.Title = "Paginated View"
v.SortOrder = i
_, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
}
// Page 0: request per_page=2
page1, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id, model.ViewQueryOpts{PerPage: 2})
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, page1, 2)
// Page 1: next page
page2, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id, model.ViewQueryOpts{PerPage: 2, Page: 1})
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, page2, 1)
// Ensure no overlap between pages
page1IDs := map[string]bool{page1[0].Id: true, page1[1].Id: true}
require.False(t, page1IDs[page2[0].Id])
})
t.Run("excludes deleted views by default", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, makeTestViewForAPI(t))
require.NoError(t, err)
_, _, err = th.Client.CreateView(context.Background(), channel.Id, makeTestViewForAPI(t))
require.NoError(t, err)
resp, err := th.Client.DeleteView(context.Background(), channel.Id, created.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
views, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, views, 1)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
_, _, err := th.Client.CreateView(context.Background(), channel.Id, makeTestViewForAPI(t))
require.NoError(t, err)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
_, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("include_total_count returns views with count", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
for i := range 3 {
v := makeTestViewForAPI(t)
v.Title = "Count View"
v.SortOrder = i
_, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
}
views, totalCount, resp, err := th.Client.GetViewsForChannelWithCount(context.Background(), channel.Id, model.ViewQueryOpts{PerPage: 2})
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, views, 2)
require.Equal(t, int64(3), totalCount)
})
t.Run("out-of-bounds page returns empty list", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
_, _, err := th.Client.CreateView(context.Background(), channel.Id, makeTestViewForAPI(t))
require.NoError(t, err)
views, resp, err := th.Client.GetViewsForChannel(context.Background(), channel.Id, model.ViewQueryOpts{PerPage: 1, Page: 999})
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Empty(t, views)
})
}
func TestGetView(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("gets a view by ID", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
got, resp, err := th.Client.GetView(context.Background(), th.BasicChannel.Id, created.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Equal(t, created.Id, got.Id)
require.Equal(t, created.Title, got.Title)
})
t.Run("not found returns 404", func(t *testing.T) {
_, resp, err := th.Client.GetView(context.Background(), th.BasicChannel.Id, model.NewId())
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("wrong channel returns 404", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
otherChannel := th.CreatePublicChannel(t)
_, resp, err := th.Client.GetView(context.Background(), otherChannel.Id, created.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("deleted view returns 404", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
resp, err := th.Client.DeleteView(context.Background(), th.BasicChannel.Id, created.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
_, resp, err = th.Client.GetView(context.Background(), th.BasicChannel.Id, created.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
_, resp, err := th.Client.GetView(context.Background(), channel.Id, created.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("permission denied for non-member", func(t *testing.T) {
channel := th.CreatePrivateChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.SystemAdminClient.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
user2Client := th.CreateClient()
th.LoginBasic2WithClient(t, user2Client)
_, resp, err := user2Client.GetView(context.Background(), channel.Id, created.Id)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
}
func TestUpdateView(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("updates a view", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
newTitle := "Updated Title"
updated, resp, err := th.Client.UpdateView(context.Background(), th.BasicChannel.Id, created.Id, &model.ViewPatch{Title: &newTitle})
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Equal(t, newTitle, updated.Title)
require.Equal(t, created.Id, updated.Id)
})
t.Run("not found returns 404", func(t *testing.T) {
newTitle := "Title"
_, resp, err := th.Client.UpdateView(context.Background(), th.BasicChannel.Id, model.NewId(), &model.ViewPatch{Title: &newTitle})
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("wrong channel returns 404", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
otherChannel := th.CreatePublicChannel(t)
newTitle := "Title"
_, resp, err := th.Client.UpdateView(context.Background(), otherChannel.Id, created.Id, &model.ViewPatch{Title: &newTitle})
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("invalid patch returns 400", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
_, resp, err := th.Client.UpdateView(context.Background(), th.BasicChannel.Id, created.Id, nil)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
newTitle := "Title"
_, resp, err := th.Client.UpdateView(context.Background(), channel.Id, created.Id, &model.ViewPatch{Title: &newTitle})
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("permission denied without create_post", func(t *testing.T) {
th.RemovePermissionFromRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
defer th.AddPermissionToRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
view := makeTestViewForAPI(t)
created, _, err := th.SystemAdminClient.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
newTitle := "Title"
_, resp, err := th.Client.UpdateView(context.Background(), th.BasicChannel.Id, created.Id, &model.ViewPatch{Title: &newTitle})
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
}
func TestDeleteView(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("deletes a view", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
resp, err := th.Client.DeleteView(context.Background(), th.BasicChannel.Id, created.Id)
require.NoError(t, err)
CheckOKStatus(t, resp)
// verify it's gone
_, resp, err = th.Client.GetView(context.Background(), th.BasicChannel.Id, created.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("not found returns 404", func(t *testing.T) {
resp, err := th.Client.DeleteView(context.Background(), th.BasicChannel.Id, model.NewId())
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("wrong channel returns 404", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
otherChannel := th.CreatePublicChannel(t)
resp, err := th.Client.DeleteView(context.Background(), otherChannel.Id, created.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
resp, err := th.Client.DeleteView(context.Background(), channel.Id, created.Id)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("permission denied without create_post", func(t *testing.T) {
th.RemovePermissionFromRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
defer th.AddPermissionToRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
view := makeTestViewForAPI(t)
created, _, err := th.SystemAdminClient.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
resp, err := th.Client.DeleteView(context.Background(), th.BasicChannel.Id, created.Id)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
}
func TestUpdateViewSortOrder(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("reorders views in a channel", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
var created []*model.View
for i := range 3 {
v := makeTestViewForAPI(t)
v.Title = "Sort View"
v.SortOrder = i
c, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
created = append(created, c)
}
views, resp, err := th.Client.UpdateViewSortOrder(context.Background(), channel.Id, created[2].Id, 0)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, views, 3)
require.Equal(t, created[2].Id, views[0].Id)
require.Equal(t, created[0].Id, views[1].Id)
require.Equal(t, created[1].Id, views[2].Id)
})
t.Run("negative sort order returns 400", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
v := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), channel.Id, created.Id, -1)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("out of bounds returns 400", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
v := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), channel.Id, created.Id, 99)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("non-existent view returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
v := makeTestViewForAPI(t)
_, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), channel.Id, model.NewId(), 0)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
v := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), channel.Id, created.Id, 0)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("permission denied without create_post", func(t *testing.T) {
th.RemovePermissionFromRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
defer th.AddPermissionToRole(t, model.PermissionCreatePost.Id, model.ChannelUserRoleId)
channel := th.CreatePublicChannel(t)
v := makeTestViewForAPI(t)
created, _, err := th.SystemAdminClient.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), channel.Id, created.Id, 0)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("wrong channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
v := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, v)
require.NoError(t, err)
otherChannel := th.CreatePublicChannel(t)
// Create a view in otherChannel so the store has views to search through
_, _, err = th.Client.CreateView(context.Background(), otherChannel.Id, makeTestViewForAPI(t))
require.NoError(t, err)
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), otherChannel.Id, created.Id, 0)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("unauthenticated returns 401", func(t *testing.T) {
client := th.CreateClient()
_, resp, err := client.UpdateViewSortOrder(context.Background(), th.BasicChannel.Id, model.NewId(), 0)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
})
}
func TestGetPostsForView(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("returns all post types", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
normalPost, _, err := th.Client.CreatePost(context.Background(), &model.Post{ChannelId: channel.Id, Message: "normal post"})
require.NoError(t, err)
cardPost, _, appErr := th.App.CreatePost(th.Context, &model.Post{
ChannelId: channel.Id,
UserId: th.BasicUser.Id,
Message: "card post",
Type: model.PostTypeCard,
}, channel, model.CreatePostFlags{})
require.Nil(t, appErr)
postList, resp, err := th.Client.GetPostsForView(context.Background(), channel.Id, created.Id, 0, 60)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Contains(t, postList.Order, normalPost.Id)
require.Contains(t, postList.Order, cardPost.Id)
})
t.Run("wrong channel for view returns 404", func(t *testing.T) {
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.NoError(t, err)
otherChannel := th.CreatePublicChannel(t)
_, resp, err := th.Client.GetPostsForView(context.Background(), otherChannel.Id, created.Id, 0, 60)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("non-existent view returns 404", func(t *testing.T) {
_, resp, err := th.Client.GetPostsForView(context.Background(), th.BasicChannel.Id, model.NewId(), 0, 60)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("no channel read permission returns 403", func(t *testing.T) {
channel := th.CreatePrivateChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.SystemAdminClient.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
user2Client := th.CreateClient()
th.LoginBasic2WithClient(t, user2Client)
_, resp, err := user2Client.GetPostsForView(context.Background(), channel.Id, created.Id, 0, 60)
require.Error(t, err)
CheckForbiddenStatus(t, resp)
})
t.Run("unauthenticated returns 401", func(t *testing.T) {
client := th.CreateClient()
_, resp, err := client.GetPostsForView(context.Background(), th.BasicChannel.Id, model.NewId(), 0, 60)
require.Error(t, err)
CheckUnauthorizedStatus(t, resp)
})
t.Run("deleted channel returns 404", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
appErr := th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
require.Nil(t, appErr)
_, resp, err := th.Client.GetPostsForView(context.Background(), channel.Id, created.Id, 0, 60)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("pagination works", func(t *testing.T) {
channel := th.CreatePublicChannel(t)
view := makeTestViewForAPI(t)
created, _, err := th.Client.CreateView(context.Background(), channel.Id, view)
require.NoError(t, err)
// Channel already has a system join post, so we create 5 more for 6 total
for i := range 5 {
_, _, err = th.Client.CreatePost(context.Background(), &model.Post{
ChannelId: channel.Id,
Message: fmt.Sprintf("post %d", i),
})
require.NoError(t, err)
}
page1, resp, err := th.Client.GetPostsForView(context.Background(), channel.Id, created.Id, 0, 3)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, page1.Order, 3)
page2, resp, err := th.Client.GetPostsForView(context.Background(), channel.Id, created.Id, 1, 3)
require.NoError(t, err)
CheckOKStatus(t, resp)
require.Len(t, page2.Order, 3)
// No overlap between pages
for _, id := range page2.Order {
require.NotContains(t, page1.Order, id)
}
})
}
func TestGetPostsForViewFeatureFlagOff(t *testing.T) {
mainHelper.Parallel(t)
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.IntegratedBoards = false
}).InitBasic(t)
_, resp, err := th.Client.GetPostsForView(context.Background(), th.BasicChannel.Id, model.NewId(), 0, 60)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
}
func TestViewFeatureFlagOff(t *testing.T) {
mainHelper.Parallel(t)
th := SetupConfig(t, func(cfg *model.Config) {
cfg.FeatureFlags.IntegratedBoards = false
}).InitBasic(t)
view := makeTestViewForAPI(t)
t.Run("create returns 404 when flag is off", func(t *testing.T) {
_, resp, err := th.Client.CreateView(context.Background(), th.BasicChannel.Id, view)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("list returns 404 when flag is off", func(t *testing.T) {
_, resp, err := th.Client.GetViewsForChannel(context.Background(), th.BasicChannel.Id, model.ViewQueryOpts{})
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("get returns 404 when flag is off", func(t *testing.T) {
_, resp, err := th.Client.GetView(context.Background(), th.BasicChannel.Id, model.NewId())
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("update returns 404 when flag is off", func(t *testing.T) {
newTitle := "Title"
_, resp, err := th.Client.UpdateView(context.Background(), th.BasicChannel.Id, model.NewId(), &model.ViewPatch{Title: &newTitle})
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("delete returns 404 when flag is off", func(t *testing.T) {
resp, err := th.Client.DeleteView(context.Background(), th.BasicChannel.Id, model.NewId())
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
t.Run("sort order returns 404 when flag is off", func(t *testing.T) {
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), th.BasicChannel.Id, model.NewId(), 0)
require.Error(t, err)
CheckNotFoundStatus(t, resp)
})
}
func TestViewInvalidId(t *testing.T) {
mainHelper.Parallel(t)
th := setupViewTest(t)
t.Run("get with invalid view_id returns 400", func(t *testing.T) {
_, resp, err := th.Client.GetView(context.Background(), th.BasicChannel.Id, "invalid")
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("update with invalid view_id returns 400", func(t *testing.T) {
newTitle := "Title"
_, resp, err := th.Client.UpdateView(context.Background(), th.BasicChannel.Id, "invalid", &model.ViewPatch{Title: &newTitle})
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("delete with invalid view_id returns 400", func(t *testing.T) {
resp, err := th.Client.DeleteView(context.Background(), th.BasicChannel.Id, "invalid")
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
t.Run("sort order with invalid view_id returns 400", func(t *testing.T) {
_, resp, err := th.Client.UpdateViewSortOrder(context.Background(), th.BasicChannel.Id, "invalid", 0)
require.Error(t, err)
CheckBadRequestStatus(t, resp)
})
}