mirror of
https://github.com/hanzoai/team.git
synced 2026-09-30 01:57:35 +00:00
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.
870 lines
30 KiB
Go
870 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/hanzoteam/server/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.Empty(t, createdScheduledPost.ErrorCode)
|
|
require.Empty(t, updatedScheduledPost.ErrorCode)
|
|
require.Zero(t, updatedScheduledPost.ProcessedAt)
|
|
})
|
|
|
|
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
|
|
}
|
|
})
|
|
}
|