1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package app
5
6import (
7	"context"
8	"fmt"
9	"net/http"
10	"os"
11	"sort"
12	"strings"
13	"sync"
14	"testing"
15
16	"github.com/stretchr/testify/assert"
17	"github.com/stretchr/testify/mock"
18	"github.com/stretchr/testify/require"
19
20	"github.com/mattermost/mattermost-server/v6/model"
21	"github.com/mattermost/mattermost-server/v6/services/users"
22	"github.com/mattermost/mattermost-server/v6/store/storetest/mocks"
23)
24
25func TestPermanentDeleteChannel(t *testing.T) {
26	th := Setup(t).InitBasic()
27	defer th.TearDown()
28
29	th.App.UpdateConfig(func(cfg *model.Config) {
30		*cfg.ServiceSettings.EnableIncomingWebhooks = true
31		*cfg.ServiceSettings.EnableOutgoingWebhooks = true
32	})
33
34	channel, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "deletion-test", Name: "deletion-test", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
35	require.NotNil(t, channel, "Channel shouldn't be nil")
36	require.Nil(t, err)
37	defer func() {
38		th.App.PermanentDeleteChannel(channel)
39	}()
40
41	incoming, err := th.App.CreateIncomingWebhookForChannel(th.BasicUser.Id, channel, &model.IncomingWebhook{ChannelId: channel.Id})
42	require.NotNil(t, incoming, "incoming webhook should not be nil")
43	require.Nil(t, err, "Unable to create Incoming Webhook for Channel")
44	defer th.App.DeleteIncomingWebhook(incoming.Id)
45
46	incoming, err = th.App.GetIncomingWebhook(incoming.Id)
47	require.NotNil(t, incoming, "incoming webhook should not be nil")
48	require.Nil(t, err, "Unable to get new incoming webhook")
49
50	outgoing, err := th.App.CreateOutgoingWebhook(&model.OutgoingWebhook{
51		ChannelId:    channel.Id,
52		TeamId:       channel.TeamId,
53		CreatorId:    th.BasicUser.Id,
54		CallbackURLs: []string{"http://foo"},
55	})
56	require.Nil(t, err)
57	defer th.App.DeleteOutgoingWebhook(outgoing.Id)
58
59	outgoing, err = th.App.GetOutgoingWebhook(outgoing.Id)
60	require.NotNil(t, outgoing, "Outgoing webhook should not be nil")
61	require.Nil(t, err, "Unable to get new outgoing webhook")
62
63	err = th.App.PermanentDeleteChannel(channel)
64	require.Nil(t, err)
65
66	incoming, err = th.App.GetIncomingWebhook(incoming.Id)
67	require.Nil(t, incoming, "Incoming webhook should be nil")
68	require.NotNil(t, err, "Incoming webhook wasn't deleted")
69
70	outgoing, err = th.App.GetOutgoingWebhook(outgoing.Id)
71	require.Nil(t, outgoing, "Outgoing webhook should be nil")
72	require.NotNil(t, err, "Outgoing webhook wasn't deleted")
73}
74
75func TestRemoveAllDeactivatedMembersFromChannel(t *testing.T) {
76	th := Setup(t).InitBasic()
77	defer th.TearDown()
78	var err *model.AppError
79
80	team := th.CreateTeam()
81	channel := th.CreateChannel(team)
82	defer func() {
83		th.App.PermanentDeleteChannel(channel)
84		th.App.PermanentDeleteTeam(team)
85	}()
86
87	_, _, err = th.App.AddUserToTeam(th.Context, team.Id, th.BasicUser.Id, "")
88	require.Nil(t, err)
89
90	deacivatedUser := th.CreateUser()
91	_, _, err = th.App.AddUserToTeam(th.Context, team.Id, deacivatedUser.Id, "")
92	require.Nil(t, err)
93	_, err = th.App.AddUserToChannel(deacivatedUser, channel, false)
94	require.Nil(t, err)
95	channelMembers, err := th.App.GetChannelMembersPage(channel.Id, 0, 10000000)
96	require.Nil(t, err)
97	require.Len(t, channelMembers, 2)
98	_, err = th.App.UpdateActive(th.Context, deacivatedUser, false)
99	require.Nil(t, err)
100
101	err = th.App.RemoveAllDeactivatedMembersFromChannel(channel)
102	require.Nil(t, err)
103
104	channelMembers, err = th.App.GetChannelMembersPage(channel.Id, 0, 10000000)
105	require.Nil(t, err)
106	require.Len(t, channelMembers, 1)
107}
108
109func TestMoveChannel(t *testing.T) {
110	t.Run("should move channels between teams", func(t *testing.T) {
111		th := Setup(t).InitBasic()
112		defer th.TearDown()
113		var err *model.AppError
114
115		sourceTeam := th.CreateTeam()
116		targetTeam := th.CreateTeam()
117		channel1 := th.CreateChannel(sourceTeam)
118		defer func() {
119			th.App.PermanentDeleteChannel(channel1)
120			th.App.PermanentDeleteTeam(sourceTeam)
121			th.App.PermanentDeleteTeam(targetTeam)
122		}()
123
124		_, _, err = th.App.AddUserToTeam(th.Context, sourceTeam.Id, th.BasicUser.Id, "")
125		require.Nil(t, err)
126
127		_, _, err = th.App.AddUserToTeam(th.Context, sourceTeam.Id, th.BasicUser2.Id, "")
128		require.Nil(t, err)
129
130		_, _, err = th.App.AddUserToTeam(th.Context, targetTeam.Id, th.BasicUser.Id, "")
131		require.Nil(t, err)
132
133		_, err = th.App.AddUserToChannel(th.BasicUser, channel1, false)
134		require.Nil(t, err)
135
136		_, err = th.App.AddUserToChannel(th.BasicUser2, channel1, false)
137		require.Nil(t, err)
138
139		err = th.App.MoveChannel(th.Context, targetTeam, channel1, th.BasicUser)
140		require.NotNil(t, err, "Should have failed due to mismatched members.")
141
142		_, _, err = th.App.AddUserToTeam(th.Context, targetTeam.Id, th.BasicUser2.Id, "")
143		require.Nil(t, err)
144
145		err = th.App.MoveChannel(th.Context, targetTeam, channel1, th.BasicUser)
146		require.Nil(t, err)
147
148		// Test moving a channel with a deactivated user who isn't in the destination team.
149		// It should fail, unless removeDeactivatedMembers is true.
150		deacivatedUser := th.CreateUser()
151		channel2 := th.CreateChannel(sourceTeam)
152		defer th.App.PermanentDeleteChannel(channel2)
153
154		_, _, err = th.App.AddUserToTeam(th.Context, sourceTeam.Id, deacivatedUser.Id, "")
155		require.Nil(t, err)
156		_, err = th.App.AddUserToChannel(th.BasicUser, channel2, false)
157		require.Nil(t, err)
158
159		_, err = th.App.AddUserToChannel(deacivatedUser, channel2, false)
160		require.Nil(t, err)
161
162		_, err = th.App.UpdateActive(th.Context, deacivatedUser, false)
163		require.Nil(t, err)
164
165		err = th.App.MoveChannel(th.Context, targetTeam, channel2, th.BasicUser)
166		require.NotNil(t, err, "Should have failed due to mismatched deacivated member.")
167
168		// Test moving a channel with no members.
169		channel3 := &model.Channel{
170			DisplayName: "dn_" + model.NewId(),
171			Name:        "name_" + model.NewId(),
172			Type:        model.ChannelTypeOpen,
173			TeamId:      sourceTeam.Id,
174			CreatorId:   th.BasicUser.Id,
175		}
176
177		channel3, err = th.App.CreateChannel(th.Context, channel3, false)
178		require.Nil(t, err)
179		defer th.App.PermanentDeleteChannel(channel3)
180
181		err = th.App.MoveChannel(th.Context, targetTeam, channel3, th.BasicUser)
182		assert.Nil(t, err)
183	})
184
185	t.Run("should remove sidebar entries when moving channels from one team to another", func(t *testing.T) {
186		th := Setup(t).InitBasic()
187		defer th.TearDown()
188
189		sourceTeam := th.CreateTeam()
190		targetTeam := th.CreateTeam()
191		channel := th.CreateChannel(sourceTeam)
192
193		th.LinkUserToTeam(th.BasicUser, sourceTeam)
194		th.LinkUserToTeam(th.BasicUser, targetTeam)
195		th.AddUserToChannel(th.BasicUser, channel)
196
197		// Put the channel in a custom category so that it explicitly exists in SidebarChannels
198		category, err := th.App.CreateSidebarCategory(th.BasicUser.Id, sourceTeam.Id, &model.SidebarCategoryWithChannels{
199			SidebarCategory: model.SidebarCategory{
200				DisplayName: "new category",
201			},
202			Channels: []string{channel.Id},
203		})
204		require.Nil(t, err)
205		require.Equal(t, []string{channel.Id}, category.Channels)
206
207		err = th.App.MoveChannel(th.Context, targetTeam, channel, th.BasicUser)
208		require.Nil(t, err)
209
210		moved, err := th.App.GetChannel(channel.Id)
211		require.Nil(t, err)
212		require.Equal(t, targetTeam.Id, moved.TeamId)
213
214		// The channel should no longer be on the old team
215		updatedCategory, err := th.App.GetSidebarCategory(category.Id)
216		require.Nil(t, err)
217		assert.Equal(t, []string{}, updatedCategory.Channels)
218
219		// And it should be on the new team instead
220		categories, err := th.App.GetSidebarCategories(th.BasicUser.Id, targetTeam.Id)
221		require.Nil(t, err)
222		require.Equal(t, model.SidebarCategoryChannels, categories.Categories[1].Type)
223		assert.Contains(t, categories.Categories[1].Channels, channel.Id)
224	})
225}
226
227func TestRemoveUsersFromChannelNotMemberOfTeam(t *testing.T) {
228	th := Setup(t).InitBasic()
229	defer th.TearDown()
230
231	team := th.CreateTeam()
232	team2 := th.CreateTeam()
233	channel1 := th.CreateChannel(team)
234	defer func() {
235		th.App.PermanentDeleteChannel(channel1)
236		th.App.PermanentDeleteTeam(team)
237		th.App.PermanentDeleteTeam(team2)
238	}()
239
240	_, _, err := th.App.AddUserToTeam(th.Context, team.Id, th.BasicUser.Id, "")
241	require.Nil(t, err)
242	_, _, err = th.App.AddUserToTeam(th.Context, team2.Id, th.BasicUser.Id, "")
243	require.Nil(t, err)
244	_, _, err = th.App.AddUserToTeam(th.Context, team.Id, th.BasicUser2.Id, "")
245	require.Nil(t, err)
246
247	_, err = th.App.AddUserToChannel(th.BasicUser, channel1, false)
248	require.Nil(t, err)
249	_, err = th.App.AddUserToChannel(th.BasicUser2, channel1, false)
250	require.Nil(t, err)
251
252	err = th.App.RemoveUsersFromChannelNotMemberOfTeam(th.Context, th.SystemAdminUser, channel1, team2)
253	require.Nil(t, err)
254
255	channelMembers, err := th.App.GetChannelMembersPage(channel1.Id, 0, 10000000)
256	require.Nil(t, err)
257	require.Len(t, channelMembers, 1)
258	members := make([]model.ChannelMember, len(channelMembers))
259	for i, m := range channelMembers {
260		members[i] = m
261	}
262	require.Equal(t, members[0].UserId, th.BasicUser.Id)
263}
264
265func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordTownSquare(t *testing.T) {
266	th := Setup(t).InitBasic()
267	defer th.TearDown()
268
269	// figure out the initial number of users in town square
270	channel, err := th.App.Srv().Store.Channel().GetByName(th.BasicTeam.Id, "town-square", true)
271	require.NoError(t, err)
272	townSquareChannelId := channel.Id
273	users, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, townSquareChannelId)
274	require.NoError(t, nErr)
275	initialNumTownSquareUsers := len(users)
276
277	// create a new user that joins the default channels
278	user := th.CreateUser()
279	th.App.JoinDefaultChannels(th.Context, th.BasicTeam.Id, user, false, "")
280
281	// there should be a ChannelMemberHistory record for the user
282	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, townSquareChannelId)
283	require.NoError(t, nErr)
284	assert.Len(t, histories, initialNumTownSquareUsers+1)
285
286	found := false
287	for _, history := range histories {
288		if user.Id == history.UserId && townSquareChannelId == history.ChannelId {
289			found = true
290			break
291		}
292	}
293	assert.True(t, found)
294}
295
296func TestJoinDefaultChannelsCreatesChannelMemberHistoryRecordOffTopic(t *testing.T) {
297	th := Setup(t).InitBasic()
298	defer th.TearDown()
299
300	// figure out the initial number of users in off-topic
301	channel, err := th.App.Srv().Store.Channel().GetByName(th.BasicTeam.Id, "off-topic", true)
302	require.NoError(t, err)
303	offTopicChannelId := channel.Id
304	users, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, offTopicChannelId)
305	require.NoError(t, nErr)
306	initialNumTownSquareUsers := len(users)
307
308	// create a new user that joins the default channels
309	user := th.CreateUser()
310	th.App.JoinDefaultChannels(th.Context, th.BasicTeam.Id, user, false, "")
311
312	// there should be a ChannelMemberHistory record for the user
313	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, offTopicChannelId)
314	require.NoError(t, nErr)
315	assert.Len(t, histories, initialNumTownSquareUsers+1)
316
317	found := false
318	for _, history := range histories {
319		if user.Id == history.UserId && offTopicChannelId == history.ChannelId {
320			found = true
321			break
322		}
323	}
324	assert.True(t, found)
325}
326
327func TestJoinDefaultChannelsExperimentalDefaultChannels(t *testing.T) {
328	th := Setup(t).InitBasic()
329	defer th.TearDown()
330
331	basicChannel2 := th.CreateChannel(th.BasicTeam)
332	defer th.App.PermanentDeleteChannel(basicChannel2)
333	defaultChannelList := []string{th.BasicChannel.Name, basicChannel2.Name, basicChannel2.Name}
334	th.App.Config().TeamSettings.ExperimentalDefaultChannels = defaultChannelList
335
336	user := th.CreateUser()
337	th.App.JoinDefaultChannels(th.Context, th.BasicTeam.Id, user, false, "")
338
339	for _, channelName := range defaultChannelList {
340		channel, err := th.App.GetChannelByName(channelName, th.BasicTeam.Id, false)
341		require.Nil(t, err, "Expected nil, didn't receive nil")
342
343		member, err := th.App.GetChannelMember(context.Background(), channel.Id, user.Id)
344
345		require.NotNil(t, member, "Expected member object, got nil")
346		require.Nil(t, err, "Expected nil object, didn't receive nil")
347	}
348}
349
350func TestCreateChannelPublicCreatesChannelMemberHistoryRecord(t *testing.T) {
351	th := Setup(t).InitBasic()
352	defer th.TearDown()
353
354	// creates a public channel and adds basic user to it
355	publicChannel := th.createChannel(th.BasicTeam, model.ChannelTypeOpen)
356
357	// there should be a ChannelMemberHistory record for the user
358	histories, err := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, publicChannel.Id)
359	require.NoError(t, err)
360	assert.Len(t, histories, 1)
361	assert.Equal(t, th.BasicUser.Id, histories[0].UserId)
362	assert.Equal(t, publicChannel.Id, histories[0].ChannelId)
363}
364
365func TestCreateChannelPrivateCreatesChannelMemberHistoryRecord(t *testing.T) {
366	th := Setup(t).InitBasic()
367	defer th.TearDown()
368
369	// creates a private channel and adds basic user to it
370	privateChannel := th.createChannel(th.BasicTeam, model.ChannelTypePrivate)
371
372	// there should be a ChannelMemberHistory record for the user
373	histories, err := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, privateChannel.Id)
374	require.NoError(t, err)
375	assert.Len(t, histories, 1)
376	assert.Equal(t, th.BasicUser.Id, histories[0].UserId)
377	assert.Equal(t, privateChannel.Id, histories[0].ChannelId)
378}
379func TestCreateChannelDisplayNameTrimsWhitespace(t *testing.T) {
380	th := Setup(t).InitBasic()
381	defer th.TearDown()
382
383	channel, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "  Public 1  ", Name: "public1", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
384	defer th.App.PermanentDeleteChannel(channel)
385	require.Nil(t, err)
386	require.Equal(t, channel.DisplayName, "Public 1")
387}
388
389func TestUpdateChannelPrivacy(t *testing.T) {
390	th := Setup(t).InitBasic()
391	defer th.TearDown()
392
393	privateChannel := th.createChannel(th.BasicTeam, model.ChannelTypePrivate)
394	privateChannel.Type = model.ChannelTypeOpen
395
396	publicChannel, err := th.App.UpdateChannelPrivacy(th.Context, privateChannel, th.BasicUser)
397	require.Nil(t, err, "Failed to update channel privacy.")
398	assert.Equal(t, publicChannel.Id, privateChannel.Id)
399	assert.Equal(t, publicChannel.Type, model.ChannelTypeOpen)
400}
401
402func TestCreateGroupChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
403	th := Setup(t).InitBasic()
404	defer th.TearDown()
405
406	user1 := th.CreateUser()
407	user2 := th.CreateUser()
408
409	groupUserIds := make([]string, 0)
410	groupUserIds = append(groupUserIds, user1.Id)
411	groupUserIds = append(groupUserIds, user2.Id)
412	groupUserIds = append(groupUserIds, th.BasicUser.Id)
413
414	channel, err := th.App.CreateGroupChannel(groupUserIds, th.BasicUser.Id)
415
416	require.Nil(t, err, "Failed to create group channel.")
417	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)
418	require.NoError(t, nErr)
419	assert.Len(t, histories, 3)
420
421	channelMemberHistoryUserIds := make([]string, 0)
422	for _, history := range histories {
423		assert.Equal(t, channel.Id, history.ChannelId)
424		channelMemberHistoryUserIds = append(channelMemberHistoryUserIds, history.UserId)
425	}
426
427	sort.Strings(groupUserIds)
428	sort.Strings(channelMemberHistoryUserIds)
429	assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
430}
431
432func TestCreateDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
433	th := Setup(t)
434	defer th.TearDown()
435
436	user1 := th.CreateUser()
437	user2 := th.CreateUser()
438
439	channel, err := th.App.GetOrCreateDirectChannel(th.Context, user1.Id, user2.Id)
440	require.Nil(t, err, "Failed to create direct channel.")
441
442	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)
443	require.NoError(t, nErr)
444	assert.Len(t, histories, 2)
445
446	historyId0 := histories[0].UserId
447	historyId1 := histories[1].UserId
448	switch historyId0 {
449	case user1.Id:
450		assert.Equal(t, user2.Id, historyId1)
451	case user2.Id:
452		assert.Equal(t, user1.Id, historyId1)
453	default:
454		require.Fail(t, "Unexpected user id in ChannelMemberHistory table", historyId0)
455	}
456}
457
458func TestGetDirectChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
459	th := Setup(t)
460	defer th.TearDown()
461
462	user1 := th.CreateUser()
463	user2 := th.CreateUser()
464
465	// this function call implicitly creates a direct channel between the two users if one doesn't already exist
466	channel, err := th.App.GetOrCreateDirectChannel(th.Context, user1.Id, user2.Id)
467	require.Nil(t, err, "Failed to create direct channel.")
468
469	// there should be a ChannelMemberHistory record for both users
470	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)
471	require.NoError(t, nErr)
472	assert.Len(t, histories, 2)
473
474	historyId0 := histories[0].UserId
475	historyId1 := histories[1].UserId
476	switch historyId0 {
477	case user1.Id:
478		assert.Equal(t, user2.Id, historyId1)
479	case user2.Id:
480		assert.Equal(t, user1.Id, historyId1)
481	default:
482		require.Fail(t, "Unexpected user id in ChannelMemberHistory table", historyId0)
483	}
484}
485
486func TestAddUserToChannelCreatesChannelMemberHistoryRecord(t *testing.T) {
487	th := Setup(t).InitBasic()
488	defer th.TearDown()
489
490	// create a user and add it to a channel
491	user := th.CreateUser()
492	_, err := th.App.AddTeamMember(th.Context, th.BasicTeam.Id, user.Id)
493	require.Nil(t, err, "Failed to add user to team.")
494
495	groupUserIds := make([]string, 0)
496	groupUserIds = append(groupUserIds, th.BasicUser.Id)
497	groupUserIds = append(groupUserIds, user.Id)
498
499	channel := th.createChannel(th.BasicTeam, model.ChannelTypeOpen)
500
501	_, err = th.App.AddUserToChannel(user, channel, false)
502	require.Nil(t, err, "Failed to add user to channel.")
503
504	// there should be a ChannelMemberHistory record for the user
505	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)
506	require.NoError(t, nErr)
507	assert.Len(t, histories, 2)
508	channelMemberHistoryUserIds := make([]string, 0)
509	for _, history := range histories {
510		assert.Equal(t, channel.Id, history.ChannelId)
511		channelMemberHistoryUserIds = append(channelMemberHistoryUserIds, history.UserId)
512	}
513	assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
514}
515
516func TestLeaveDefaultChannel(t *testing.T) {
517	th := Setup(t).InitBasic()
518	defer th.TearDown()
519
520	guest := th.CreateGuest()
521	th.LinkUserToTeam(guest, th.BasicTeam)
522
523	townSquare, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, false)
524	require.Nil(t, err)
525	th.AddUserToChannel(guest, townSquare)
526	th.AddUserToChannel(th.BasicUser, townSquare)
527
528	t.Run("User tries to leave the default channel", func(t *testing.T) {
529		err = th.App.LeaveChannel(th.Context, townSquare.Id, th.BasicUser.Id)
530		assert.NotNil(t, err, "It should fail to remove a regular user from the default channel")
531		assert.Equal(t, err.Id, "api.channel.remove.default.app_error")
532		_, err = th.App.GetChannelMember(context.Background(), townSquare.Id, th.BasicUser.Id)
533		assert.Nil(t, err)
534	})
535
536	t.Run("Guest leaves the default channel", func(t *testing.T) {
537		err = th.App.LeaveChannel(th.Context, townSquare.Id, guest.Id)
538		assert.Nil(t, err, "It should allow to remove a guest user from the default channel")
539		_, err = th.App.GetChannelMember(context.Background(), townSquare.Id, guest.Id)
540		assert.NotNil(t, err)
541	})
542}
543
544func TestLeaveLastChannel(t *testing.T) {
545	th := Setup(t).InitBasic()
546	defer th.TearDown()
547
548	guest := th.CreateGuest()
549	th.LinkUserToTeam(guest, th.BasicTeam)
550
551	townSquare, err := th.App.GetChannelByName("town-square", th.BasicTeam.Id, false)
552	require.Nil(t, err)
553	th.AddUserToChannel(guest, townSquare)
554	th.AddUserToChannel(guest, th.BasicChannel)
555
556	t.Run("Guest leaves not last channel", func(t *testing.T) {
557		err = th.App.LeaveChannel(th.Context, townSquare.Id, guest.Id)
558		require.Nil(t, err)
559		_, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
560		assert.Nil(t, err, "It should maintain the team membership")
561	})
562
563	t.Run("Guest leaves last channel", func(t *testing.T) {
564		err = th.App.LeaveChannel(th.Context, th.BasicChannel.Id, guest.Id)
565		assert.Nil(t, err, "It should allow to remove a guest user from the default channel")
566		_, err = th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, guest.Id)
567		assert.NotNil(t, err)
568		_, err = th.App.GetTeamMember(th.BasicTeam.Id, guest.Id)
569		assert.Nil(t, err, "It should remove the team membership")
570	})
571}
572
573func TestAddChannelMemberNoUserRequestor(t *testing.T) {
574	th := Setup(t).InitBasic()
575	defer th.TearDown()
576
577	// create a user and add it to a channel
578	user := th.CreateUser()
579	_, err := th.App.AddTeamMember(th.Context, th.BasicTeam.Id, user.Id)
580	require.Nil(t, err)
581
582	groupUserIds := make([]string, 0)
583	groupUserIds = append(groupUserIds, th.BasicUser.Id)
584	groupUserIds = append(groupUserIds, user.Id)
585
586	channel := th.createChannel(th.BasicTeam, model.ChannelTypeOpen)
587
588	_, err = th.App.AddChannelMember(th.Context, user.Id, channel, ChannelMemberOpts{})
589	require.Nil(t, err, "Failed to add user to channel.")
590
591	// there should be a ChannelMemberHistory record for the user
592	histories, nErr := th.App.Srv().Store.ChannelMemberHistory().GetUsersInChannelDuring(model.GetMillis()-100, model.GetMillis()+100, channel.Id)
593	require.NoError(t, nErr)
594	assert.Len(t, histories, 2)
595	channelMemberHistoryUserIds := make([]string, 0)
596	for _, history := range histories {
597		assert.Equal(t, channel.Id, history.ChannelId)
598		channelMemberHistoryUserIds = append(channelMemberHistoryUserIds, history.UserId)
599	}
600	assert.Equal(t, groupUserIds, channelMemberHistoryUserIds)
601
602	postList, nErr := th.App.Srv().Store.Post().GetPosts(model.GetPostsOptions{ChannelId: channel.Id, Page: 0, PerPage: 1}, false)
603	require.NoError(t, nErr)
604
605	if assert.Len(t, postList.Order, 1) {
606		post := postList.Posts[postList.Order[0]]
607
608		assert.Equal(t, model.PostTypeJoinChannel, post.Type)
609		assert.Equal(t, user.Id, post.UserId)
610		assert.Equal(t, user.Username, post.GetProp("username"))
611	}
612}
613
614func TestAppUpdateChannelScheme(t *testing.T) {
615	th := Setup(t).InitBasic()
616	defer th.TearDown()
617
618	channel := th.BasicChannel
619	mockID := model.NewString("x")
620	channel.SchemeId = mockID
621
622	updatedChannel, err := th.App.UpdateChannelScheme(channel)
623	require.Nil(t, err)
624
625	if updatedChannel.SchemeId != mockID {
626		require.Fail(t, "Wrong Channel SchemeId")
627	}
628}
629
630func TestSetChannelsMuted(t *testing.T) {
631	t.Run("should mute and unmute the given channels", func(t *testing.T) {
632		th := Setup(t).InitBasic()
633		defer th.TearDown()
634
635		channel1 := th.BasicChannel
636
637		channel2 := th.CreateChannel(th.BasicTeam)
638		th.AddUserToChannel(th.BasicUser, channel2)
639
640		// Ensure that both channels start unmuted
641		member1, err := th.App.GetChannelMember(context.Background(), channel1.Id, th.BasicUser.Id)
642		require.Nil(t, err)
643		require.False(t, member1.IsChannelMuted())
644
645		member2, err := th.App.GetChannelMember(context.Background(), channel2.Id, th.BasicUser.Id)
646		require.Nil(t, err)
647		require.False(t, member2.IsChannelMuted())
648
649		// Mute both channels
650		updated, err := th.App.setChannelsMuted([]string{channel1.Id, channel2.Id}, th.BasicUser.Id, true)
651		require.Nil(t, err)
652		assert.True(t, updated[0].IsChannelMuted())
653		assert.True(t, updated[1].IsChannelMuted())
654
655		// Verify that the channels are muted in the database
656		member1, err = th.App.GetChannelMember(context.Background(), channel1.Id, th.BasicUser.Id)
657		require.Nil(t, err)
658		require.True(t, member1.IsChannelMuted())
659
660		member2, err = th.App.GetChannelMember(context.Background(), channel2.Id, th.BasicUser.Id)
661		require.Nil(t, err)
662		require.True(t, member2.IsChannelMuted())
663
664		// Unm both channels
665		updated, err = th.App.setChannelsMuted([]string{channel1.Id, channel2.Id}, th.BasicUser.Id, false)
666		require.Nil(t, err)
667		assert.False(t, updated[0].IsChannelMuted())
668		assert.False(t, updated[1].IsChannelMuted())
669
670		// Verify that the channels are muted in the database
671		member1, err = th.App.GetChannelMember(context.Background(), channel1.Id, th.BasicUser.Id)
672		require.Nil(t, err)
673		require.False(t, member1.IsChannelMuted())
674
675		member2, err = th.App.GetChannelMember(context.Background(), channel2.Id, th.BasicUser.Id)
676		require.Nil(t, err)
677		require.False(t, member2.IsChannelMuted())
678	})
679}
680
681func TestFillInChannelProps(t *testing.T) {
682	th := Setup(t).InitBasic()
683	defer th.TearDown()
684
685	channelPublic1, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "Public 1", Name: "public1", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
686	require.Nil(t, err)
687	defer th.App.PermanentDeleteChannel(channelPublic1)
688
689	channelPublic2, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "Public 2", Name: "public2", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
690	require.Nil(t, err)
691	defer th.App.PermanentDeleteChannel(channelPublic2)
692
693	channelPrivate, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "Private", Name: "private", Type: model.ChannelTypePrivate, TeamId: th.BasicTeam.Id}, false)
694	require.Nil(t, err)
695	defer th.App.PermanentDeleteChannel(channelPrivate)
696
697	otherTeamId := model.NewId()
698	otherTeam := &model.Team{
699		DisplayName: "dn_" + otherTeamId,
700		Name:        "name" + otherTeamId,
701		Email:       "success+" + otherTeamId + "@simulator.amazonses.com",
702		Type:        model.TeamOpen,
703	}
704	otherTeam, err = th.App.CreateTeam(th.Context, otherTeam)
705	require.Nil(t, err)
706	defer th.App.PermanentDeleteTeam(otherTeam)
707
708	channelOtherTeam, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "Other Team Channel", Name: "other-team", Type: model.ChannelTypeOpen, TeamId: otherTeam.Id}, false)
709	require.Nil(t, err)
710	defer th.App.PermanentDeleteChannel(channelOtherTeam)
711
712	// Note that purpose is intentionally plaintext below.
713
714	t.Run("single channels", func(t *testing.T) {
715		testCases := []struct {
716			Description          string
717			Channel              *model.Channel
718			ExpectedChannelProps map[string]interface{}
719		}{
720			{
721				"channel on basic team without references",
722				&model.Channel{
723					TeamId:  th.BasicTeam.Id,
724					Header:  "No references",
725					Purpose: "No references",
726				},
727				nil,
728			},
729			{
730				"channel on basic team",
731				&model.Channel{
732					TeamId:  th.BasicTeam.Id,
733					Header:  "~public1, ~private, ~other-team",
734					Purpose: "~public2, ~private, ~other-team",
735				},
736				map[string]interface{}{
737					"channel_mentions": map[string]interface{}{
738						"public1": map[string]interface{}{
739							"display_name": "Public 1",
740						},
741					},
742				},
743			},
744			{
745				"channel on other team",
746				&model.Channel{
747					TeamId:  otherTeam.Id,
748					Header:  "~public1, ~private, ~other-team",
749					Purpose: "~public2, ~private, ~other-team",
750				},
751				map[string]interface{}{
752					"channel_mentions": map[string]interface{}{
753						"other-team": map[string]interface{}{
754							"display_name": "Other Team Channel",
755						},
756					},
757				},
758			},
759		}
760
761		for _, testCase := range testCases {
762			t.Run(testCase.Description, func(t *testing.T) {
763				err = th.App.FillInChannelProps(testCase.Channel)
764				require.Nil(t, err)
765
766				assert.Equal(t, testCase.ExpectedChannelProps, testCase.Channel.Props)
767			})
768		}
769	})
770
771	t.Run("multiple channels", func(t *testing.T) {
772		testCases := []struct {
773			Description          string
774			Channels             model.ChannelList
775			ExpectedChannelProps map[string]interface{}
776		}{
777			{
778				"single channel on basic team",
779				model.ChannelList{
780					{
781						Name:    "test",
782						TeamId:  th.BasicTeam.Id,
783						Header:  "~public1, ~private, ~other-team",
784						Purpose: "~public2, ~private, ~other-team",
785					},
786				},
787				map[string]interface{}{
788					"test": map[string]interface{}{
789						"channel_mentions": map[string]interface{}{
790							"public1": map[string]interface{}{
791								"display_name": "Public 1",
792							},
793						},
794					},
795				},
796			},
797			{
798				"multiple channels on basic team",
799				model.ChannelList{
800					{
801						Name:    "test",
802						TeamId:  th.BasicTeam.Id,
803						Header:  "~public1, ~private, ~other-team",
804						Purpose: "~public2, ~private, ~other-team",
805					},
806					{
807						Name:    "test2",
808						TeamId:  th.BasicTeam.Id,
809						Header:  "~private, ~other-team",
810						Purpose: "~public2, ~private, ~other-team",
811					},
812					{
813						Name:    "test3",
814						TeamId:  th.BasicTeam.Id,
815						Header:  "No references",
816						Purpose: "No references",
817					},
818				},
819				map[string]interface{}{
820					"test": map[string]interface{}{
821						"channel_mentions": map[string]interface{}{
822							"public1": map[string]interface{}{
823								"display_name": "Public 1",
824							},
825						},
826					},
827					"test2": map[string]interface{}(nil),
828					"test3": map[string]interface{}(nil),
829				},
830			},
831			{
832				"multiple channels across teams",
833				model.ChannelList{
834					{
835						Name:    "test",
836						TeamId:  th.BasicTeam.Id,
837						Header:  "~public1, ~private, ~other-team",
838						Purpose: "~public2, ~private, ~other-team",
839					},
840					{
841						Name:    "test2",
842						TeamId:  otherTeam.Id,
843						Header:  "~private, ~other-team",
844						Purpose: "~public2, ~private, ~other-team",
845					},
846					{
847						Name:    "test3",
848						TeamId:  th.BasicTeam.Id,
849						Header:  "No references",
850						Purpose: "No references",
851					},
852				},
853				map[string]interface{}{
854					"test": map[string]interface{}{
855						"channel_mentions": map[string]interface{}{
856							"public1": map[string]interface{}{
857								"display_name": "Public 1",
858							},
859						},
860					},
861					"test2": map[string]interface{}{
862						"channel_mentions": map[string]interface{}{
863							"other-team": map[string]interface{}{
864								"display_name": "Other Team Channel",
865							},
866						},
867					},
868					"test3": map[string]interface{}(nil),
869				},
870			},
871		}
872
873		for _, testCase := range testCases {
874			t.Run(testCase.Description, func(t *testing.T) {
875				err = th.App.FillInChannelsProps(testCase.Channels)
876				require.Nil(t, err)
877
878				for _, channel := range testCase.Channels {
879					assert.Equal(t, testCase.ExpectedChannelProps[channel.Name], channel.Props)
880				}
881			})
882		}
883	})
884}
885
886func TestRenameChannel(t *testing.T) {
887	th := Setup(t).InitBasic()
888	defer th.TearDown()
889
890	testCases := []struct {
891		Name                string
892		Channel             *model.Channel
893		ExpectError         bool
894		ChannelName         string
895		ExpectedName        string
896		ExpectedDisplayName string
897	}{
898		{
899			"Rename open channel",
900			th.createChannel(th.BasicTeam, model.ChannelTypeOpen),
901			false,
902			"newchannelname",
903			"newchannelname",
904			"New Display Name",
905		},
906		{
907			"Fail on rename open channel with bad name",
908			th.createChannel(th.BasicTeam, model.ChannelTypeOpen),
909			true,
910			"6zii9a9g6pruzj451x3esok54h__wr4j4g8zqtnhmkw771pfpynqwo",
911			"",
912			"",
913		},
914		{
915			"Success on rename open channel with consecutive underscores in name",
916			th.createChannel(th.BasicTeam, model.ChannelTypeOpen),
917			false,
918			"foo__bar",
919			"foo__bar",
920			"New Display Name",
921		},
922		{
923			"Fail on rename direct message channel",
924			th.CreateDmChannel(th.BasicUser2),
925			true,
926			"newchannelname",
927			"",
928			"",
929		},
930		{
931			"Fail on rename group message channel",
932			th.CreateGroupChannel(th.BasicUser2, th.CreateUser()),
933			true,
934			"newchannelname",
935			"",
936			"",
937		},
938	}
939
940	for _, tc := range testCases {
941		t.Run(tc.Name, func(t *testing.T) {
942			channel, err := th.App.RenameChannel(tc.Channel, tc.ChannelName, "New Display Name")
943			if tc.ExpectError {
944				assert.NotNil(t, err)
945			} else {
946				assert.Equal(t, tc.ExpectedName, channel.Name)
947				assert.Equal(t, tc.ExpectedDisplayName, channel.DisplayName)
948			}
949		})
950	}
951}
952
953func TestGetChannelMembersTimezones(t *testing.T) {
954	th := Setup(t).InitBasic()
955	defer th.TearDown()
956
957	_, err := th.App.AddChannelMember(th.Context, th.BasicUser2.Id, th.BasicChannel, ChannelMemberOpts{})
958	require.Nil(t, err, "Failed to add user to channel.")
959
960	user := th.BasicUser
961	user.Timezone["useAutomaticTimezone"] = "false"
962	user.Timezone["manualTimezone"] = "XOXO/BLABLA"
963	th.App.UpdateUser(user, false)
964
965	user2 := th.BasicUser2
966	user2.Timezone["automaticTimezone"] = "NoWhere/Island"
967	th.App.UpdateUser(user2, false)
968
969	user3 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
970	ruser, _ := th.App.CreateUser(th.Context, &user3)
971	th.App.AddUserToChannel(ruser, th.BasicChannel, false)
972
973	ruser.Timezone["automaticTimezone"] = "NoWhere/Island"
974	th.App.UpdateUser(ruser, false)
975
976	user4 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
977	ruser, _ = th.App.CreateUser(th.Context, &user4)
978	th.App.AddUserToChannel(ruser, th.BasicChannel, false)
979
980	timezones, err := th.App.GetChannelMembersTimezones(th.BasicChannel.Id)
981	require.Nil(t, err, "Failed to get the timezones for a channel.")
982
983	assert.Equal(t, 2, len(timezones))
984}
985
986func TestGetChannelsForUser(t *testing.T) {
987	th := Setup(t).InitBasic()
988	channel := &model.Channel{
989		DisplayName: "Public",
990		Name:        "public",
991		Type:        model.ChannelTypeOpen,
992		CreatorId:   th.BasicUser.Id,
993		TeamId:      th.BasicTeam.Id,
994	}
995	th.App.CreateChannel(th.Context, channel, true)
996	defer th.App.PermanentDeleteChannel(channel)
997	defer th.TearDown()
998
999	channelList, err := th.App.GetChannelsForUser(th.BasicTeam.Id, th.BasicUser.Id, false, 0)
1000	require.Nil(t, err)
1001	require.Len(t, channelList, 4)
1002
1003	th.App.DeleteChannel(th.Context, channel, th.BasicUser.Id)
1004
1005	// Now we get all the non-archived channels for the user
1006	channelList, err = th.App.GetChannelsForUser(th.BasicTeam.Id, th.BasicUser.Id, false, 0)
1007	require.Nil(t, err)
1008	require.Len(t, channelList, 3)
1009
1010	// Now we get all the channels, even though are archived, for the user
1011	channelList, err = th.App.GetChannelsForUser(th.BasicTeam.Id, th.BasicUser.Id, true, 0)
1012	require.Nil(t, err)
1013	require.Len(t, channelList, 4)
1014}
1015
1016func TestGetPublicChannelsForTeam(t *testing.T) {
1017	th := Setup(t)
1018	team := th.CreateTeam()
1019	defer th.TearDown()
1020
1021	var expectedChannels []*model.Channel
1022
1023	townSquare, err := th.App.GetChannelByName("town-square", team.Id, false)
1024	require.Nil(t, err)
1025	require.NotNil(t, townSquare)
1026	expectedChannels = append(expectedChannels, townSquare)
1027
1028	offTopic, err := th.App.GetChannelByName("off-topic", team.Id, false)
1029	require.Nil(t, err)
1030	require.NotNil(t, offTopic)
1031	expectedChannels = append(expectedChannels, offTopic)
1032
1033	for i := 0; i < 8; i++ {
1034		channel := model.Channel{
1035			DisplayName: fmt.Sprintf("Public %v", i),
1036			Name:        fmt.Sprintf("public_%v", i),
1037			Type:        model.ChannelTypeOpen,
1038			TeamId:      team.Id,
1039		}
1040		var rchannel *model.Channel
1041		rchannel, err = th.App.CreateChannel(th.Context, &channel, false)
1042		require.Nil(t, err)
1043		require.NotNil(t, rchannel)
1044		defer th.App.PermanentDeleteChannel(rchannel)
1045
1046		// Store the user ids for comparison later
1047		expectedChannels = append(expectedChannels, rchannel)
1048	}
1049
1050	// Fetch public channels multipile times
1051	channelList, err := th.App.GetPublicChannelsForTeam(team.Id, 0, 5)
1052	require.Nil(t, err)
1053	channelList2, err := th.App.GetPublicChannelsForTeam(team.Id, 5, 5)
1054	require.Nil(t, err)
1055
1056	channels := append(channelList, channelList2...)
1057	assert.ElementsMatch(t, expectedChannels, channels)
1058}
1059
1060func TestGetPrivateChannelsForTeam(t *testing.T) {
1061	th := Setup(t)
1062	team := th.CreateTeam()
1063	defer th.TearDown()
1064
1065	var expectedChannels []*model.Channel
1066	for i := 0; i < 8; i++ {
1067		channel := model.Channel{
1068			DisplayName: fmt.Sprintf("Private %v", i),
1069			Name:        fmt.Sprintf("private_%v", i),
1070			Type:        model.ChannelTypePrivate,
1071			TeamId:      team.Id,
1072		}
1073		var rchannel *model.Channel
1074		rchannel, err := th.App.CreateChannel(th.Context, &channel, false)
1075		require.Nil(t, err)
1076		require.NotNil(t, rchannel)
1077		defer th.App.PermanentDeleteChannel(rchannel)
1078
1079		// Store the user ids for comparison later
1080		expectedChannels = append(expectedChannels, rchannel)
1081	}
1082
1083	// Fetch private channels multipile times
1084	channelList, err := th.App.GetPrivateChannelsForTeam(team.Id, 0, 5)
1085	require.Nil(t, err)
1086	channelList2, err := th.App.GetPrivateChannelsForTeam(team.Id, 5, 5)
1087	require.Nil(t, err)
1088
1089	channels := append(channelList, channelList2...)
1090	assert.ElementsMatch(t, expectedChannels, channels)
1091}
1092
1093func TestUpdateChannelMemberRolesChangingGuest(t *testing.T) {
1094	th := Setup(t).InitBasic()
1095	defer th.TearDown()
1096
1097	t.Run("from guest to user", func(t *testing.T) {
1098		user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1099		ruser, _ := th.App.CreateGuest(th.Context, &user)
1100
1101		_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
1102		require.Nil(t, err)
1103
1104		_, err = th.App.AddUserToChannel(ruser, th.BasicChannel, false)
1105		require.Nil(t, err)
1106
1107		_, err = th.App.UpdateChannelMemberRoles(th.BasicChannel.Id, ruser.Id, "channel_user")
1108		require.NotNil(t, err, "Should fail when try to modify the guest role")
1109	})
1110
1111	t.Run("from user to guest", func(t *testing.T) {
1112		user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1113		ruser, _ := th.App.CreateUser(th.Context, &user)
1114
1115		_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
1116		require.Nil(t, err)
1117
1118		_, err = th.App.AddUserToChannel(ruser, th.BasicChannel, false)
1119		require.Nil(t, err)
1120
1121		_, err = th.App.UpdateChannelMemberRoles(th.BasicChannel.Id, ruser.Id, "channel_guest")
1122		require.NotNil(t, err, "Should fail when try to modify the guest role")
1123	})
1124
1125	t.Run("from user to admin", func(t *testing.T) {
1126		user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1127		ruser, _ := th.App.CreateUser(th.Context, &user)
1128
1129		_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
1130		require.Nil(t, err)
1131
1132		_, err = th.App.AddUserToChannel(ruser, th.BasicChannel, false)
1133		require.Nil(t, err)
1134
1135		_, err = th.App.UpdateChannelMemberRoles(th.BasicChannel.Id, ruser.Id, "channel_user channel_admin")
1136		require.Nil(t, err, "Should work when you not modify guest role")
1137	})
1138
1139	t.Run("from guest to guest plus custom", func(t *testing.T) {
1140		user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1141		ruser, _ := th.App.CreateGuest(th.Context, &user)
1142
1143		_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
1144		require.Nil(t, err)
1145
1146		_, err = th.App.AddUserToChannel(ruser, th.BasicChannel, false)
1147		require.Nil(t, err)
1148
1149		_, err = th.App.CreateRole(&model.Role{Name: "custom", DisplayName: "custom", Description: "custom"})
1150		require.Nil(t, err)
1151
1152		_, err = th.App.UpdateChannelMemberRoles(th.BasicChannel.Id, ruser.Id, "channel_guest custom")
1153		require.Nil(t, err, "Should work when you not modify guest role")
1154	})
1155
1156	t.Run("a guest cant have user role", func(t *testing.T) {
1157		user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1158		ruser, _ := th.App.CreateGuest(th.Context, &user)
1159
1160		_, _, err := th.App.AddUserToTeam(th.Context, th.BasicTeam.Id, ruser.Id, "")
1161		require.Nil(t, err)
1162
1163		_, err = th.App.AddUserToChannel(ruser, th.BasicChannel, false)
1164		require.Nil(t, err)
1165
1166		_, err = th.App.UpdateChannelMemberRoles(th.BasicChannel.Id, ruser.Id, "channel_guest channel_user")
1167		require.NotNil(t, err, "Should work when you not modify guest role")
1168	})
1169}
1170
1171func TestDefaultChannelNames(t *testing.T) {
1172	th := Setup(t)
1173	defer th.TearDown()
1174
1175	actual := th.App.DefaultChannelNames()
1176	expect := []string{"town-square", "off-topic"}
1177	require.ElementsMatch(t, expect, actual)
1178
1179	th.App.UpdateConfig(func(cfg *model.Config) {
1180		cfg.TeamSettings.ExperimentalDefaultChannels = []string{"foo", "bar"}
1181	})
1182
1183	actual = th.App.DefaultChannelNames()
1184	expect = []string{"town-square", "foo", "bar"}
1185	require.ElementsMatch(t, expect, actual)
1186}
1187
1188func TestSearchChannelsForUser(t *testing.T) {
1189	th := Setup(t).InitBasic()
1190	defer th.TearDown()
1191
1192	c1, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "test-dev-1", Name: "test-dev-1", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
1193	require.Nil(t, err)
1194
1195	c2, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "test-dev-2", Name: "test-dev-2", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
1196	require.Nil(t, err)
1197
1198	c3, err := th.App.CreateChannel(th.Context, &model.Channel{DisplayName: "dev-3", Name: "dev-3", Type: model.ChannelTypeOpen, TeamId: th.BasicTeam.Id}, false)
1199	require.Nil(t, err)
1200
1201	defer func() {
1202		th.App.PermanentDeleteChannel(c1)
1203		th.App.PermanentDeleteChannel(c2)
1204		th.App.PermanentDeleteChannel(c3)
1205	}()
1206
1207	// add user to test-dev-1 and dev3
1208	_, err = th.App.AddUserToChannel(th.BasicUser, c1, false)
1209	require.Nil(t, err)
1210	_, err = th.App.AddUserToChannel(th.BasicUser, c3, false)
1211	require.Nil(t, err)
1212
1213	searchAndCheck := func(t *testing.T, term string, expectedDisplayNames []string) {
1214		res, searchErr := th.App.SearchChannelsForUser(th.BasicUser.Id, th.BasicTeam.Id, term)
1215		require.Nil(t, searchErr)
1216		require.Len(t, res, len(expectedDisplayNames))
1217
1218		resultDisplayNames := []string{}
1219		for _, c := range res {
1220			resultDisplayNames = append(resultDisplayNames, c.Name)
1221		}
1222		require.ElementsMatch(t, expectedDisplayNames, resultDisplayNames)
1223	}
1224
1225	t.Run("Search for test, only test-dev-1 should be returned", func(t *testing.T) {
1226		searchAndCheck(t, "test", []string{"test-dev-1"})
1227	})
1228
1229	t.Run("Search for dev, both test-dev-1 and dev-3 should be returned", func(t *testing.T) {
1230		searchAndCheck(t, "dev", []string{"test-dev-1", "dev-3"})
1231	})
1232
1233	t.Run("After adding user to test-dev-2, search for dev, the three channels should be returned", func(t *testing.T) {
1234		_, err = th.App.AddUserToChannel(th.BasicUser, c2, false)
1235		require.Nil(t, err)
1236
1237		searchAndCheck(t, "dev", []string{"test-dev-1", "test-dev-2", "dev-3"})
1238	})
1239}
1240
1241func TestMarkChannelAsUnreadFromPost(t *testing.T) {
1242	th := Setup(t).InitBasic()
1243	defer th.TearDown()
1244
1245	u1 := th.BasicUser
1246	u2 := th.BasicUser2
1247	c1 := th.BasicChannel
1248	pc1 := th.CreatePrivateChannel(th.BasicTeam)
1249	th.AddUserToChannel(u2, c1)
1250	th.AddUserToChannel(u1, pc1)
1251	th.AddUserToChannel(u2, pc1)
1252
1253	p1 := th.CreatePost(c1)
1254	p2 := th.CreatePost(c1)
1255	p3 := th.CreatePost(c1)
1256
1257	pp1 := th.CreatePost(pc1)
1258	require.NotNil(t, pp1)
1259	pp2 := th.CreatePost(pc1)
1260
1261	unread, err := th.App.GetChannelUnread(c1.Id, u1.Id)
1262	require.Nil(t, err)
1263	require.Equal(t, int64(4), unread.MsgCount)
1264	unread, err = th.App.GetChannelUnread(c1.Id, u2.Id)
1265	require.Nil(t, err)
1266	require.Equal(t, int64(4), unread.MsgCount)
1267	err = th.App.UpdateChannelLastViewedAt([]string{c1.Id, pc1.Id}, u1.Id)
1268	require.Nil(t, err)
1269	err = th.App.UpdateChannelLastViewedAt([]string{c1.Id, pc1.Id}, u2.Id)
1270	require.Nil(t, err)
1271	unread, err = th.App.GetChannelUnread(c1.Id, u2.Id)
1272	require.Nil(t, err)
1273	require.Equal(t, int64(0), unread.MsgCount)
1274
1275	t.Run("Unread but last one", func(t *testing.T) {
1276		response, err := th.App.MarkChannelAsUnreadFromPost(p2.Id, u1.Id, true, true)
1277		require.Nil(t, err)
1278		require.NotNil(t, response)
1279		assert.Equal(t, int64(2), response.MsgCount)
1280		unread, err := th.App.GetChannelUnread(c1.Id, u1.Id)
1281		require.Nil(t, err)
1282		assert.Equal(t, int64(2), unread.MsgCount)
1283		assert.Equal(t, p2.CreateAt-1, response.LastViewedAt)
1284	})
1285
1286	t.Run("Unread last one", func(t *testing.T) {
1287		response, err := th.App.MarkChannelAsUnreadFromPost(p3.Id, u1.Id, true, true)
1288		require.Nil(t, err)
1289		require.NotNil(t, response)
1290		assert.Equal(t, int64(3), response.MsgCount)
1291		unread, err := th.App.GetChannelUnread(c1.Id, u1.Id)
1292		require.Nil(t, err)
1293		assert.Equal(t, int64(1), unread.MsgCount)
1294		assert.Equal(t, p3.CreateAt-1, response.LastViewedAt)
1295	})
1296
1297	t.Run("Unread first one", func(t *testing.T) {
1298		response, err := th.App.MarkChannelAsUnreadFromPost(p1.Id, u1.Id, true, true)
1299		require.Nil(t, err)
1300		require.NotNil(t, response)
1301		assert.Equal(t, int64(1), response.MsgCount)
1302		unread, err := th.App.GetChannelUnread(c1.Id, u1.Id)
1303		require.Nil(t, err)
1304		assert.Equal(t, int64(3), unread.MsgCount)
1305		assert.Equal(t, p1.CreateAt-1, response.LastViewedAt)
1306	})
1307
1308	t.Run("Other users are unaffected", func(t *testing.T) {
1309		unread, err := th.App.GetChannelUnread(c1.Id, u2.Id)
1310		require.Nil(t, err)
1311		assert.Equal(t, int64(0), unread.MsgCount)
1312	})
1313
1314	t.Run("Unread on a private channel", func(t *testing.T) {
1315		response, err := th.App.MarkChannelAsUnreadFromPost(pp1.Id, u1.Id, true, true)
1316		require.Nil(t, err)
1317		require.NotNil(t, response)
1318		assert.Equal(t, int64(0), response.MsgCount)
1319		unread, err := th.App.GetChannelUnread(pc1.Id, u1.Id)
1320		require.Nil(t, err)
1321		assert.Equal(t, int64(2), unread.MsgCount)
1322		assert.Equal(t, pp1.CreateAt-1, response.LastViewedAt)
1323
1324		response, err = th.App.MarkChannelAsUnreadFromPost(pp2.Id, u1.Id, true, true)
1325		assert.Nil(t, err)
1326		assert.Equal(t, int64(1), response.MsgCount)
1327		unread, err = th.App.GetChannelUnread(pc1.Id, u1.Id)
1328		require.Nil(t, err)
1329		assert.Equal(t, int64(1), unread.MsgCount)
1330		assert.Equal(t, pp2.CreateAt-1, response.LastViewedAt)
1331	})
1332
1333	t.Run("Unread with mentions", func(t *testing.T) {
1334		c2 := th.CreateChannel(th.BasicTeam)
1335		_, err := th.App.AddUserToChannel(u2, c2, false)
1336		require.Nil(t, err)
1337
1338		p4, err := th.App.CreatePost(th.Context, &model.Post{
1339			UserId:    u2.Id,
1340			ChannelId: c2.Id,
1341			Message:   "@" + u1.Username,
1342		}, c2, false, true)
1343		require.Nil(t, err)
1344		th.CreatePost(c2)
1345
1346		th.App.CreatePost(th.Context, &model.Post{
1347			UserId:    u2.Id,
1348			ChannelId: c2.Id,
1349			RootId:    p4.Id,
1350			Message:   "@" + u1.Username,
1351		}, c2, false, true)
1352
1353		response, err := th.App.MarkChannelAsUnreadFromPost(p4.Id, u1.Id, true, true)
1354		assert.Nil(t, err)
1355		assert.Equal(t, int64(1), response.MsgCount)
1356		assert.Equal(t, int64(2), response.MentionCount)
1357		assert.Equal(t, int64(1), response.MentionCountRoot)
1358
1359		unread, err := th.App.GetChannelUnread(c2.Id, u1.Id)
1360		require.Nil(t, err)
1361		assert.Equal(t, int64(2), unread.MsgCount)
1362		assert.Equal(t, int64(2), unread.MentionCount)
1363		assert.Equal(t, int64(1), unread.MentionCountRoot)
1364	})
1365
1366	t.Run("Unread on a DM channel", func(t *testing.T) {
1367		dc := th.CreateDmChannel(u2)
1368
1369		dm1 := th.CreatePost(dc)
1370		th.CreatePost(dc)
1371		th.CreatePost(dc)
1372
1373		_, err := th.App.CreatePost(th.Context, &model.Post{ChannelId: dc.Id, UserId: th.BasicUser.Id, Message: "testReply", RootId: dm1.Id}, dc, false, false)
1374		assert.Nil(t, err)
1375
1376		response, err := th.App.MarkChannelAsUnreadFromPost(dm1.Id, u2.Id, true, true)
1377		assert.Nil(t, err)
1378		assert.Equal(t, int64(0), response.MsgCount)
1379		assert.Equal(t, int64(4), response.MentionCount)
1380		assert.Equal(t, int64(3), response.MentionCountRoot)
1381
1382		unread, err := th.App.GetChannelUnread(dc.Id, u2.Id)
1383		require.Nil(t, err)
1384		assert.Equal(t, int64(4), unread.MsgCount)
1385		assert.Equal(t, int64(4), unread.MentionCount)
1386		assert.Equal(t, int64(3), unread.MentionCountRoot)
1387	})
1388
1389	t.Run("Can't unread an imaginary post", func(t *testing.T) {
1390		response, err := th.App.MarkChannelAsUnreadFromPost("invalid4ofngungryquinj976y", u1.Id, true, true)
1391		assert.NotNil(t, err)
1392		assert.Nil(t, response)
1393	})
1394}
1395
1396func TestAddUserToChannel(t *testing.T) {
1397	th := Setup(t).InitBasic()
1398	defer th.TearDown()
1399
1400	user1 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1401	ruser1, _ := th.App.CreateUser(th.Context, &user1)
1402	defer th.App.PermanentDeleteUser(th.Context, &user1)
1403	bot := th.CreateBot()
1404	botUser, _ := th.App.GetUser(bot.UserId)
1405	defer th.App.PermanentDeleteBot(botUser.Id)
1406
1407	th.App.AddTeamMember(th.Context, th.BasicTeam.Id, ruser1.Id)
1408	th.App.AddTeamMember(th.Context, th.BasicTeam.Id, bot.UserId)
1409
1410	group := th.CreateGroup()
1411
1412	_, err := th.App.UpsertGroupMember(group.Id, user1.Id)
1413	require.Nil(t, err)
1414
1415	gs, err := th.App.UpsertGroupSyncable(&model.GroupSyncable{
1416		AutoAdd:     true,
1417		SyncableId:  th.BasicChannel.Id,
1418		Type:        model.GroupSyncableTypeChannel,
1419		GroupId:     group.Id,
1420		SchemeAdmin: false,
1421	})
1422	require.Nil(t, err)
1423
1424	err = th.App.JoinChannel(th.Context, th.BasicChannel, ruser1.Id)
1425	require.Nil(t, err)
1426
1427	// verify user was added as a non-admin
1428	cm1, err := th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, ruser1.Id)
1429	require.Nil(t, err)
1430	require.False(t, cm1.SchemeAdmin)
1431
1432	user2 := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1433	ruser2, _ := th.App.CreateUser(th.Context, &user2)
1434	defer th.App.PermanentDeleteUser(th.Context, &user2)
1435	th.App.AddTeamMember(th.Context, th.BasicTeam.Id, ruser2.Id)
1436
1437	_, err = th.App.UpsertGroupMember(group.Id, user2.Id)
1438	require.Nil(t, err)
1439
1440	gs.SchemeAdmin = true
1441	_, err = th.App.UpdateGroupSyncable(gs)
1442	require.Nil(t, err)
1443
1444	err = th.App.JoinChannel(th.Context, th.BasicChannel, ruser2.Id)
1445	require.Nil(t, err)
1446
1447	// Should allow a bot to be added to a public group synced channel
1448	_, err = th.App.AddUserToChannel(botUser, th.BasicChannel, false)
1449	require.Nil(t, err)
1450
1451	// verify user was added as an admin
1452	cm2, err := th.App.GetChannelMember(context.Background(), th.BasicChannel.Id, ruser2.Id)
1453	require.Nil(t, err)
1454	require.True(t, cm2.SchemeAdmin)
1455
1456	privateChannel := th.CreatePrivateChannel(th.BasicTeam)
1457	privateChannel.GroupConstrained = model.NewBool(true)
1458	_, err = th.App.UpdateChannel(privateChannel)
1459	require.Nil(t, err)
1460
1461	_, err = th.App.UpsertGroupSyncable(&model.GroupSyncable{
1462		GroupId:    group.Id,
1463		SyncableId: privateChannel.Id,
1464		Type:       model.GroupSyncableTypeChannel,
1465	})
1466	require.Nil(t, err)
1467
1468	// Should allow a group synced user to be added to a group synced private channel
1469	_, err = th.App.AddUserToChannel(ruser1, privateChannel, false)
1470	require.Nil(t, err)
1471
1472	// Should allow a bot to be added to a private group synced channel
1473	_, err = th.App.AddUserToChannel(botUser, privateChannel, false)
1474	require.Nil(t, err)
1475}
1476
1477func TestRemoveUserFromChannel(t *testing.T) {
1478	th := Setup(t).InitBasic()
1479	defer th.TearDown()
1480
1481	user := model.User{Email: strings.ToLower(model.NewId()) + "success+test@example.com", Nickname: "Darth Vader", Username: "vader" + model.NewId(), Password: "passwd1", AuthService: ""}
1482	ruser, _ := th.App.CreateUser(th.Context, &user)
1483	defer th.App.PermanentDeleteUser(th.Context, ruser)
1484
1485	bot := th.CreateBot()
1486	botUser, _ := th.App.GetUser(bot.UserId)
1487	defer th.App.PermanentDeleteBot(botUser.Id)
1488
1489	th.App.AddTeamMember(th.Context, th.BasicTeam.Id, ruser.Id)
1490	th.App.AddTeamMember(th.Context, th.BasicTeam.Id, bot.UserId)
1491
1492	privateChannel := th.CreatePrivateChannel(th.BasicTeam)
1493
1494	_, err := th.App.AddUserToChannel(ruser, privateChannel, false)
1495	require.Nil(t, err)
1496	_, err = th.App.AddUserToChannel(botUser, privateChannel, false)
1497	require.Nil(t, err)
1498
1499	group := th.CreateGroup()
1500	_, err = th.App.UpsertGroupMember(group.Id, ruser.Id)
1501	require.Nil(t, err)
1502
1503	_, err = th.App.UpsertGroupSyncable(&model.GroupSyncable{
1504		GroupId:    group.Id,
1505		SyncableId: privateChannel.Id,
1506		Type:       model.GroupSyncableTypeChannel,
1507	})
1508	require.Nil(t, err)
1509
1510	privateChannel.GroupConstrained = model.NewBool(true)
1511	_, err = th.App.UpdateChannel(privateChannel)
1512	require.Nil(t, err)
1513
1514	// Should not allow a group synced user to be removed from channel
1515	err = th.App.RemoveUserFromChannel(th.Context, ruser.Id, th.SystemAdminUser.Id, privateChannel)
1516	assert.Equal(t, err.Id, "api.channel.remove_members.denied")
1517
1518	// Should allow a user to remove themselves from group synced channel
1519	err = th.App.RemoveUserFromChannel(th.Context, ruser.Id, ruser.Id, privateChannel)
1520	require.Nil(t, err)
1521
1522	// Should allow a bot to be removed from a group synced channel
1523	err = th.App.RemoveUserFromChannel(th.Context, botUser.Id, th.SystemAdminUser.Id, privateChannel)
1524	require.Nil(t, err)
1525}
1526
1527func TestPatchChannelModerationsForChannel(t *testing.T) {
1528	th := Setup(t).InitBasic()
1529	defer th.TearDown()
1530
1531	th.App.SetPhase2PermissionsMigrationStatus(true)
1532	channel := th.BasicChannel
1533
1534	user := th.BasicUser
1535	th.AddUserToChannel(user, channel)
1536
1537	createPosts := model.ChannelModeratedPermissions[0]
1538	createReactions := model.ChannelModeratedPermissions[1]
1539	manageMembers := model.ChannelModeratedPermissions[2]
1540	channelMentions := model.ChannelModeratedPermissions[3]
1541
1542	nonChannelModeratedPermission := model.PermissionCreateBot.Id
1543
1544	testCases := []struct {
1545		Name                          string
1546		ChannelModerationsPatch       []*model.ChannelModerationPatch
1547		PermissionsModeratedByPatch   map[string]*model.ChannelModeratedRoles
1548		RevertChannelModerationsPatch []*model.ChannelModerationPatch
1549		HigherScopedMemberPermissions []string
1550		HigherScopedGuestPermissions  []string
1551		ShouldError                   bool
1552		ShouldHaveNoChannelScheme     bool
1553	}{
1554		{
1555			Name: "Removing create posts from members role",
1556			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1557				{
1558					Name:  &createPosts,
1559					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
1560				},
1561			},
1562			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1563				createPosts: {
1564					Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
1565				},
1566			},
1567			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1568				{
1569					Name:  &createPosts,
1570					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
1571				},
1572			},
1573		},
1574		{
1575			Name: "Removing create reactions from members role",
1576			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1577				{
1578					Name:  &createReactions,
1579					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
1580				},
1581			},
1582			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1583				createReactions: {
1584					Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
1585				},
1586			},
1587			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1588				{
1589					Name:  &createReactions,
1590					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
1591				},
1592			},
1593		},
1594		{
1595			Name: "Removing channel mentions from members role",
1596			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1597				{
1598					Name:  &channelMentions,
1599					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
1600				},
1601			},
1602			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1603				channelMentions: {
1604					Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
1605				},
1606			},
1607			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1608				{
1609					Name:  &channelMentions,
1610					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
1611				},
1612			},
1613		},
1614		{
1615			Name: "Removing manage members from members role",
1616			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1617				{
1618					Name:  &manageMembers,
1619					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(false)},
1620				},
1621			},
1622			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1623				manageMembers: {
1624					Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
1625				},
1626			},
1627			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1628				{
1629					Name:  &manageMembers,
1630					Roles: &model.ChannelModeratedRolesPatch{Members: model.NewBool(true)},
1631				},
1632			},
1633		},
1634		{
1635			Name: "Removing create posts from guests role",
1636			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1637				{
1638					Name:  &createPosts,
1639					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
1640				},
1641			},
1642			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1643				createPosts: {
1644					Guests: &model.ChannelModeratedRole{Value: false, Enabled: true},
1645				},
1646			},
1647			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1648				{
1649					Name:  &createPosts,
1650					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(true)},
1651				},
1652			},
1653		},
1654		{
1655			Name: "Removing create reactions from guests role",
1656			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1657				{
1658					Name:  &createReactions,
1659					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
1660				},
1661			},
1662			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1663				createReactions: {
1664					Guests: &model.ChannelModeratedRole{Value: false, Enabled: true},
1665				},
1666			},
1667			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1668				{
1669					Name:  &createReactions,
1670					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(true)},
1671				},
1672			},
1673		},
1674		{
1675			Name: "Removing channel mentions from guests role",
1676			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1677				{
1678					Name:  &channelMentions,
1679					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
1680				},
1681			},
1682			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1683				channelMentions: {
1684					Guests: &model.ChannelModeratedRole{Value: false, Enabled: true},
1685				},
1686			},
1687			RevertChannelModerationsPatch: []*model.ChannelModerationPatch{
1688				{
1689					Name:  &channelMentions,
1690					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(true)},
1691				},
1692			},
1693		},
1694		{
1695			Name: "Removing manage members from guests role should not error",
1696			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1697				{
1698					Name:  &manageMembers,
1699					Roles: &model.ChannelModeratedRolesPatch{Guests: model.NewBool(false)},
1700				},
1701			},
1702			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
1703			ShouldError:                 false,
1704			ShouldHaveNoChannelScheme:   true,
1705		},
1706		{
1707			Name: "Removing a permission that is not channel moderated should not error",
1708			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1709				{
1710					Name: &nonChannelModeratedPermission,
1711					Roles: &model.ChannelModeratedRolesPatch{
1712						Members: model.NewBool(false),
1713						Guests:  model.NewBool(false),
1714					},
1715				},
1716			},
1717			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
1718			ShouldError:                 false,
1719			ShouldHaveNoChannelScheme:   true,
1720		},
1721		{
1722			Name: "Error when adding a permission that is disabled in the parent member role",
1723			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1724				{
1725					Name: &createPosts,
1726					Roles: &model.ChannelModeratedRolesPatch{
1727						Members: model.NewBool(true),
1728						Guests:  model.NewBool(false),
1729					},
1730				},
1731			},
1732			PermissionsModeratedByPatch:   map[string]*model.ChannelModeratedRoles{},
1733			HigherScopedMemberPermissions: []string{},
1734			ShouldError:                   true,
1735		},
1736		{
1737			Name: "Error when adding a permission that is disabled in the parent guest role",
1738			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1739				{
1740					Name: &createPosts,
1741					Roles: &model.ChannelModeratedRolesPatch{
1742						Members: model.NewBool(false),
1743						Guests:  model.NewBool(true),
1744					},
1745				},
1746			},
1747			PermissionsModeratedByPatch:  map[string]*model.ChannelModeratedRoles{},
1748			HigherScopedGuestPermissions: []string{},
1749			ShouldError:                  true,
1750		},
1751		{
1752			Name: "Removing a permission from the member role that is disabled in the parent guest role",
1753			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1754				{
1755					Name: &createPosts,
1756					Roles: &model.ChannelModeratedRolesPatch{
1757						Members: model.NewBool(false),
1758					},
1759				},
1760			},
1761			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{
1762				createPosts: {
1763					Members: &model.ChannelModeratedRole{Value: false, Enabled: true},
1764					Guests:  &model.ChannelModeratedRole{Value: false, Enabled: false},
1765				},
1766				createReactions: {
1767					Guests: &model.ChannelModeratedRole{Value: false, Enabled: false},
1768				},
1769				channelMentions: {
1770					Guests: &model.ChannelModeratedRole{Value: false, Enabled: false},
1771				},
1772			},
1773			HigherScopedGuestPermissions: []string{},
1774			ShouldError:                  false,
1775		},
1776		{
1777			Name: "Channel should have no scheme when all moderated permissions are equivalent to higher scoped role",
1778			ChannelModerationsPatch: []*model.ChannelModerationPatch{
1779				{
1780					Name: &createPosts,
1781					Roles: &model.ChannelModeratedRolesPatch{
1782						Members: model.NewBool(true),
1783						Guests:  model.NewBool(true),
1784					},
1785				},
1786				{
1787					Name: &createReactions,
1788					Roles: &model.ChannelModeratedRolesPatch{
1789						Members: model.NewBool(true),
1790						Guests:  model.NewBool(true),
1791					},
1792				},
1793				{
1794					Name: &channelMentions,
1795					Roles: &model.ChannelModeratedRolesPatch{
1796						Members: model.NewBool(true),
1797						Guests:  model.NewBool(true),
1798					},
1799				},
1800				{
1801					Name: &manageMembers,
1802					Roles: &model.ChannelModeratedRolesPatch{
1803						Members: model.NewBool(true),
1804					},
1805				},
1806			},
1807			PermissionsModeratedByPatch: map[string]*model.ChannelModeratedRoles{},
1808			ShouldHaveNoChannelScheme:   true,
1809		},
1810	}
1811
1812	for _, tc := range testCases {
1813		t.Run(tc.Name, func(t *testing.T) {
1814			higherScopedPermissionsOverriden := tc.HigherScopedMemberPermissions != nil || tc.HigherScopedGuestPermissions != nil
1815			// If the test case restricts higher scoped permissions.
1816			if higherScopedPermissionsOverriden {
1817				higherScopedGuestRoleName, higherScopedMemberRoleName, _, _ := th.App.GetTeamSchemeChannelRoles(channel.TeamId)
1818				if tc.HigherScopedMemberPermissions != nil {
1819					higherScopedMemberRole, err := th.App.GetRoleByName(context.Background(), higherScopedMemberRoleName)
1820					require.Nil(t, err)
1821					originalPermissions := higherScopedMemberRole.Permissions
1822
1823					th.App.PatchRole(higherScopedMemberRole, &model.RolePatch{Permissions: &tc.HigherScopedMemberPermissions})
1824					defer th.App.PatchRole(higherScopedMemberRole, &model.RolePatch{Permissions: &originalPermissions})
1825				}
1826
1827				if tc.HigherScopedGuestPermissions != nil {
1828					higherScopedGuestRole, err := th.App.GetRoleByName(context.Background(), higherScopedGuestRoleName)
1829					require.Nil(t, err)
1830					originalPermissions := higherScopedGuestRole.Permissions
1831
1832					th.App.PatchRole(higherScopedGuestRole, &model.RolePatch{Permissions: &tc.HigherScopedGuestPermissions})
1833					defer th.App.PatchRole(higherScopedGuestRole, &model.RolePatch{Permissions: &originalPermissions})
1834				}
1835			}
1836
1837			moderations, appErr := th.App.PatchChannelModerationsForChannel(channel, tc.ChannelModerationsPatch)
1838			if tc.ShouldError {
1839				require.NotNil(t, appErr)
1840				return
1841			}
1842			require.Nil(t, appErr)
1843
1844			updatedChannel, _ := th.App.GetChannel(channel.Id)
1845			if tc.ShouldHaveNoChannelScheme {
1846				require.Nil(t, updatedChannel.SchemeId)
1847			} else {
1848				require.NotNil(t, updatedChannel.SchemeId)
1849			}
1850
1851			for _, moderation := range moderations {
1852				// If the permission is not found in the expected modified permissions table then require it to be true
1853				if permission, found := tc.PermissionsModeratedByPatch[moderation.Name]; found && permission.Members != nil {
1854					require.Equal(t, moderation.Roles.Members.Value, permission.Members.Value)
1855					require.Equal(t, moderation.Roles.Members.Enabled, permission.Members.Enabled)
1856				} else {
1857					require.Equal(t, moderation.Roles.Members.Value, true)
1858					require.Equal(t, moderation.Roles.Members.Enabled, true)
1859				}
1860
1861				if permission, found := tc.PermissionsModeratedByPatch[moderation.Name]; found && permission.Guests != nil {
1862					require.Equal(t, moderation.Roles.Guests.Value, permission.Guests.Value)
1863					require.Equal(t, moderation.Roles.Guests.Enabled, permission.Guests.Enabled)
1864				} else if moderation.Name == manageMembers {
1865					require.Empty(t, moderation.Roles.Guests)
1866				} else {
1867					require.Equal(t, moderation.Roles.Guests.Value, true)
1868					require.Equal(t, moderation.Roles.Guests.Enabled, true)
1869				}
1870			}
1871
1872			if tc.RevertChannelModerationsPatch != nil {
1873				th.App.PatchChannelModerationsForChannel(channel, tc.RevertChannelModerationsPatch)
1874			}
1875		})
1876	}
1877
1878	t.Run("Handles concurrent patch requests gracefully", func(t *testing.T) {
1879		addCreatePosts := []*model.ChannelModerationPatch{
1880			{
1881				Name: &createPosts,
1882				Roles: &model.ChannelModeratedRolesPatch{
1883					Members: model.NewBool(false),
1884					Guests:  model.NewBool(false),
1885				},
1886			},
1887		}
1888		removeCreatePosts := []*model.ChannelModerationPatch{
1889			{
1890				Name: &createPosts,
1891				Roles: &model.ChannelModeratedRolesPatch{
1892					Members: model.NewBool(false),
1893					Guests:  model.NewBool(false),
1894				},
1895			},
1896		}
1897
1898		wg := sync.WaitGroup{}
1899		wg.Add(20)
1900		for i := 0; i < 10; i++ {
1901			go func() {
1902				th.App.PatchChannelModerationsForChannel(channel.DeepCopy(), addCreatePosts)
1903				th.App.PatchChannelModerationsForChannel(channel.DeepCopy(), removeCreatePosts)
1904				wg.Done()
1905			}()
1906		}
1907		for i := 0; i < 10; i++ {
1908			go func() {
1909				th.App.PatchChannelModerationsForChannel(channel.DeepCopy(), addCreatePosts)
1910				th.App.PatchChannelModerationsForChannel(channel.DeepCopy(), removeCreatePosts)
1911				wg.Done()
1912			}()
1913		}
1914		wg.Wait()
1915
1916		higherScopedGuestRoleName, higherScopedMemberRoleName, _, _ := th.App.GetTeamSchemeChannelRoles(channel.TeamId)
1917		higherScopedMemberRole, _ := th.App.GetRoleByName(context.Background(), higherScopedMemberRoleName)
1918		higherScopedGuestRole, _ := th.App.GetRoleByName(context.Background(), higherScopedGuestRoleName)
1919		assert.Contains(t, higherScopedMemberRole.Permissions, createPosts)
1920		assert.Contains(t, higherScopedGuestRole.Permissions, createPosts)
1921	})
1922
1923	t.Run("Updates the authorization to create post", func(t *testing.T) {
1924		addCreatePosts := []*model.ChannelModerationPatch{
1925			{
1926				Name: &createPosts,
1927				Roles: &model.ChannelModeratedRolesPatch{
1928					Members: model.NewBool(true),
1929				},
1930			},
1931		}
1932		removeCreatePosts := []*model.ChannelModerationPatch{
1933			{
1934				Name: &createPosts,
1935				Roles: &model.ChannelModeratedRolesPatch{
1936					Members: model.NewBool(false),
1937				},
1938			},
1939		}
1940
1941		mockSession := model.Session{UserId: user.Id}
1942
1943		_, err := th.App.PatchChannelModerationsForChannel(channel.DeepCopy(), addCreatePosts)
1944		require.Nil(t, err)
1945		require.True(t, th.App.SessionHasPermissionToChannel(mockSession, channel.Id, model.PermissionCreatePost))
1946
1947		_, err = th.App.PatchChannelModerationsForChannel(channel.DeepCopy(), removeCreatePosts)
1948		require.Nil(t, err)
1949		require.False(t, th.App.SessionHasPermissionToChannel(mockSession, channel.Id, model.PermissionCreatePost))
1950	})
1951}
1952
1953// TestMarkChannelsAsViewedPanic verifies that returning an error from a.GetUser
1954// does not cause a panic.
1955func TestMarkChannelsAsViewedPanic(t *testing.T) {
1956	th := SetupWithStoreMock(t)
1957	defer th.TearDown()
1958
1959	mockStore := th.App.Srv().Store.(*mocks.Store)
1960	mockUserStore := mocks.UserStore{}
1961	mockUserStore.On("Get", context.Background(), "userID").Return(nil, model.NewAppError("SqlUserStore.Get", "app.user.get.app_error", nil, "user_id=userID", http.StatusInternalServerError))
1962	mockChannelStore := mocks.ChannelStore{}
1963	mockChannelStore.On("Get", "channelID", true).Return(&model.Channel{}, nil)
1964	mockChannelStore.On("GetMember", context.Background(), "channelID", "userID").Return(&model.ChannelMember{
1965		NotifyProps: model.StringMap{
1966			model.PushNotifyProp: model.ChannelNotifyDefault,
1967		}}, nil)
1968	times := map[string]int64{
1969		"userID": 1,
1970	}
1971	mockChannelStore.On("UpdateLastViewedAt", []string{"channelID"}, "userID", false).Return(times, nil)
1972	mockSessionStore := mocks.SessionStore{}
1973	mockOAuthStore := mocks.OAuthStore{}
1974	var err error
1975	th.App.srv.userService, err = users.New(users.ServiceConfig{
1976		UserStore:    &mockUserStore,
1977		SessionStore: &mockSessionStore,
1978		OAuthStore:   &mockOAuthStore,
1979		ConfigFn:     th.App.srv.Config,
1980		LicenseFn:    th.App.srv.License,
1981	})
1982	require.NoError(t, err)
1983	mockPreferenceStore := mocks.PreferenceStore{}
1984	mockPreferenceStore.On("Get", mock.AnythingOfType("string"), mock.AnythingOfType("string"), mock.AnythingOfType("string")).Return(&model.Preference{Value: "test"}, nil)
1985	mockThreadStore := mocks.ThreadStore{}
1986	mockThreadStore.On("MarkAllAsReadInChannels", "userID", []string{"channelID"}).Return(nil)
1987	mockStore.On("Channel").Return(&mockChannelStore)
1988	mockStore.On("Preference").Return(&mockPreferenceStore)
1989	mockStore.On("Thread").Return(&mockThreadStore)
1990
1991	_, appErr := th.App.MarkChannelsAsViewed([]string{"channelID"}, "userID", th.Context.Session().Id, false)
1992	require.Nil(t, appErr)
1993}
1994
1995func TestClearChannelMembersCache(t *testing.T) {
1996	th := SetupWithStoreMock(t)
1997	defer th.TearDown()
1998
1999	mockStore := th.App.Srv().Store.(*mocks.Store)
2000	mockChannelStore := mocks.ChannelStore{}
2001	cms := model.ChannelMembers{}
2002	for i := 0; i < 200; i++ {
2003		cms = append(cms, model.ChannelMember{
2004			ChannelId: "1",
2005		})
2006	}
2007	mockChannelStore.On("GetMembers", "channelID", 0, 100).Return(cms, nil)
2008	mockChannelStore.On("GetMembers", "channelID", 100, 100).Return(model.ChannelMembers{
2009		model.ChannelMember{
2010			ChannelId: "1",
2011		}}, nil)
2012	mockStore.On("Channel").Return(&mockChannelStore)
2013
2014	th.App.ClearChannelMembersCache("channelID")
2015}
2016
2017func TestGetMemberCountsByGroup(t *testing.T) {
2018	th := SetupWithStoreMock(t)
2019	defer th.TearDown()
2020
2021	mockStore := th.App.Srv().Store.(*mocks.Store)
2022	mockChannelStore := mocks.ChannelStore{}
2023	cmc := []*model.ChannelMemberCountByGroup{}
2024	for i := 0; i < 5; i++ {
2025		cmc = append(cmc, &model.ChannelMemberCountByGroup{
2026			GroupId:                     model.NewId(),
2027			ChannelMemberCount:          int64(i),
2028			ChannelMemberTimezonesCount: int64(i),
2029		})
2030	}
2031	mockChannelStore.On("GetMemberCountsByGroup", context.Background(), "channelID", true).Return(cmc, nil)
2032	mockStore.On("Channel").Return(&mockChannelStore)
2033	resp, err := th.App.GetMemberCountsByGroup(context.Background(), "channelID", true)
2034	require.Nil(t, err)
2035	require.ElementsMatch(t, cmc, resp)
2036}
2037
2038func TestViewChannelCollapsedThreadsTurnedOff(t *testing.T) {
2039	th := Setup(t).InitBasic()
2040	defer th.TearDown()
2041
2042	u1 := th.BasicUser
2043	u2 := th.BasicUser2
2044	c1 := th.BasicChannel
2045	th.AddUserToChannel(u2, c1)
2046
2047	// Enable CRT
2048	os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true")
2049	defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS")
2050	th.App.UpdateConfig(func(cfg *model.Config) {
2051		*cfg.ServiceSettings.ThreadAutoFollow = true
2052		*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
2053	})
2054
2055	// Turn off CRT for user
2056	preference := model.Preference{
2057		UserId:   u1.Id,
2058		Category: model.PreferenceCategoryDisplaySettings,
2059		Name:     model.PreferenceNameCollapsedThreadsEnabled,
2060		Value:    "off",
2061	}
2062	var preferences model.Preferences
2063	preferences = append(preferences, preference)
2064	err := th.App.Srv().Store.Preference().Save(preferences)
2065	require.NoError(t, err)
2066
2067	// mention the user in a root post
2068	post1 := &model.Post{
2069		ChannelId: c1.Id,
2070		Message:   "root post @" + u1.Username,
2071		UserId:    u2.Id,
2072	}
2073	rpost1, appErr := th.App.CreatePost(th.Context, post1, c1, false, true)
2074	require.Nil(t, appErr)
2075
2076	// mention the user in a reply post
2077	post2 := &model.Post{
2078		ChannelId: c1.Id,
2079		Message:   "reply post @" + u1.Username,
2080		UserId:    u2.Id,
2081		RootId:    rpost1.Id,
2082	}
2083	_, appErr = th.App.CreatePost(th.Context, post2, c1, false, true)
2084	require.Nil(t, appErr)
2085
2086	// Check we have unread mention in the thread
2087	threads, appErr := th.App.GetThreadsForUser(u1.Id, c1.TeamId, model.GetUserThreadsOpts{})
2088	require.Nil(t, appErr)
2089	found := false
2090	for _, thread := range threads.Threads {
2091		if thread.PostId == rpost1.Id {
2092			require.EqualValues(t, int64(1), thread.UnreadMentions)
2093			found = true
2094			break
2095		}
2096	}
2097	require.Truef(t, found, "did not find created thread in user's threads")
2098
2099	// Mark channel as read from a client that supports CRT
2100	_, appErr = th.App.MarkChannelsAsViewed([]string{c1.Id}, u1.Id, th.Context.Session().Id, true)
2101	require.Nil(t, appErr)
2102
2103	// Thread should be marked as read because CRT has been turned off by user
2104	threads, appErr = th.App.GetThreadsForUser(u1.Id, c1.TeamId, model.GetUserThreadsOpts{})
2105	require.Nil(t, appErr)
2106	found = false
2107	for _, thread := range threads.Threads {
2108		if thread.PostId == rpost1.Id {
2109			require.Zero(t, thread.UnreadMentions)
2110			found = true
2111			break
2112		}
2113	}
2114	require.Truef(t, found, "did not find created thread in user's threads")
2115}
2116
2117func TestMarkChannelAsUnreadFromPostCollapsedThreadsTurnedOff(t *testing.T) {
2118	// Enable CRT
2119	os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true")
2120	defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS")
2121
2122	th := Setup(t).InitBasic()
2123	defer th.TearDown()
2124	th.App.UpdateConfig(func(cfg *model.Config) {
2125		*cfg.ServiceSettings.ThreadAutoFollow = true
2126		*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
2127	})
2128
2129	th.AddUserToChannel(th.BasicUser2, th.BasicChannel)
2130
2131	// Turn off CRT for user
2132	preference := model.Preference{
2133		UserId:   th.BasicUser.Id,
2134		Category: model.PreferenceCategoryDisplaySettings,
2135		Name:     model.PreferenceNameCollapsedThreadsEnabled,
2136		Value:    "off",
2137	}
2138	var preferences model.Preferences
2139	preferences = append(preferences, preference)
2140	err := th.App.Srv().Store.Preference().Save(preferences)
2141	require.NoError(t, err)
2142
2143	// user2: first root mention @user1
2144	//   - user1: hello
2145	//   - user2: mention @u1
2146	//   - user1: another repoy
2147	//   - user2: another mention @u1
2148	// user1: a root post
2149	// user2: Another root mention @u1
2150	user1Mention := " @" + th.BasicUser.Username
2151	rootPost1, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "first root mention" + user1Mention}, th.BasicChannel, false, false)
2152	require.Nil(t, appErr)
2153	_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hello"}, th.BasicChannel, false, false)
2154	require.Nil(t, appErr)
2155	replyPost1, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "mention" + user1Mention}, th.BasicChannel, false, false)
2156	require.Nil(t, appErr)
2157	_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another reply"}, th.BasicChannel, false, false)
2158	require.Nil(t, appErr)
2159	_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost1.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another mention" + user1Mention}, th.BasicChannel, false, false)
2160	require.Nil(t, appErr)
2161	_, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "a root post"}, th.BasicChannel, false, false)
2162	require.Nil(t, appErr)
2163	_, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "another root mention" + user1Mention}, th.BasicChannel, false, false)
2164	require.Nil(t, appErr)
2165
2166	t.Run("Mark reply post as unread", func(t *testing.T) {
2167		_, err := th.App.MarkChannelAsUnreadFromPost(replyPost1.Id, th.BasicUser.Id, true, true)
2168		require.Nil(t, err)
2169		// Get channel unreads
2170		// Easier to reason with ChannelUnread now, than channelUnreadAt from the previous call
2171		channelUnread, err := th.App.GetChannelUnread(th.BasicChannel.Id, th.BasicUser.Id)
2172		require.Nil(t, err)
2173
2174		require.Equal(t, int64(3), channelUnread.MentionCount)
2175		//  MentionCountRoot should be zero for a user that has CRT turned off
2176		require.Equal(t, int64(0), channelUnread.MentionCountRoot)
2177
2178		require.Equal(t, int64(5), channelUnread.MsgCount)
2179		//  MentionCountRoot should be zero for a user that has CRT turned off
2180		require.Equal(t, channelUnread.MsgCountRoot, int64(0))
2181
2182		threadMembership, err := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost1.Id)
2183		require.Nil(t, err)
2184		thread, err := th.App.GetThreadForUser(th.BasicTeam.Id, threadMembership, false)
2185		require.Nil(t, err)
2186		require.Equal(t, int64(2), thread.UnreadMentions)
2187		require.Equal(t, int64(3), thread.UnreadReplies)
2188	})
2189
2190	t.Run("Mark root post as unread", func(t *testing.T) {
2191		_, err := th.App.MarkChannelAsUnreadFromPost(rootPost1.Id, th.BasicUser.Id, true, true)
2192		require.Nil(t, err)
2193		// Get channel unreads
2194		// Easier to reason with ChannelUnread now, than channelUnreadAt from the previous call
2195		channelUnread, err := th.App.GetChannelUnread(th.BasicChannel.Id, th.BasicUser.Id)
2196		require.Nil(t, err)
2197
2198		require.Equal(t, int64(4), channelUnread.MentionCount)
2199		require.Equal(t, int64(2), channelUnread.MentionCountRoot)
2200
2201		require.Equal(t, int64(7), channelUnread.MsgCount)
2202		require.Equal(t, int64(3), channelUnread.MsgCountRoot)
2203	})
2204}
2205
2206func TestMarkUnreadWithThreads(t *testing.T) {
2207	os.Setenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS", "true")
2208	defer os.Unsetenv("MM_FEATUREFLAGS_COLLAPSEDTHREADS")
2209	th := Setup(t).InitBasic()
2210	defer th.TearDown()
2211	th.App.UpdateConfig(func(cfg *model.Config) {
2212		*cfg.ServiceSettings.ThreadAutoFollow = true
2213		*cfg.ServiceSettings.CollapsedThreads = model.CollapsedThreadsDefaultOn
2214	})
2215
2216	t.Run("Follow threads only if specified", func(t *testing.T) {
2217		rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2218		require.Nil(t, appErr)
2219		replyPost, appErr := th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2220		require.Nil(t, appErr)
2221		threads, appErr := th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{})
2222		require.Nil(t, appErr)
2223		require.Zero(t, threads.Total)
2224
2225		_, appErr = th.App.MarkChannelAsUnreadFromPost(replyPost.Id, th.BasicUser.Id, true, true)
2226		require.Nil(t, appErr)
2227
2228		threads, appErr = th.App.GetThreadsForUser(th.BasicUser.Id, th.BasicTeam.Id, model.GetUserThreadsOpts{})
2229		require.Nil(t, appErr)
2230		require.NotZero(t, threads.Total)
2231
2232		threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, replyPost.RootId)
2233		require.Nil(t, appErr)
2234		require.NotNil(t, threadMembership)
2235		assert.True(t, threadMembership.Following)
2236
2237		// Create a new thread
2238		rootPost, appErr = th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi2"}, th.BasicChannel, false, false)
2239		require.Nil(t, appErr)
2240		replyPost, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi2"}, th.BasicChannel, false, false)
2241		require.Nil(t, appErr)
2242
2243		_, appErr = th.App.MarkChannelAsUnreadFromPost(replyPost.Id, th.BasicUser.Id, true, false)
2244		require.Nil(t, appErr)
2245
2246		threadMembership, appErr = th.App.GetThreadMembershipForUser(th.BasicUser.Id, replyPost.RootId)
2247		require.Nil(t, appErr)
2248		require.NotNil(t, threadMembership)
2249		assert.False(t, threadMembership.Following)
2250	})
2251
2252	t.Run("Set unread mentions correctly", func(t *testing.T) {
2253		t.Run("Never followed root post with no replies or mentions", func(t *testing.T) {
2254			rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2255			require.Nil(t, appErr)
2256			_, appErr = th.App.MarkChannelAsUnreadFromPost(rootPost.Id, th.BasicUser.Id, true, true)
2257			require.Nil(t, appErr)
2258
2259			threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
2260			require.Nil(t, appErr)
2261			require.NotNil(t, threadMembership)
2262			assert.Zero(t, threadMembership.UnreadMentions)
2263		})
2264
2265		t.Run("Never followed root post with replies and no mentions", func(t *testing.T) {
2266			rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2267			require.Nil(t, appErr)
2268			_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2269			require.Nil(t, appErr)
2270			_, appErr = th.App.MarkChannelAsUnreadFromPost(rootPost.Id, th.BasicUser.Id, true, true)
2271			require.Nil(t, appErr)
2272
2273			threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
2274			require.Nil(t, appErr)
2275			require.NotNil(t, threadMembership)
2276			assert.Zero(t, threadMembership.UnreadMentions)
2277		})
2278
2279		t.Run("Never followed root post with replies and mentions", func(t *testing.T) {
2280			rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2281			require.Nil(t, appErr)
2282			_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi @" + th.BasicUser.Username}, th.BasicChannel, false, false)
2283			require.Nil(t, appErr)
2284			_, appErr = th.App.MarkChannelAsUnreadFromPost(rootPost.Id, th.BasicUser.Id, true, true)
2285			require.Nil(t, appErr)
2286
2287			threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
2288			require.Nil(t, appErr)
2289			require.NotNil(t, threadMembership)
2290			assert.Equal(t, int64(1), threadMembership.UnreadMentions)
2291		})
2292
2293		t.Run("Previously followed root post with no replies or mentions", func(t *testing.T) {
2294			rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2295			require.Nil(t, appErr)
2296			appErr = th.App.UpdateThreadFollowForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost.Id, true)
2297			require.Nil(t, appErr)
2298			appErr = th.App.UpdateThreadFollowForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost.Id, false)
2299			require.Nil(t, appErr)
2300
2301			_, appErr = th.App.MarkChannelAsUnreadFromPost(rootPost.Id, th.BasicUser.Id, true, true)
2302			require.Nil(t, appErr)
2303
2304			threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
2305			require.Nil(t, appErr)
2306			require.NotNil(t, threadMembership)
2307			assert.Zero(t, threadMembership.UnreadMentions)
2308		})
2309
2310		t.Run("Previously followed root post with replies and no mentions", func(t *testing.T) {
2311			rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2312			require.Nil(t, appErr)
2313			_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2314			require.Nil(t, appErr)
2315			appErr = th.App.UpdateThreadFollowForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost.Id, true)
2316			require.Nil(t, appErr)
2317			appErr = th.App.UpdateThreadFollowForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost.Id, false)
2318			require.Nil(t, appErr)
2319
2320			_, appErr = th.App.MarkChannelAsUnreadFromPost(rootPost.Id, th.BasicUser.Id, true, true)
2321			require.Nil(t, appErr)
2322
2323			threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
2324			require.Nil(t, appErr)
2325			require.NotNil(t, threadMembership)
2326			assert.Zero(t, threadMembership.UnreadMentions)
2327		})
2328
2329		t.Run("Previously followed root post with replies and mentions", func(t *testing.T) {
2330			rootPost, appErr := th.App.CreatePost(th.Context, &model.Post{UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi"}, th.BasicChannel, false, false)
2331			require.Nil(t, appErr)
2332			_, appErr = th.App.CreatePost(th.Context, &model.Post{RootId: rootPost.Id, UserId: th.BasicUser2.Id, CreateAt: model.GetMillis(), ChannelId: th.BasicChannel.Id, Message: "hi @" + th.BasicUser.Username}, th.BasicChannel, false, false)
2333			require.Nil(t, appErr)
2334			appErr = th.App.UpdateThreadFollowForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost.Id, true)
2335			require.Nil(t, appErr)
2336			appErr = th.App.UpdateThreadFollowForUser(th.BasicUser.Id, th.BasicTeam.Id, rootPost.Id, false)
2337			require.Nil(t, appErr)
2338
2339			_, appErr = th.App.MarkChannelAsUnreadFromPost(rootPost.Id, th.BasicUser.Id, true, true)
2340			require.Nil(t, appErr)
2341
2342			threadMembership, appErr := th.App.GetThreadMembershipForUser(th.BasicUser.Id, rootPost.Id)
2343			require.Nil(t, appErr)
2344			require.NotNil(t, threadMembership)
2345			assert.Equal(t, int64(1), threadMembership.UnreadMentions)
2346		})
2347	})
2348}
2349