mirror of
https://github.com/hanzoai/team.git
synced 2026-10-30 14:49:35 +00:00
* Guest cannot add file to post without upload_file permission * Move checks to api layer, addd checks in update patch post scheduled post * Minor * Linter fixes * i18n translations * removed the duplicated check from scheduled_post app layer * Move scheduled post permission test from app layer to API layer The permission check for updating scheduled posts belonging to other users was moved from the app layer to the API layer in the PR. This commit moves the corresponding test to the API layer to match. * Move scheduled post delete permission check to API layer Move the permission check for deleting scheduled posts from the app layer to the API layer, consistent with update permission check. Also enhance API tests to verify posts aren't modified after forbidden operations. * Fix inconsistent status code for non-existent scheduled post Return StatusNotFound instead of StatusInternalServerError when a scheduled post doesn't exist in UpdateScheduledPost, matching the API layer behavior. * Fix flaky TestAddUserToChannelCreatesChannelMemberHistoryRecord test Use ElementsMatch instead of Equal to compare user ID slices since the order returned from GetUsersInChannelDuring is not guaranteed. --------- Co-authored-by: Mattermost Build <[email protected]> Co-authored-by: Jesse Hallam <[email protected]>
868 lines
30 KiB
Go
868 lines
30 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package app
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSaveScheduledPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
user1ConnID := model.NewId()
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("cannot save invalid scheduled post", func(t *testing.T) {
|
|
scheduledPost := &model.ScheduledPost{
|
|
// a completely empty scheduled post
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("cannot save post scheduled in the past", func(t *testing.T) {
|
|
userId := model.NewId()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: "channel_id",
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() - 100000, // 100 seconds in the past
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("cannot scheduled post in a channel you don't belong to", func(t *testing.T) {
|
|
userId := model.NewId()
|
|
|
|
// we didn't create any channel member entry, so the user doesn't
|
|
// belong to the channel
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: "channel_id",
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("cannot schedule post in an archived channel", func(t *testing.T) {
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("can scheduled multiple posts in the same channel", func(t *testing.T) {
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
scheduledPost.Message = "this is a second scheduled post"
|
|
scheduledPost.Id = model.NewId()
|
|
createdScheduledPost, appErr = th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("cannot save an empty post", func(t *testing.T) {
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, createdScheduledPost)
|
|
})
|
|
|
|
t.Run("cannot save scheduled post in restricted DM", func(t *testing.T) {
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageTeam
|
|
})
|
|
|
|
// Create a DM channel between two users who don't share a team
|
|
dmChannel := th.CreateDmChannel(t, th.BasicUser2)
|
|
|
|
// Ensure the two users do not share a team
|
|
teams, err := th.App.GetTeamsForUser(th.BasicUser.Id)
|
|
require.Nil(t, err)
|
|
for _, team := range teams {
|
|
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, teamErr)
|
|
}
|
|
teams, err = th.App.GetTeamsForUser(th.BasicUser2.Id)
|
|
require.Nil(t, err)
|
|
for _, team := range teams {
|
|
teamErr := th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser2.Id, th.SystemAdminUser.Id)
|
|
require.Nil(t, teamErr)
|
|
}
|
|
|
|
// Create separate teams for each user
|
|
team1 := th.CreateTeam(t)
|
|
team2 := th.CreateTeam(t)
|
|
th.LinkUserToTeam(t, th.BasicUser, team1)
|
|
th.LinkUserToTeam(t, th.BasicUser2, team2)
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: dmChannel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 1000,
|
|
}
|
|
|
|
_, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Equal(t, "app.save_scheduled_post.restricted_dm.error", appErr.Id)
|
|
require.Equal(t, http.StatusBadRequest, appErr.StatusCode)
|
|
|
|
// Reset config
|
|
th.App.UpdateConfig(func(cfg *model.Config) {
|
|
*cfg.TeamSettings.RestrictDirectMessage = model.DirectMessageAny
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestGetUserTeamScheduledPosts(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
user1ConnID := model.NewId()
|
|
|
|
t.Run("should get created scheduled posts", func(t *testing.T) {
|
|
scheduledPost1 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost1, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost1, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost1)
|
|
|
|
// this wait is to ensure scheduled post 2 and 1 have some time gap between the two
|
|
// to ensure a deterministic ordering
|
|
time.Sleep(1 * time.Second)
|
|
|
|
scheduledPost2 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a second scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost2, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost2, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost2)
|
|
|
|
defer func() {
|
|
_ = th.Server.Store().ScheduledPost().PermanentlyDeleteScheduledPosts([]string{scheduledPost1.Id, createdScheduledPost2.Id})
|
|
}()
|
|
|
|
retrievedScheduledPosts, appErr := th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, th.BasicChannel.TeamId)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 2, len(retrievedScheduledPosts))
|
|
|
|
// more recently created scheduled post appears first in the list
|
|
require.Equal(t, createdScheduledPost1.Id, retrievedScheduledPosts[0].Id)
|
|
require.Equal(t, createdScheduledPost2.Id, retrievedScheduledPosts[1].Id)
|
|
})
|
|
|
|
t.Run("should handle no scheduled posts", func(t *testing.T) {
|
|
retrievedScheduledPosts, appErr := th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, th.BasicChannel.TeamId)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 0, len(retrievedScheduledPosts))
|
|
})
|
|
|
|
t.Run("should restrict to specified teams and DM/GMs", func(t *testing.T) {
|
|
scheduledPost1 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost1, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost1, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost1)
|
|
|
|
scheduledPost2 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a second scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost2, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost2, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost2)
|
|
|
|
defer func() {
|
|
_ = th.Server.Store().ScheduledPost().PermanentlyDeleteScheduledPosts([]string{scheduledPost1.Id, createdScheduledPost2.Id})
|
|
}()
|
|
|
|
// create a dummy team
|
|
secondTeam := th.CreateTeam(t)
|
|
_, appErr = th.App.JoinUserToTeam(th.Context, secondTeam, th.BasicUser, th.BasicUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
retrievedScheduledPosts, appErr := th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, secondTeam.Id)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 0, len(retrievedScheduledPosts))
|
|
})
|
|
|
|
t.Run("should not return scheduled posts from DMs and GMs when teamId is specified", func(t *testing.T) {
|
|
// start a DM between BasicUser1 and BasicUser2
|
|
dm, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// create a GM. Since a GM needs at least 3 users, we'll create a third user first
|
|
thirdUser := th.CreateUser(t)
|
|
_, appErr = th.App.JoinUserToTeam(th.Context, th.BasicTeam, thirdUser, thirdUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
gm, appErr := th.App.CreateGroupChannel(th.Context, []string{th.BasicUser.Id, th.BasicUser2.Id, thirdUser.Id}, th.BasicUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
scheduledPost1 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: dm.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost1, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost1, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost1)
|
|
|
|
scheduledPost2 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: gm.Id,
|
|
Message: "this is a second scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost2, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost2, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost2)
|
|
|
|
defer func() {
|
|
_ = th.Server.Store().ScheduledPost().PermanentlyDeleteScheduledPosts([]string{scheduledPost1.Id, createdScheduledPost2.Id})
|
|
}()
|
|
|
|
retrievedScheduledPosts, appErr := th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, th.BasicChannel.TeamId)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 0, len(retrievedScheduledPosts))
|
|
})
|
|
|
|
t.Run("should return scheduled posts from DMs and GMs when teamId is empty", func(t *testing.T) {
|
|
// start a DM between BasicUser1 and BasicUser2
|
|
dm, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// create a GM. Since a GM needs at least 3 users, we'll create a third user first
|
|
thirdUser := th.CreateUser(t)
|
|
_, appErr = th.App.JoinUserToTeam(th.Context, th.BasicTeam, thirdUser, thirdUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
gm, appErr := th.App.CreateGroupChannel(th.Context, []string{th.BasicUser.Id, th.BasicUser2.Id, thirdUser.Id}, th.BasicUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
scheduledPost1 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: dm.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost1, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost1, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost1)
|
|
|
|
scheduledPost2 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis() + 100,
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: gm.Id,
|
|
Message: "this is a second scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost2, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost2, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost2)
|
|
|
|
defer func() {
|
|
_ = th.Server.Store().ScheduledPost().PermanentlyDeleteScheduledPosts([]string{scheduledPost1.Id, createdScheduledPost2.Id})
|
|
}()
|
|
|
|
retrievedScheduledPosts, appErr := th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, "")
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 2, len(retrievedScheduledPosts))
|
|
|
|
// more recently created scheduled post appears first in the list
|
|
require.Equal(t, createdScheduledPost1.Id, retrievedScheduledPosts[0].Id)
|
|
require.Equal(t, createdScheduledPost2.Id, retrievedScheduledPosts[1].Id)
|
|
})
|
|
|
|
t.Run("should not be able to fetch scheduled posts for team user doesn't belong to", func(t *testing.T) {
|
|
// create a dummy team
|
|
team := th.CreateTeam(t)
|
|
_, appErr := th.App.JoinUserToTeam(th.Context, team, th.BasicUser, th.BasicUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// create a channel in this team
|
|
channel := th.CreateChannel(t, team)
|
|
|
|
// create scheduled post
|
|
scheduledPost1 := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost1, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost1, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost1)
|
|
|
|
// verify we are able to fetch this scheduled post
|
|
retrievedScheduledPosts, appErr := th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, team.Id)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 1, len(retrievedScheduledPosts))
|
|
|
|
appErr = th.App.RemoveUserFromTeam(th.Context, team.Id, th.BasicUser.Id, th.BasicUser.Id)
|
|
require.Nil(t, appErr)
|
|
|
|
// now we should not be able to fetch this scheduled post
|
|
retrievedScheduledPosts, appErr = th.App.GetUserTeamScheduledPosts(th.Context, th.BasicUser.Id, th.BasicChannel.TeamId)
|
|
require.Nil(t, appErr)
|
|
require.Equal(t, 0, len(retrievedScheduledPosts))
|
|
})
|
|
}
|
|
|
|
func TestUpdateScheduledPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
user1ConnID := model.NewId()
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
// first we'll create a scheduled post
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
// now we'll try updating it
|
|
newScheduledAtTime := model.GetMillis() + 9999999
|
|
createdScheduledPost.ScheduledAt = newScheduledAtTime
|
|
createdScheduledPost.Message = "Updated Message!!!"
|
|
|
|
updatedScheduledPost, appErr := th.App.UpdateScheduledPost(th.Context, userId, createdScheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, updatedScheduledPost)
|
|
|
|
require.Equal(t, newScheduledAtTime, updatedScheduledPost.ScheduledAt)
|
|
require.Equal(t, "Updated Message!!!", updatedScheduledPost.Message)
|
|
})
|
|
|
|
t.Run("should only allow updating limited fields", func(t *testing.T) {
|
|
// first we'll create a scheduled post
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
// now we'll try updating it
|
|
newUpdatedAt := model.GetMillis() + 1000000
|
|
createdScheduledPost.UpdateAt = newUpdatedAt // this should be overridden by the actual update time
|
|
createdScheduledPost.Message = "Updated Message" // this will update
|
|
newChannelId := model.NewId()
|
|
createdScheduledPost.ChannelId = newChannelId // this won't update
|
|
newCreateAt := model.GetMillis() + 5000000
|
|
createdScheduledPost.CreateAt = newCreateAt // this won't update
|
|
createdScheduledPost.FileIds = []string{model.NewId(), model.NewId()}
|
|
createdScheduledPost.ErrorCode = model.ScheduledPostErrorUnknownError
|
|
|
|
updatedScheduledPost, appErr := th.App.UpdateScheduledPost(th.Context, userId, createdScheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
require.NotEqual(t, newUpdatedAt, updatedScheduledPost.UpdateAt)
|
|
require.Equal(t, "Updated Message", updatedScheduledPost.Message)
|
|
require.NotEqual(t, newChannelId, updatedScheduledPost.ChannelId)
|
|
require.NotEqual(t, newCreateAt, updatedScheduledPost.CreateAt)
|
|
require.Equal(t, 2, len(updatedScheduledPost.FileIds))
|
|
require.Equal(t, model.ScheduledPostErrorUnknownError, createdScheduledPost.ErrorCode)
|
|
})
|
|
|
|
t.Run("should be able to update scheduled posts for channels user does not belong to", func(t *testing.T) {
|
|
channel := th.CreateChannel(t, th.BasicTeam)
|
|
th.AddUserToChannel(t, th.BasicUser, channel)
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
// now user will leave the channel
|
|
appErr = th.RemoveUserFromChannel(t, th.BasicUser, channel)
|
|
require.Nil(t, appErr)
|
|
|
|
createdScheduledPost.Message = "Updated message"
|
|
|
|
updatedScheduledPost, appErr := th.App.UpdateScheduledPost(th.Context, th.BasicUser.Id, createdScheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, updatedScheduledPost)
|
|
require.Equal(t, updatedScheduledPost.Message, "Updated message")
|
|
})
|
|
|
|
t.Run("should not be able to update a non existing scheduled post", func(t *testing.T) {
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
Id: model.NewId(),
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
|
|
updatedScheduledPost, appErr := th.App.UpdateScheduledPost(th.Context, th.BasicUser.Id, scheduledPost, user1ConnID)
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, updatedScheduledPost)
|
|
})
|
|
|
|
t.Run("burn on read scheduled post - verify type is preserved on create and update", func(t *testing.T) {
|
|
// Create a scheduled post with burn on read type
|
|
userId := model.NewId()
|
|
|
|
channel, err := th.GetSqlStore().Channel().Save(th.Context, &model.Channel{
|
|
Name: model.NewId(),
|
|
DisplayName: "Channel",
|
|
Type: model.ChannelTypeOpen,
|
|
}, 1000)
|
|
require.NoError(t, err)
|
|
|
|
_, err = th.GetSqlStore().Channel().SaveMember(th.Context, &model.ChannelMember{
|
|
ChannelId: channel.Id,
|
|
UserId: userId,
|
|
NotifyProps: model.GetDefaultChannelNotifyProps(),
|
|
SchemeGuest: false,
|
|
SchemeUser: true,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
defer func() {
|
|
_ = th.GetSqlStore().Channel().Delete(channel.Id, model.GetMillis())
|
|
_ = th.GetSqlStore().Channel().RemoveMember(th.Context, channel.Id, userId)
|
|
}()
|
|
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userId,
|
|
ChannelId: channel.Id,
|
|
Message: "this is a burn on read scheduled post",
|
|
Type: model.PostTypeBurnOnRead,
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
require.Equal(t, model.PostTypeBurnOnRead, createdScheduledPost.Type)
|
|
|
|
fetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(createdScheduledPost.Id)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, fetchedScheduledPost)
|
|
require.Equal(t, model.PostTypeBurnOnRead, fetchedScheduledPost.Type)
|
|
|
|
createdScheduledPost.Message = "Updated burn on read message"
|
|
createdScheduledPost.ScheduledAt = model.GetMillis() + 200000
|
|
// Try to change the type - it should NOT change
|
|
createdScheduledPost.Type = ""
|
|
|
|
updatedScheduledPost, appErr := th.App.UpdateScheduledPost(th.Context, userId, createdScheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, updatedScheduledPost)
|
|
|
|
// Verify the type is NOT changed - it should still be burn on read
|
|
require.Equal(t, model.PostTypeBurnOnRead, updatedScheduledPost.Type)
|
|
require.Equal(t, "Updated burn on read message", updatedScheduledPost.Message)
|
|
|
|
// Fetch again from store to verify the type is still burn on read in the database
|
|
reFetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(createdScheduledPost.Id)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, reFetchedScheduledPost)
|
|
require.Equal(t, model.PostTypeBurnOnRead, reFetchedScheduledPost.Type)
|
|
|
|
// Try another update with a different type value - verify the type is still NOT changed
|
|
existingPost, err := th.Server.Store().ScheduledPost().Get(createdScheduledPost.Id)
|
|
require.NoError(t, err)
|
|
existingPost.Message = "Another update attempt"
|
|
existingPost.ScheduledAt = model.GetMillis() + 300000
|
|
// Try to change the type to a different value - verify it doesn't change
|
|
existingPost.Type = "some_other_type"
|
|
|
|
updatedScheduledPost2, appErr := th.App.UpdateScheduledPost(th.Context, userId, existingPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, updatedScheduledPost2)
|
|
|
|
// Verify the type remains burn on read even when we try to change it to a different value
|
|
require.Equal(t, model.PostTypeBurnOnRead, updatedScheduledPost2.Type)
|
|
|
|
// Final verification from store
|
|
finalFetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(createdScheduledPost.Id)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, finalFetchedScheduledPost)
|
|
require.Equal(t, model.PostTypeBurnOnRead, finalFetchedScheduledPost.Type)
|
|
})
|
|
}
|
|
|
|
func TestDeleteScheduledPost(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
user1ConnID := model.NewId()
|
|
|
|
t.Run("base case", func(t *testing.T) {
|
|
// first we'll create a scheduled post
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: th.BasicUser.Id,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000, // 100 seconds in the future
|
|
}
|
|
createdScheduledPost, appErr := th.App.SaveScheduledPost(th.Context, scheduledPost, user1ConnID)
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, createdScheduledPost)
|
|
|
|
fetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(scheduledPost.Id)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, fetchedScheduledPost)
|
|
require.Equal(t, createdScheduledPost.Id, fetchedScheduledPost.Id)
|
|
require.Equal(t, createdScheduledPost.Message, fetchedScheduledPost.Message)
|
|
|
|
// now we'll delete it
|
|
var deletedScheduledPost *model.ScheduledPost
|
|
deletedScheduledPost, appErr = th.App.DeleteScheduledPost(th.Context, th.BasicUser.Id, scheduledPost.Id, "connection_id")
|
|
require.Nil(t, appErr)
|
|
require.NotNil(t, deletedScheduledPost)
|
|
|
|
require.Equal(t, scheduledPost.Id, deletedScheduledPost.Id)
|
|
require.Equal(t, scheduledPost.Message, deletedScheduledPost.Message)
|
|
|
|
// try to fetch it again
|
|
reFetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(scheduledPost.Id)
|
|
require.Error(t, err) // This will produce error as the row doesn't exist
|
|
require.Nil(t, reFetchedScheduledPost)
|
|
})
|
|
|
|
t.Run("should producer error when deleting non existing scheduled post", func(t *testing.T) {
|
|
var deletedScheduledPost *model.ScheduledPost
|
|
deletedScheduledPost, appErr := th.App.DeleteScheduledPost(th.Context, th.BasicUser.Id, model.NewId(), "connection_id")
|
|
require.NotNil(t, appErr)
|
|
require.Nil(t, deletedScheduledPost)
|
|
})
|
|
}
|
|
|
|
func TestPublishScheduledPostEvent(t *testing.T) {
|
|
mainHelper.Parallel(t)
|
|
th := Setup(t).InitBasic(t)
|
|
|
|
userID := th.BasicUser.Id
|
|
|
|
messages, closeWS := connectFakeWebSocket(t, th, userID, "", []model.WebsocketEventType{model.WebsocketScheduledPostCreated})
|
|
defer closeWS()
|
|
|
|
t.Run("should publish ws event when scheduledPost is valid", func(t *testing.T) {
|
|
scheduledPost := &model.ScheduledPost{
|
|
Draft: model.Draft{
|
|
CreateAt: model.GetMillis(),
|
|
UserId: userID,
|
|
ChannelId: th.BasicChannel.Id,
|
|
Message: "this is a scheduled post",
|
|
},
|
|
ScheduledAt: model.GetMillis() + 100000,
|
|
}
|
|
|
|
th.App.PublishScheduledPostEvent(th.Context, model.WebsocketScheduledPostCreated, scheduledPost, "fake_connection_id")
|
|
|
|
received := <-messages
|
|
require.Equal(t, model.WebsocketScheduledPostCreated, received.EventType())
|
|
require.Equal(t, userID, received.GetBroadcast().UserId)
|
|
|
|
scheduledPostJSON, err := json.Marshal(scheduledPost)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(scheduledPostJSON), received.GetData()["scheduledPost"])
|
|
})
|
|
|
|
t.Run("should handle nil scheduledPost scenario", func(t *testing.T) {
|
|
// Drain any existing messages
|
|
drained := false
|
|
for !drained {
|
|
select {
|
|
case <-messages:
|
|
default:
|
|
drained = true
|
|
}
|
|
}
|
|
|
|
th.App.PublishScheduledPostEvent(th.Context, model.WebsocketScheduledPostCreated, nil, "fake_connection_id")
|
|
|
|
select {
|
|
case msg := <-messages:
|
|
t.Errorf("Expected no message, but got one: %+v", msg)
|
|
case <-time.After(100 * time.Millisecond):
|
|
// there was no message sent to the channel, so test is successful
|
|
}
|
|
})
|
|
}
|