mattermost-community-enterp.../channels/app/scheduled_post_test.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
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>
2025-12-17 23:59:07 +09:00

864 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()
defer th.TearDown()
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(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()
team2 := th.CreateTeam()
th.LinkUserToTeam(th.BasicUser, team1)
th.LinkUserToTeam(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()
defer th.TearDown()
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()
_, 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()
_, 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()
_, 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()
_, 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(th.Context, 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()
defer th.TearDown()
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 ot be allowed to updated a scheduled post not belonging to the user", 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, th.BasicUser2.Id, createdScheduledPost, user1ConnID)
require.NotNil(t, appErr)
require.Equal(t, http.StatusForbidden, appErr.StatusCode)
require.Nil(t, updatedScheduledPost)
})
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(th.Context, th.BasicTeam)
th.AddUserToChannel(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(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)
})
}
func TestDeleteScheduledPost(t *testing.T) {
mainHelper.Parallel(t)
th := Setup(t).InitBasic()
defer th.TearDown()
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 not allow deleting someone else's scheduled post", 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.BasicUser2.Id, scheduledPost.Id, "connection_id")
require.NotNil(t, appErr)
require.Nil(t, deletedScheduledPost)
// try to fetch it again
reFetchedScheduledPost, err := th.Server.Store().ScheduledPost().Get(scheduledPost.Id)
require.NoError(t, err)
require.NotNil(t, reFetchedScheduledPost)
require.Equal(t, createdScheduledPost.Id, reFetchedScheduledPost.Id)
require.Equal(t, createdScheduledPost.Message, reFetchedScheduledPost.Message)
})
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()
defer th.TearDown()
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
}
})
}